master
wxy 2024-05-24 14:53:21 +08:00
parent 8aa2ea8399
commit 08b1e36548
15 changed files with 2280 additions and 0 deletions

14
bin/run-auth.bat 100644
View File

@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Auth工程。
echo.
cd %~dp0
cd ../jing-auth/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar jing-auth.jar
cd bin
pause

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.jing</groupId>
<artifactId>jing-common</artifactId>
<version>3.6.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jing-common-core</artifactId>
<description>
jing-common-core核心模块
</description>
<dependencies>
<!-- SpringCloud Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringCloud Loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Spring Context Support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Transmittable ThreadLocal -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<!-- Pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Alibaba Fastjson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- Jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- Apache Lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Commons Io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- Java Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,50 @@
package com.jing.file.config;
import java.io.File;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
*
*
* @author ruoyi
*/
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
/**
*
*/
@Value("${file.path}")
private String localFilePath;
/**
*
*/
@Value("${file.prefix}")
public String localFilePrefix;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 本地文件上传路径 */
registry.addResourceHandler(localFilePrefix + "/**")
.addResourceLocations("file:" + localFilePath + File.separator);
}
/**
*
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路由
registry.addMapping(localFilePrefix + "/**")
// 设置允许跨域请求的域名
.allowedOrigins("*")
// 设置允许的方法
.allowedMethods("GET");
}
}

View File

@ -0,0 +1,148 @@
package com.jing.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
/**
*
*
* @author ruoyi
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RouterVo
{
/**
*
*/
private String name;
/**
*
*/
private String path;
/**
* true
*/
private boolean hidden;
/**
* noRedirect
*/
private String redirect;
/**
*
*/
private String component;
/**
* {"id": 1, "name": "ry"}
*/
private String query;
/**
* children 1--
*/
private Boolean alwaysShow;
/**
*
*/
private MetaVo meta;
/**
*
*/
private List<RouterVo> children;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getPath()
{
return path;
}
public void setPath(String path)
{
this.path = path;
}
public boolean getHidden()
{
return hidden;
}
public void setHidden(boolean hidden)
{
this.hidden = hidden;
}
public String getRedirect()
{
return redirect;
}
public void setRedirect(String redirect)
{
this.redirect = redirect;
}
public String getComponent()
{
return component;
}
public void setComponent(String component)
{
this.component = component;
}
public String getQuery()
{
return query;
}
public void setQuery(String query)
{
this.query = query;
}
public Boolean getAlwaysShow()
{
return alwaysShow;
}
public void setAlwaysShow(Boolean alwaysShow)
{
this.alwaysShow = alwaysShow;
}
public MetaVo getMeta()
{
return meta;
}
public void setMeta(MetaVo meta)
{
this.meta = meta;
}
public List<RouterVo> getChildren()
{
return children;
}
public void setChildren(List<RouterVo> children)
{
this.children = children;
}
}

View File

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@ -0,0 +1,119 @@
import request from '@/utils/request'
// 查询角色列表
export function listRole(query) {
return request({
url: '/system/role/list',
method: 'get',
params: query
})
}
// 查询角色详细
export function getRole(roleId) {
return request({
url: '/system/role/' + roleId,
method: 'get'
})
}
// 新增角色
export function addRole(data) {
return request({
url: '/system/role',
method: 'post',
data: data
})
}
// 修改角色
export function updateRole(data) {
return request({
url: '/system/role',
method: 'put',
data: data
})
}
// 角色数据权限
export function dataScope(data) {
return request({
url: '/system/role/dataScope',
method: 'put',
data: data
})
}
// 角色状态修改
export function changeRoleStatus(roleId, status) {
const data = {
roleId,
status
}
return request({
url: '/system/role/changeStatus',
method: 'put',
data: data
})
}
// 删除角色
export function delRole(roleId) {
return request({
url: '/system/role/' + roleId,
method: 'delete'
})
}
// 查询角色已授权用户列表
export function allocatedUserList(query) {
return request({
url: '/system/role/authUser/allocatedList',
method: 'get',
params: query
})
}
// 查询角色未授权用户列表
export function unallocatedUserList(query) {
return request({
url: '/system/role/authUser/unallocatedList',
method: 'get',
params: query
})
}
// 取消用户授权角色
export function authUserCancel(data) {
return request({
url: '/system/role/authUser/cancel',
method: 'put',
data: data
})
}
// 批量取消用户授权角色
export function authUserCancelAll(data) {
return request({
url: '/system/role/authUser/cancelAll',
method: 'put',
params: data
})
}
// 授权用户选择
export function authUserSelectAll(data) {
return request({
url: '/system/role/authUser/selectAll',
method: 'put',
params: data
})
}
// 根据角色ID查询部门树结构
export function deptTreeSelect(roleId) {
return request({
url: '/system/role/deptTree/' + roleId,
method: 'get'
})
}

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg>

After

Width:  |  Height:  |  Size: 731 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1567417214476" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2266" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M761.503029 2.90619 242.121921 2.90619c-32.405037 0-58.932204 26.060539-58.932204 58.527998l0 902.302287c0 32.156374 26.217105 58.216913 58.932204 58.216913l519.381108 0c32.344662 0 58.591443-26.060539 58.591443-58.216913L820.094472 61.123103C820.094472 28.966729 793.847691 2.90619 761.503029 2.90619M452.878996 61.123103l98.147344 0c6.780427 0 12.31549 5.536087 12.31549 12.253068 0 6.748704-5.535063 12.253068-12.31549 12.253068l-98.147344 0c-6.779404 0-12.345166-5.504364-12.345166-12.253068C440.532807 66.659189 446.099592 61.123103 452.878996 61.123103M501.641583 980.593398c-29.636994 0-53.987588-23.946388-53.987588-53.677527 0-29.356608 24.039509-53.614082 53.987588-53.614082 29.91738 0 53.987588 23.883967 53.987588 53.614082C555.629171 956.647009 531.559986 980.593398 501.641583 980.593398M766.35657 803.142893c0 16.23373-13.186324 29.107945-29.233811 29.107945l-470.618521 0c-16.35755 0-29.325909-13.186324-29.325909-29.107945L237.178329 163.500794c0-16.232706 13.279445-29.138644 29.325909-29.138644l470.246037 0c16.420995 0 29.357632 13.1853 29.357632 29.138644l0 639.642099L766.35657 803.142893zM766.35657 803.142893" p-id="2267"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1579339929870" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1182" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M152 854.856875h325.7146875V237.715625H134.856875v600q0 6.99375 5.0746875 12.0684375T152 854.856875z m737.143125-17.1421875v-600H546.284375v617.1421875H872q6.99375 0 12.0684375-5.07375t5.0746875-12.0684375z m68.5715625-651.429375V837.715625q0 35.3821875-25.16625 60.5484375T872 923.4284375H152q-35.383125 0-60.5484375-25.1653125T66.284375 837.7146875V186.284375q0-35.3821875 25.16625-60.5484375T152 100.5715625h720q35.383125 0 60.5484375 25.1653125t25.16625 60.5484375z" p-id="1183"></path></svg>

After

Width:  |  Height:  |  Size: 873 B

View File

@ -0,0 +1,559 @@
<template>
<div class="popup-result">
<p class="title">最近5次运行时间</p>
<ul class="popup-result-scroll">
<template v-if='isShow'>
<li v-for='item in resultList' :key="item">{{item}}</li>
</template>
<li v-else>...</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
dayRule: '',
dayRuleSup: '',
dateArr: [],
resultList: [],
isShow: false
}
},
name: 'crontab-result',
methods: {
//
expressionChange() {
// -
this.isShow = false;
// [0123456]
let ruleArr = this.$options.propsData.ex.split(' ');
//
let nums = 0;
//
let resultArr = [];
// []
let nTime = new Date();
let nYear = nTime.getFullYear();
let nMonth = nTime.getMonth() + 1;
let nDay = nTime.getDate();
let nHour = nTime.getHours();
let nMin = nTime.getMinutes();
let nSecond = nTime.getSeconds();
// 100
this.getSecondArr(ruleArr[0]);
this.getMinArr(ruleArr[1]);
this.getHourArr(ruleArr[2]);
this.getDayArr(ruleArr[3]);
this.getMonthArr(ruleArr[4]);
this.getWeekArr(ruleArr[5]);
this.getYearArr(ruleArr[6], nYear);
// -便使
let sDate = this.dateArr[0];
let mDate = this.dateArr[1];
let hDate = this.dateArr[2];
let DDate = this.dateArr[3];
let MDate = this.dateArr[4];
let YDate = this.dateArr[5];
//
let sIdx = this.getIndex(sDate, nSecond);
let mIdx = this.getIndex(mDate, nMin);
let hIdx = this.getIndex(hDate, nHour);
let DIdx = this.getIndex(DDate, nDay);
let MIdx = this.getIndex(MDate, nMonth);
let YIdx = this.getIndex(YDate, nYear);
// ()
const resetSecond = function () {
sIdx = 0;
nSecond = sDate[sIdx]
}
const resetMin = function () {
mIdx = 0;
nMin = mDate[mIdx]
resetSecond();
}
const resetHour = function () {
hIdx = 0;
nHour = hDate[hIdx]
resetMin();
}
const resetDay = function () {
DIdx = 0;
nDay = DDate[DIdx]
resetHour();
}
const resetMonth = function () {
MIdx = 0;
nMonth = MDate[MIdx]
resetDay();
}
//
if (nYear !== YDate[YIdx]) {
resetMonth();
}
//
if (nMonth !== MDate[MIdx]) {
resetDay();
}
//
if (nDay !== DDate[DIdx]) {
resetHour();
}
//
if (nHour !== hDate[hIdx]) {
resetMin();
}
//
if (nMin !== mDate[mIdx]) {
resetSecond();
}
//
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
let YY = YDate[Yi];
//
if (nMonth > MDate[MDate.length - 1]) {
resetMonth();
continue;
}
//
goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
// 便
let MM = MDate[Mi];
MM = MM < 10 ? '0' + MM : MM;
//
if (nDay > DDate[DDate.length - 1]) {
resetDay();
if (Mi == MDate.length - 1) {
resetMonth();
continue goYear;
}
continue;
}
//
goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
// 便
let DD = DDate[Di];
let thisDD = DD < 10 ? '0' + DD : DD;
//
if (nHour > hDate[hDate.length - 1]) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMonth();
continue goYear;
}
continue goMonth;
}
continue;
}
//
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && this.dayRule !== 'workDay' && this.dayRule !== 'lastWeek' && this.dayRule !== 'lastDay') {
resetDay();
continue goMonth;
}
//
if (this.dayRule == 'lastDay') {
//
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD--;
thisDD = DD < 10 ? '0' + DD : DD;
}
}
} else if (this.dayRule == 'workDay') {
// 230
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD--;
thisDD = DD < 10 ? '0' + DD : DD;
}
}
// X
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
//
if (thisWeek == 1) {
//
DD++;
thisDD = DD < 10 ? '0' + DD : DD;
//
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD -= 3;
}
} else if (thisWeek == 7) {
// 61
if (this.dayRuleSup !== 1) {
DD--;
} else {
DD += 2;
}
}
} else if (this.dayRule == 'weekDay') {
//
//
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
// dayRuleSup
if (this.dayRuleSup.indexOf(thisWeek) < 0) {
//
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMonth();
continue goYear;
}
continue goMonth;
}
continue;
}
} else if (this.dayRule == 'assWeek') {
//
// 1
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
if (this.dayRuleSup[1] >= thisWeek) {
DD = (this.dayRuleSup[0] - 1) * 7 + this.dayRuleSup[1] - thisWeek + 1;
} else {
DD = this.dayRuleSup[0] * 7 + this.dayRuleSup[1] - thisWeek + 1;
}
} else if (this.dayRule == 'lastWeek') {
//
// 230
if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
DD--;
thisDD = DD < 10 ? '0' + DD : DD;
}
}
//
let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
//
if (this.dayRuleSup < thisWeek) {
DD -= thisWeek - this.dayRuleSup;
} else if (this.dayRuleSup > thisWeek) {
DD -= 7 - (this.dayRuleSup - thisWeek)
}
}
// 1005
DD = DD < 10 ? '0' + DD : DD;
//
goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
//
if (nMin > mDate[mDate.length - 1]) {
resetMin();
if (hi == hDate.length - 1) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMonth();
continue goYear;
}
continue goMonth;
}
continue goDay;
}
continue;
}
// ""
goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];
//
if (nSecond > sDate[sDate.length - 1]) {
resetSecond();
if (mi == mDate.length - 1) {
resetMin();
if (hi == hDate.length - 1) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMonth();
continue goYear;
}
continue goMonth;
}
continue goDay;
}
continue goHour;
}
continue;
}
// ""
goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];
//
if (MM !== '00' && DD !== '00') {
resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
nums++;
}
// 退
if (nums == 5) break goYear;
//
if (si == sDate.length - 1) {
resetSecond();
if (mi == mDate.length - 1) {
resetMin();
if (hi == hDate.length - 1) {
resetHour();
if (Di == DDate.length - 1) {
resetDay();
if (Mi == MDate.length - 1) {
resetMonth();
continue goYear;
}
continue goMonth;
}
continue goDay;
}
continue goHour;
}
continue goMin;
}
} //goSecond
} //goMin
}//goHour
}//goDay
}//goMonth
}
// 100
if (resultArr.length == 0) {
this.resultList = ['没有达到条件的结果!'];
} else {
this.resultList = resultArr;
if (resultArr.length !== 5) {
this.resultList.push('最近100年内只有上面' + resultArr.length + '条结果!')
}
}
// -
this.isShow = true;
},
//
getIndex(arr, value) {
if (value <= arr[0] || value > arr[arr.length - 1]) {
return 0;
} else {
for (let i = 0; i < arr.length - 1; i++) {
if (value > arr[i] && value <= arr[i + 1]) {
return i + 1;
}
}
}
},
// ""
getYearArr(rule, year) {
this.dateArr[5] = this.getOrderArr(year, year + 100);
if (rule !== undefined) {
if (rule.indexOf('-') >= 0) {
this.dateArr[5] = this.getCycleArr(rule, year + 100, false)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[5] = this.getAverageArr(rule, year + 100)
} else if (rule !== '*') {
this.dateArr[5] = this.getAssignArr(rule)
}
}
},
// ""
getMonthArr(rule) {
this.dateArr[4] = this.getOrderArr(1, 12);
if (rule.indexOf('-') >= 0) {
this.dateArr[4] = this.getCycleArr(rule, 12, false)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[4] = this.getAverageArr(rule, 12)
} else if (rule !== '*') {
this.dateArr[4] = this.getAssignArr(rule)
}
},
// ""-
getWeekArr(rule) {
//
if (this.dayRule == '' && this.dayRuleSup == '') {
if (rule.indexOf('-') >= 0) {
this.dayRule = 'weekDay';
this.dayRuleSup = this.getCycleArr(rule, 7, false)
} else if (rule.indexOf('#') >= 0) {
this.dayRule = 'assWeek';
let matchRule = rule.match(/[0-9]{1}/g);
this.dayRuleSup = [Number(matchRule[1]), Number(matchRule[0])];
this.dateArr[3] = [1];
if (this.dayRuleSup[1] == 7) {
this.dayRuleSup[1] = 0;
}
} else if (rule.indexOf('L') >= 0) {
this.dayRule = 'lastWeek';
this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
this.dateArr[3] = [31];
if (this.dayRuleSup == 7) {
this.dayRuleSup = 0;
}
} else if (rule !== '*' && rule !== '?') {
this.dayRule = 'weekDay';
this.dayRuleSup = this.getAssignArr(rule)
}
}
},
// ""-
getDayArr(rule) {
this.dateArr[3] = this.getOrderArr(1, 31);
this.dayRule = '';
this.dayRuleSup = '';
if (rule.indexOf('-') >= 0) {
this.dateArr[3] = this.getCycleArr(rule, 31, false)
this.dayRuleSup = 'null';
} else if (rule.indexOf('/') >= 0) {
this.dateArr[3] = this.getAverageArr(rule, 31)
this.dayRuleSup = 'null';
} else if (rule.indexOf('W') >= 0) {
this.dayRule = 'workDay';
this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
this.dateArr[3] = [this.dayRuleSup];
} else if (rule.indexOf('L') >= 0) {
this.dayRule = 'lastDay';
this.dayRuleSup = 'null';
this.dateArr[3] = [31];
} else if (rule !== '*' && rule !== '?') {
this.dateArr[3] = this.getAssignArr(rule)
this.dayRuleSup = 'null';
} else if (rule == '*') {
this.dayRuleSup = 'null';
}
},
// ""
getHourArr(rule) {
this.dateArr[2] = this.getOrderArr(0, 23);
if (rule.indexOf('-') >= 0) {
this.dateArr[2] = this.getCycleArr(rule, 24, true)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[2] = this.getAverageArr(rule, 23)
} else if (rule !== '*') {
this.dateArr[2] = this.getAssignArr(rule)
}
},
// ""
getMinArr(rule) {
this.dateArr[1] = this.getOrderArr(0, 59);
if (rule.indexOf('-') >= 0) {
this.dateArr[1] = this.getCycleArr(rule, 60, true)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[1] = this.getAverageArr(rule, 59)
} else if (rule !== '*') {
this.dateArr[1] = this.getAssignArr(rule)
}
},
// ""
getSecondArr(rule) {
this.dateArr[0] = this.getOrderArr(0, 59);
if (rule.indexOf('-') >= 0) {
this.dateArr[0] = this.getCycleArr(rule, 60, true)
} else if (rule.indexOf('/') >= 0) {
this.dateArr[0] = this.getAverageArr(rule, 59)
} else if (rule !== '*') {
this.dateArr[0] = this.getAssignArr(rule)
}
},
// min-max
getOrderArr(min, max) {
let arr = [];
for (let i = min; i <= max; i++) {
arr.push(i);
}
return arr;
},
//
getAssignArr(rule) {
let arr = [];
let assiginArr = rule.split(',');
for (let i = 0; i < assiginArr.length; i++) {
arr[i] = Number(assiginArr[i])
}
arr.sort(this.compare)
return arr;
},
//
getAverageArr(rule, limit) {
let arr = [];
let agArr = rule.split('/');
let min = Number(agArr[0]);
let step = Number(agArr[1]);
while (min <= limit) {
arr.push(min);
min += step;
}
return arr;
},
//
getCycleArr(rule, limit, status) {
// status--01
let arr = [];
let cycleArr = rule.split('-');
let min = Number(cycleArr[0]);
let max = Number(cycleArr[1]);
if (min > max) {
max += limit;
}
for (let i = min; i <= max; i++) {
let add = 0;
if (status == false && i % limit == 0) {
add = limit;
}
arr.push(Math.round(i % limit + add))
}
arr.sort(this.compare)
return arr;
},
// Array.sort
compare(value1, value2) {
if (value2 - value1 > 0) {
return -1;
} else {
return 1;
}
},
// 2017-9-19 18:04:33
formatDate(value, type) {
//
let time = typeof value == 'number' ? new Date(value) : value;
let Y = time.getFullYear();
let M = time.getMonth() + 1;
let D = time.getDate();
let h = time.getHours();
let m = time.getMinutes();
let s = time.getSeconds();
let week = time.getDay();
// type
if (type == undefined) {
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
} else if (type == 'week') {
// quartz 1
return week + 1;
}
},
//
checkDate(value) {
let time = new Date(value);
let format = this.formatDate(time)
return value === format;
}
},
watch: {
'ex': 'expressionChange'
},
props: ['ex'],
mounted: function () {
//
this.expressionChange();
}
}
</script>

View File

@ -0,0 +1,58 @@
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isRelogin } from '@/utils/request'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register']
router.beforeEach((to, from, next) => {
NProgress.start()
if (getToken()) {
to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
if (store.getters.roles.length === 0) {
isRelogin.show = true
// 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(() => {
isRelogin.show = false
store.dispatch('GenerateRoutes').then(accessRoutes => {
// 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
})
}).catch(err => {
store.dispatch('LogOut').then(() => {
Message.error(err)
next({ path: '/' })
})
})
} else {
next()
}
}
} else {
// 没有token
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
} else {
next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页
NProgress.done()
}
}
})
router.afterEach(() => {
NProgress.done()
})

View File

@ -0,0 +1,137 @@
import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink'
const permission = {
state: {
routes: [],
addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: []
},
mutations: {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
},
SET_DEFAULT_ROUTES: (state, routes) => {
state.defaultRoutes = constantRoutes.concat(routes)
},
SET_TOPBAR_ROUTES: (state, routes) => {
state.topbarRouters = routes
},
SET_SIDEBAR_ROUTERS: (state, routes) => {
state.sidebarRouters = routes
},
},
actions: {
// 生成路由
GenerateRoutes({ commit }) {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
router.addRoutes(asyncRoutes);
commit('SET_ROUTES', rewriteRoutes)
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
commit('SET_TOPBAR_ROUTES', sidebarRoutes)
resolve(rewriteRoutes)
})
})
}
}
}
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => {
if (type && route.children) {
route.children = filterChildren(route.children)
}
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else if (route.component === 'ParentView') {
route.component = ParentView
} else if (route.component === 'InnerLink') {
route.component = InnerLink
} else {
route.component = loadView(route.component)
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type)
} else {
delete route['children']
delete route['redirect']
}
return true
})
}
function filterChildren(childrenMap, lastRouter = false) {
var children = []
childrenMap.forEach((el, index) => {
if (el.children && el.children.length) {
if (el.component === 'ParentView' && !lastRouter) {
el.children.forEach(c => {
c.path = el.path + '/' + c.path
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c))
return
}
children.push(c)
})
return
}
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path
if (el.children && el.children.length) {
children = children.concat(filterChildren(el.children, el))
return
}
}
children = children.concat(el)
})
return children
}
// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
const res = []
routes.forEach(route => {
if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) {
res.push(route)
}
} else if (route.roles) {
if (auth.hasRoleOr(route.roles)) {
res.push(route)
}
}
})
return res
}
export const loadView = (view) => {
if (process.env.NODE_ENV === 'development') {
return (resolve) => require([`@/views/${view}`], resolve)
} else {
// 使用 import 实现生产环境的路由懒加载
return () => import(`@/views/${view}`)
}
}
export default permission

View File

@ -0,0 +1,47 @@
import store from '@/store'
/**
* 字符权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkPermi(value) {
if (value && value instanceof Array && value.length > 0) {
const permissions = store.getters && store.getters.permissions
const permissionDatas = value
const all_permission = "*:*:*";
const hasPermission = permissions.some(permission => {
return all_permission === permission || permissionDatas.includes(permission)
})
return hasPermission;
} else {
console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
return false
}
}
/**
* 角色权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkRole(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const super_admin = "admin";
const hasRole = roles.some(role => {
return super_admin === role || permissionRoles.includes(role)
})
return hasRole;
} else {
console.error(`need roles! Like checkRole="['admin','editor']"`)
return false
}
}

View File

@ -0,0 +1,79 @@
<template>
<div :class="className" :style="{height:height,width:width}" />
</template>
<script>
import * as echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
left: 'center',
bottom: '10',
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts']
},
series: [
{
name: 'WEEKLY WRITE ARTICLES',
type: 'pie',
roseType: 'radius',
radius: [15, 95],
center: ['50%', '38%'],
data: [
{ value: 320, name: 'Industries' },
{ value: 240, name: 'Technology' },
{ value: 149, name: 'Forex' },
{ value: 100, name: 'Gold' },
{ value: 59, name: 'Forecasts' }
],
animationEasing: 'cubicInOut',
animationDuration: 2600
}
]
})
}
}
}
</script>

View File

@ -0,0 +1,946 @@
<template>
<div class="right-board">
<el-tabs v-model="currentTab" class="center-tabs">
<el-tab-pane label="组件属性" name="field" />
<el-tab-pane label="表单属性" name="form" />
</el-tabs>
<div class="field-box">
<a class="document-link" target="_blank" :href="documentLink" title="查看组件文档">
<i class="el-icon-link" />
</a>
<el-scrollbar class="right-scrollbar">
<!-- 组件属性 -->
<el-form v-show="currentTab==='field' && showField" size="small" label-width="90px">
<el-form-item v-if="activeData.changeTag" label="组件类型">
<el-select
v-model="activeData.tagIcon"
placeholder="请选择组件类型"
:style="{width: '100%'}"
@change="tagChange"
>
<el-option-group v-for="group in tagList" :key="group.label" :label="group.label">
<el-option
v-for="item in group.options"
:key="item.label"
:label="item.label"
:value="item.tagIcon"
>
<svg-icon class="node-icon" :icon-class="item.tagIcon" />
<span> {{ item.label }}</span>
</el-option>
</el-option-group>
</el-select>
</el-form-item>
<el-form-item v-if="activeData.vModel!==undefined" label="字段名">
<el-input v-model="activeData.vModel" placeholder="请输入字段名v-model" />
</el-form-item>
<el-form-item v-if="activeData.componentName!==undefined" label="组件名">
{{ activeData.componentName }}
</el-form-item>
<el-form-item v-if="activeData.label!==undefined" label="标题">
<el-input v-model="activeData.label" placeholder="请输入标题" />
</el-form-item>
<el-form-item v-if="activeData.placeholder!==undefined" label="占位提示">
<el-input v-model="activeData.placeholder" placeholder="请输入占位提示" />
</el-form-item>
<el-form-item v-if="activeData['start-placeholder']!==undefined" label="开始占位">
<el-input v-model="activeData['start-placeholder']" placeholder="请输入占位提示" />
</el-form-item>
<el-form-item v-if="activeData['end-placeholder']!==undefined" label="结束占位">
<el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" />
</el-form-item>
<el-form-item v-if="activeData.span!==undefined" label="表单栅格">
<el-slider v-model="activeData.span" :max="24" :min="1" :marks="{12:''}" @change="spanChange" />
</el-form-item>
<el-form-item v-if="activeData.layout==='rowFormItem'" label="栅格间隔">
<el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" />
</el-form-item>
<el-form-item v-if="activeData.layout==='rowFormItem'" label="布局模式">
<el-radio-group v-model="activeData.type">
<el-radio-button label="default" />
<el-radio-button label="flex" />
</el-radio-group>
</el-form-item>
<el-form-item v-if="activeData.justify!==undefined&&activeData.type==='flex'" label="水平排列">
<el-select v-model="activeData.justify" placeholder="请选择水平排列" :style="{width: '100%'}">
<el-option
v-for="(item, index) in justifyOptions"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="activeData.align!==undefined&&activeData.type==='flex'" label="垂直排列">
<el-radio-group v-model="activeData.align">
<el-radio-button label="top" />
<el-radio-button label="middle" />
<el-radio-button label="bottom" />
</el-radio-group>
</el-form-item>
<el-form-item v-if="activeData.labelWidth!==undefined" label="标签宽度">
<el-input v-model.number="activeData.labelWidth" type="number" placeholder="请输入标签宽度" />
</el-form-item>
<el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度">
<el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable />
</el-form-item>
<el-form-item v-if="activeData.vModel!==undefined" label="默认值">
<el-input
:value="setDefaultValue(activeData.defaultValue)"
placeholder="请输入默认值"
@input="onDefaultValueInput"
/>
</el-form-item>
<el-form-item v-if="activeData.tag==='el-checkbox-group'" label="至少应选">
<el-input-number
:value="activeData.min"
:min="0"
placeholder="至少应选"
@input="$set(activeData, 'min', $event?$event:undefined)"
/>
</el-form-item>
<el-form-item v-if="activeData.tag==='el-checkbox-group'" label="最多可选">
<el-input-number
:value="activeData.max"
:min="0"
placeholder="最多可选"
@input="$set(activeData, 'max', $event?$event:undefined)"
/>
</el-form-item>
<el-form-item v-if="activeData.prepend!==undefined" label="前缀">
<el-input v-model="activeData.prepend" placeholder="请输入前缀" />
</el-form-item>
<el-form-item v-if="activeData.append!==undefined" label="后缀">
<el-input v-model="activeData.append" placeholder="请输入后缀" />
</el-form-item>
<el-form-item v-if="activeData['prefix-icon']!==undefined" label="前图标">
<el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称">
<el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('prefix-icon')">
选择
</el-button>
</el-input>
</el-form-item>
<el-form-item v-if="activeData['suffix-icon'] !== undefined" label="后图标">
<el-input v-model="activeData['suffix-icon']" placeholder="请输入后图标名称">
<el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('suffix-icon')">
选择
</el-button>
</el-input>
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="选项分隔符">
<el-input v-model="activeData.separator" placeholder="请输入选项分隔符" />
</el-form-item>
<el-form-item v-if="activeData.autosize !== undefined" label="最小行数">
<el-input-number v-model="activeData.autosize.minRows" :min="1" placeholder="最小行数" />
</el-form-item>
<el-form-item v-if="activeData.autosize !== undefined" label="最大行数">
<el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" />
</el-form-item>
<el-form-item v-if="activeData.min !== undefined" label="最小值">
<el-input-number v-model="activeData.min" placeholder="最小值" />
</el-form-item>
<el-form-item v-if="activeData.max !== undefined" label="最大值">
<el-input-number v-model="activeData.max" placeholder="最大值" />
</el-form-item>
<el-form-item v-if="activeData.step !== undefined" label="步长">
<el-input-number v-model="activeData.step" placeholder="步数" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-input-number'" label="精度">
<el-input-number v-model="activeData.precision" :min="0" placeholder="精度" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-input-number'" label="按钮位置">
<el-radio-group v-model="activeData['controls-position']">
<el-radio-button label="">
默认
</el-radio-button>
<el-radio-button label="right">
右侧
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="activeData.maxlength !== undefined" label="最多输入">
<el-input v-model="activeData.maxlength" placeholder="请输入字符长度">
<template slot="append">
个字符
</template>
</el-input>
</el-form-item>
<el-form-item v-if="activeData['active-text'] !== undefined" label="开启提示">
<el-input v-model="activeData['active-text']" placeholder="请输入开启提示" />
</el-form-item>
<el-form-item v-if="activeData['inactive-text'] !== undefined" label="关闭提示">
<el-input v-model="activeData['inactive-text']" placeholder="请输入关闭提示" />
</el-form-item>
<el-form-item v-if="activeData['active-value'] !== undefined" label="开启值">
<el-input
:value="setDefaultValue(activeData['active-value'])"
placeholder="请输入开启值"
@input="onSwitchValueInput($event, 'active-value')"
/>
</el-form-item>
<el-form-item v-if="activeData['inactive-value'] !== undefined" label="关闭值">
<el-input
:value="setDefaultValue(activeData['inactive-value'])"
placeholder="请输入关闭值"
@input="onSwitchValueInput($event, 'inactive-value')"
/>
</el-form-item>
<el-form-item
v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag"
label="时间类型"
>
<el-select
v-model="activeData.type"
placeholder="请选择时间类型"
:style="{ width: '100%' }"
@change="dateTypeChange"
>
<el-option
v-for="(item, index) in dateOptions"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="activeData.name !== undefined" label="文件字段名">
<el-input v-model="activeData.name" placeholder="请输入上传文件字段名" />
</el-form-item>
<el-form-item v-if="activeData.accept !== undefined" label="文件类型">
<el-select
v-model="activeData.accept"
placeholder="请选择文件类型"
:style="{ width: '100%' }"
clearable
>
<el-option label="图片" value="image/*" />
<el-option label="视频" value="video/*" />
<el-option label="音频" value="audio/*" />
<el-option label="excel" value=".xls,.xlsx" />
<el-option label="word" value=".doc,.docx" />
<el-option label="pdf" value=".pdf" />
<el-option label="txt" value=".txt" />
</el-select>
</el-form-item>
<el-form-item v-if="activeData.fileSize !== undefined" label="文件大小">
<el-input v-model.number="activeData.fileSize" placeholder="请输入文件大小">
<el-select slot="append" v-model="activeData.sizeUnit" :style="{ width: '66px' }">
<el-option label="KB" value="KB" />
<el-option label="MB" value="MB" />
<el-option label="GB" value="GB" />
</el-select>
</el-input>
</el-form-item>
<el-form-item v-if="activeData.action !== undefined" label="上传地址">
<el-input v-model="activeData.action" placeholder="请输入上传地址" clearable />
</el-form-item>
<el-form-item v-if="activeData['list-type'] !== undefined" label="列表类型">
<el-radio-group v-model="activeData['list-type']" size="small">
<el-radio-button label="text">
text
</el-radio-button>
<el-radio-button label="picture">
picture
</el-radio-button>
<el-radio-button label="picture-card">
picture-card
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="activeData.buttonText !== undefined"
v-show="'picture-card' !== activeData['list-type']"
label="按钮文字"
>
<el-input v-model="activeData.buttonText" placeholder="请输入按钮文字" />
</el-form-item>
<el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符">
<el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" />
</el-form-item>
<el-form-item v-if="activeData['picker-options'] !== undefined" label="时间段">
<el-input
v-model="activeData['picker-options'].selectableRange"
placeholder="请输入时间段"
/>
</el-form-item>
<el-form-item v-if="activeData.format !== undefined" label="时间格式">
<el-input
:value="activeData.format"
placeholder="请输入时间格式"
@input="setTimeValue($event)"
/>
</el-form-item>
<template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1">
<el-divider>选项</el-divider>
<draggable
:list="activeData.options"
:animation="340"
group="selectItem"
handle=".option-drag"
>
<div v-for="(item, index) in activeData.options" :key="index" class="select-item">
<div class="select-line-icon option-drag">
<i class="el-icon-s-operation" />
</div>
<el-input v-model="item.label" placeholder="选项名" size="small" />
<el-input
placeholder="选项值"
size="small"
:value="item.value"
@input="setOptionValue(item, $event)"
/>
<div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)">
<i class="el-icon-remove-outline" />
</div>
</div>
</draggable>
<div style="margin-left: 20px;">
<el-button
style="padding-bottom: 0"
icon="el-icon-circle-plus-outline"
type="text"
@click="addSelectItem"
>
添加选项
</el-button>
</div>
<el-divider />
</template>
<template v-if="['el-cascader'].indexOf(activeData.tag) > -1">
<el-divider>选项</el-divider>
<el-form-item label="数据类型">
<el-radio-group v-model="activeData.dataType" size="small">
<el-radio-button label="dynamic">
动态数据
</el-radio-button>
<el-radio-button label="static">
静态数据
</el-radio-button>
</el-radio-group>
</el-form-item>
<template v-if="activeData.dataType === 'dynamic'">
<el-form-item label="标签键名">
<el-input v-model="activeData.labelKey" placeholder="请输入标签键名" />
</el-form-item>
<el-form-item label="值键名">
<el-input v-model="activeData.valueKey" placeholder="请输入值键名" />
</el-form-item>
<el-form-item label="子级键名">
<el-input v-model="activeData.childrenKey" placeholder="请输入子级键名" />
</el-form-item>
</template>
<el-tree
v-if="activeData.dataType === 'static'"
draggable
:data="activeData.options"
node-key="id"
:expand-on-click-node="false"
:render-content="renderContent"
/>
<div v-if="activeData.dataType === 'static'" style="margin-left: 20px">
<el-button
style="padding-bottom: 0"
icon="el-icon-circle-plus-outline"
type="text"
@click="addTreeItem"
>
添加父级
</el-button>
</div>
<el-divider />
</template>
<el-form-item v-if="activeData.optionType !== undefined" label="选项样式">
<el-radio-group v-model="activeData.optionType">
<el-radio-button label="default">
默认
</el-radio-button>
<el-radio-button label="button">
按钮
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="activeData['active-color'] !== undefined" label="开启颜色">
<el-color-picker v-model="activeData['active-color']" />
</el-form-item>
<el-form-item v-if="activeData['inactive-color'] !== undefined" label="关闭颜色">
<el-color-picker v-model="activeData['inactive-color']" />
</el-form-item>
<el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选">
<el-switch v-model="activeData['allow-half']" />
</el-form-item>
<el-form-item v-if="activeData['show-text'] !== undefined" label="辅助文字">
<el-switch v-model="activeData['show-text']" @change="rateTextChange" />
</el-form-item>
<el-form-item v-if="activeData['show-score'] !== undefined" label="显示分数">
<el-switch v-model="activeData['show-score']" @change="rateScoreChange" />
</el-form-item>
<el-form-item v-if="activeData['show-stops'] !== undefined" label="显示间断点">
<el-switch v-model="activeData['show-stops']" />
</el-form-item>
<el-form-item v-if="activeData.range !== undefined" label="范围选择">
<el-switch v-model="activeData.range" @change="rangeChange" />
</el-form-item>
<el-form-item
v-if="activeData.border !== undefined && activeData.optionType === 'default'"
label="是否带边框"
>
<el-switch v-model="activeData.border" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-color-picker'" label="颜色格式">
<el-select
v-model="activeData['color-format']"
placeholder="请选择颜色格式"
:style="{ width: '100%' }"
@change="colorFormatChange"
>
<el-option
v-for="(item, index) in colorFormatOptions"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="activeData.size !== undefined &&
(activeData.optionType === 'button' ||
activeData.border ||
activeData.tag === 'el-color-picker')"
label="选项尺寸"
>
<el-radio-group v-model="activeData.size">
<el-radio-button label="medium">
中等
</el-radio-button>
<el-radio-button label="small">
较小
</el-radio-button>
<el-radio-button label="mini">
迷你
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计">
<el-switch v-model="activeData['show-word-limit']" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-input-number'" label="严格步数">
<el-switch v-model="activeData['step-strictly']" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="是否多选">
<el-switch v-model="activeData.props.props.multiple" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="展示全路径">
<el-switch v-model="activeData['show-all-levels']" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-cascader'" label="可否筛选">
<el-switch v-model="activeData.filterable" />
</el-form-item>
<el-form-item v-if="activeData.clearable !== undefined" label="能否清空">
<el-switch v-model="activeData.clearable" />
</el-form-item>
<el-form-item v-if="activeData.showTip !== undefined" label="显示提示">
<el-switch v-model="activeData.showTip" />
</el-form-item>
<el-form-item v-if="activeData.multiple !== undefined" label="多选文件">
<el-switch v-model="activeData.multiple" />
</el-form-item>
<el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传">
<el-switch v-model="activeData['auto-upload']" />
</el-form-item>
<el-form-item v-if="activeData.readonly !== undefined" label="是否只读">
<el-switch v-model="activeData.readonly" />
</el-form-item>
<el-form-item v-if="activeData.disabled !== undefined" label="是否禁用">
<el-switch v-model="activeData.disabled" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-select'" label="是否可搜索">
<el-switch v-model="activeData.filterable" />
</el-form-item>
<el-form-item v-if="activeData.tag === 'el-select'" label="是否多选">
<el-switch v-model="activeData.multiple" @change="multipleChange" />
</el-form-item>
<el-form-item v-if="activeData.required !== undefined" label="是否必填">
<el-switch v-model="activeData.required" />
</el-form-item>
<template v-if="activeData.layoutTree">
<el-divider>布局结构树</el-divider>
<el-tree
:data="[activeData]"
:props="layoutTreeProps"
node-key="renderKey"
default-expand-all
draggable
>
<span slot-scope="{ node, data }">
<span class="node-label">
<svg-icon class="node-icon" :icon-class="data.tagIcon" />
{{ node.label }}
</span>
</span>
</el-tree>
</template>
<template v-if="activeData.layout === 'colFormItem' && activeData.tag !== 'el-button'">
<el-divider>正则校验</el-divider>
<div
v-for="(item, index) in activeData.regList"
:key="index"
class="reg-item"
>
<span class="close-btn" @click="activeData.regList.splice(index, 1)">
<i class="el-icon-close" />
</span>
<el-form-item label="表达式">
<el-input v-model="item.pattern" placeholder="请输入正则" />
</el-form-item>
<el-form-item label="错误提示" style="margin-bottom:0">
<el-input v-model="item.message" placeholder="请输入错误提示" />
</el-form-item>
</div>
<div style="margin-left: 20px">
<el-button icon="el-icon-circle-plus-outline" type="text" @click="addReg">
添加规则
</el-button>
</div>
</template>
</el-form>
<!-- 表单属性 -->
<el-form v-show="currentTab === 'form'" size="small" label-width="90px">
<el-form-item label="表单名">
<el-input v-model="formConf.formRef" placeholder="请输入表单名ref" />
</el-form-item>
<el-form-item label="表单模型">
<el-input v-model="formConf.formModel" placeholder="请输入数据模型" />
</el-form-item>
<el-form-item label="校验模型">
<el-input v-model="formConf.formRules" placeholder="请输入校验模型" />
</el-form-item>
<el-form-item label="表单尺寸">
<el-radio-group v-model="formConf.size">
<el-radio-button label="medium">
中等
</el-radio-button>
<el-radio-button label="small">
较小
</el-radio-button>
<el-radio-button label="mini">
迷你
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="标签对齐">
<el-radio-group v-model="formConf.labelPosition">
<el-radio-button label="left">
左对齐
</el-radio-button>
<el-radio-button label="right">
右对齐
</el-radio-button>
<el-radio-button label="top">
顶部对齐
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="标签宽度">
<el-input-number v-model="formConf.labelWidth" placeholder="标签宽度" />
</el-form-item>
<el-form-item label="栅格间隔">
<el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" />
</el-form-item>
<el-form-item label="禁用表单">
<el-switch v-model="formConf.disabled" />
</el-form-item>
<el-form-item label="表单按钮">
<el-switch v-model="formConf.formBtns" />
</el-form-item>
<el-form-item label="显示未选中组件边框">
<el-switch v-model="formConf.unFocusedComponentBorder" />
</el-form-item>
</el-form>
</el-scrollbar>
</div>
<treeNode-dialog :visible.sync="dialogVisible" title="添加选项" @commit="addNode" />
<icons-dialog :visible.sync="iconsVisible" :current="activeData[currentIconModel]" @select="setIcon" />
</div>
</template>
<script>
import { isArray } from 'util'
import draggable from 'vuedraggable'
import TreeNodeDialog from './TreeNodeDialog'
import { isNumberStr } from '@/utils/index'
import IconsDialog from './IconsDialog'
import {
inputComponents,
selectComponents,
layoutComponents
} from '@/utils/generator/config'
const dateTimeFormat = {
date: 'yyyy-MM-dd',
week: 'yyyy 第 WW 周',
month: 'yyyy-MM',
year: 'yyyy',
datetime: 'yyyy-MM-dd HH:mm:ss',
daterange: 'yyyy-MM-dd',
monthrange: 'yyyy-MM',
datetimerange: 'yyyy-MM-dd HH:mm:ss'
}
export default {
components: {
draggable,
TreeNodeDialog,
IconsDialog
},
props: ['showField', 'activeData', 'formConf'],
data() {
return {
currentTab: 'field',
currentNode: null,
dialogVisible: false,
iconsVisible: false,
currentIconModel: null,
dateTypeOptions: [
{
label: '日(date)',
value: 'date'
},
{
label: '周(week)',
value: 'week'
},
{
label: '月(month)',
value: 'month'
},
{
label: '年(year)',
value: 'year'
},
{
label: '日期时间(datetime)',
value: 'datetime'
}
],
dateRangeTypeOptions: [
{
label: '日期范围(daterange)',
value: 'daterange'
},
{
label: '月范围(monthrange)',
value: 'monthrange'
},
{
label: '日期时间范围(datetimerange)',
value: 'datetimerange'
}
],
colorFormatOptions: [
{
label: 'hex',
value: 'hex'
},
{
label: 'rgb',
value: 'rgb'
},
{
label: 'rgba',
value: 'rgba'
},
{
label: 'hsv',
value: 'hsv'
},
{
label: 'hsl',
value: 'hsl'
}
],
justifyOptions: [
{
label: 'start',
value: 'start'
},
{
label: 'end',
value: 'end'
},
{
label: 'center',
value: 'center'
},
{
label: 'space-around',
value: 'space-around'
},
{
label: 'space-between',
value: 'space-between'
}
],
layoutTreeProps: {
label(data, node) {
return data.componentName || `${data.label}: ${data.vModel}`
}
}
}
},
computed: {
documentLink() {
return (
this.activeData.document
|| 'https://element.eleme.cn/#/zh-CN/component/installation'
)
},
dateOptions() {
if (
this.activeData.type !== undefined
&& this.activeData.tag === 'el-date-picker'
) {
if (this.activeData['start-placeholder'] === undefined) {
return this.dateTypeOptions
}
return this.dateRangeTypeOptions
}
return []
},
tagList() {
return [
{
label: '输入型组件',
options: inputComponents
},
{
label: '选择型组件',
options: selectComponents
}
]
}
},
methods: {
addReg() {
this.activeData.regList.push({
pattern: '',
message: ''
})
},
addSelectItem() {
this.activeData.options.push({
label: '',
value: ''
})
},
addTreeItem() {
++this.idGlobal
this.dialogVisible = true
this.currentNode = this.activeData.options
},
renderContent(h, { node, data, store }) {
return (
<div class="custom-tree-node">
<span>{node.label}</span>
<span class="node-operation">
<i on-click={() => this.append(data)}
class="el-icon-plus"
title="添加"
></i>
<i on-click={() => this.remove(node, data)}
class="el-icon-delete"
title="删除"
></i>
</span>
</div>
)
},
append(data) {
if (!data.children) {
this.$set(data, 'children', [])
}
this.dialogVisible = true
this.currentNode = data.children
},
remove(node, data) {
const { parent } = node
const children = parent.data.children || parent.data
const index = children.findIndex(d => d.id === data.id)
children.splice(index, 1)
},
addNode(data) {
this.currentNode.push(data)
},
setOptionValue(item, val) {
item.value = isNumberStr(val) ? +val : val
},
setDefaultValue(val) {
if (Array.isArray(val)) {
return val.join(',')
}
if (['string', 'number'].indexOf(val) > -1) {
return val
}
if (typeof val === 'boolean') {
return `${val}`
}
return val
},
onDefaultValueInput(str) {
if (isArray(this.activeData.defaultValue)) {
//
this.$set(
this.activeData,
'defaultValue',
str.split(',').map(val => (isNumberStr(val) ? +val : val))
)
} else if (['true', 'false'].indexOf(str) > -1) {
//
this.$set(this.activeData, 'defaultValue', JSON.parse(str))
} else {
//
this.$set(
this.activeData,
'defaultValue',
isNumberStr(str) ? +str : str
)
}
},
onSwitchValueInput(val, name) {
if (['true', 'false'].indexOf(val) > -1) {
this.$set(this.activeData, name, JSON.parse(val))
} else {
this.$set(this.activeData, name, isNumberStr(val) ? +val : val)
}
},
setTimeValue(val, type) {
const valueFormat = type === 'week' ? dateTimeFormat.date : val
this.$set(this.activeData, 'defaultValue', null)
this.$set(this.activeData, 'value-format', valueFormat)
this.$set(this.activeData, 'format', val)
},
spanChange(val) {
this.formConf.span = val
},
multipleChange(val) {
this.$set(this.activeData, 'defaultValue', val ? [] : '')
},
dateTypeChange(val) {
this.setTimeValue(dateTimeFormat[val], val)
},
rangeChange(val) {
this.$set(
this.activeData,
'defaultValue',
val ? [this.activeData.min, this.activeData.max] : this.activeData.min
)
},
rateTextChange(val) {
if (val) this.activeData['show-score'] = false
},
rateScoreChange(val) {
if (val) this.activeData['show-text'] = false
},
colorFormatChange(val) {
this.activeData.defaultValue = null
this.activeData['show-alpha'] = val.indexOf('a') > -1
this.activeData.renderKey = +new Date() // renderKey,
},
openIconsDialog(model) {
this.iconsVisible = true
this.currentIconModel = model
},
setIcon(val) {
this.activeData[this.currentIconModel] = val
},
tagChange(tagIcon) {
let target = inputComponents.find(item => item.tagIcon === tagIcon)
if (!target) target = selectComponents.find(item => item.tagIcon === tagIcon)
this.$emit('tag-change', target)
}
}
}
</script>
<style lang="scss" scoped>
.right-board {
width: 350px;
position: absolute;
right: 0;
top: 0;
padding-top: 3px;
.field-box {
position: relative;
height: calc(100vh - 42px);
box-sizing: border-box;
overflow: hidden;
}
.el-scrollbar {
height: 100%;
}
}
.select-item {
display: flex;
border: 1px dashed #fff;
box-sizing: border-box;
& .close-btn {
cursor: pointer;
color: #f56c6c;
}
& .el-input + .el-input {
margin-left: 4px;
}
}
.select-item + .select-item {
margin-top: 4px;
}
.select-item.sortable-chosen {
border: 1px dashed #409eff;
}
.select-line-icon {
line-height: 32px;
font-size: 22px;
padding: 0 4px;
color: #777;
}
.option-drag {
cursor: move;
}
.time-range {
.el-date-editor {
width: 227px;
}
::v-deep .el-icon-time {
display: none;
}
}
.document-link {
position: absolute;
display: block;
width: 26px;
height: 26px;
top: 0;
left: 0;
cursor: pointer;
background: #409eff;
z-index: 1;
border-radius: 0 0 6px 0;
text-align: center;
line-height: 26px;
color: #fff;
font-size: 18px;
}
.node-label{
font-size: 14px;
}
.node-icon{
color: #bebfc3;
}
</style>