初始化
parent
e2e0641bf6
commit
5f88e0bc34
|
@ -0,0 +1,82 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {mergeRecursive} from "@/utils/muyu";
|
||||||
|
import DictMeta from './DictMeta'
|
||||||
|
import DictData from './DictData'
|
||||||
|
|
||||||
|
const DEFAULT_DICT_OPTIONS = {
|
||||||
|
types: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc 字典
|
||||||
|
* @property {Object} label 标签对象,内部属性名为字典类型名称
|
||||||
|
* @property {Object} dict 字段数组,内部属性名为字典类型名称
|
||||||
|
* @property {Array.<DictMeta>} _dictMetas 字典元数据数组
|
||||||
|
*/
|
||||||
|
export default class Dict {
|
||||||
|
constructor() {
|
||||||
|
this.owner = null
|
||||||
|
this.label = {}
|
||||||
|
this.type = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(options) {
|
||||||
|
if (options instanceof Array) {
|
||||||
|
options = {types: options}
|
||||||
|
}
|
||||||
|
const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)
|
||||||
|
if (opts.types === undefined) {
|
||||||
|
throw new Error('need dict types')
|
||||||
|
}
|
||||||
|
const ps = []
|
||||||
|
this._dictMetas = opts.types.map(t => DictMeta.parse(t))
|
||||||
|
this._dictMetas.forEach(dictMeta => {
|
||||||
|
const type = dictMeta.type
|
||||||
|
Vue.set(this.label, type, {})
|
||||||
|
Vue.set(this.type, type, [])
|
||||||
|
if (dictMeta.lazy) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ps.push(loadDict(this, dictMeta))
|
||||||
|
})
|
||||||
|
return Promise.all(ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新加载字典
|
||||||
|
* @param {String} type 字典类型
|
||||||
|
*/
|
||||||
|
reloadDict(type) {
|
||||||
|
const dictMeta = this._dictMetas.find(e => e.type === type)
|
||||||
|
if (dictMeta === undefined) {
|
||||||
|
return Promise.reject(`the dict meta of ${type} was not found`)
|
||||||
|
}
|
||||||
|
return loadDict(this, dictMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载字典
|
||||||
|
* @param {Dict} dict 字典
|
||||||
|
* @param {DictMeta} dictMeta 字典元数据
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
function loadDict(dict, dictMeta) {
|
||||||
|
return dictMeta.request(dictMeta)
|
||||||
|
.then(response => {
|
||||||
|
const type = dictMeta.type
|
||||||
|
let dicts = dictMeta.responseConverter(response, dictMeta)
|
||||||
|
if (!(dicts instanceof Array)) {
|
||||||
|
console.error('the return of responseConverter must be Array.<DictData>')
|
||||||
|
dicts = []
|
||||||
|
} else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) {
|
||||||
|
console.error('the type of elements in dicts must be DictData')
|
||||||
|
dicts = []
|
||||||
|
}
|
||||||
|
dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)
|
||||||
|
dicts.forEach(d => {
|
||||||
|
Vue.set(dict.label[type], d.value, d.label)
|
||||||
|
})
|
||||||
|
return dicts
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import DictOptions from './DictOptions'
|
||||||
|
import DictData from './DictData'
|
||||||
|
|
||||||
|
export default function (dict, dictMeta) {
|
||||||
|
const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS)
|
||||||
|
const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS)
|
||||||
|
return new DictData(dict[label], dict[value], dict)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定字典字段
|
||||||
|
* @param {DictData} dict
|
||||||
|
* @param {...String} fields
|
||||||
|
*/
|
||||||
|
function determineDictField(dict, ...fields) {
|
||||||
|
return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f))
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
/**
|
||||||
|
* @classdesc 字典数据
|
||||||
|
* @property {String} label 标签
|
||||||
|
* @property {*} value 标签
|
||||||
|
* @property {Object} raw 原始数据
|
||||||
|
*/
|
||||||
|
export default class DictData {
|
||||||
|
constructor(label, value, raw) {
|
||||||
|
this.label = label
|
||||||
|
this.value = value
|
||||||
|
this.raw = raw
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import {mergeRecursive} from "@/utils/muyu";
|
||||||
|
import DictOptions from './DictOptions'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc 字典元数据
|
||||||
|
* @property {String} type 类型
|
||||||
|
* @property {Function} request 请求
|
||||||
|
* @property {String} label 标签字段
|
||||||
|
* @property {String} value 值字段
|
||||||
|
*/
|
||||||
|
export default class DictMeta {
|
||||||
|
constructor(options) {
|
||||||
|
this.type = options.type
|
||||||
|
this.request = options.request
|
||||||
|
this.responseConverter = options.responseConverter
|
||||||
|
this.labelField = options.labelField
|
||||||
|
this.valueField = options.valueField
|
||||||
|
this.lazy = options.lazy === true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析字典元数据
|
||||||
|
* @param {Object} options
|
||||||
|
* @returns {DictMeta}
|
||||||
|
*/
|
||||||
|
DictMeta.parse = function (options) {
|
||||||
|
let opts = null
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
opts = DictOptions.metas[options] || {}
|
||||||
|
opts.type = options
|
||||||
|
} else if (typeof options === 'object') {
|
||||||
|
opts = options
|
||||||
|
}
|
||||||
|
opts = mergeRecursive(DictOptions.metas['*'], opts)
|
||||||
|
return new DictMeta(opts)
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {mergeRecursive} from "@/utils/muyu";
|
||||||
|
import dictConverter from './DictConverter'
|
||||||
|
|
||||||
|
export const options = {
|
||||||
|
metas: {
|
||||||
|
'*': {
|
||||||
|
/**
|
||||||
|
* 字典请求,方法签名为function(dictMeta: DictMeta): Promise
|
||||||
|
*/
|
||||||
|
request: (dictMeta) => {
|
||||||
|
console.log(`load dict ${dictMeta.type}`)
|
||||||
|
return Promise.resolve([])
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData
|
||||||
|
*/
|
||||||
|
responseConverter,
|
||||||
|
labelField: 'label',
|
||||||
|
valueField: 'value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 默认标签字段
|
||||||
|
*/
|
||||||
|
DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'],
|
||||||
|
/**
|
||||||
|
* 默认值字段
|
||||||
|
*/
|
||||||
|
DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 映射字典
|
||||||
|
* @param {Object} response 字典数据
|
||||||
|
* @param {DictMeta} dictMeta 字典元数据
|
||||||
|
* @returns {DictData}
|
||||||
|
*/
|
||||||
|
function responseConverter(response, dictMeta) {
|
||||||
|
const dicts = response.content instanceof Array ? response.content : response
|
||||||
|
if (dicts === undefined) {
|
||||||
|
console.warn(`no dict data of "${dictMeta.type}" found in the response`)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return dicts.map(d => dictConverter(d, dictMeta))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mergeOptions(src) {
|
||||||
|
mergeRecursive(options, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default options
|
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
'401': '认证失败,无法访问系统资源',
|
||||||
|
'403': '当前操作没有权限',
|
||||||
|
'404': '访问资源不存在',
|
||||||
|
'default': '系统未知错误,请反馈给管理员'
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
layout: 'colFormItem',
|
||||||
|
tagIcon: 'input',
|
||||||
|
label: '手机号',
|
||||||
|
vModel: 'mobile',
|
||||||
|
formId: 6,
|
||||||
|
tag: 'el-input',
|
||||||
|
placeholder: '请输入手机号',
|
||||||
|
defaultValue: '',
|
||||||
|
span: 24,
|
||||||
|
style: {width: '100%'},
|
||||||
|
clearable: true,
|
||||||
|
prepend: '',
|
||||||
|
append: '',
|
||||||
|
'prefix-icon': 'el-icon-mobile',
|
||||||
|
'suffix-icon': '',
|
||||||
|
maxlength: 11,
|
||||||
|
'show-word-limit': true,
|
||||||
|
readonly: false,
|
||||||
|
disabled: false,
|
||||||
|
required: true,
|
||||||
|
changeTag: true,
|
||||||
|
regList: [{
|
||||||
|
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||||
|
message: '手机号格式错误'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,359 @@
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
import {trigger} from './config'
|
||||||
|
|
||||||
|
let confGlobal
|
||||||
|
let someSpanIsNot24
|
||||||
|
|
||||||
|
export function dialogWrapper(str) {
|
||||||
|
return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Title">
|
||||||
|
${str}
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function vueTemplate(str) {
|
||||||
|
return `<template>
|
||||||
|
<div>
|
||||||
|
${str}
|
||||||
|
</div>
|
||||||
|
</template>`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function vueScript(str) {
|
||||||
|
return `<script>
|
||||||
|
${str}
|
||||||
|
</script>`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cssStyle(cssStr) {
|
||||||
|
return `<style>
|
||||||
|
${cssStr}
|
||||||
|
</style>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFormTemplate(conf, child, type) {
|
||||||
|
let labelPosition = ''
|
||||||
|
if (conf.labelPosition !== 'right') {
|
||||||
|
labelPosition = `label-position="${conf.labelPosition}"`
|
||||||
|
}
|
||||||
|
const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
|
||||||
|
let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
|
||||||
|
${child}
|
||||||
|
${buildFromBtns(conf, type)}
|
||||||
|
</el-form>`
|
||||||
|
if (someSpanIsNot24) {
|
||||||
|
str = `<el-row :gutter="${conf.gutter}">
|
||||||
|
${str}
|
||||||
|
</el-row>`
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFromBtns(conf, type) {
|
||||||
|
let str = ''
|
||||||
|
if (conf.formBtns && type === 'file') {
|
||||||
|
str = `<el-form-item size="large">
|
||||||
|
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||||
|
<el-button @click="resetForm">重置</el-button>
|
||||||
|
</el-form-item>`
|
||||||
|
if (someSpanIsNot24) {
|
||||||
|
str = `<el-col :span="24">
|
||||||
|
${str}
|
||||||
|
</el-col>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// span不为24的用el-col包裹
|
||||||
|
function colWrapper(element, str) {
|
||||||
|
if (someSpanIsNot24 || element.span !== 24) {
|
||||||
|
return `<el-col :span="${element.span}">
|
||||||
|
${str}
|
||||||
|
</el-col>`
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
const layouts = {
|
||||||
|
colFormItem(element) {
|
||||||
|
let labelWidth = ''
|
||||||
|
if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
|
||||||
|
labelWidth = `label-width="${element.labelWidth}px"`
|
||||||
|
}
|
||||||
|
const required = !trigger[element.tag] && element.required ? 'required' : ''
|
||||||
|
const tagDom = tags[element.tag] ? tags[element.tag](element) : null
|
||||||
|
let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}>
|
||||||
|
${tagDom}
|
||||||
|
</el-form-item>`
|
||||||
|
str = colWrapper(element, str)
|
||||||
|
return str
|
||||||
|
},
|
||||||
|
rowFormItem(element) {
|
||||||
|
const type = element.type === 'default' ? '' : `type="${element.type}"`
|
||||||
|
const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
|
||||||
|
const align = element.type === 'default' ? '' : `align="${element.align}"`
|
||||||
|
const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
|
||||||
|
const children = element.children.map(el => layouts[el.layout](el))
|
||||||
|
let str = `<el-row ${type} ${justify} ${align} ${gutter}>
|
||||||
|
${children.join('\n')}
|
||||||
|
</el-row>`
|
||||||
|
str = colWrapper(element, str)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = {
|
||||||
|
'el-button': el => {
|
||||||
|
const {
|
||||||
|
tag, disabled
|
||||||
|
} = attrBuilder(el)
|
||||||
|
const type = el.type ? `type="${el.type}"` : ''
|
||||||
|
const icon = el.icon ? `icon="${el.icon}"` : ''
|
||||||
|
const size = el.size ? `size="${el.size}"` : ''
|
||||||
|
let child = buildElButtonChild(el)
|
||||||
|
|
||||||
|
if (child) child = `\n${child}\n` // 换行
|
||||||
|
return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}</${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-input': el => {
|
||||||
|
const {
|
||||||
|
disabled, vModel, clearable, placeholder, width
|
||||||
|
} = attrBuilder(el)
|
||||||
|
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
|
||||||
|
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
|
||||||
|
const readonly = el.readonly ? 'readonly' : ''
|
||||||
|
const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
|
||||||
|
const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
|
||||||
|
const showPassword = el['show-password'] ? 'show-password' : ''
|
||||||
|
const type = el.type ? `type="${el.type}"` : ''
|
||||||
|
const autosize = el.autosize && el.autosize.minRows
|
||||||
|
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
|
||||||
|
: ''
|
||||||
|
let child = buildElInputChild(el)
|
||||||
|
|
||||||
|
if (child) child = `\n${child}\n` // 换行
|
||||||
|
return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-input-number': el => {
|
||||||
|
const {disabled, vModel, placeholder} = attrBuilder(el)
|
||||||
|
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
|
||||||
|
const min = el.min ? `:min='${el.min}'` : ''
|
||||||
|
const max = el.max ? `:max='${el.max}'` : ''
|
||||||
|
const step = el.step ? `:step='${el.step}'` : ''
|
||||||
|
const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
|
||||||
|
const precision = el.precision ? `:precision='${el.precision}'` : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-select': el => {
|
||||||
|
const {
|
||||||
|
disabled, vModel, clearable, placeholder, width
|
||||||
|
} = attrBuilder(el)
|
||||||
|
const filterable = el.filterable ? 'filterable' : ''
|
||||||
|
const multiple = el.multiple ? 'multiple' : ''
|
||||||
|
let child = buildElSelectChild(el)
|
||||||
|
|
||||||
|
if (child) child = `\n${child}\n` // 换行
|
||||||
|
return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-radio-group': el => {
|
||||||
|
const {disabled, vModel} = attrBuilder(el)
|
||||||
|
const size = `size="${el.size}"`
|
||||||
|
let child = buildElRadioGroupChild(el)
|
||||||
|
|
||||||
|
if (child) child = `\n${child}\n` // 换行
|
||||||
|
return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-checkbox-group': el => {
|
||||||
|
const {disabled, vModel} = attrBuilder(el)
|
||||||
|
const size = `size="${el.size}"`
|
||||||
|
const min = el.min ? `:min="${el.min}"` : ''
|
||||||
|
const max = el.max ? `:max="${el.max}"` : ''
|
||||||
|
let child = buildElCheckboxGroupChild(el)
|
||||||
|
|
||||||
|
if (child) child = `\n${child}\n` // 换行
|
||||||
|
return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-switch': el => {
|
||||||
|
const {disabled, vModel} = attrBuilder(el)
|
||||||
|
const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
|
||||||
|
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
|
||||||
|
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
|
||||||
|
const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
|
||||||
|
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
|
||||||
|
const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-cascader': el => {
|
||||||
|
const {
|
||||||
|
disabled, vModel, clearable, placeholder, width
|
||||||
|
} = attrBuilder(el)
|
||||||
|
const options = el.options ? `:options="${el.vModel}Options"` : ''
|
||||||
|
const props = el.props ? `:props="${el.vModel}Props"` : ''
|
||||||
|
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
|
||||||
|
const filterable = el.filterable ? 'filterable' : ''
|
||||||
|
const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
|
||||||
|
|
||||||
|
return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-slider': el => {
|
||||||
|
const {disabled, vModel} = attrBuilder(el)
|
||||||
|
const min = el.min ? `:min='${el.min}'` : ''
|
||||||
|
const max = el.max ? `:max='${el.max}'` : ''
|
||||||
|
const step = el.step ? `:step='${el.step}'` : ''
|
||||||
|
const range = el.range ? 'range' : ''
|
||||||
|
const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-time-picker': el => {
|
||||||
|
const {
|
||||||
|
disabled, vModel, clearable, placeholder, width
|
||||||
|
} = attrBuilder(el)
|
||||||
|
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||||
|
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||||
|
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||||
|
const isRange = el['is-range'] ? 'is-range' : ''
|
||||||
|
const format = el.format ? `format="${el.format}"` : ''
|
||||||
|
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||||
|
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-date-picker': el => {
|
||||||
|
const {
|
||||||
|
disabled, vModel, clearable, placeholder, width
|
||||||
|
} = attrBuilder(el)
|
||||||
|
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||||
|
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||||
|
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||||
|
const format = el.format ? `format="${el.format}"` : ''
|
||||||
|
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||||
|
const type = el.type === 'date' ? '' : `type="${el.type}"`
|
||||||
|
const readonly = el.readonly ? 'readonly' : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-rate': el => {
|
||||||
|
const {disabled, vModel} = attrBuilder(el)
|
||||||
|
const max = el.max ? `:max='${el.max}'` : ''
|
||||||
|
const allowHalf = el['allow-half'] ? 'allow-half' : ''
|
||||||
|
const showText = el['show-text'] ? 'show-text' : ''
|
||||||
|
const showScore = el['show-score'] ? 'show-score' : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-color-picker': el => {
|
||||||
|
const {disabled, vModel} = attrBuilder(el)
|
||||||
|
const size = `size="${el.size}"`
|
||||||
|
const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
|
||||||
|
const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
|
||||||
|
|
||||||
|
return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
|
||||||
|
},
|
||||||
|
'el-upload': el => {
|
||||||
|
const disabled = el.disabled ? ':disabled=\'true\'' : ''
|
||||||
|
const action = el.action ? `:action="${el.vModel}Action"` : ''
|
||||||
|
const multiple = el.multiple ? 'multiple' : ''
|
||||||
|
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
|
||||||
|
const accept = el.accept ? `accept="${el.accept}"` : ''
|
||||||
|
const name = el.name !== 'file' ? `name="${el.name}"` : ''
|
||||||
|
const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
|
||||||
|
const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
|
||||||
|
const fileList = `:file-list="${el.vModel}fileList"`
|
||||||
|
const ref = `ref="${el.vModel}"`
|
||||||
|
let child = buildElUploadChild(el)
|
||||||
|
|
||||||
|
if (child) child = `\n${child}\n` // 换行
|
||||||
|
return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function attrBuilder(el) {
|
||||||
|
return {
|
||||||
|
vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`,
|
||||||
|
clearable: el.clearable ? 'clearable' : '',
|
||||||
|
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
|
||||||
|
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
|
||||||
|
disabled: el.disabled ? ':disabled=\'true\'' : ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// el-buttin 子级
|
||||||
|
function buildElButtonChild(conf) {
|
||||||
|
const children = []
|
||||||
|
if (conf.default) {
|
||||||
|
children.push(conf.default)
|
||||||
|
}
|
||||||
|
return children.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
// el-input innerHTML
|
||||||
|
function buildElInputChild(conf) {
|
||||||
|
const children = []
|
||||||
|
if (conf.prepend) {
|
||||||
|
children.push(`<template slot="prepend">${conf.prepend}</template>`)
|
||||||
|
}
|
||||||
|
if (conf.append) {
|
||||||
|
children.push(`<template slot="append">${conf.append}</template>`)
|
||||||
|
}
|
||||||
|
return children.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildElSelectChild(conf) {
|
||||||
|
const children = []
|
||||||
|
if (conf.options && conf.options.length) {
|
||||||
|
children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
|
||||||
|
}
|
||||||
|
return children.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildElRadioGroupChild(conf) {
|
||||||
|
const children = []
|
||||||
|
if (conf.options && conf.options.length) {
|
||||||
|
const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
|
||||||
|
const border = conf.border ? 'border' : ''
|
||||||
|
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||||
|
}
|
||||||
|
return children.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildElCheckboxGroupChild(conf) {
|
||||||
|
const children = []
|
||||||
|
if (conf.options && conf.options.length) {
|
||||||
|
const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
|
||||||
|
const border = conf.border ? 'border' : ''
|
||||||
|
children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||||
|
}
|
||||||
|
return children.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildElUploadChild(conf) {
|
||||||
|
const list = []
|
||||||
|
if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
|
||||||
|
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`)
|
||||||
|
if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
|
||||||
|
return list.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeUpHtml(conf, type) {
|
||||||
|
const htmlList = []
|
||||||
|
confGlobal = conf
|
||||||
|
someSpanIsNot24 = conf.fields.some(item => item.span !== 24)
|
||||||
|
conf.fields.forEach(el => {
|
||||||
|
htmlList.push(layouts[el.layout](el))
|
||||||
|
})
|
||||||
|
const htmlStr = htmlList.join('\n')
|
||||||
|
|
||||||
|
let temp = buildFormTemplate(conf, htmlStr, type)
|
||||||
|
if (type === 'dialog') {
|
||||||
|
temp = dialogWrapper(temp)
|
||||||
|
}
|
||||||
|
confGlobal = null
|
||||||
|
return temp
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
[
|
||||||
|
"platform-eleme",
|
||||||
|
"eleme",
|
||||||
|
"delete-solid",
|
||||||
|
"delete",
|
||||||
|
"s-tools",
|
||||||
|
"setting",
|
||||||
|
"user-solid",
|
||||||
|
"user",
|
||||||
|
"phone",
|
||||||
|
"phone-outline",
|
||||||
|
"more",
|
||||||
|
"more-outline",
|
||||||
|
"star-on",
|
||||||
|
"star-off",
|
||||||
|
"s-goods",
|
||||||
|
"goods",
|
||||||
|
"warning",
|
||||||
|
"warning-outline",
|
||||||
|
"question",
|
||||||
|
"info",
|
||||||
|
"remove",
|
||||||
|
"circle-plus",
|
||||||
|
"success",
|
||||||
|
"error",
|
||||||
|
"zoom-in",
|
||||||
|
"zoom-out",
|
||||||
|
"remove-outline",
|
||||||
|
"circle-plus-outline",
|
||||||
|
"circle-check",
|
||||||
|
"circle-close",
|
||||||
|
"s-help",
|
||||||
|
"help",
|
||||||
|
"minus",
|
||||||
|
"plus",
|
||||||
|
"check",
|
||||||
|
"close",
|
||||||
|
"picture",
|
||||||
|
"picture-outline",
|
||||||
|
"picture-outline-round",
|
||||||
|
"upload",
|
||||||
|
"upload2",
|
||||||
|
"download",
|
||||||
|
"camera-solid",
|
||||||
|
"camera",
|
||||||
|
"video-camera-solid",
|
||||||
|
"video-camera",
|
||||||
|
"message-solid",
|
||||||
|
"bell",
|
||||||
|
"s-cooperation",
|
||||||
|
"s-order",
|
||||||
|
"s-platform",
|
||||||
|
"s-fold",
|
||||||
|
"s-unfold",
|
||||||
|
"s-operation",
|
||||||
|
"s-promotion",
|
||||||
|
"s-home",
|
||||||
|
"s-release",
|
||||||
|
"s-ticket",
|
||||||
|
"s-management",
|
||||||
|
"s-open",
|
||||||
|
"s-shop",
|
||||||
|
"s-marketing",
|
||||||
|
"s-flag",
|
||||||
|
"s-comment",
|
||||||
|
"s-finance",
|
||||||
|
"s-claim",
|
||||||
|
"s-custom",
|
||||||
|
"s-opportunity",
|
||||||
|
"s-data",
|
||||||
|
"s-check",
|
||||||
|
"s-grid",
|
||||||
|
"menu",
|
||||||
|
"share",
|
||||||
|
"d-caret",
|
||||||
|
"caret-left",
|
||||||
|
"caret-right",
|
||||||
|
"caret-bottom",
|
||||||
|
"caret-top",
|
||||||
|
"bottom-left",
|
||||||
|
"bottom-right",
|
||||||
|
"back",
|
||||||
|
"right",
|
||||||
|
"bottom",
|
||||||
|
"top",
|
||||||
|
"top-left",
|
||||||
|
"top-right",
|
||||||
|
"arrow-left",
|
||||||
|
"arrow-right",
|
||||||
|
"arrow-down",
|
||||||
|
"arrow-up",
|
||||||
|
"d-arrow-left",
|
||||||
|
"d-arrow-right",
|
||||||
|
"video-pause",
|
||||||
|
"video-play",
|
||||||
|
"refresh",
|
||||||
|
"refresh-right",
|
||||||
|
"refresh-left",
|
||||||
|
"finished",
|
||||||
|
"sort",
|
||||||
|
"sort-up",
|
||||||
|
"sort-down",
|
||||||
|
"rank",
|
||||||
|
"loading",
|
||||||
|
"view",
|
||||||
|
"c-scale-to-original",
|
||||||
|
"date",
|
||||||
|
"edit",
|
||||||
|
"edit-outline",
|
||||||
|
"folder",
|
||||||
|
"folder-opened",
|
||||||
|
"folder-add",
|
||||||
|
"folder-remove",
|
||||||
|
"folder-delete",
|
||||||
|
"folder-checked",
|
||||||
|
"tickets",
|
||||||
|
"document-remove",
|
||||||
|
"document-delete",
|
||||||
|
"document-copy",
|
||||||
|
"document-checked",
|
||||||
|
"document",
|
||||||
|
"document-add",
|
||||||
|
"printer",
|
||||||
|
"paperclip",
|
||||||
|
"takeaway-box",
|
||||||
|
"search",
|
||||||
|
"monitor",
|
||||||
|
"attract",
|
||||||
|
"mobile",
|
||||||
|
"scissors",
|
||||||
|
"umbrella",
|
||||||
|
"headset",
|
||||||
|
"brush",
|
||||||
|
"mouse",
|
||||||
|
"coordinate",
|
||||||
|
"magic-stick",
|
||||||
|
"reading",
|
||||||
|
"data-line",
|
||||||
|
"data-board",
|
||||||
|
"pie-chart",
|
||||||
|
"data-analysis",
|
||||||
|
"collection-tag",
|
||||||
|
"film",
|
||||||
|
"suitcase",
|
||||||
|
"suitcase-1",
|
||||||
|
"receiving",
|
||||||
|
"collection",
|
||||||
|
"files",
|
||||||
|
"notebook-1",
|
||||||
|
"notebook-2",
|
||||||
|
"toilet-paper",
|
||||||
|
"office-building",
|
||||||
|
"school",
|
||||||
|
"table-lamp",
|
||||||
|
"house",
|
||||||
|
"no-smoking",
|
||||||
|
"smoking",
|
||||||
|
"shopping-cart-full",
|
||||||
|
"shopping-cart-1",
|
||||||
|
"shopping-cart-2",
|
||||||
|
"shopping-bag-1",
|
||||||
|
"shopping-bag-2",
|
||||||
|
"sold-out",
|
||||||
|
"sell",
|
||||||
|
"present",
|
||||||
|
"box",
|
||||||
|
"bank-card",
|
||||||
|
"money",
|
||||||
|
"coin",
|
||||||
|
"wallet",
|
||||||
|
"discount",
|
||||||
|
"price-tag",
|
||||||
|
"news",
|
||||||
|
"guide",
|
||||||
|
"male",
|
||||||
|
"female",
|
||||||
|
"thumb",
|
||||||
|
"cpu",
|
||||||
|
"link",
|
||||||
|
"connection",
|
||||||
|
"open",
|
||||||
|
"turn-off",
|
||||||
|
"set-up",
|
||||||
|
"chat-round",
|
||||||
|
"chat-line-round",
|
||||||
|
"chat-square",
|
||||||
|
"chat-dot-round",
|
||||||
|
"chat-dot-square",
|
||||||
|
"chat-line-square",
|
||||||
|
"message",
|
||||||
|
"postcard",
|
||||||
|
"position",
|
||||||
|
"turn-off-microphone",
|
||||||
|
"microphone",
|
||||||
|
"close-notification",
|
||||||
|
"bangzhu",
|
||||||
|
"time",
|
||||||
|
"odometer",
|
||||||
|
"crop",
|
||||||
|
"aim",
|
||||||
|
"switch-button",
|
||||||
|
"full-screen",
|
||||||
|
"copy-document",
|
||||||
|
"mic",
|
||||||
|
"stopwatch",
|
||||||
|
"medal-1",
|
||||||
|
"medal",
|
||||||
|
"trophy",
|
||||||
|
"trophy-1",
|
||||||
|
"first-aid-kit",
|
||||||
|
"discover",
|
||||||
|
"place",
|
||||||
|
"location",
|
||||||
|
"location-outline",
|
||||||
|
"location-information",
|
||||||
|
"add-location",
|
||||||
|
"delete-location",
|
||||||
|
"map-location",
|
||||||
|
"alarm-clock",
|
||||||
|
"timer",
|
||||||
|
"watch-1",
|
||||||
|
"watch",
|
||||||
|
"lock",
|
||||||
|
"unlock",
|
||||||
|
"key",
|
||||||
|
"service",
|
||||||
|
"mobile-phone",
|
||||||
|
"bicycle",
|
||||||
|
"truck",
|
||||||
|
"ship",
|
||||||
|
"basketball",
|
||||||
|
"football",
|
||||||
|
"soccer",
|
||||||
|
"baseball",
|
||||||
|
"wind-power",
|
||||||
|
"light-rain",
|
||||||
|
"lightning",
|
||||||
|
"heavy-rain",
|
||||||
|
"sunrise",
|
||||||
|
"sunrise-1",
|
||||||
|
"sunset",
|
||||||
|
"sunny",
|
||||||
|
"cloudy",
|
||||||
|
"partly-cloudy",
|
||||||
|
"cloudy-and-sunny",
|
||||||
|
"moon",
|
||||||
|
"moon-night",
|
||||||
|
"dish",
|
||||||
|
"dish-1",
|
||||||
|
"food",
|
||||||
|
"chicken",
|
||||||
|
"fork-spoon",
|
||||||
|
"knife-fork",
|
||||||
|
"burger",
|
||||||
|
"tableware",
|
||||||
|
"sugar",
|
||||||
|
"dessert",
|
||||||
|
"ice-cream",
|
||||||
|
"hot-water",
|
||||||
|
"water-cup",
|
||||||
|
"coffee-cup",
|
||||||
|
"cold-drink",
|
||||||
|
"goblet",
|
||||||
|
"goblet-full",
|
||||||
|
"goblet-square",
|
||||||
|
"goblet-square-full",
|
||||||
|
"refrigerator",
|
||||||
|
"grape",
|
||||||
|
"watermelon",
|
||||||
|
"cherry",
|
||||||
|
"apple",
|
||||||
|
"pear",
|
||||||
|
"orange",
|
||||||
|
"coffee",
|
||||||
|
"ice-tea",
|
||||||
|
"ice-drink",
|
||||||
|
"milk-tea",
|
||||||
|
"potato-strips",
|
||||||
|
"lollipop",
|
||||||
|
"ice-cream-square",
|
||||||
|
"ice-cream-round"
|
||||||
|
]
|
|
@ -0,0 +1,241 @@
|
||||||
|
import {exportDefault, titleCase} from '@/utils/index'
|
||||||
|
import {trigger} from './config'
|
||||||
|
|
||||||
|
const units = {
|
||||||
|
KB: '1024',
|
||||||
|
MB: '1024 / 1024',
|
||||||
|
GB: '1024 / 1024 / 1024'
|
||||||
|
}
|
||||||
|
let confGlobal
|
||||||
|
const inheritAttrs = {
|
||||||
|
file: '',
|
||||||
|
dialog: 'inheritAttrs: false,'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function makeUpJs(conf, type) {
|
||||||
|
confGlobal = conf = JSON.parse(JSON.stringify(conf))
|
||||||
|
const dataList = []
|
||||||
|
const ruleList = []
|
||||||
|
const optionsList = []
|
||||||
|
const propsList = []
|
||||||
|
const methodList = mixinMethod(type)
|
||||||
|
const uploadVarList = []
|
||||||
|
|
||||||
|
conf.fields.forEach(el => {
|
||||||
|
buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
|
||||||
|
})
|
||||||
|
|
||||||
|
const script = buildexport(
|
||||||
|
conf,
|
||||||
|
type,
|
||||||
|
dataList.join('\n'),
|
||||||
|
ruleList.join('\n'),
|
||||||
|
optionsList.join('\n'),
|
||||||
|
uploadVarList.join('\n'),
|
||||||
|
propsList.join('\n'),
|
||||||
|
methodList.join('\n')
|
||||||
|
)
|
||||||
|
confGlobal = null
|
||||||
|
return script
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
|
||||||
|
buildData(el, dataList)
|
||||||
|
buildRules(el, ruleList)
|
||||||
|
|
||||||
|
if (el.options && el.options.length) {
|
||||||
|
buildOptions(el, optionsList)
|
||||||
|
if (el.dataType === 'dynamic') {
|
||||||
|
const model = `${el.vModel}Options`
|
||||||
|
const options = titleCase(model)
|
||||||
|
buildOptionMethod(`get${options}`, model, methodList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.props && el.props.props) {
|
||||||
|
buildProps(el, propsList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.action && el.tag === 'el-upload') {
|
||||||
|
uploadVarList.push(
|
||||||
|
`${el.vModel}Action: '${el.action}',
|
||||||
|
${el.vModel}fileList: [],`
|
||||||
|
)
|
||||||
|
methodList.push(buildBeforeUpload(el))
|
||||||
|
if (!el['auto-upload']) {
|
||||||
|
methodList.push(buildSubmitUpload(el))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.children) {
|
||||||
|
el.children.forEach(el2 => {
|
||||||
|
buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mixinMethod(type) {
|
||||||
|
const list = [];
|
||||||
|
const
|
||||||
|
minxins = {
|
||||||
|
file: confGlobal.formBtns ? {
|
||||||
|
submitForm: `submitForm() {
|
||||||
|
this.$refs['${confGlobal.formRef}'].validate(valid => {
|
||||||
|
if(!valid) return
|
||||||
|
// TODO 提交表单
|
||||||
|
})
|
||||||
|
},`,
|
||||||
|
resetForm: `resetForm() {
|
||||||
|
this.$refs['${confGlobal.formRef}'].resetFields()
|
||||||
|
},`
|
||||||
|
} : null,
|
||||||
|
dialog: {
|
||||||
|
onOpen: 'onOpen() {},',
|
||||||
|
onClose: `onClose() {
|
||||||
|
this.$refs['${confGlobal.formRef}'].resetFields()
|
||||||
|
},`,
|
||||||
|
close: `close() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
},`,
|
||||||
|
handleConfirm: `handleConfirm() {
|
||||||
|
this.$refs['${confGlobal.formRef}'].validate(valid => {
|
||||||
|
if(!valid) return
|
||||||
|
this.close()
|
||||||
|
})
|
||||||
|
},`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const methods = minxins[type]
|
||||||
|
if (methods) {
|
||||||
|
Object.keys(methods).forEach(key => {
|
||||||
|
list.push(methods[key])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildData(conf, dataList) {
|
||||||
|
if (conf.vModel === undefined) return
|
||||||
|
let defaultValue
|
||||||
|
if (typeof (conf.defaultValue) === 'string' && !conf.multiple) {
|
||||||
|
defaultValue = `'${conf.defaultValue}'`
|
||||||
|
} else {
|
||||||
|
defaultValue = `${JSON.stringify(conf.defaultValue)}`
|
||||||
|
}
|
||||||
|
dataList.push(`${conf.vModel}: ${defaultValue},`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRules(conf, ruleList) {
|
||||||
|
if (conf.vModel === undefined) return
|
||||||
|
const rules = []
|
||||||
|
if (trigger[conf.tag]) {
|
||||||
|
if (conf.required) {
|
||||||
|
const type = Array.isArray(conf.defaultValue) ? 'type: \'array\',' : ''
|
||||||
|
let message = Array.isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder
|
||||||
|
if (message === undefined) message = `${conf.label}不能为空`
|
||||||
|
rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`)
|
||||||
|
}
|
||||||
|
if (conf.regList && Array.isArray(conf.regList)) {
|
||||||
|
conf.regList.forEach(item => {
|
||||||
|
if (item.pattern) {
|
||||||
|
rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ruleList.push(`${conf.vModel}: [${rules.join(',')}],`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOptions(conf, optionsList) {
|
||||||
|
if (conf.vModel === undefined) return
|
||||||
|
if (conf.dataType === 'dynamic') {
|
||||||
|
conf.options = []
|
||||||
|
}
|
||||||
|
const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},`
|
||||||
|
optionsList.push(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildProps(conf, propsList) {
|
||||||
|
if (conf.dataType === 'dynamic') {
|
||||||
|
conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey)
|
||||||
|
conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey)
|
||||||
|
conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey)
|
||||||
|
}
|
||||||
|
const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},`
|
||||||
|
propsList.push(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBeforeUpload(conf) {
|
||||||
|
const unitNum = units[conf.sizeUnit];
|
||||||
|
let rightSizeCode = '';
|
||||||
|
let acceptCode = '';
|
||||||
|
const
|
||||||
|
returnList = []
|
||||||
|
if (conf.fileSize) {
|
||||||
|
rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize}
|
||||||
|
if(!isRightSize){
|
||||||
|
this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}')
|
||||||
|
}`
|
||||||
|
returnList.push('isRightSize')
|
||||||
|
}
|
||||||
|
if (conf.accept) {
|
||||||
|
acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type)
|
||||||
|
if(!isAccept){
|
||||||
|
this.$message.error('应该选择${conf.accept}类型的文件')
|
||||||
|
}`
|
||||||
|
returnList.push('isAccept')
|
||||||
|
}
|
||||||
|
const str = `${conf.vModel}BeforeUpload(file) {
|
||||||
|
${rightSizeCode}
|
||||||
|
${acceptCode}
|
||||||
|
return ${returnList.join('&&')}
|
||||||
|
},`
|
||||||
|
return returnList.length ? str : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSubmitUpload(conf) {
|
||||||
|
const str = `submitUpload() {
|
||||||
|
this.$refs['${conf.vModel}'].submit()
|
||||||
|
},`
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOptionMethod(methodName, model, methodList) {
|
||||||
|
const str = `${methodName}() {
|
||||||
|
// TODO 发起请求获取数据
|
||||||
|
this.${model}
|
||||||
|
},`
|
||||||
|
methodList.push(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
|
||||||
|
const str = `${exportDefault}{
|
||||||
|
${inheritAttrs[type]}
|
||||||
|
components: {},
|
||||||
|
props: [],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
${conf.formModel}: {
|
||||||
|
${data}
|
||||||
|
},
|
||||||
|
${conf.formRules}: {
|
||||||
|
${rules}
|
||||||
|
},
|
||||||
|
${uploadVar}
|
||||||
|
${selectOptions}
|
||||||
|
${props}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {},
|
||||||
|
created () {},
|
||||||
|
mounted () {},
|
||||||
|
methods: {
|
||||||
|
${methods}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
return str
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
import {makeMap} from '@/utils/index'
|
||||||
|
|
||||||
|
// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js
|
||||||
|
const isAttr = makeMap(
|
||||||
|
'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,'
|
||||||
|
+ 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,'
|
||||||
|
+ 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,'
|
||||||
|
+ 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,'
|
||||||
|
+ 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,'
|
||||||
|
+ 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,'
|
||||||
|
+ 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,'
|
||||||
|
+ 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,'
|
||||||
|
+ 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,'
|
||||||
|
+ 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,'
|
||||||
|
+ 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,'
|
||||||
|
+ 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,'
|
||||||
|
+ 'target,title,type,usemap,value,width,wrap'
|
||||||
|
)
|
||||||
|
|
||||||
|
function vModel(self, dataObject, defaultValue) {
|
||||||
|
dataObject.props.value = defaultValue
|
||||||
|
|
||||||
|
dataObject.on.input = val => {
|
||||||
|
self.$emit('input', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentChild = {
|
||||||
|
'el-button': {
|
||||||
|
default(h, conf, key) {
|
||||||
|
return conf[key]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'el-input': {
|
||||||
|
prepend(h, conf, key) {
|
||||||
|
return <template slot="prepend">{conf[key]}</template>
|
||||||
|
},
|
||||||
|
append(h, conf, key) {
|
||||||
|
return <template slot="append">{conf[key]}</template>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'el-select': {
|
||||||
|
options(h, conf, key) {
|
||||||
|
const list = []
|
||||||
|
conf.options.forEach(item => {
|
||||||
|
list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'el-radio-group': {
|
||||||
|
options(h, conf, key) {
|
||||||
|
const list = []
|
||||||
|
conf.options.forEach(item => {
|
||||||
|
if (conf.optionType === 'button') list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)
|
||||||
|
else list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'el-checkbox-group': {
|
||||||
|
options(h, conf, key) {
|
||||||
|
const list = []
|
||||||
|
conf.options.forEach(item => {
|
||||||
|
if (conf.optionType === 'button') {
|
||||||
|
list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
|
||||||
|
} else {
|
||||||
|
list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'el-upload': {
|
||||||
|
'list-type': (h, conf, key) => {
|
||||||
|
const list = []
|
||||||
|
if (conf['list-type'] === 'picture-card') {
|
||||||
|
list.push(<i class="el-icon-plus"></i>)
|
||||||
|
} else {
|
||||||
|
list.push(<el-button size="small" type="primary" icon="el-icon-upload">{conf.buttonText}</el-button>)
|
||||||
|
}
|
||||||
|
if (conf.showTip) {
|
||||||
|
list.push(<div slot="tip"
|
||||||
|
class="el-upload__tip">只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件</div>)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
render(h) {
|
||||||
|
const dataObject = {
|
||||||
|
attrs: {},
|
||||||
|
props: {},
|
||||||
|
on: {},
|
||||||
|
style: {}
|
||||||
|
}
|
||||||
|
const confClone = JSON.parse(JSON.stringify(this.conf))
|
||||||
|
const children = []
|
||||||
|
|
||||||
|
const childObjs = componentChild[confClone.tag]
|
||||||
|
if (childObjs) {
|
||||||
|
Object.keys(childObjs).forEach(key => {
|
||||||
|
const childFunc = childObjs[key]
|
||||||
|
if (confClone[key]) {
|
||||||
|
children.push(childFunc(h, confClone, key))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(confClone).forEach(key => {
|
||||||
|
const val = confClone[key]
|
||||||
|
if (key === 'vModel') {
|
||||||
|
vModel(this, dataObject, confClone.defaultValue)
|
||||||
|
} else if (dataObject[key]) {
|
||||||
|
dataObject[key] = val
|
||||||
|
} else if (!isAttr(key)) {
|
||||||
|
dataObject.props[key] = val
|
||||||
|
} else {
|
||||||
|
dataObject.attrs[key] = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return h(this.conf.tag, dataObject, children)
|
||||||
|
},
|
||||||
|
props: ['conf']
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
|
||||||
|
|
||||||
|
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
||||||
|
|
||||||
|
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
|
||||||
|
'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||||
|
|
||||||
|
const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
|
||||||
|
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
|
||||||
|
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
|
||||||
|
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
|
||||||
|
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
|
||||||
|
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
|
||||||
|
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
|
||||||
|
'UP8iWi1Qw0Y='
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
export function encrypt(txt) {
|
||||||
|
const encryptor = new JSEncrypt()
|
||||||
|
encryptor.setPublicKey(publicKey) // 设置公钥
|
||||||
|
return encryptor.encrypt(txt) // 对数据进行加密
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
export function decrypt(txt) {
|
||||||
|
const encryptor = new JSEncrypt()
|
||||||
|
encryptor.setPrivateKey(privateKey) // 设置私钥
|
||||||
|
return encryptor.decrypt(txt) // 对数据进行解密
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
/**
|
||||||
|
* 通用js方法封装处理
|
||||||
|
* Copyright (c) 2019 muyu
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 日期格式化
|
||||||
|
export function parseTime(time, pattern) {
|
||||||
|
if (arguments.length === 0 || !time) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||||
|
let date
|
||||||
|
if (typeof time === 'object') {
|
||||||
|
date = time
|
||||||
|
} else {
|
||||||
|
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||||
|
time = parseInt(time)
|
||||||
|
} else if (typeof time === 'string') {
|
||||||
|
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
|
||||||
|
}
|
||||||
|
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||||
|
time = time * 1000
|
||||||
|
}
|
||||||
|
date = new Date(time)
|
||||||
|
}
|
||||||
|
const formatObj = {
|
||||||
|
y: date.getFullYear(),
|
||||||
|
m: date.getMonth() + 1,
|
||||||
|
d: date.getDate(),
|
||||||
|
h: date.getHours(),
|
||||||
|
i: date.getMinutes(),
|
||||||
|
s: date.getSeconds(),
|
||||||
|
a: date.getDay()
|
||||||
|
}
|
||||||
|
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||||
|
let value = formatObj[key]
|
||||||
|
// Note: getDay() returns 0 on Sunday
|
||||||
|
if (key === 'a') {
|
||||||
|
return ['日', '一', '二', '三', '四', '五', '六'][value]
|
||||||
|
}
|
||||||
|
if (result.length > 0 && value < 10) {
|
||||||
|
value = '0' + value
|
||||||
|
}
|
||||||
|
return value || 0
|
||||||
|
})
|
||||||
|
return time_str
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单重置
|
||||||
|
export function resetForm(refName) {
|
||||||
|
if (this.$refs[refName]) {
|
||||||
|
this.$refs[refName].resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加日期范围
|
||||||
|
export function addDateRange(params, dateRange, propName) {
|
||||||
|
let search = params;
|
||||||
|
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
|
||||||
|
dateRange = Array.isArray(dateRange) ? dateRange : [];
|
||||||
|
if (typeof (propName) === 'undefined') {
|
||||||
|
search.params['beginTime'] = dateRange[0];
|
||||||
|
search.params['endTime'] = dateRange[1];
|
||||||
|
} else {
|
||||||
|
search.params['begin' + propName] = dateRange[0];
|
||||||
|
search.params['end' + propName] = dateRange[1];
|
||||||
|
}
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回显数据字典
|
||||||
|
export function selectDictLabel(datas, value) {
|
||||||
|
if (value === undefined) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
var actions = [];
|
||||||
|
Object.keys(datas).some((key) => {
|
||||||
|
if (datas[key].value == ('' + value)) {
|
||||||
|
actions.push(datas[key].label);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (actions.length === 0) {
|
||||||
|
actions.push(value);
|
||||||
|
}
|
||||||
|
return actions.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回显数据字典(字符串、数组)
|
||||||
|
export function selectDictLabels(datas, value, separator) {
|
||||||
|
if (value === undefined || value.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value = value.join(",");
|
||||||
|
}
|
||||||
|
var actions = [];
|
||||||
|
var currentSeparator = undefined === separator ? "," : separator;
|
||||||
|
var temp = value.split(currentSeparator);
|
||||||
|
Object.keys(value.split(currentSeparator)).some((val) => {
|
||||||
|
var match = false;
|
||||||
|
Object.keys(datas).some((key) => {
|
||||||
|
if (datas[key].value == ('' + temp[val])) {
|
||||||
|
actions.push(datas[key].label + currentSeparator);
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!match) {
|
||||||
|
actions.push(temp[val] + currentSeparator);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return actions.join('').substring(0, actions.join('').length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字符串格式化(%s )
|
||||||
|
export function sprintf(str) {
|
||||||
|
var args = arguments, flag = true, i = 1;
|
||||||
|
str = str.replace(/%s/g, function () {
|
||||||
|
var arg = args[i++];
|
||||||
|
if (typeof arg === 'undefined') {
|
||||||
|
flag = false;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
});
|
||||||
|
return flag ? str : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换字符串,undefined,null等转化为""
|
||||||
|
export function parseStrEmpty(str) {
|
||||||
|
if (!str || str == "undefined" || str == "null") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据合并
|
||||||
|
export function mergeRecursive(source, target) {
|
||||||
|
for (var p in target) {
|
||||||
|
try {
|
||||||
|
if (target[p].constructor == Object) {
|
||||||
|
source[p] = mergeRecursive(source[p], target[p]);
|
||||||
|
} else {
|
||||||
|
source[p] = target[p];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
source[p] = target[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造树型结构数据
|
||||||
|
* @param {*} data 数据源
|
||||||
|
* @param {*} id id字段 默认 'id'
|
||||||
|
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||||
|
* @param {*} children 孩子节点字段 默认 'children'
|
||||||
|
*/
|
||||||
|
export function handleTree(data, id, parentId, children) {
|
||||||
|
let config = {
|
||||||
|
id: id || 'id',
|
||||||
|
parentId: parentId || 'parentId',
|
||||||
|
childrenList: children || 'children'
|
||||||
|
};
|
||||||
|
|
||||||
|
var childrenListMap = {};
|
||||||
|
var nodeIds = {};
|
||||||
|
var tree = [];
|
||||||
|
|
||||||
|
for (let d of data) {
|
||||||
|
let parentId = d[config.parentId];
|
||||||
|
if (childrenListMap[parentId] == null) {
|
||||||
|
childrenListMap[parentId] = [];
|
||||||
|
}
|
||||||
|
nodeIds[d[config.id]] = d;
|
||||||
|
childrenListMap[parentId].push(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let d of data) {
|
||||||
|
let parentId = d[config.parentId];
|
||||||
|
if (nodeIds[parentId] == null) {
|
||||||
|
tree.push(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let t of tree) {
|
||||||
|
adaptToChildrenList(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function adaptToChildrenList(o) {
|
||||||
|
if (childrenListMap[o[config.id]] !== null) {
|
||||||
|
o[config.childrenList] = childrenListMap[o[config.id]];
|
||||||
|
}
|
||||||
|
if (o[config.childrenList]) {
|
||||||
|
for (let c of o[config.childrenList]) {
|
||||||
|
adaptToChildrenList(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数处理
|
||||||
|
* @param {*} params 参数
|
||||||
|
*/
|
||||||
|
export function tansParams(params) {
|
||||||
|
let result = ''
|
||||||
|
for (const propName of Object.keys(params)) {
|
||||||
|
const value = params[propName];
|
||||||
|
var part = encodeURIComponent(propName) + "=";
|
||||||
|
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
|
||||||
|
let params = propName + '[' + key + ']';
|
||||||
|
var subPart = encodeURIComponent(params) + "=";
|
||||||
|
result += subPart + encodeURIComponent(value[key]) + "&";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result += part + encodeURIComponent(value) + "&";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证是否为blob格式
|
||||||
|
export function blobValidate(data) {
|
||||||
|
return data.type !== 'application/json'
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!hasPermission) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} 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)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!hasRole) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
console.error(`need roles! Like checkRole="['admin','editor']"`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
import {Loading, Message, MessageBox, Notification} from 'element-ui'
|
||||||
|
import store from '@/store'
|
||||||
|
import {getToken} from '@/utils/auth'
|
||||||
|
import errorCode from '@/utils/errorCode'
|
||||||
|
import {blobValidate, tansParams} from "@/utils/muyu";
|
||||||
|
import cache from '@/plugins/cache'
|
||||||
|
import {saveAs} from 'file-saver'
|
||||||
|
|
||||||
|
let downloadLoadingInstance;
|
||||||
|
// 是否显示重新登录
|
||||||
|
export let isRelogin = {show: false};
|
||||||
|
|
||||||
|
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
|
// 创建axios实例
|
||||||
|
const service = axios.create({
|
||||||
|
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||||
|
baseURL: process.env.VUE_APP_BASE_API,
|
||||||
|
// 超时
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
// request拦截器
|
||||||
|
service.interceptors.request.use(config => {
|
||||||
|
// 是否需要设置 token
|
||||||
|
const isToken = (config.headers || {}).isToken === false
|
||||||
|
// 是否需要防止数据重复提交
|
||||||
|
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
|
||||||
|
if (getToken() && !isToken) {
|
||||||
|
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||||
|
}
|
||||||
|
// get请求映射params参数
|
||||||
|
if (config.method === 'get' && config.params) {
|
||||||
|
let url = config.url + '?' + tansParams(config.params);
|
||||||
|
url = url.slice(0, -1);
|
||||||
|
config.params = {};
|
||||||
|
config.url = url;
|
||||||
|
}
|
||||||
|
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
||||||
|
const requestObj = {
|
||||||
|
url: config.url,
|
||||||
|
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
|
||||||
|
time: new Date().getTime()
|
||||||
|
}
|
||||||
|
const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小
|
||||||
|
const limitSize = 5 * 1024 * 1024; // 限制存放数据5M
|
||||||
|
if (requestSize >= limitSize) {
|
||||||
|
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
const sessionObj = cache.session.getJSON('sessionObj')
|
||||||
|
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
|
||||||
|
cache.session.setJSON('sessionObj', requestObj)
|
||||||
|
} else {
|
||||||
|
const s_url = sessionObj.url; // 请求地址
|
||||||
|
const s_data = sessionObj.data; // 请求数据
|
||||||
|
const s_time = sessionObj.time; // 请求时间
|
||||||
|
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
|
||||||
|
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
|
||||||
|
const message = '数据正在处理,请勿重复提交';
|
||||||
|
console.warn(`[${s_url}]: ` + message)
|
||||||
|
return Promise.reject(new Error(message))
|
||||||
|
} else {
|
||||||
|
cache.session.setJSON('sessionObj', requestObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
service.interceptors.response.use(res => {
|
||||||
|
// 未设置状态码则默认成功状态
|
||||||
|
const code = res.data.code || 200;
|
||||||
|
// 获取错误信息
|
||||||
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||||
|
// 二进制数据则直接返回
|
||||||
|
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
if (code === 401) {
|
||||||
|
if (!isRelogin.show) {
|
||||||
|
isRelogin.show = true;
|
||||||
|
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
|
||||||
|
confirmButtonText: '重新登录',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
isRelogin.show = false;
|
||||||
|
store.dispatch('LogOut').then(() => {
|
||||||
|
location.href = '/index';
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
isRelogin.show = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||||
|
} else if (code === 500) {
|
||||||
|
Message({message: msg, type: 'error'})
|
||||||
|
return Promise.reject(new Error(msg))
|
||||||
|
} else if (code === 601) {
|
||||||
|
Message({message: msg, type: 'warning'})
|
||||||
|
return Promise.reject('error')
|
||||||
|
} else if (code !== 200) {
|
||||||
|
Notification.error({title: msg})
|
||||||
|
return Promise.reject('error')
|
||||||
|
} else {
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.log('err' + error)
|
||||||
|
let {message} = error;
|
||||||
|
if (message == "Network Error") {
|
||||||
|
message = "后端接口连接异常";
|
||||||
|
} else if (message.includes("timeout")) {
|
||||||
|
message = "系统接口请求超时";
|
||||||
|
} else if (message.includes("Request failed with status code")) {
|
||||||
|
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
||||||
|
}
|
||||||
|
Message({message: message, type: 'error', duration: 5 * 1000})
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 通用下载方法
|
||||||
|
export function download(url, params, filename, config) {
|
||||||
|
downloadLoadingInstance = Loading.service({
|
||||||
|
text: "正在下载数据,请稍候",
|
||||||
|
spinner: "el-icon-loading",
|
||||||
|
background: "rgba(0, 0, 0, 0.7)",
|
||||||
|
})
|
||||||
|
return service.post(url, params, {
|
||||||
|
transformRequest: [(params) => {
|
||||||
|
return tansParams(params)
|
||||||
|
}],
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
responseType: 'blob',
|
||||||
|
...config
|
||||||
|
}).then(async (data) => {
|
||||||
|
const isBlob = blobValidate(data);
|
||||||
|
if (isBlob) {
|
||||||
|
const blob = new Blob([data])
|
||||||
|
saveAs(blob, filename)
|
||||||
|
} else {
|
||||||
|
const resText = await data.text();
|
||||||
|
const rspObj = JSON.parse(resText);
|
||||||
|
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||||
|
Message.error(errMsg);
|
||||||
|
}
|
||||||
|
downloadLoadingInstance.close();
|
||||||
|
}).catch((r) => {
|
||||||
|
console.error(r)
|
||||||
|
Message.error('下载文件出现错误,请联系管理员!')
|
||||||
|
downloadLoadingInstance.close();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default service
|
|
@ -0,0 +1,60 @@
|
||||||
|
Math.easeInOutQuad = function (t, b, c, d) {
|
||||||
|
t /= d / 2
|
||||||
|
if (t < 1) {
|
||||||
|
return c / 2 * t * t + b
|
||||||
|
}
|
||||||
|
t--
|
||||||
|
return -c / 2 * (t * (t - 2) - 1) + b
|
||||||
|
}
|
||||||
|
|
||||||
|
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
|
||||||
|
var requestAnimFrame = (function () {
|
||||||
|
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
|
||||||
|
window.setTimeout(callback, 1000 / 60)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because it's so fucking difficult to detect the scrolling element, just move them all
|
||||||
|
* @param {number} amount
|
||||||
|
*/
|
||||||
|
function move(amount) {
|
||||||
|
document.documentElement.scrollTop = amount
|
||||||
|
document.body.parentNode.scrollTop = amount
|
||||||
|
document.body.scrollTop = amount
|
||||||
|
}
|
||||||
|
|
||||||
|
function position() {
|
||||||
|
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} to
|
||||||
|
* @param {number} duration
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
export function scrollTo(to, duration, callback) {
|
||||||
|
const start = position()
|
||||||
|
const change = to - start
|
||||||
|
const increment = 20
|
||||||
|
let currentTime = 0
|
||||||
|
duration = (typeof (duration) === 'undefined') ? 500 : duration
|
||||||
|
var animateScroll = function () {
|
||||||
|
// increment the time
|
||||||
|
currentTime += increment
|
||||||
|
// find the value with the quadratic in-out easing function
|
||||||
|
var val = Math.easeInOutQuad(currentTime, start, change, duration)
|
||||||
|
// move the document.body
|
||||||
|
move(val)
|
||||||
|
// do the animation unless its over
|
||||||
|
if (currentTime < duration) {
|
||||||
|
requestAnimFrame(animateScroll)
|
||||||
|
} else {
|
||||||
|
if (callback && typeof (callback) === 'function') {
|
||||||
|
// the animation is done so lets callback
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animateScroll()
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']
|
||||||
|
|
||||||
|
export default elementIcons
|
|
@ -0,0 +1,10 @@
|
||||||
|
const req = require.context('../../../assets/icons/svg', false, /\.svg$/)
|
||||||
|
const requireAll = requireContext => requireContext.keys()
|
||||||
|
|
||||||
|
const re = /\.\/(.*)\.svg/
|
||||||
|
|
||||||
|
const svgIcons = requireAll(req).map(i => {
|
||||||
|
return i.match(re)[1]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default svgIcons
|
|
@ -0,0 +1,136 @@
|
||||||
|
<template>
|
||||||
|
<div :class="className" :style="{height:height,width:width}"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import resize from './mixins/resize'
|
||||||
|
|
||||||
|
require('echarts/theme/macarons') // echarts theme
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [resize],
|
||||||
|
props: {
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: 'chart'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '350px'
|
||||||
|
},
|
||||||
|
autoResize: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
chartData: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
chart: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
chartData: {
|
||||||
|
deep: true,
|
||||||
|
handler(val) {
|
||||||
|
this.setOptions(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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.setOptions(this.chartData)
|
||||||
|
},
|
||||||
|
setOptions({expectedData, actualData} = {}) {
|
||||||
|
this.chart.setOption({
|
||||||
|
xAxis: {
|
||||||
|
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||||
|
boundaryGap: false,
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
bottom: 20,
|
||||||
|
top: 30,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross'
|
||||||
|
},
|
||||||
|
padding: [5, 10]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['expected', 'actual']
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: 'expected', itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#FF005A',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#FF005A',
|
||||||
|
width: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
smooth: true,
|
||||||
|
type: 'line',
|
||||||
|
data: expectedData,
|
||||||
|
animationDuration: 2800,
|
||||||
|
animationEasing: 'cubicInOut'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actual',
|
||||||
|
smooth: true,
|
||||||
|
type: 'line',
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#3888fa',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#3888fa',
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: '#f3f8ff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: actualData,
|
||||||
|
animationDuration: 2800,
|
||||||
|
animationEasing: 'quadraticOut'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,181 @@
|
||||||
|
<template>
|
||||||
|
<el-row :gutter="40" class="panel-group">
|
||||||
|
<el-col :lg="6" :sm="12" :xs="12" class="card-panel-col">
|
||||||
|
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
|
||||||
|
<div class="card-panel-icon-wrapper icon-people">
|
||||||
|
<svg-icon class-name="card-panel-icon" icon-class="peoples"/>
|
||||||
|
</div>
|
||||||
|
<div class="card-panel-description">
|
||||||
|
<div class="card-panel-text">
|
||||||
|
访客
|
||||||
|
</div>
|
||||||
|
<count-to :duration="2600" :end-val="102400" :start-val="0" class="card-panel-num"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="6" :sm="12" :xs="12" class="card-panel-col">
|
||||||
|
<div class="card-panel" @click="handleSetLineChartData('messages')">
|
||||||
|
<div class="card-panel-icon-wrapper icon-message">
|
||||||
|
<svg-icon class-name="card-panel-icon" icon-class="message"/>
|
||||||
|
</div>
|
||||||
|
<div class="card-panel-description">
|
||||||
|
<div class="card-panel-text">
|
||||||
|
消息
|
||||||
|
</div>
|
||||||
|
<count-to :duration="3000" :end-val="81212" :start-val="0" class="card-panel-num"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="6" :sm="12" :xs="12" class="card-panel-col">
|
||||||
|
<div class="card-panel" @click="handleSetLineChartData('purchases')">
|
||||||
|
<div class="card-panel-icon-wrapper icon-money">
|
||||||
|
<svg-icon class-name="card-panel-icon" icon-class="money"/>
|
||||||
|
</div>
|
||||||
|
<div class="card-panel-description">
|
||||||
|
<div class="card-panel-text">
|
||||||
|
金额
|
||||||
|
</div>
|
||||||
|
<count-to :duration="3200" :end-val="9280" :start-val="0" class="card-panel-num"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="6" :sm="12" :xs="12" class="card-panel-col">
|
||||||
|
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
|
||||||
|
<div class="card-panel-icon-wrapper icon-shopping">
|
||||||
|
<svg-icon class-name="card-panel-icon" icon-class="shopping"/>
|
||||||
|
</div>
|
||||||
|
<div class="card-panel-description">
|
||||||
|
<div class="card-panel-text">
|
||||||
|
订单
|
||||||
|
</div>
|
||||||
|
<count-to :duration="3600" :end-val="13600" :start-val="0" class="card-panel-num"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CountTo from 'vue-count-to'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CountTo
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSetLineChartData(type) {
|
||||||
|
this.$emit('handleSetLineChartData', type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.panel-group {
|
||||||
|
margin-top: 18px;
|
||||||
|
|
||||||
|
.card-panel-col {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-panel {
|
||||||
|
height: 108px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #666;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
|
||||||
|
border-color: rgba(0, 0, 0, .05);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.card-panel-icon-wrapper {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-people {
|
||||||
|
background: #40c9c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-message {
|
||||||
|
background: #36a3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-money {
|
||||||
|
background: #f4516c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shopping {
|
||||||
|
background: #34bfa3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-people {
|
||||||
|
color: #40c9c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-message {
|
||||||
|
color: #36a3f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-money {
|
||||||
|
color: #f4516c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shopping {
|
||||||
|
color: #34bfa3
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-panel-icon-wrapper {
|
||||||
|
float: left;
|
||||||
|
margin: 14px 0 0 14px;
|
||||||
|
padding: 16px;
|
||||||
|
transition: all 0.38s ease-out;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-panel-icon {
|
||||||
|
float: left;
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-panel-description {
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 26px;
|
||||||
|
margin-left: 0px;
|
||||||
|
|
||||||
|
.card-panel-text {
|
||||||
|
line-height: 18px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-panel-num {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 550px) {
|
||||||
|
.card-panel-description {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-panel-icon-wrapper {
|
||||||
|
float: none !important;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 !important;
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
display: block;
|
||||||
|
margin: 14px auto !important;
|
||||||
|
float: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div :class="className" :style="{height:height,width:width}"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import resize from './mixins/resize'
|
||||||
|
|
||||||
|
require('echarts/theme/macarons') // echarts theme
|
||||||
|
|
||||||
|
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>
|
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div :class="className" :style="{height:height,width:width}"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import resize from './mixins/resize'
|
||||||
|
|
||||||
|
require('echarts/theme/macarons') // echarts theme
|
||||||
|
|
||||||
|
const animationDuration = 3000
|
||||||
|
|
||||||
|
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: 'axis',
|
||||||
|
axisPointer: { // 坐标轴指示器,坐标轴触发有效
|
||||||
|
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
radar: {
|
||||||
|
radius: '66%',
|
||||||
|
center: ['50%', '42%'],
|
||||||
|
splitNumber: 8,
|
||||||
|
splitArea: {
|
||||||
|
areaStyle: {
|
||||||
|
color: 'rgba(127,95,132,.3)',
|
||||||
|
opacity: 1,
|
||||||
|
shadowBlur: 45,
|
||||||
|
shadowColor: 'rgba(0,0,0,.5)',
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indicator: [
|
||||||
|
{name: 'Sales', max: 10000},
|
||||||
|
{name: 'Administration', max: 20000},
|
||||||
|
{name: 'Information Techology', max: 20000},
|
||||||
|
{name: 'Customer Support', max: 20000},
|
||||||
|
{name: 'Development', max: 20000},
|
||||||
|
{name: 'Marketing', max: 20000}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
left: 'center',
|
||||||
|
bottom: '10',
|
||||||
|
data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'radar',
|
||||||
|
symbolSize: 0,
|
||||||
|
areaStyle: {
|
||||||
|
normal: {
|
||||||
|
shadowBlur: 13,
|
||||||
|
shadowColor: 'rgba(0,0,0,.2)',
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 10,
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: [5000, 7000, 12000, 11000, 15000, 14000],
|
||||||
|
name: 'Allocated Budget'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: [4000, 9000, 15000, 15000, 13000, 11000],
|
||||||
|
name: 'Expected Spending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: [5500, 11000, 12000, 15000, 12000, 12000],
|
||||||
|
name: 'Actual Spending'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
animationDuration: animationDuration
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {debounce} from '@/utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
$_sidebarElm: null,
|
||||||
|
$_resizeHandler: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initListener()
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
if (!this.$_resizeHandler) {
|
||||||
|
// avoid duplication init
|
||||||
|
this.initListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
// when keep-alive chart activated, auto resize
|
||||||
|
this.resize()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.destroyListener()
|
||||||
|
},
|
||||||
|
deactivated() {
|
||||||
|
this.destroyListener()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// use $_ for mixins properties
|
||||||
|
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
||||||
|
$_sidebarResizeHandler(e) {
|
||||||
|
if (e.propertyName === 'width') {
|
||||||
|
this.$_resizeHandler()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initListener() {
|
||||||
|
this.$_resizeHandler = debounce(() => {
|
||||||
|
this.resize()
|
||||||
|
}, 100)
|
||||||
|
window.addEventListener('resize', this.$_resizeHandler)
|
||||||
|
|
||||||
|
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||||
|
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
|
||||||
|
},
|
||||||
|
destroyListener() {
|
||||||
|
window.removeEventListener('resize', this.$_resizeHandler)
|
||||||
|
this.$_resizeHandler = null
|
||||||
|
|
||||||
|
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
|
||||||
|
},
|
||||||
|
resize() {
|
||||||
|
const {chart} = this
|
||||||
|
chart && chart.resize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container home">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Index",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.home {
|
||||||
|
blockquote {
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin: 0 0 20px;
|
||||||
|
font-size: 17.5px;
|
||||||
|
border-left: 5px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #676a6c;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-log {
|
||||||
|
ol {
|
||||||
|
display: block;
|
||||||
|
list-style-type: decimal;
|
||||||
|
margin-block-start: 1em;
|
||||||
|
margin-block-end: 1em;
|
||||||
|
margin-inline-start: 0;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
padding-inline-start: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-editor-container">
|
||||||
|
|
||||||
|
<panel-group @handleSetLineChartData="handleSetLineChartData"/>
|
||||||
|
|
||||||
|
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||||
|
<line-chart :chart-data="lineChartData"/>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="32">
|
||||||
|
<el-col :lg="8" :sm="24" :xs="24">
|
||||||
|
<div class="chart-wrapper">
|
||||||
|
<raddar-chart/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="8" :sm="24" :xs="24">
|
||||||
|
<div class="chart-wrapper">
|
||||||
|
<pie-chart/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="8" :sm="24" :xs="24">
|
||||||
|
<div class="chart-wrapper">
|
||||||
|
<bar-chart/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PanelGroup from './dashboard/PanelGroup'
|
||||||
|
import LineChart from './dashboard/LineChart'
|
||||||
|
import RaddarChart from './dashboard/RaddarChart'
|
||||||
|
import PieChart from './dashboard/PieChart'
|
||||||
|
import BarChart from './dashboard/BarChart'
|
||||||
|
|
||||||
|
const lineChartData = {
|
||||||
|
newVisitis: {
|
||||||
|
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
||||||
|
actualData: [120, 82, 91, 154, 162, 140, 145]
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
expectedData: [200, 192, 120, 144, 160, 130, 140],
|
||||||
|
actualData: [180, 160, 151, 106, 145, 150, 130]
|
||||||
|
},
|
||||||
|
purchases: {
|
||||||
|
expectedData: [80, 100, 121, 104, 105, 90, 100],
|
||||||
|
actualData: [120, 90, 100, 138, 142, 130, 130]
|
||||||
|
},
|
||||||
|
shoppings: {
|
||||||
|
expectedData: [130, 140, 141, 142, 145, 150, 160],
|
||||||
|
actualData: [120, 82, 91, 154, 162, 140, 130]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Index',
|
||||||
|
components: {
|
||||||
|
PanelGroup,
|
||||||
|
LineChart,
|
||||||
|
RaddarChart,
|
||||||
|
PieChart,
|
||||||
|
BarChart
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
lineChartData: lineChartData.newVisitis
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSetLineChartData(type) {
|
||||||
|
this.lineChartData = lineChartData[type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dashboard-editor-container {
|
||||||
|
padding: 32px;
|
||||||
|
background-color: rgb(240, 242, 245);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.chart-wrapper {
|
||||||
|
background: #fff;
|
||||||
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.chart-wrapper {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,229 @@
|
||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||||
|
<h3 class="title">若依后台管理系统</h3>
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<el-input
|
||||||
|
v-model="loginForm.username"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="账号"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="user"/>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input
|
||||||
|
v-model="loginForm.password"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="密码"
|
||||||
|
type="password"
|
||||||
|
@keyup.enter.native="handleLogin"
|
||||||
|
>
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="password"/>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="captchaEnabled" prop="code">
|
||||||
|
<el-input
|
||||||
|
v-model="loginForm.code"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="验证码"
|
||||||
|
style="width: 63%"
|
||||||
|
@keyup.enter.native="handleLogin"
|
||||||
|
>
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="validCode"/>
|
||||||
|
</el-input>
|
||||||
|
<div class="login-code">
|
||||||
|
<img :src="codeUrl" class="login-code-img" @click="getCode"/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
||||||
|
<el-form-item style="width:100%;">
|
||||||
|
<el-button
|
||||||
|
:loading="loading"
|
||||||
|
size="medium"
|
||||||
|
style="width:100%;"
|
||||||
|
type="primary"
|
||||||
|
@click.native.prevent="handleLogin"
|
||||||
|
>
|
||||||
|
<span v-if="!loading">登 录</span>
|
||||||
|
<span v-else>登 录 中...</span>
|
||||||
|
</el-button>
|
||||||
|
<div v-if="register" style="float: right;">
|
||||||
|
<router-link :to="'/register'" class="link-type">立即注册</router-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<!-- 底部 -->
|
||||||
|
<div class="el-login-footer">
|
||||||
|
<span>Copyright © 2018-2023 muyu.vip All Rights Reserved.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getCodeImg} from "@/api/login";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import {decrypt, encrypt} from '@/utils/jsencrypt'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Login",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
codeUrl: "",
|
||||||
|
loginForm: {
|
||||||
|
username: "admin",
|
||||||
|
password: "admin123",
|
||||||
|
rememberMe: false,
|
||||||
|
code: "",
|
||||||
|
uuid: ""
|
||||||
|
},
|
||||||
|
loginRules: {
|
||||||
|
username: [
|
||||||
|
{required: true, trigger: "blur", message: "请输入您的账号"}
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{required: true, trigger: "blur", message: "请输入您的密码"}
|
||||||
|
],
|
||||||
|
code: [{required: true, trigger: "change", message: "请输入验证码"}]
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
// 验证码开关
|
||||||
|
captchaEnabled: true,
|
||||||
|
// 注册开关
|
||||||
|
register: true,
|
||||||
|
redirect: undefined
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: {
|
||||||
|
handler: function (route) {
|
||||||
|
this.redirect = route.query && route.query.redirect;
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getCode();
|
||||||
|
this.getCookie();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCode() {
|
||||||
|
getCodeImg().then(res => {
|
||||||
|
this.captchaEnabled = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;
|
||||||
|
if (this.captchaEnabled) {
|
||||||
|
this.codeUrl = "data:image/gif;base64," + res.data.img;
|
||||||
|
this.loginForm.uuid = res.data.uuid;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getCookie() {
|
||||||
|
const username = Cookies.get("username");
|
||||||
|
const password = Cookies.get("password");
|
||||||
|
const rememberMe = Cookies.get('rememberMe')
|
||||||
|
this.loginForm = {
|
||||||
|
username: username === undefined ? this.loginForm.username : username,
|
||||||
|
password: password === undefined ? this.loginForm.password : decrypt(password),
|
||||||
|
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleLogin() {
|
||||||
|
this.$refs.loginForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.loading = true;
|
||||||
|
if (this.loginForm.rememberMe) {
|
||||||
|
Cookies.set("username", this.loginForm.username, {expires: 30});
|
||||||
|
Cookies.set("password", encrypt(this.loginForm.password), {expires: 30});
|
||||||
|
Cookies.set('rememberMe', this.loginForm.rememberMe, {expires: 30});
|
||||||
|
} else {
|
||||||
|
Cookies.remove("username");
|
||||||
|
Cookies.remove("password");
|
||||||
|
Cookies.remove('rememberMe');
|
||||||
|
}
|
||||||
|
this.$store.dispatch("Login", this.loginForm).then(() => {
|
||||||
|
this.$router.push({path: this.redirect || "/"}).catch(() => {
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
if (this.captchaEnabled) {
|
||||||
|
this.getCode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" rel="stylesheet/scss">
|
||||||
|
.login {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url("../assets/images/login-background.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0px auto 30px auto;
|
||||||
|
text-align: center;
|
||||||
|
color: #707070;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #ffffff;
|
||||||
|
width: 400px;
|
||||||
|
padding: 25px 25px 5px 25px;
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
height: 38px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
height: 39px;
|
||||||
|
width: 14px;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-tip {
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
color: #bfbfbf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-code {
|
||||||
|
width: 33%;
|
||||||
|
height: 38px;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
img {
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-login-footer {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-code-img {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
created() {
|
||||||
|
const {params, query} = this.$route
|
||||||
|
const {path} = params
|
||||||
|
this.$router.replace({path: '/' + path, query})
|
||||||
|
},
|
||||||
|
render: function (h) {
|
||||||
|
return h() // avoid warning message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,219 @@
|
||||||
|
<template>
|
||||||
|
<div class="register">
|
||||||
|
<el-form ref="registerForm" :model="registerForm" :rules="registerRules" class="register-form">
|
||||||
|
<h3 class="title">若依后台管理系统</h3>
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<el-input v-model="registerForm.username" auto-complete="off" placeholder="账号" type="text">
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="user"/>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input
|
||||||
|
v-model="registerForm.password"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="密码"
|
||||||
|
type="password"
|
||||||
|
@keyup.enter.native="handleRegister"
|
||||||
|
>
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="password"/>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="confirmPassword">
|
||||||
|
<el-input
|
||||||
|
v-model="registerForm.confirmPassword"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="确认密码"
|
||||||
|
type="password"
|
||||||
|
@keyup.enter.native="handleRegister"
|
||||||
|
>
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="password"/>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="captchaEnabled" prop="code">
|
||||||
|
<el-input
|
||||||
|
v-model="registerForm.code"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="验证码"
|
||||||
|
style="width: 63%"
|
||||||
|
@keyup.enter.native="handleRegister"
|
||||||
|
>
|
||||||
|
<svg-icon slot="prefix" class="el-input__icon input-icon" icon-class="validCode"/>
|
||||||
|
</el-input>
|
||||||
|
<div class="register-code">
|
||||||
|
<img :src="codeUrl" class="register-code-img" @click="getCode"/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item style="width:100%;">
|
||||||
|
<el-button
|
||||||
|
:loading="loading"
|
||||||
|
size="medium"
|
||||||
|
style="width:100%;"
|
||||||
|
type="primary"
|
||||||
|
@click.native.prevent="handleRegister"
|
||||||
|
>
|
||||||
|
<span v-if="!loading">注 册</span>
|
||||||
|
<span v-else>注 册 中...</span>
|
||||||
|
</el-button>
|
||||||
|
<div style="float: right;">
|
||||||
|
<router-link :to="'/login'" class="link-type">使用已有账户登录</router-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<!-- 底部 -->
|
||||||
|
<div class="el-register-footer">
|
||||||
|
<span>Copyright © 2018-2023 muyu.vip All Rights Reserved.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getCodeImg, register} from "@/api/login";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Register",
|
||||||
|
data() {
|
||||||
|
const equalToPassword = (rule, value, callback) => {
|
||||||
|
if (this.registerForm.password !== value) {
|
||||||
|
callback(new Error("两次输入的密码不一致"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
codeUrl: "",
|
||||||
|
registerForm: {
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
code: "",
|
||||||
|
uuid: ""
|
||||||
|
},
|
||||||
|
registerRules: {
|
||||||
|
username: [
|
||||||
|
{required: true, trigger: "blur", message: "请输入您的账号"},
|
||||||
|
{min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur'}
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{required: true, trigger: "blur", message: "请输入您的密码"},
|
||||||
|
{min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur'}
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{required: true, trigger: "blur", message: "请再次输入您的密码"},
|
||||||
|
{required: true, validator: equalToPassword, trigger: "blur"}
|
||||||
|
],
|
||||||
|
code: [{required: true, trigger: "change", message: "请输入验证码"}]
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
captchaEnabled: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getCode();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCode() {
|
||||||
|
getCodeImg().then(res => {
|
||||||
|
this.captchaEnabled = res.data.captchaEnabled === undefined ? true : res.data.captchaEnabled;
|
||||||
|
if (this.captchaEnabled) {
|
||||||
|
this.codeUrl = "data:image/gif;base64," + res.data.img;
|
||||||
|
this.registerForm.uuid = res.data.uuid;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleRegister() {
|
||||||
|
this.$refs.registerForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.loading = true;
|
||||||
|
register(this.registerForm).then(res => {
|
||||||
|
const username = this.registerForm.username;
|
||||||
|
this.$alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", '系统提示', {
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
type: 'success'
|
||||||
|
}).then(() => {
|
||||||
|
this.$router.push("/login");
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
if (this.captchaEnabled) {
|
||||||
|
this.getCode();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" rel="stylesheet/scss">
|
||||||
|
.register {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url("../assets/images/login-background.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0px auto 30px auto;
|
||||||
|
text-align: center;
|
||||||
|
color: #707070;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-form {
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #ffffff;
|
||||||
|
width: 400px;
|
||||||
|
padding: 25px 25px 5px 25px;
|
||||||
|
|
||||||
|
.el-input {
|
||||||
|
height: 38px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
height: 39px;
|
||||||
|
width: 14px;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-tip {
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
color: #bfbfbf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-code {
|
||||||
|
width: 33%;
|
||||||
|
height: 38px;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
img {
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-register-footer {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-code-img {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,140 @@
|
||||||
|
<template>
|
||||||
|
<!-- 授权用户 -->
|
||||||
|
<el-dialog :visible.sync="visible" append-to-body title="选择用户" top="5vh" width="800px">
|
||||||
|
<el-form ref="queryForm" :inline="true" :model="queryParams" size="small">
|
||||||
|
<el-form-item label="用户名称" prop="userName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.userName"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户名称"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.phonenumber"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入手机号码"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-row>
|
||||||
|
<el-table ref="table" :data="userList" height="260px" @row-click="clickRow"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55"></el-table-column>
|
||||||
|
<el-table-column :show-overflow-tooltip="true" label="用户名称" prop="userName"/>
|
||||||
|
<el-table-column :show-overflow-tooltip="true" label="用户昵称" prop="nickName"/>
|
||||||
|
<el-table-column :show-overflow-tooltip="true" label="邮箱" prop="email"/>
|
||||||
|
<el-table-column :show-overflow-tooltip="true" label="手机" prop="phonenumber"/>
|
||||||
|
<el-table-column align="center" label="状态" prop="status">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="创建时间" prop="createTime" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="handleSelectUser">确 定</el-button>
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {authUserSelectAll, unallocatedUserList} from "@/api/system/role";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
dicts: ['sys_normal_disable'],
|
||||||
|
props: {
|
||||||
|
// 角色编号
|
||||||
|
roleId: {
|
||||||
|
type: [Number, String]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
visible: false,
|
||||||
|
// 选中数组值
|
||||||
|
userIds: [],
|
||||||
|
// 总条数
|
||||||
|
total: 0,
|
||||||
|
// 未授权用户数据
|
||||||
|
userList: [],
|
||||||
|
// 查询参数
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
roleId: undefined,
|
||||||
|
userName: undefined,
|
||||||
|
phonenumber: undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 显示弹框
|
||||||
|
show() {
|
||||||
|
this.queryParams.roleId = this.roleId;
|
||||||
|
this.getList();
|
||||||
|
this.visible = true;
|
||||||
|
},
|
||||||
|
clickRow(row) {
|
||||||
|
this.$refs.table.toggleRowSelection(row);
|
||||||
|
},
|
||||||
|
// 多选框选中数据
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.userIds = selection.map(item => item.userId);
|
||||||
|
},
|
||||||
|
// 查询表数据
|
||||||
|
getList() {
|
||||||
|
unallocatedUserList(this.queryParams).then(res => {
|
||||||
|
this.userList = res.data.rows;
|
||||||
|
this.total = res.data.total;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
resetQuery() {
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
/** 选择授权用户操作 */
|
||||||
|
handleSelectUser() {
|
||||||
|
const roleId = this.queryParams.roleId;
|
||||||
|
const userIds = this.userIds.join(",");
|
||||||
|
if (userIds == "") {
|
||||||
|
this.$modal.msgError("请选择要分配的用户");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
authUserSelectAll({roleId: roleId, userIds: userIds}).then(res => {
|
||||||
|
this.$modal.msgSuccess(res.data.msg);
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
this.visible = false;
|
||||||
|
this.$emit("ok");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,700 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<!--部门数据-->
|
||||||
|
<el-col :span="4" :xs="24">
|
||||||
|
<div class="head-container">
|
||||||
|
<el-input
|
||||||
|
v-model="deptName"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入部门名称"
|
||||||
|
prefix-icon="el-icon-search"
|
||||||
|
size="small"
|
||||||
|
style="margin-bottom: 20px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="head-container">
|
||||||
|
<el-tree
|
||||||
|
ref="tree"
|
||||||
|
:data="deptOptions"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
:props="defaultProps"
|
||||||
|
default-expand-all
|
||||||
|
highlight-current
|
||||||
|
node-key="id"
|
||||||
|
@node-click="handleNodeClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<!--用户数据-->
|
||||||
|
<el-col :span="20" :xs="24">
|
||||||
|
<el-form v-show="showSearch" ref="queryForm" :inline="true" :model="queryParams" label-width="68px"
|
||||||
|
size="small">
|
||||||
|
<el-form-item label="用户名称" prop="userName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.userName"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户名称"
|
||||||
|
style="width: 240px"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.phonenumber"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入手机号码"
|
||||||
|
style="width: 240px"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.status"
|
||||||
|
clearable
|
||||||
|
placeholder="用户状态"
|
||||||
|
style="width: 240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in dict.type.sys_normal_disable"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="创建时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
style="width: 240px"
|
||||||
|
type="daterange"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:add']"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="handleAdd"
|
||||||
|
>新增
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:edit']"
|
||||||
|
:disabled="single"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="success"
|
||||||
|
@click="handleUpdate"
|
||||||
|
>修改
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:remove']"
|
||||||
|
:disabled="multiple"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete"
|
||||||
|
>删除
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:import']"
|
||||||
|
icon="el-icon-upload2"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="info"
|
||||||
|
@click="handleImport"
|
||||||
|
>导入
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:export']"
|
||||||
|
icon="el-icon-download"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="warning"
|
||||||
|
@click="handleExport"
|
||||||
|
>导出
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :columns="columns" :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||||
|
<el-table-column align="center" type="selection" width="50"/>
|
||||||
|
<el-table-column v-if="columns[0].visible" key="userId" align="center" label="用户编号" prop="userId"/>
|
||||||
|
<el-table-column v-if="columns[1].visible" key="userName" :show-overflow-tooltip="true" align="center" label="用户名称"
|
||||||
|
prop="userName"/>
|
||||||
|
<el-table-column v-if="columns[2].visible" key="nickName" :show-overflow-tooltip="true" align="center" label="用户昵称"
|
||||||
|
prop="nickName"/>
|
||||||
|
<el-table-column v-if="columns[3].visible" key="deptName" :show-overflow-tooltip="true" align="center" label="部门"
|
||||||
|
prop="dept.deptName"/>
|
||||||
|
<el-table-column v-if="columns[4].visible" key="phonenumber" align="center" label="手机号码"
|
||||||
|
prop="phonenumber" width="120"/>
|
||||||
|
<el-table-column v-if="columns[5].visible" key="status" align="center" label="状态">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-switch
|
||||||
|
v-model="scope.row.status"
|
||||||
|
active-value="0"
|
||||||
|
inactive-value="1"
|
||||||
|
@change="handleStatusChange(scope.row)"
|
||||||
|
></el-switch>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column v-if="columns[6].visible" align="center" label="创建时间" prop="createTime" width="160">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
align="center"
|
||||||
|
class-name="small-padding fixed-width"
|
||||||
|
label="操作"
|
||||||
|
width="160"
|
||||||
|
>
|
||||||
|
<template v-if="scope.row.userId !== 1" slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:edit']"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
@click="handleUpdate(scope.row)"
|
||||||
|
>修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['system:user:remove']"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
>删除
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown v-hasPermi="['system:user:resetPwd', 'system:user:edit']" size="mini"
|
||||||
|
@command="(command) => handleCommand(command, scope.row)">
|
||||||
|
<el-button icon="el-icon-d-arrow-right" size="mini" type="text">更多</el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item v-hasPermi="['system:user:resetPwd']" command="handleResetPwd"
|
||||||
|
icon="el-icon-key">重置密码
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-hasPermi="['system:user:edit']" command="handleAuthRole"
|
||||||
|
icon="el-icon-circle-check">分配角色
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 添加或修改用户配置对话框 -->
|
||||||
|
<el-dialog :title="title" :visible.sync="open" append-to-body width="600px">
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="用户昵称" prop="nickName">
|
||||||
|
<el-input v-model="form.nickName" maxlength="30" placeholder="请输入用户昵称"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="归属部门" prop="deptId">
|
||||||
|
<treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
|
<el-input v-model="form.phonenumber" maxlength="11" placeholder="请输入手机号码"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-input v-model="form.email" maxlength="50" placeholder="请输入邮箱"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
|
||||||
|
<el-input v-model="form.userName" maxlength="30" placeholder="请输入用户名称"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
|
||||||
|
<el-input v-model="form.password" maxlength="20" placeholder="请输入用户密码" show-password
|
||||||
|
type="password"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="用户性别">
|
||||||
|
<el-select v-model="form.sex" placeholder="请选择性别">
|
||||||
|
<el-option
|
||||||
|
v-for="dict in dict.type.sys_user_sex"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-radio-group v-model="form.status">
|
||||||
|
<el-radio
|
||||||
|
v-for="dict in dict.type.sys_normal_disable"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.value"
|
||||||
|
>{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="岗位">
|
||||||
|
<el-select v-model="form.postIds" multiple placeholder="请选择岗位">
|
||||||
|
<el-option
|
||||||
|
v-for="item in postOptions"
|
||||||
|
:key="item.postId"
|
||||||
|
:disabled="item.status == 1"
|
||||||
|
:label="item.postName"
|
||||||
|
:value="item.postId"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="角色">
|
||||||
|
<el-select v-model="form.roleIds" multiple placeholder="请选择角色">
|
||||||
|
<el-option
|
||||||
|
v-for="item in roleOptions"
|
||||||
|
:key="item.roleId"
|
||||||
|
:disabled="item.status == 1"
|
||||||
|
:label="item.roleName"
|
||||||
|
:value="item.roleId"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="form.remark" placeholder="请输入内容" type="textarea"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 用户导入对话框 -->
|
||||||
|
<el-dialog :title="upload.title" :visible.sync="upload.open" append-to-body width="400px">
|
||||||
|
<el-upload
|
||||||
|
ref="upload"
|
||||||
|
:action="upload.url + '?updateSupport=' + upload.updateSupport"
|
||||||
|
:auto-upload="false"
|
||||||
|
:disabled="upload.isUploading"
|
||||||
|
:headers="upload.headers"
|
||||||
|
:limit="1"
|
||||||
|
:on-progress="handleFileUploadProgress"
|
||||||
|
:on-success="handleFileSuccess"
|
||||||
|
accept=".xlsx, .xls"
|
||||||
|
drag
|
||||||
|
>
|
||||||
|
<i class="el-icon-upload"></i>
|
||||||
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
<div slot="tip" class="el-upload__tip text-center">
|
||||||
|
<div slot="tip" class="el-upload__tip">
|
||||||
|
<el-checkbox v-model="upload.updateSupport"/>
|
||||||
|
是否更新已经存在的用户数据
|
||||||
|
</div>
|
||||||
|
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||||
|
<el-link :underline="false" style="font-size:12px;vertical-align: baseline;" type="primary"
|
||||||
|
@click="importTemplate">下载模板
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</el-upload>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||||
|
<el-button @click="upload.open = false">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
addUser,
|
||||||
|
changeUserStatus,
|
||||||
|
delUser,
|
||||||
|
deptTreeSelect,
|
||||||
|
getUser,
|
||||||
|
listUser,
|
||||||
|
resetUserPwd,
|
||||||
|
updateUser
|
||||||
|
} from "@/api/system/user";
|
||||||
|
import {getToken} from "@/utils/auth";
|
||||||
|
import Treeselect from "@riophae/vue-treeselect";
|
||||||
|
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "User",
|
||||||
|
dicts: ['sys_normal_disable', 'sys_user_sex'],
|
||||||
|
components: {Treeselect},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
loading: true,
|
||||||
|
// 选中数组
|
||||||
|
ids: [],
|
||||||
|
// 非单个禁用
|
||||||
|
single: true,
|
||||||
|
// 非多个禁用
|
||||||
|
multiple: true,
|
||||||
|
// 显示搜索条件
|
||||||
|
showSearch: true,
|
||||||
|
// 总条数
|
||||||
|
total: 0,
|
||||||
|
// 用户表格数据
|
||||||
|
userList: null,
|
||||||
|
// 弹出层标题
|
||||||
|
title: "",
|
||||||
|
// 部门树选项
|
||||||
|
deptOptions: undefined,
|
||||||
|
// 是否显示弹出层
|
||||||
|
open: false,
|
||||||
|
// 部门名称
|
||||||
|
deptName: undefined,
|
||||||
|
// 默认密码
|
||||||
|
initPassword: undefined,
|
||||||
|
// 日期范围
|
||||||
|
dateRange: [],
|
||||||
|
// 岗位选项
|
||||||
|
postOptions: [],
|
||||||
|
// 角色选项
|
||||||
|
roleOptions: [],
|
||||||
|
// 表单参数
|
||||||
|
form: {},
|
||||||
|
defaultProps: {
|
||||||
|
children: "children",
|
||||||
|
label: "label"
|
||||||
|
},
|
||||||
|
// 用户导入参数
|
||||||
|
upload: {
|
||||||
|
// 是否显示弹出层(用户导入)
|
||||||
|
open: false,
|
||||||
|
// 弹出层标题(用户导入)
|
||||||
|
title: "",
|
||||||
|
// 是否禁用上传
|
||||||
|
isUploading: false,
|
||||||
|
// 是否更新已经存在的用户数据
|
||||||
|
updateSupport: 0,
|
||||||
|
// 设置上传的请求头部
|
||||||
|
headers: {Authorization: "Bearer " + getToken()},
|
||||||
|
// 上传的地址
|
||||||
|
url: process.env.VUE_APP_BASE_API + "/system/user/importData"
|
||||||
|
},
|
||||||
|
// 查询参数
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
userName: undefined,
|
||||||
|
phonenumber: undefined,
|
||||||
|
status: undefined,
|
||||||
|
deptId: undefined
|
||||||
|
},
|
||||||
|
// 列信息
|
||||||
|
columns: [
|
||||||
|
{key: 0, label: `用户编号`, visible: true},
|
||||||
|
{key: 1, label: `用户名称`, visible: true},
|
||||||
|
{key: 2, label: `用户昵称`, visible: true},
|
||||||
|
{key: 3, label: `部门`, visible: true},
|
||||||
|
{key: 4, label: `手机号码`, visible: true},
|
||||||
|
{key: 5, label: `状态`, visible: true},
|
||||||
|
{key: 6, label: `创建时间`, visible: true}
|
||||||
|
],
|
||||||
|
// 表单校验
|
||||||
|
rules: {
|
||||||
|
userName: [
|
||||||
|
{required: true, message: "用户名称不能为空", trigger: "blur"},
|
||||||
|
{min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur'}
|
||||||
|
],
|
||||||
|
nickName: [
|
||||||
|
{required: true, message: "用户昵称不能为空", trigger: "blur"}
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{required: true, message: "用户密码不能为空", trigger: "blur"},
|
||||||
|
{min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur'}
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{
|
||||||
|
type: "email",
|
||||||
|
message: "请输入正确的邮箱地址",
|
||||||
|
trigger: ["blur", "change"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
phonenumber: [
|
||||||
|
{
|
||||||
|
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||||
|
message: "请输入正确的手机号码",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 根据名称筛选部门树
|
||||||
|
deptName(val) {
|
||||||
|
this.$refs.tree.filter(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
this.getDeptTree();
|
||||||
|
this.getConfigKey("sys.user.initPassword").then(response => {
|
||||||
|
this.initPassword = response.data.msg;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 查询用户列表 */
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
|
||||||
|
this.userList = response.data.rows;
|
||||||
|
this.total = response.data.total;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
/** 查询部门下拉树结构 */
|
||||||
|
getDeptTree() {
|
||||||
|
deptTreeSelect().then(response => {
|
||||||
|
this.deptOptions = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 筛选节点
|
||||||
|
filterNode(value, data) {
|
||||||
|
if (!value) return true;
|
||||||
|
return data.label.indexOf(value) !== -1;
|
||||||
|
},
|
||||||
|
// 节点单击事件
|
||||||
|
handleNodeClick(data) {
|
||||||
|
this.queryParams.deptId = data.id;
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
// 用户状态修改
|
||||||
|
handleStatusChange(row) {
|
||||||
|
let text = row.status === "0" ? "启用" : "停用";
|
||||||
|
this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
|
||||||
|
return changeUserStatus(row.userId, row.status);
|
||||||
|
}).then(() => {
|
||||||
|
this.$modal.msgSuccess(text + "成功");
|
||||||
|
}).catch(function () {
|
||||||
|
row.status = row.status === "0" ? "1" : "0";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 取消按钮
|
||||||
|
cancel() {
|
||||||
|
this.open = false;
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
// 表单重置
|
||||||
|
reset() {
|
||||||
|
this.form = {
|
||||||
|
userId: undefined,
|
||||||
|
deptId: undefined,
|
||||||
|
userName: undefined,
|
||||||
|
nickName: undefined,
|
||||||
|
password: undefined,
|
||||||
|
phonenumber: undefined,
|
||||||
|
email: undefined,
|
||||||
|
sex: undefined,
|
||||||
|
status: "0",
|
||||||
|
remark: undefined,
|
||||||
|
postIds: [],
|
||||||
|
roleIds: []
|
||||||
|
};
|
||||||
|
this.resetForm("form");
|
||||||
|
},
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.queryParams.deptId = undefined;
|
||||||
|
this.$refs.tree.setCurrentKey(null);
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
// 多选框选中数据
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.ids = selection.map(item => item.userId);
|
||||||
|
this.single = selection.length != 1;
|
||||||
|
this.multiple = !selection.length;
|
||||||
|
},
|
||||||
|
// 更多操作触发
|
||||||
|
handleCommand(command, row) {
|
||||||
|
switch (command) {
|
||||||
|
case "handleResetPwd":
|
||||||
|
this.handleResetPwd(row);
|
||||||
|
break;
|
||||||
|
case "handleAuthRole":
|
||||||
|
this.handleAuthRole(row);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 新增按钮操作 */
|
||||||
|
handleAdd() {
|
||||||
|
this.reset();
|
||||||
|
getUser().then(response => {
|
||||||
|
this.postOptions = response.data.posts;
|
||||||
|
this.roleOptions = response.data.roles;
|
||||||
|
this.open = true;
|
||||||
|
this.title = "添加用户";
|
||||||
|
this.form.password = this.initPassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 修改按钮操作 */
|
||||||
|
handleUpdate(row) {
|
||||||
|
this.reset();
|
||||||
|
const userId = row.userId || this.ids;
|
||||||
|
getUser(userId).then(response => {
|
||||||
|
this.form = response.data.sysUser;
|
||||||
|
this.postOptions = response.data.posts;
|
||||||
|
this.roleOptions = response.data.roles;
|
||||||
|
this.$set(this.form, "postIds", response.data.postIds);
|
||||||
|
this.$set(this.form, "roleIds", response.data.roleIds);
|
||||||
|
this.open = true;
|
||||||
|
this.title = "修改用户";
|
||||||
|
this.form.password = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 重置密码按钮操作 */
|
||||||
|
handleResetPwd(row) {
|
||||||
|
this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
closeOnClickModal: false,
|
||||||
|
inputPattern: /^.{5,20}$/,
|
||||||
|
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间"
|
||||||
|
}).then(({value}) => {
|
||||||
|
resetUserPwd(row.userId, value).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功,新密码是:" + value);
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 分配角色操作 */
|
||||||
|
handleAuthRole: function (row) {
|
||||||
|
const userId = row.userId;
|
||||||
|
this.$router.push("/system/user-auth/role/" + userId);
|
||||||
|
},
|
||||||
|
/** 提交按钮 */
|
||||||
|
submitForm: function () {
|
||||||
|
this.$refs["form"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (this.form.userId != undefined) {
|
||||||
|
updateUser(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
addUser(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("新增成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
handleDelete(row) {
|
||||||
|
const userIds = row.userId || this.ids;
|
||||||
|
this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function () {
|
||||||
|
return delUser(userIds);
|
||||||
|
}).then(() => {
|
||||||
|
this.getList();
|
||||||
|
this.$modal.msgSuccess("删除成功");
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
handleExport() {
|
||||||
|
this.download('system/user/export', {
|
||||||
|
...this.queryParams
|
||||||
|
}, `user_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
/** 导入按钮操作 */
|
||||||
|
handleImport() {
|
||||||
|
this.upload.title = "用户导入";
|
||||||
|
this.upload.open = true;
|
||||||
|
},
|
||||||
|
/** 下载模板操作 */
|
||||||
|
importTemplate() {
|
||||||
|
this.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
// 文件上传中处理
|
||||||
|
handleFileUploadProgress(event, file, fileList) {
|
||||||
|
this.upload.isUploading = true;
|
||||||
|
},
|
||||||
|
// 文件上传成功处理
|
||||||
|
handleFileSuccess(response, file, fileList) {
|
||||||
|
this.upload.open = false;
|
||||||
|
this.upload.isUploading = false;
|
||||||
|
this.$refs.upload.clearFiles();
|
||||||
|
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.data.msg + "</div>", "导入结果", {dangerouslyUseHTMLString: true});
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
// 提交上传文件
|
||||||
|
submitFileForm() {
|
||||||
|
this.$refs.upload.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6" :xs="24">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span>个人信息</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-center">
|
||||||
|
<userAvatar/>
|
||||||
|
</div>
|
||||||
|
<ul class="list-group list-group-striped">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="user"/>
|
||||||
|
用户名称
|
||||||
|
<div class="pull-right">{{ user.userName }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="phone"/>
|
||||||
|
手机号码
|
||||||
|
<div class="pull-right">{{ user.phonenumber }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="email"/>
|
||||||
|
用户邮箱
|
||||||
|
<div class="pull-right">{{ user.email }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="tree"/>
|
||||||
|
所属部门
|
||||||
|
<div v-if="user.dept" class="pull-right">{{ user.dept.deptName }} / {{ postGroup }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="peoples"/>
|
||||||
|
所属角色
|
||||||
|
<div class="pull-right">{{ roleGroup }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="date"/>
|
||||||
|
创建日期
|
||||||
|
<div class="pull-right">{{ user.createTime }}</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="18" :xs="24">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span>基本资料</span>
|
||||||
|
</div>
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="基本资料" name="userinfo">
|
||||||
|
<userInfo :user="user"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="修改密码" name="resetPwd">
|
||||||
|
<resetPwd/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import userAvatar from "./userAvatar";
|
||||||
|
import userInfo from "./userInfo";
|
||||||
|
import resetPwd from "./resetPwd";
|
||||||
|
import {getUserProfile} from "@/api/system/user";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Profile",
|
||||||
|
components: {userAvatar, userInfo, resetPwd},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: {},
|
||||||
|
roleGroup: {},
|
||||||
|
postGroup: {},
|
||||||
|
activeTab: "userinfo"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getUser();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getUser() {
|
||||||
|
getUserProfile().then(response => {
|
||||||
|
this.user = response.data.sysUser;
|
||||||
|
this.roleGroup = response.data.roleGroup;
|
||||||
|
this.postGroup = response.data.postGroup;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,68 @@
|
||||||
|
<template>
|
||||||
|
<el-form ref="form" :model="user" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item label="旧密码" prop="oldPassword">
|
||||||
|
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" show-password type="password"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="新密码" prop="newPassword">
|
||||||
|
<el-input v-model="user.newPassword" placeholder="请输入新密码" show-password type="password"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="确认密码" prop="confirmPassword">
|
||||||
|
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" show-password type="password"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button size="mini" type="primary" @click="submit">保存</el-button>
|
||||||
|
<el-button size="mini" type="danger" @click="close">关闭</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {updateUserPwd} from "@/api/system/user";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
const equalToPassword = (rule, value, callback) => {
|
||||||
|
if (this.user.newPassword !== value) {
|
||||||
|
callback(new Error("两次输入的密码不一致"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
oldPassword: undefined,
|
||||||
|
newPassword: undefined,
|
||||||
|
confirmPassword: undefined
|
||||||
|
},
|
||||||
|
// 表单校验
|
||||||
|
rules: {
|
||||||
|
oldPassword: [
|
||||||
|
{required: true, message: "旧密码不能为空", trigger: "blur"}
|
||||||
|
],
|
||||||
|
newPassword: [
|
||||||
|
{required: true, message: "新密码不能为空", trigger: "blur"},
|
||||||
|
{min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur"}
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{required: true, message: "确认密码不能为空", trigger: "blur"},
|
||||||
|
{required: true, validator: equalToPassword, trigger: "blur"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.$refs["form"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.$tab.closePage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,184 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="user-info-head" @click="editCropper()"><img class="img-circle img-lg" title="点击上传头像"
|
||||||
|
v-bind:src="options.img"/></div>
|
||||||
|
<el-dialog :title="title" :visible.sync="open" append-to-body width="800px" @close="closeDialog"
|
||||||
|
@opened="modalOpened">
|
||||||
|
<el-row>
|
||||||
|
<el-col :md="12" :style="{height: '350px'}" :xs="24">
|
||||||
|
<vue-cropper
|
||||||
|
v-if="visible"
|
||||||
|
ref="cropper"
|
||||||
|
:autoCrop="options.autoCrop"
|
||||||
|
:autoCropHeight="options.autoCropHeight"
|
||||||
|
:autoCropWidth="options.autoCropWidth"
|
||||||
|
:fixedBox="options.fixedBox"
|
||||||
|
:img="options.img"
|
||||||
|
:info="true"
|
||||||
|
:outputType="options.outputType"
|
||||||
|
@realTime="realTime"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :md="12" :style="{height: '350px'}" :xs="24">
|
||||||
|
<div class="avatar-upload-preview">
|
||||||
|
<img :src="previews.url" :style="previews.img"/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<br/>
|
||||||
|
<el-row>
|
||||||
|
<el-col :lg="2" :sm="3" :xs="3">
|
||||||
|
<el-upload :before-upload="beforeUpload" :http-request="requestUpload" :show-file-list="false" action="#">
|
||||||
|
<el-button size="small">
|
||||||
|
选择
|
||||||
|
<i class="el-icon-upload el-icon--right"></i>
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{span: 1, offset: 2}" :sm="2" :xs="2">
|
||||||
|
<el-button icon="el-icon-plus" size="small" @click="changeScale(1)"></el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{span: 1, offset: 1}" :sm="2" :xs="2">
|
||||||
|
<el-button icon="el-icon-minus" size="small" @click="changeScale(-1)"></el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{span: 1, offset: 1}" :sm="2" :xs="2">
|
||||||
|
<el-button icon="el-icon-refresh-left" size="small" @click="rotateLeft()"></el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{span: 1, offset: 1}" :sm="2" :xs="2">
|
||||||
|
<el-button icon="el-icon-refresh-right" size="small" @click="rotateRight()"></el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{span: 2, offset: 6}" :sm="2" :xs="2">
|
||||||
|
<el-button size="small" type="primary" @click="uploadImg()">提 交</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import store from "@/store";
|
||||||
|
import {VueCropper} from "vue-cropper";
|
||||||
|
import {uploadAvatar} from "@/api/system/user";
|
||||||
|
import {debounce} from '@/utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {VueCropper},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 是否显示弹出层
|
||||||
|
open: false,
|
||||||
|
// 是否显示cropper
|
||||||
|
visible: false,
|
||||||
|
// 弹出层标题
|
||||||
|
title: "修改头像",
|
||||||
|
options: {
|
||||||
|
img: store.getters.avatar, //裁剪图片的地址
|
||||||
|
autoCrop: true, // 是否默认生成截图框
|
||||||
|
autoCropWidth: 200, // 默认生成截图框宽度
|
||||||
|
autoCropHeight: 200, // 默认生成截图框高度
|
||||||
|
fixedBox: true, // 固定截图框大小 不允许改变
|
||||||
|
outputType: "png" // 默认生成截图为PNG格式
|
||||||
|
},
|
||||||
|
previews: {},
|
||||||
|
resizeHandler: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 编辑头像
|
||||||
|
editCropper() {
|
||||||
|
this.open = true;
|
||||||
|
},
|
||||||
|
// 打开弹出层结束时的回调
|
||||||
|
modalOpened() {
|
||||||
|
this.visible = true;
|
||||||
|
if (!this.resizeHandler) {
|
||||||
|
this.resizeHandler = debounce(() => {
|
||||||
|
this.refresh()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
window.addEventListener("resize", this.resizeHandler)
|
||||||
|
},
|
||||||
|
// 刷新组件
|
||||||
|
refresh() {
|
||||||
|
this.$refs.cropper.refresh();
|
||||||
|
},
|
||||||
|
// 覆盖默认的上传行为
|
||||||
|
requestUpload() {
|
||||||
|
},
|
||||||
|
// 向左旋转
|
||||||
|
rotateLeft() {
|
||||||
|
this.$refs.cropper.rotateLeft();
|
||||||
|
},
|
||||||
|
// 向右旋转
|
||||||
|
rotateRight() {
|
||||||
|
this.$refs.cropper.rotateRight();
|
||||||
|
},
|
||||||
|
// 图片缩放
|
||||||
|
changeScale(num) {
|
||||||
|
num = num || 1;
|
||||||
|
this.$refs.cropper.changeScale(num);
|
||||||
|
},
|
||||||
|
// 上传预处理
|
||||||
|
beforeUpload(file) {
|
||||||
|
if (file.type.indexOf("image/") == -1) {
|
||||||
|
this.$modal.msgError("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。");
|
||||||
|
} else {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => {
|
||||||
|
this.options.img = reader.result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 上传图片
|
||||||
|
uploadImg() {
|
||||||
|
this.$refs.cropper.getCropBlob(data => {
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("avatarfile", data);
|
||||||
|
uploadAvatar(formData).then(response => {
|
||||||
|
this.open = false;
|
||||||
|
this.options.img = process.env.VUE_APP_BASE_API + response.data.imgUrl;
|
||||||
|
store.commit('SET_AVATAR', this.options.img);
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
this.visible = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 实时预览
|
||||||
|
realTime(data) {
|
||||||
|
this.previews = data;
|
||||||
|
},
|
||||||
|
// 关闭窗口
|
||||||
|
closeDialog() {
|
||||||
|
this.options.img = store.getters.avatar
|
||||||
|
this.visible = false;
|
||||||
|
window.removeEventListener("resize", this.resizeHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.user-info-head {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info-head:hover:after {
|
||||||
|
content: '+';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
color: #eee;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
font-size: 24px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 110px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<el-form ref="form" :model="user" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item label="用户昵称" prop="nickName">
|
||||||
|
<el-input v-model="user.nickName" maxlength="30"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
|
<el-input v-model="user.phonenumber" maxlength="11"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-input v-model="user.email" maxlength="50"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="性别">
|
||||||
|
<el-radio-group v-model="user.sex">
|
||||||
|
<el-radio label="0">男</el-radio>
|
||||||
|
<el-radio label="1">女</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button size="mini" type="primary" @click="submit">保存</el-button>
|
||||||
|
<el-button size="mini" type="danger" @click="close">关闭</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {updateUserProfile} from "@/api/system/user";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 表单校验
|
||||||
|
rules: {
|
||||||
|
nickName: [
|
||||||
|
{required: true, message: "用户昵称不能为空", trigger: "blur"}
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{required: true, message: "邮箱地址不能为空", trigger: "blur"},
|
||||||
|
{
|
||||||
|
type: "email",
|
||||||
|
message: "请输入正确的邮箱地址",
|
||||||
|
trigger: ["blur", "change"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
phonenumber: [
|
||||||
|
{required: true, message: "手机号码不能为空", trigger: "blur"},
|
||||||
|
{
|
||||||
|
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||||
|
message: "请输入正确的手机号码",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.$refs["form"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
updateUserProfile(this.user).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.$tab.closePage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,108 @@
|
||||||
|
<script>
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import render from '@/utils/generator/render'
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
itemBtns(h, element, index, parent) {
|
||||||
|
const {copyItem, deleteItem} = this.$listeners
|
||||||
|
return [
|
||||||
|
<span class="drawing-item-copy" title="复制" onClick={event => {
|
||||||
|
copyItem(element, parent);
|
||||||
|
event.stopPropagation()
|
||||||
|
}}>
|
||||||
|
<i class="el-icon-copy-document"/>
|
||||||
|
</span>,
|
||||||
|
<span class="drawing-item-delete" title="删除" onClick={event => {
|
||||||
|
deleteItem(index, parent);
|
||||||
|
event.stopPropagation()
|
||||||
|
}}>
|
||||||
|
<i class="el-icon-delete"/>
|
||||||
|
</span>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const layouts = {
|
||||||
|
colFormItem(h, element, index, parent) {
|
||||||
|
const {activeItem} = this.$listeners
|
||||||
|
let className = this.activeId === element.formId ? 'drawing-item active-from-item' : 'drawing-item'
|
||||||
|
if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
|
||||||
|
return (
|
||||||
|
<el-col span={element.span} class={className}
|
||||||
|
nativeOnClick={event => {
|
||||||
|
activeItem(element);
|
||||||
|
event.stopPropagation()
|
||||||
|
}}>
|
||||||
|
<el-form-item label-width={element.labelWidth ? `${element.labelWidth}px` : null}
|
||||||
|
label={element.label} required={element.required}>
|
||||||
|
<render key={element.renderKey} conf={element} onInput={event => {
|
||||||
|
this.$set(element, 'defaultValue', event)
|
||||||
|
}}/>
|
||||||
|
</el-form-item>
|
||||||
|
{components.itemBtns.apply(this, arguments)}
|
||||||
|
</el-col>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
rowFormItem(h, element, index, parent) {
|
||||||
|
const {activeItem} = this.$listeners
|
||||||
|
const className = this.activeId === element.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item'
|
||||||
|
let child = renderChildren.apply(this, arguments)
|
||||||
|
if (element.type === 'flex') {
|
||||||
|
child = <el-row type={element.type} justify={element.justify} align={element.align}>
|
||||||
|
{child}
|
||||||
|
</el-row>
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<el-col span={element.span}>
|
||||||
|
<el-row gutter={element.gutter} class={className}
|
||||||
|
nativeOnClick={event => {
|
||||||
|
activeItem(element);
|
||||||
|
event.stopPropagation()
|
||||||
|
}}>
|
||||||
|
<span class="component-name">{element.componentName}</span>
|
||||||
|
<draggable list={element.children} animation={340} group="componentsGroup" class="drag-wrapper">
|
||||||
|
{child}
|
||||||
|
</draggable>
|
||||||
|
{components.itemBtns.apply(this, arguments)}
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChildren(h, element, index, parent) {
|
||||||
|
if (!Array.isArray(element.children)) return null
|
||||||
|
return element.children.map((el, i) => {
|
||||||
|
const layout = layouts[el.layout]
|
||||||
|
if (layout) {
|
||||||
|
return layout.call(this, h, el, i, element.children)
|
||||||
|
}
|
||||||
|
return layoutIsNotFound()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function layoutIsNotFound() {
|
||||||
|
throw new Error(`没有与${this.element.layout}匹配的layout`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
render,
|
||||||
|
draggable
|
||||||
|
},
|
||||||
|
props: [
|
||||||
|
'element',
|
||||||
|
'index',
|
||||||
|
'drawingList',
|
||||||
|
'activeId',
|
||||||
|
'formConf'
|
||||||
|
],
|
||||||
|
render(h) {
|
||||||
|
const layout = layouts[this.element.layout]
|
||||||
|
|
||||||
|
if (layout) {
|
||||||
|
return layout.call(this, h, this.element, this.index, this.drawingList)
|
||||||
|
}
|
||||||
|
return layoutIsNotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,131 @@
|
||||||
|
<template>
|
||||||
|
<div class="icon-dialog">
|
||||||
|
<el-dialog
|
||||||
|
:modal-append-to-body="false"
|
||||||
|
v-bind="$attrs"
|
||||||
|
width="980px"
|
||||||
|
@close="onClose"
|
||||||
|
@open="onOpen"
|
||||||
|
v-on="$listeners"
|
||||||
|
>
|
||||||
|
<div slot="title">
|
||||||
|
选择图标
|
||||||
|
<el-input
|
||||||
|
v-model="key"
|
||||||
|
:style="{width: '260px'}"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入图标名称"
|
||||||
|
prefix-icon="el-icon-search"
|
||||||
|
size="mini"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ul class="icon-ul">
|
||||||
|
<li
|
||||||
|
v-for="icon in iconList"
|
||||||
|
:key="icon"
|
||||||
|
:class="active===icon?'active-item':''"
|
||||||
|
@click="onSelect(icon)"
|
||||||
|
>
|
||||||
|
<i :class="icon"/>
|
||||||
|
<div>{{ icon }}</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import iconList from '@/utils/generator/icon.json'
|
||||||
|
|
||||||
|
const originList = iconList.map(name => `el-icon-${name}`)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: ['current'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
iconList: originList,
|
||||||
|
active: null,
|
||||||
|
key: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
key(val) {
|
||||||
|
if (val) {
|
||||||
|
this.iconList = originList.filter(name => name.indexOf(val) > -1)
|
||||||
|
} else {
|
||||||
|
this.iconList = originList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onOpen() {
|
||||||
|
this.active = this.current
|
||||||
|
this.key = ''
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
},
|
||||||
|
onSelect(icon) {
|
||||||
|
this.active = icon
|
||||||
|
this.$emit('select', icon)
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.icon-ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 16.66%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 108px;
|
||||||
|
padding: 15px 6px 6px 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active-item {
|
||||||
|
background: #e1f3fb;
|
||||||
|
color: #7a6df0
|
||||||
|
}
|
||||||
|
|
||||||
|
> i {
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dialog {
|
||||||
|
::v-deep .el-dialog {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 4vh !important;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: 92vh;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.el-dialog__header {
|
||||||
|
padding-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
margin: 0 20px 20px 20px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,956 @@
|
||||||
|
<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 :href="documentLink" class="document-link" target="_blank" title="查看组件文档">
|
||||||
|
<i class="el-icon-link"/>
|
||||||
|
</a>
|
||||||
|
<el-scrollbar class="right-scrollbar">
|
||||||
|
<!-- 组件属性 -->
|
||||||
|
<el-form v-show="currentTab==='field' && showField" label-width="90px" size="small">
|
||||||
|
<el-form-item v-if="activeData.changeTag" label="组件类型">
|
||||||
|
<el-select
|
||||||
|
v-model="activeData.tagIcon"
|
||||||
|
:style="{width: '100%'}"
|
||||||
|
placeholder="请选择组件类型"
|
||||||
|
@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 :icon-class="item.tagIcon" class="node-icon"/>
|
||||||
|
<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" :marks="{12:''}" :max="24" :min="1" @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" :style="{width: '100%'}" placeholder="请选择水平排列">
|
||||||
|
<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" placeholder="请输入标签宽度" type="number"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度">
|
||||||
|
<el-input v-model="activeData.style.width" clearable placeholder="请输入组件宽度"/>
|
||||||
|
</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
|
||||||
|
:min="0"
|
||||||
|
:value="activeData.min"
|
||||||
|
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
|
||||||
|
:min="0"
|
||||||
|
:value="activeData.max"
|
||||||
|
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"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请选择时间类型"
|
||||||
|
@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"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择文件类型"
|
||||||
|
>
|
||||||
|
<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" clearable placeholder="请输入上传地址"/>
|
||||||
|
</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
|
||||||
|
:animation="340"
|
||||||
|
:list="activeData.options"
|
||||||
|
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
|
||||||
|
:value="item.value"
|
||||||
|
placeholder="选项值"
|
||||||
|
size="small"
|
||||||
|
@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
|
||||||
|
icon="el-icon-circle-plus-outline"
|
||||||
|
style="padding-bottom: 0"
|
||||||
|
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'"
|
||||||
|
:data="activeData.options"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
:render-content="renderContent"
|
||||||
|
draggable
|
||||||
|
node-key="id"
|
||||||
|
/>
|
||||||
|
<div v-if="activeData.dataType === 'static'" style="margin-left: 20px">
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-circle-plus-outline"
|
||||||
|
style="padding-bottom: 0"
|
||||||
|
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']"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请选择颜色格式"
|
||||||
|
@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"
|
||||||
|
default-expand-all
|
||||||
|
draggable
|
||||||
|
node-key="renderKey"
|
||||||
|
>
|
||||||
|
<span slot-scope="{ node, data }">
|
||||||
|
<span class="node-label">
|
||||||
|
<svg-icon :icon-class="data.tagIcon" class="node-icon"/>
|
||||||
|
{{ 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'" label-width="90px" size="small">
|
||||||
|
<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 :current="activeData[currentIconModel]" :visible.sync="iconsVisible" @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} 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>
|
|
@ -0,0 +1,152 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:modal-append-to-body="false"
|
||||||
|
v-bind="$attrs"
|
||||||
|
@close="onClose"
|
||||||
|
@open="onOpen"
|
||||||
|
v-on="$listeners"
|
||||||
|
>
|
||||||
|
<el-row :gutter="0">
|
||||||
|
<el-form
|
||||||
|
ref="elForm"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="100px"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item
|
||||||
|
label="选项名"
|
||||||
|
prop="label"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.label"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入选项名"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item
|
||||||
|
label="选项值"
|
||||||
|
prop="value"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.value"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入选项值"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
slot="append"
|
||||||
|
v-model="dataType"
|
||||||
|
:style="{width: '100px'}"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in dataTypeOptions"
|
||||||
|
:key="index"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-form>
|
||||||
|
</el-row>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="handleConfirm"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="close">
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import {isNumberStr} from '@/utils/index'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {},
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: [],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: 100,
|
||||||
|
formData: {
|
||||||
|
label: undefined,
|
||||||
|
value: undefined
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
label: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入选项名',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入选项值',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
dataType: 'string',
|
||||||
|
dataTypeOptions: [
|
||||||
|
{
|
||||||
|
label: '字符串',
|
||||||
|
value: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '数字',
|
||||||
|
value: 'number'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {
|
||||||
|
// eslint-disable-next-line func-names
|
||||||
|
'formData.value': function (val) {
|
||||||
|
this.dataType = isNumberStr(val) ? 'number' : 'string'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onOpen() {
|
||||||
|
this.formData = {
|
||||||
|
label: undefined,
|
||||||
|
value: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
},
|
||||||
|
handleConfirm() {
|
||||||
|
this.$refs.elForm.validate(valid => {
|
||||||
|
if (!valid) return
|
||||||
|
if (this.dataType === 'number') {
|
||||||
|
this.formData.value = parseFloat(this.formData.value)
|
||||||
|
}
|
||||||
|
this.formData.id = this.id++
|
||||||
|
this.$emit('commit', this.formData)
|
||||||
|
this.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,837 @@
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="left-board">
|
||||||
|
<div class="logo-wrapper">
|
||||||
|
<div class="logo">
|
||||||
|
<img :src="logo" alt="logo"> Form Generator
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-scrollbar class="left-scrollbar">
|
||||||
|
<div class="components-list">
|
||||||
|
<div class="components-title">
|
||||||
|
<svg-icon icon-class="component"/>
|
||||||
|
输入型组件
|
||||||
|
</div>
|
||||||
|
<draggable
|
||||||
|
:clone="cloneComponent"
|
||||||
|
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
|
||||||
|
:list="inputComponents"
|
||||||
|
:sort="false"
|
||||||
|
class="components-draggable"
|
||||||
|
draggable=".components-item"
|
||||||
|
@end="onEnd"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(element, index) in inputComponents" :key="index" class="components-item"
|
||||||
|
@click="addComponent(element)"
|
||||||
|
>
|
||||||
|
<div class="components-body">
|
||||||
|
<svg-icon :icon-class="element.tagIcon"/>
|
||||||
|
{{ element.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
<div class="components-title">
|
||||||
|
<svg-icon icon-class="component"/>
|
||||||
|
选择型组件
|
||||||
|
</div>
|
||||||
|
<draggable
|
||||||
|
:clone="cloneComponent"
|
||||||
|
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
|
||||||
|
:list="selectComponents"
|
||||||
|
:sort="false"
|
||||||
|
class="components-draggable"
|
||||||
|
draggable=".components-item"
|
||||||
|
@end="onEnd"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(element, index) in selectComponents"
|
||||||
|
:key="index"
|
||||||
|
class="components-item"
|
||||||
|
@click="addComponent(element)"
|
||||||
|
>
|
||||||
|
<div class="components-body">
|
||||||
|
<svg-icon :icon-class="element.tagIcon"/>
|
||||||
|
{{ element.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
<div class="components-title">
|
||||||
|
<svg-icon icon-class="component"/>
|
||||||
|
布局型组件
|
||||||
|
</div>
|
||||||
|
<draggable
|
||||||
|
:clone="cloneComponent" :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
|
||||||
|
:list="layoutComponents" :sort="false"
|
||||||
|
class="components-draggable" draggable=".components-item" @end="onEnd"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(element, index) in layoutComponents" :key="index" class="components-item"
|
||||||
|
@click="addComponent(element)"
|
||||||
|
>
|
||||||
|
<div class="components-body">
|
||||||
|
<svg-icon :icon-class="element.tagIcon"/>
|
||||||
|
{{ element.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="center-board">
|
||||||
|
<div class="action-bar">
|
||||||
|
<el-button icon="el-icon-download" type="text" @click="download">
|
||||||
|
导出vue文件
|
||||||
|
</el-button>
|
||||||
|
<el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">
|
||||||
|
复制代码
|
||||||
|
</el-button>
|
||||||
|
<el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">
|
||||||
|
清空
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-scrollbar class="center-scrollbar">
|
||||||
|
<el-row :gutter="formConf.gutter" class="center-board-row">
|
||||||
|
<el-form
|
||||||
|
:disabled="formConf.disabled"
|
||||||
|
:label-position="formConf.labelPosition"
|
||||||
|
:label-width="formConf.labelWidth + 'px'"
|
||||||
|
:size="formConf.size"
|
||||||
|
>
|
||||||
|
<draggable :animation="340" :list="drawingList" class="drawing-board" group="componentsGroup">
|
||||||
|
<draggable-item
|
||||||
|
v-for="(element, index) in drawingList"
|
||||||
|
:key="element.renderKey"
|
||||||
|
:active-id="activeId"
|
||||||
|
:drawing-list="drawingList"
|
||||||
|
:element="element"
|
||||||
|
:form-conf="formConf"
|
||||||
|
:index="index"
|
||||||
|
@activeItem="activeFormItem"
|
||||||
|
@copyItem="drawingItemCopy"
|
||||||
|
@deleteItem="drawingItemDelete"
|
||||||
|
/>
|
||||||
|
</draggable>
|
||||||
|
<div v-show="!drawingList.length" class="empty-info">
|
||||||
|
从左侧拖入或点选组件进行表单设计
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</el-row>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<right-panel
|
||||||
|
:active-data="activeData"
|
||||||
|
:form-conf="formConf"
|
||||||
|
:show-field="!!drawingList.length"
|
||||||
|
@tag-change="tagChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<code-type-dialog
|
||||||
|
:show-file-name="showFileName"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
title="选择生成类型"
|
||||||
|
@confirm="generate"
|
||||||
|
/>
|
||||||
|
<input id="copyNode" type="hidden">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import beautifier from 'js-beautify'
|
||||||
|
import ClipboardJS from 'clipboard'
|
||||||
|
import render from '@/utils/generator/render'
|
||||||
|
import RightPanel from './RightPanel'
|
||||||
|
import {formConf, inputComponents, layoutComponents, selectComponents} from '@/utils/generator/config'
|
||||||
|
import {beautifierConf, titleCase} from '@/utils/index'
|
||||||
|
import {cssStyle, makeUpHtml, vueScript, vueTemplate} from '@/utils/generator/html'
|
||||||
|
import {makeUpJs} from '@/utils/generator/js'
|
||||||
|
import {makeUpCss} from '@/utils/generator/css'
|
||||||
|
import drawingDefault from '@/utils/generator/drawingDefault'
|
||||||
|
import logo from '@/assets/logo/logo.png'
|
||||||
|
import CodeTypeDialog from './CodeTypeDialog'
|
||||||
|
import DraggableItem from './DraggableItem'
|
||||||
|
|
||||||
|
let oldActiveId
|
||||||
|
let tempActiveData
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
draggable,
|
||||||
|
render,
|
||||||
|
RightPanel,
|
||||||
|
CodeTypeDialog,
|
||||||
|
DraggableItem
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
logo,
|
||||||
|
idGlobal: 100,
|
||||||
|
formConf,
|
||||||
|
inputComponents,
|
||||||
|
selectComponents,
|
||||||
|
layoutComponents,
|
||||||
|
labelWidth: 100,
|
||||||
|
drawingList: drawingDefault,
|
||||||
|
drawingData: {},
|
||||||
|
activeId: drawingDefault[0].formId,
|
||||||
|
drawerVisible: false,
|
||||||
|
formData: {},
|
||||||
|
dialogVisible: false,
|
||||||
|
generateConf: null,
|
||||||
|
showFileName: false,
|
||||||
|
activeData: drawingDefault[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 防止 firefox 下 拖拽 会新打卡一个选项卡
|
||||||
|
document.body.ondrop = event => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// eslint-disable-next-line func-names
|
||||||
|
'activeData.label': function (val, oldVal) {
|
||||||
|
if (
|
||||||
|
this.activeData.placeholder === undefined
|
||||||
|
|| !this.activeData.tag
|
||||||
|
|| oldActiveId !== this.activeId
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
|
||||||
|
},
|
||||||
|
activeId: {
|
||||||
|
handler(val) {
|
||||||
|
oldActiveId = val
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const clipboard = new ClipboardJS('#copyNode', {
|
||||||
|
text: trigger => {
|
||||||
|
const codeStr = this.generateCode()
|
||||||
|
this.$notify({
|
||||||
|
title: '成功',
|
||||||
|
message: '代码已复制到剪切板,可粘贴。',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
return codeStr
|
||||||
|
}
|
||||||
|
})
|
||||||
|
clipboard.on('error', e => {
|
||||||
|
this.$message.error('代码复制失败')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
activeFormItem(element) {
|
||||||
|
this.activeData = element
|
||||||
|
this.activeId = element.formId
|
||||||
|
},
|
||||||
|
onEnd(obj, a) {
|
||||||
|
if (obj.from !== obj.to) {
|
||||||
|
this.activeData = tempActiveData
|
||||||
|
this.activeId = this.idGlobal
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addComponent(item) {
|
||||||
|
const clone = this.cloneComponent(item)
|
||||||
|
this.drawingList.push(clone)
|
||||||
|
this.activeFormItem(clone)
|
||||||
|
},
|
||||||
|
cloneComponent(origin) {
|
||||||
|
const clone = JSON.parse(JSON.stringify(origin))
|
||||||
|
clone.formId = ++this.idGlobal
|
||||||
|
clone.span = formConf.span
|
||||||
|
clone.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件
|
||||||
|
if (!clone.layout) clone.layout = 'colFormItem'
|
||||||
|
if (clone.layout === 'colFormItem') {
|
||||||
|
clone.vModel = `field${this.idGlobal}`
|
||||||
|
clone.placeholder !== undefined && (clone.placeholder += clone.label)
|
||||||
|
tempActiveData = clone
|
||||||
|
} else if (clone.layout === 'rowFormItem') {
|
||||||
|
delete clone.label
|
||||||
|
clone.componentName = `row${this.idGlobal}`
|
||||||
|
clone.gutter = this.formConf.gutter
|
||||||
|
tempActiveData = clone
|
||||||
|
}
|
||||||
|
return tempActiveData
|
||||||
|
},
|
||||||
|
AssembleFormData() {
|
||||||
|
this.formData = {
|
||||||
|
fields: JSON.parse(JSON.stringify(this.drawingList)),
|
||||||
|
...this.formConf
|
||||||
|
}
|
||||||
|
},
|
||||||
|
generate(data) {
|
||||||
|
const func = this[`exec${titleCase(this.operationType)}`]
|
||||||
|
this.generateConf = data
|
||||||
|
func && func(data)
|
||||||
|
},
|
||||||
|
execRun(data) {
|
||||||
|
this.AssembleFormData()
|
||||||
|
this.drawerVisible = true
|
||||||
|
},
|
||||||
|
execDownload(data) {
|
||||||
|
const codeStr = this.generateCode()
|
||||||
|
const blob = new Blob([codeStr], {type: 'text/plain;charset=utf-8'})
|
||||||
|
this.$download.saveAs(blob, data.fileName)
|
||||||
|
},
|
||||||
|
execCopy(data) {
|
||||||
|
document.getElementById('copyNode').click()
|
||||||
|
},
|
||||||
|
empty() {
|
||||||
|
this.$confirm('确定要清空所有组件吗?', '提示', {type: 'warning'}).then(
|
||||||
|
() => {
|
||||||
|
this.drawingList = []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
drawingItemCopy(item, parent) {
|
||||||
|
let clone = JSON.parse(JSON.stringify(item))
|
||||||
|
clone = this.createIdAndKey(clone)
|
||||||
|
parent.push(clone)
|
||||||
|
this.activeFormItem(clone)
|
||||||
|
},
|
||||||
|
createIdAndKey(item) {
|
||||||
|
item.formId = ++this.idGlobal
|
||||||
|
item.renderKey = +new Date()
|
||||||
|
if (item.layout === 'colFormItem') {
|
||||||
|
item.vModel = `field${this.idGlobal}`
|
||||||
|
} else if (item.layout === 'rowFormItem') {
|
||||||
|
item.componentName = `row${this.idGlobal}`
|
||||||
|
}
|
||||||
|
if (Array.isArray(item.children)) {
|
||||||
|
item.children = item.children.map(childItem => this.createIdAndKey(childItem))
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
},
|
||||||
|
drawingItemDelete(index, parent) {
|
||||||
|
parent.splice(index, 1)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const len = this.drawingList.length
|
||||||
|
if (len) {
|
||||||
|
this.activeFormItem(this.drawingList[len - 1])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
generateCode() {
|
||||||
|
const {type} = this.generateConf
|
||||||
|
this.AssembleFormData()
|
||||||
|
const script = vueScript(makeUpJs(this.formData, type))
|
||||||
|
const html = vueTemplate(makeUpHtml(this.formData, type))
|
||||||
|
const css = cssStyle(makeUpCss(this.formData))
|
||||||
|
return beautifier.html(html + script + css, beautifierConf.html)
|
||||||
|
},
|
||||||
|
download() {
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.showFileName = true
|
||||||
|
this.operationType = 'download'
|
||||||
|
},
|
||||||
|
run() {
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.showFileName = false
|
||||||
|
this.operationType = 'run'
|
||||||
|
},
|
||||||
|
copy() {
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.showFileName = false
|
||||||
|
this.operationType = 'copy'
|
||||||
|
},
|
||||||
|
tagChange(newTag) {
|
||||||
|
newTag = this.cloneComponent(newTag)
|
||||||
|
newTag.vModel = this.activeData.vModel
|
||||||
|
newTag.formId = this.activeId
|
||||||
|
newTag.span = this.activeData.span
|
||||||
|
delete this.activeData.tag
|
||||||
|
delete this.activeData.tagIcon
|
||||||
|
delete this.activeData.document
|
||||||
|
Object.keys(newTag).forEach(key => {
|
||||||
|
if (this.activeData[key] !== undefined
|
||||||
|
&& typeof this.activeData[key] === typeof newTag[key]) {
|
||||||
|
newTag[key] = this.activeData[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.activeData = newTag
|
||||||
|
this.updateDrawingList(newTag, this.drawingList)
|
||||||
|
},
|
||||||
|
updateDrawingList(newTag, list) {
|
||||||
|
const index = list.findIndex(item => item.formId === this.activeId)
|
||||||
|
if (index > -1) {
|
||||||
|
list.splice(index, 1, newTag)
|
||||||
|
} else {
|
||||||
|
list.forEach(item => {
|
||||||
|
if (Array.isArray(item.children)) this.updateDrawingList(newTag, item.children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
.editor-tabs {
|
||||||
|
background: #121315;
|
||||||
|
|
||||||
|
.el-tabs__header {
|
||||||
|
margin: 0;
|
||||||
|
border-bottom-color: #121315;
|
||||||
|
|
||||||
|
.el-tabs__nav {
|
||||||
|
border-color: #121315;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
color: #888a8e;
|
||||||
|
border-left: 1px solid #121315 !important;
|
||||||
|
background: #363636;
|
||||||
|
margin-right: 5px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item.is-active {
|
||||||
|
background: #1e1e1e;
|
||||||
|
border-bottom-color: #1e1e1e !important;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-edit {
|
||||||
|
color: #f1fa8c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-document {
|
||||||
|
color: #a95812;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// home
|
||||||
|
.right-scrollbar {
|
||||||
|
.el-scrollbar__view {
|
||||||
|
padding: 12px 18px 15px 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-scrollbar .el-scrollbar__wrap {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-tabs {
|
||||||
|
.el-tabs__header {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__item {
|
||||||
|
width: 50%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs__nav {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reg-item {
|
||||||
|
padding: 12px 6px;
|
||||||
|
background: #f8f8f8;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: -6px;
|
||||||
|
top: -6px;
|
||||||
|
display: block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(210, 23, 23, 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& + .reg-item {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
& .el-button + .el-button {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& i {
|
||||||
|
font-size: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tree-node {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
.node-operation {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
i[class*="el-icon"] + i[class*="el-icon"] {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-plus {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon-delete {
|
||||||
|
color: #157a0c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-scrollbar .el-scrollbar__view {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-rate {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload__tip {
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectedColor: #f6f7ff;
|
||||||
|
$lighterBlue: #409EFF;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-list {
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.components-item {
|
||||||
|
display: inline-block;
|
||||||
|
width: 48%;
|
||||||
|
margin: 1%;
|
||||||
|
transition: transform 0ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-draggable {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #222;
|
||||||
|
margin: 6px 2px;
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
color: #666;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-body {
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: $selectedColor;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: move;
|
||||||
|
border: 1px dashed $selectedColor;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
color: #777;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 1px dashed #787be8;
|
||||||
|
color: #787be8;
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
color: #787be8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-board {
|
||||||
|
width: 260px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-scrollbar {
|
||||||
|
height: calc(100vh - 42px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-scrollbar {
|
||||||
|
height: calc(100vh - 42px);
|
||||||
|
overflow: hidden;
|
||||||
|
border-left: 1px solid #f1e8e8;
|
||||||
|
border-right: 1px solid #f1e8e8;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-board {
|
||||||
|
height: 100vh;
|
||||||
|
width: auto;
|
||||||
|
margin: 0 350px 0 260px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-info {
|
||||||
|
position: absolute;
|
||||||
|
top: 46%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #ccb1ea;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
position: relative;
|
||||||
|
height: 42px;
|
||||||
|
text-align: right;
|
||||||
|
padding: 0 15px;
|
||||||
|
box-sizing: border-box;;
|
||||||
|
border: 1px solid #f1e8e8;
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
color: #F56C6C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-wrapper {
|
||||||
|
position: relative;
|
||||||
|
height: 42px;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid #f1e8e8;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
top: 6px;
|
||||||
|
line-height: 30px;
|
||||||
|
color: #00afff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 17px;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: sub;
|
||||||
|
margin-left: 15px;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-board-row {
|
||||||
|
padding: 12px 12px 15px 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
& > .el-form {
|
||||||
|
// 69 = 12+15+42
|
||||||
|
height: calc(100vh - 69px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawing-board {
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.components-body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-ghost {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: rgb(89, 89, 223);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components-item.sortable-ghost {
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
background-color: $selectedColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-from-item {
|
||||||
|
& > .el-form-item {
|
||||||
|
background: $selectedColor;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .drawing-item-copy, & > .drawing-item-delete {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .component-name {
|
||||||
|
color: $lighterBlue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawing-item {
|
||||||
|
position: relative;
|
||||||
|
cursor: move;
|
||||||
|
|
||||||
|
&.unfocus-bordered:not(.activeFromItem) > div:first-child {
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawing-row-item {
|
||||||
|
position: relative;
|
||||||
|
cursor: move;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px dashed #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 2px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.drawing-row-item {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-col {
|
||||||
|
margin-top: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-wrapper {
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active-from-item {
|
||||||
|
border: 1px dashed $lighterBlue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-name {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #bbb;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawing-item, .drawing-row-item {
|
||||||
|
&:hover {
|
||||||
|
& > .el-form-item {
|
||||||
|
background: $selectedColor;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .drawing-item-copy, & > .drawing-item-delete {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .drawing-item-copy, & > .drawing-item-delete {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .drawing-item-copy {
|
||||||
|
right: 56px;
|
||||||
|
border-color: $lighterBlue;
|
||||||
|
color: $lighterBlue;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $lighterBlue;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .drawing-item-delete {
|
||||||
|
right: 24px;
|
||||||
|
border-color: #F56C6C;
|
||||||
|
color: #F56C6C;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #F56C6C;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,234 @@
|
||||||
|
<template>
|
||||||
|
<el-card>
|
||||||
|
<el-tabs v-model="activeName">
|
||||||
|
<el-tab-pane label="基本信息" name="basic">
|
||||||
|
<basic-info-form ref="basicInfo" :info="info"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="字段信息" name="columnInfo">
|
||||||
|
<el-table ref="dragTable" :data="columns" :max-height="tableHeight" row-key="columnId">
|
||||||
|
<el-table-column class-name="allowDrag" label="序号" min-width="5%" type="index"/>
|
||||||
|
<el-table-column
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
label="字段列名"
|
||||||
|
min-width="10%"
|
||||||
|
prop="columnName"
|
||||||
|
/>
|
||||||
|
<el-table-column label="字段描述" min-width="10%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.columnComment"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
label="物理类型"
|
||||||
|
min-width="10%"
|
||||||
|
prop="columnType"
|
||||||
|
/>
|
||||||
|
<el-table-column label="Java类型" min-width="11%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-select v-model="scope.row.javaType">
|
||||||
|
<el-option label="Long" value="Long"/>
|
||||||
|
<el-option label="String" value="String"/>
|
||||||
|
<el-option label="Integer" value="Integer"/>
|
||||||
|
<el-option label="Double" value="Double"/>
|
||||||
|
<el-option label="BigDecimal" value="BigDecimal"/>
|
||||||
|
<el-option label="Date" value="Date"/>
|
||||||
|
<el-option label="Boolean" value="Boolean"/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="java属性" min-width="10%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-input v-model="scope.row.javaField"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="插入" min-width="5%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-checkbox v-model="scope.row.isInsert" false-label="0" true-label="1"></el-checkbox>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="编辑" min-width="5%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-checkbox v-model="scope.row.isEdit" false-label="0" true-label="1"></el-checkbox>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="列表" min-width="5%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-checkbox v-model="scope.row.isList" false-label="0" true-label="1"></el-checkbox>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="查询" min-width="5%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-checkbox v-model="scope.row.isQuery" false-label="0" true-label="1"></el-checkbox>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="查询方式" min-width="10%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-select v-model="scope.row.queryType">
|
||||||
|
<el-option label="=" value="EQ"/>
|
||||||
|
<el-option label="!=" value="NE"/>
|
||||||
|
<el-option label=">" value="GT"/>
|
||||||
|
<el-option label=">=" value="GTE"/>
|
||||||
|
<el-option label="<" value="LT"/>
|
||||||
|
<el-option label="<=" value="LTE"/>
|
||||||
|
<el-option label="LIKE" value="LIKE"/>
|
||||||
|
<el-option label="BETWEEN" value="BETWEEN"/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="必填" min-width="5%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-checkbox v-model="scope.row.isRequired" false-label="0" true-label="1"></el-checkbox>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="显示类型" min-width="12%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-select v-model="scope.row.htmlType">
|
||||||
|
<el-option label="文本框" value="input"/>
|
||||||
|
<el-option label="文本域" value="textarea"/>
|
||||||
|
<el-option label="下拉框" value="select"/>
|
||||||
|
<el-option label="单选框" value="radio"/>
|
||||||
|
<el-option label="复选框" value="checkbox"/>
|
||||||
|
<el-option label="日期控件" value="datetime"/>
|
||||||
|
<el-option label="图片上传" value="imageUpload"/>
|
||||||
|
<el-option label="文件上传" value="fileUpload"/>
|
||||||
|
<el-option label="富文本控件" value="editor"/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="字典类型" min-width="12%">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="dict in dictOptions"
|
||||||
|
:key="dict.dictType"
|
||||||
|
:label="dict.dictName"
|
||||||
|
:value="dict.dictType">
|
||||||
|
<span style="float: left">{{ dict.dictName }}</span>
|
||||||
|
<span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dictType }}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="生成信息" name="genInfo">
|
||||||
|
<gen-info-form ref="genInfo" :info="info" :menus="menus" :tables="tables"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item style="text-align: center;margin-left:-100px;margin-top:10px;">
|
||||||
|
<el-button type="primary" @click="submitForm()">提交</el-button>
|
||||||
|
<el-button @click="close()">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {getGenTable, updateGenTable} from "@/api/tool/gen";
|
||||||
|
import {optionselect as getDictOptionselect} from "@/api/system/dict/type";
|
||||||
|
import {listMenu as getMenuTreeselect} from "@/api/system/menu";
|
||||||
|
import basicInfoForm from "./basicInfoForm";
|
||||||
|
import genInfoForm from "./genInfoForm";
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "GenEdit",
|
||||||
|
components: {
|
||||||
|
basicInfoForm,
|
||||||
|
genInfoForm
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 选中选项卡的 name
|
||||||
|
activeName: "columnInfo",
|
||||||
|
// 表格的高度
|
||||||
|
tableHeight: document.documentElement.scrollHeight - 245 + "px",
|
||||||
|
// 表信息
|
||||||
|
tables: [],
|
||||||
|
// 表列信息
|
||||||
|
columns: [],
|
||||||
|
// 字典信息
|
||||||
|
dictOptions: [],
|
||||||
|
// 菜单信息
|
||||||
|
menus: [],
|
||||||
|
// 表详细信息
|
||||||
|
info: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
const tableId = this.$route.params && this.$route.params.tableId;
|
||||||
|
if (tableId) {
|
||||||
|
// 获取表详细信息
|
||||||
|
getGenTable(tableId).then(res => {
|
||||||
|
this.columns = res.data.rows;
|
||||||
|
this.info = res.data.info;
|
||||||
|
this.tables = res.data.tables;
|
||||||
|
});
|
||||||
|
/** 查询字典下拉列表 */
|
||||||
|
getDictOptionselect().then(response => {
|
||||||
|
this.dictOptions = response.data;
|
||||||
|
});
|
||||||
|
/** 查询菜单下拉列表 */
|
||||||
|
getMenuTreeselect().then(response => {
|
||||||
|
this.menus = this.handleTree(response.data, "menuId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 提交按钮 */
|
||||||
|
submitForm() {
|
||||||
|
const basicForm = this.$refs.basicInfo.$refs.basicInfoForm;
|
||||||
|
const genForm = this.$refs.genInfo.$refs.genInfoForm;
|
||||||
|
Promise.all([basicForm, genForm].map(this.getFormPromise)).then(res => {
|
||||||
|
const validateResult = res.data.every(item => !!item);
|
||||||
|
if (validateResult) {
|
||||||
|
const genTable = Object.assign({}, basicForm.model, genForm.model);
|
||||||
|
genTable.columns = this.columns;
|
||||||
|
genTable.params = {
|
||||||
|
treeCode: genTable.treeCode,
|
||||||
|
treeName: genTable.treeName,
|
||||||
|
treeParentCode: genTable.treeParentCode,
|
||||||
|
parentMenuId: genTable.parentMenuId
|
||||||
|
};
|
||||||
|
updateGenTable(genTable).then(res => {
|
||||||
|
this.$modal.msgSuccess(res.data.msg);
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$modal.msgError("表单校验未通过,请重新检查提交内容");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getFormPromise(form) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
form.validate(res => {
|
||||||
|
resolve(res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 关闭按钮 */
|
||||||
|
close() {
|
||||||
|
const obj = {path: "/tool/gen", query: {t: Date.now(), pageNum: this.$route.query.pageNum}};
|
||||||
|
this.$tab.closeOpenPage(obj);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const el = this.$refs.dragTable.$el.querySelectorAll(".el-table__body-wrapper > table > tbody")[0];
|
||||||
|
const sortable = Sortable.create(el, {
|
||||||
|
handle: ".allowDrag",
|
||||||
|
onEnd: evt => {
|
||||||
|
const targetRow = this.columns.splice(evt.oldIndex, 1)[0];
|
||||||
|
this.columns.splice(evt.newIndex, 0, targetRow);
|
||||||
|
for (let index in this.columns) {
|
||||||
|
this.columns[index].sort = parseInt(index) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,300 @@
|
||||||
|
<template>
|
||||||
|
<el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="tplCategory">
|
||||||
|
<span slot="label">生成模板</span>
|
||||||
|
<el-select v-model="info.tplCategory" @change="tplSelectChange">
|
||||||
|
<el-option label="单表(增删改查)" value="crud"/>
|
||||||
|
<el-option label="树表(增删改查)" value="tree"/>
|
||||||
|
<el-option label="主子表(增删改查)" value="sub"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="packageName">
|
||||||
|
<span slot="label">
|
||||||
|
生成包路径
|
||||||
|
<el-tooltip content="生成在哪个java包下,例如 com.muyu.system" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-input v-model="info.packageName"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="moduleName">
|
||||||
|
<span slot="label">
|
||||||
|
生成模块名
|
||||||
|
<el-tooltip content="可理解为子系统名,例如 system" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-input v-model="info.moduleName"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="businessName">
|
||||||
|
<span slot="label">
|
||||||
|
生成业务名
|
||||||
|
<el-tooltip content="可理解为功能英文名,例如 user" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-input v-model="info.businessName"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="functionName">
|
||||||
|
<span slot="label">
|
||||||
|
生成功能名
|
||||||
|
<el-tooltip content="用作类描述,例如 用户" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-input v-model="info.functionName"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
上级菜单
|
||||||
|
<el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<treeselect
|
||||||
|
v-model="info.parentMenuId"
|
||||||
|
:append-to-body="true"
|
||||||
|
:normalizer="normalizer"
|
||||||
|
:options="menus"
|
||||||
|
:show-count="true"
|
||||||
|
placeholder="请选择系统菜单"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item prop="genType">
|
||||||
|
<span slot="label">
|
||||||
|
生成代码方式
|
||||||
|
<el-tooltip content="默认为zip压缩包下载,也可以自定义生成路径" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-radio v-model="info.genType" label="0">zip压缩包</el-radio>
|
||||||
|
<el-radio v-model="info.genType" label="1">自定义路径</el-radio>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col v-if="info.genType == '1'" :span="24">
|
||||||
|
<el-form-item prop="genPath">
|
||||||
|
<span slot="label">
|
||||||
|
自定义路径
|
||||||
|
<el-tooltip content="填写磁盘绝对路径,若不填写,则生成到当前Web项目下" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-input v-model="info.genPath">
|
||||||
|
<el-dropdown slot="append">
|
||||||
|
<el-button type="primary">
|
||||||
|
最近路径快速选择
|
||||||
|
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item @click.native="info.genPath = '/'">恢复默认的生成基础路径</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row v-show="info.tplCategory == 'tree'">
|
||||||
|
<h4 class="form-header">其他信息</h4>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
树编码字段
|
||||||
|
<el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-select v-model="info.treeCode" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="(column, index) in info.columns"
|
||||||
|
:key="index"
|
||||||
|
:label="column.columnName + ':' + column.columnComment"
|
||||||
|
:value="column.columnName"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
树父编码字段
|
||||||
|
<el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-select v-model="info.treeParentCode" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="(column, index) in info.columns"
|
||||||
|
:key="index"
|
||||||
|
:label="column.columnName + ':' + column.columnComment"
|
||||||
|
:value="column.columnName"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
树名称字段
|
||||||
|
<el-tooltip content="树节点的显示名称字段名, 如:dept_name" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-select v-model="info.treeName" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="(column, index) in info.columns"
|
||||||
|
:key="index"
|
||||||
|
:label="column.columnName + ':' + column.columnComment"
|
||||||
|
:value="column.columnName"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row v-show="info.tplCategory == 'sub'">
|
||||||
|
<h4 class="form-header">关联信息</h4>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
关联子表的表名
|
||||||
|
<el-tooltip content="关联子表的表名, 如:sys_user" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange">
|
||||||
|
<el-option
|
||||||
|
v-for="(table, index) in tables"
|
||||||
|
:key="index"
|
||||||
|
:label="table.tableName + ':' + table.tableComment"
|
||||||
|
:value="table.tableName"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
子表关联的外键名
|
||||||
|
<el-tooltip content="子表关联的外键名, 如:user_id" placement="top">
|
||||||
|
<i class="el-icon-question"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-select v-model="info.subTableFkName" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="(column, index) in subColumns"
|
||||||
|
:key="index"
|
||||||
|
:label="column.columnName + ':' + column.columnComment"
|
||||||
|
:value="column.columnName"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Treeselect from "@riophae/vue-treeselect";
|
||||||
|
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {Treeselect},
|
||||||
|
props: {
|
||||||
|
info: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
tables: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
menus: {
|
||||||
|
type: Array,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
subColumns: [],
|
||||||
|
rules: {
|
||||||
|
tplCategory: [
|
||||||
|
{required: true, message: "请选择生成模板", trigger: "blur"}
|
||||||
|
],
|
||||||
|
packageName: [
|
||||||
|
{required: true, message: "请输入生成包路径", trigger: "blur"}
|
||||||
|
],
|
||||||
|
moduleName: [
|
||||||
|
{required: true, message: "请输入生成模块名", trigger: "blur"}
|
||||||
|
],
|
||||||
|
businessName: [
|
||||||
|
{required: true, message: "请输入生成业务名", trigger: "blur"}
|
||||||
|
],
|
||||||
|
functionName: [
|
||||||
|
{required: true, message: "请输入生成功能名", trigger: "blur"}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'info.subTableName': function (val) {
|
||||||
|
this.setSubTableColumns(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 转换菜单数据结构 */
|
||||||
|
normalizer(node) {
|
||||||
|
if (node.children && !node.children.length) {
|
||||||
|
delete node.children;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: node.menuId,
|
||||||
|
label: node.menuName,
|
||||||
|
children: node.children
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/** 选择子表名触发 */
|
||||||
|
subSelectChange(value) {
|
||||||
|
this.info.subTableFkName = '';
|
||||||
|
},
|
||||||
|
/** 选择生成模板触发 */
|
||||||
|
tplSelectChange(value) {
|
||||||
|
if (value !== 'sub') {
|
||||||
|
this.info.subTableName = '';
|
||||||
|
this.info.subTableFkName = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 设置关联外键 */
|
||||||
|
setSubTableColumns(value) {
|
||||||
|
for (var item in this.tables) {
|
||||||
|
const name = this.tables[item].tableName;
|
||||||
|
if (value === name) {
|
||||||
|
this.subColumns = this.tables[item].columns;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,118 @@
|
||||||
|
<template>
|
||||||
|
<!-- 导入表 -->
|
||||||
|
<el-dialog :visible.sync="visible" append-to-body title="导入表" top="5vh" width="800px">
|
||||||
|
<el-form ref="queryForm" :inline="true" :model="queryParams" size="small">
|
||||||
|
<el-form-item label="表名称" prop="tableName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.tableName"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入表名称"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="表描述" prop="tableComment">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.tableComment"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入表描述"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-row>
|
||||||
|
<el-table ref="table" :data="dbTableList" height="260px" @row-click="clickRow"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55"></el-table-column>
|
||||||
|
<el-table-column :show-overflow-tooltip="true" label="表名称" prop="tableName"></el-table-column>
|
||||||
|
<el-table-column :show-overflow-tooltip="true" label="表描述" prop="tableComment"></el-table-column>
|
||||||
|
<el-table-column label="创建时间" prop="createTime"></el-table-column>
|
||||||
|
<el-table-column label="更新时间" prop="updateTime"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="handleImportTable">确 定</el-button>
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {importTable, listDbTable} from "@/api/tool/gen";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
visible: false,
|
||||||
|
// 选中数组值
|
||||||
|
tables: [],
|
||||||
|
// 总条数
|
||||||
|
total: 0,
|
||||||
|
// 表数据
|
||||||
|
dbTableList: [],
|
||||||
|
// 查询参数
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
tableName: undefined,
|
||||||
|
tableComment: undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 显示弹框
|
||||||
|
show() {
|
||||||
|
this.getList();
|
||||||
|
this.visible = true;
|
||||||
|
},
|
||||||
|
clickRow(row) {
|
||||||
|
this.$refs.table.toggleRowSelection(row);
|
||||||
|
},
|
||||||
|
// 多选框选中数据
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.tables = selection.map(item => item.tableName);
|
||||||
|
},
|
||||||
|
// 查询表数据
|
||||||
|
getList() {
|
||||||
|
listDbTable(this.queryParams).then(res => {
|
||||||
|
this.dbTableList = res.data.rows;
|
||||||
|
this.total = res.data.total;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
resetQuery() {
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
/** 导入按钮操作 */
|
||||||
|
handleImportTable() {
|
||||||
|
const tableNames = this.tables.join(",");
|
||||||
|
if (tableNames == "") {
|
||||||
|
this.$modal.msgError("请选择要导入的表");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
importTable({tables: tableNames}).then(res => {
|
||||||
|
this.$modal.msgSuccess(res.data.msg);
|
||||||
|
this.visible = false;
|
||||||
|
this.$emit("ok");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,352 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form v-show="showSearch" ref="queryForm" :inline="true" :model="queryParams" label-width="68px" size="small">
|
||||||
|
<el-form-item label="表名称" prop="tableName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.tableName"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入表名称"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="表描述" prop="tableComment">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.tableComment"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入表描述"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="创建时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
style="width: 240px"
|
||||||
|
type="daterange"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:code']"
|
||||||
|
icon="el-icon-download"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="handleGenTable"
|
||||||
|
>生成
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:import']"
|
||||||
|
icon="el-icon-upload"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="info"
|
||||||
|
@click="openImportTable"
|
||||||
|
>导入
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:edit']"
|
||||||
|
:disabled="single"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="success"
|
||||||
|
@click="handleEditTable"
|
||||||
|
>修改
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:remove']"
|
||||||
|
:disabled="multiple"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
plain
|
||||||
|
size="mini"
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete"
|
||||||
|
>删除
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
|
||||||
|
<el-table-column align="center" type="selection" width="55"></el-table-column>
|
||||||
|
<el-table-column align="center" label="序号" type="index" width="50">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
align="center"
|
||||||
|
label="表名称"
|
||||||
|
prop="tableName"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
align="center"
|
||||||
|
label="表描述"
|
||||||
|
prop="tableComment"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
align="center"
|
||||||
|
label="实体"
|
||||||
|
prop="className"
|
||||||
|
width="120"
|
||||||
|
/>
|
||||||
|
<el-table-column align="center" label="创建时间" prop="createTime" width="160"/>
|
||||||
|
<el-table-column align="center" label="更新时间" prop="updateTime" width="160"/>
|
||||||
|
<el-table-column align="center" class-name="small-padding fixed-width" label="操作">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:preview']"
|
||||||
|
icon="el-icon-view"
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
@click="handlePreview(scope.row)"
|
||||||
|
>预览
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:edit']"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
@click="handleEditTable(scope.row)"
|
||||||
|
>编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:remove']"
|
||||||
|
icon="el-icon-delete"
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
>删除
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:edit']"
|
||||||
|
icon="el-icon-refresh"
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
@click="handleSynchDb(scope.row)"
|
||||||
|
>同步
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['tool:gen:code']"
|
||||||
|
icon="el-icon-download"
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
@click="handleGenTable(scope.row)"
|
||||||
|
>生成代码
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
<!-- 预览界面 -->
|
||||||
|
<el-dialog :title="preview.title" :visible.sync="preview.open" append-to-body class="scrollbar" top="5vh"
|
||||||
|
width="80%">
|
||||||
|
<el-tabs v-model="preview.activeName">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="(value, key) in preview.data"
|
||||||
|
:key="key"
|
||||||
|
:label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
|
||||||
|
:name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.vm'))"
|
||||||
|
>
|
||||||
|
<el-link v-clipboard:copy="value" v-clipboard:success="clipboardSuccess" :underline="false"
|
||||||
|
icon="el-icon-document-copy" style="float:right">复制
|
||||||
|
</el-link>
|
||||||
|
<pre><code class="hljs" v-html="highlightedCode(value, key)"></code></pre>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-dialog>
|
||||||
|
<import-table ref="import" @ok="handleQuery"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {delTable, genCode, listTable, previewTable, synchDb} from "@/api/tool/gen";
|
||||||
|
import importTable from "./importTable";
|
||||||
|
import hljs from "highlight.js/lib/highlight";
|
||||||
|
import "highlight.js/styles/github-gist.css";
|
||||||
|
|
||||||
|
hljs.registerLanguage("java", require("highlight.js/lib/languages/java"));
|
||||||
|
hljs.registerLanguage("xml", require("highlight.js/lib/languages/xml"));
|
||||||
|
hljs.registerLanguage("html", require("highlight.js/lib/languages/xml"));
|
||||||
|
hljs.registerLanguage("vue", require("highlight.js/lib/languages/xml"));
|
||||||
|
hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascript"));
|
||||||
|
hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql"));
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Gen",
|
||||||
|
components: {importTable},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 遮罩层
|
||||||
|
loading: true,
|
||||||
|
// 唯一标识符
|
||||||
|
uniqueId: "",
|
||||||
|
// 选中数组
|
||||||
|
ids: [],
|
||||||
|
// 选中表数组
|
||||||
|
tableNames: [],
|
||||||
|
// 非单个禁用
|
||||||
|
single: true,
|
||||||
|
// 非多个禁用
|
||||||
|
multiple: true,
|
||||||
|
// 显示搜索条件
|
||||||
|
showSearch: true,
|
||||||
|
// 总条数
|
||||||
|
total: 0,
|
||||||
|
// 表数据
|
||||||
|
tableList: [],
|
||||||
|
// 日期范围
|
||||||
|
dateRange: "",
|
||||||
|
// 查询参数
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
tableName: undefined,
|
||||||
|
tableComment: undefined
|
||||||
|
},
|
||||||
|
// 预览参数
|
||||||
|
preview: {
|
||||||
|
open: false,
|
||||||
|
title: "代码预览",
|
||||||
|
data: {},
|
||||||
|
activeName: "domain.java"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
const time = this.$route.query.t;
|
||||||
|
if (time != null && time != this.uniqueId) {
|
||||||
|
this.uniqueId = time;
|
||||||
|
this.queryParams.pageNum = Number(this.$route.query.pageNum);
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 查询表集合 */
|
||||||
|
getList() {
|
||||||
|
this.loading = true;
|
||||||
|
listTable(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
|
||||||
|
this.tableList = response.data.rows;
|
||||||
|
this.total = response.data.total;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
/** 生成代码操作 */
|
||||||
|
handleGenTable(row) {
|
||||||
|
const tableNames = row.tableName || this.tableNames;
|
||||||
|
if (tableNames == "") {
|
||||||
|
this.$modal.msgError("请选择要生成的数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (row.genType === "1") {
|
||||||
|
genCode(row.tableName).then(response => {
|
||||||
|
this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$download.zip("/code/gen/batchGenCode?tables=" + tableNames, "muyu.zip");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 同步数据库操作 */
|
||||||
|
handleSynchDb(row) {
|
||||||
|
const tableName = row.tableName;
|
||||||
|
this.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function () {
|
||||||
|
return synchDb(tableName);
|
||||||
|
}).then(() => {
|
||||||
|
this.$modal.msgSuccess("同步成功");
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 打开导入表弹窗 */
|
||||||
|
openImportTable() {
|
||||||
|
this.$refs.import.show();
|
||||||
|
},
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
resetQuery() {
|
||||||
|
this.dateRange = [];
|
||||||
|
this.resetForm("queryForm");
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
/** 预览按钮 */
|
||||||
|
handlePreview(row) {
|
||||||
|
previewTable(row.tableId).then(response => {
|
||||||
|
this.preview.data = response.data;
|
||||||
|
this.preview.open = true;
|
||||||
|
this.preview.activeName = "domain.java";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 高亮显示 */
|
||||||
|
highlightedCode(code, key) {
|
||||||
|
const vmName = key.substring(key.lastIndexOf("/") + 1, key.indexOf(".vm"));
|
||||||
|
var language = vmName.substring(vmName.indexOf(".") + 1, vmName.length);
|
||||||
|
const result = hljs.highlight(language, code || "", true);
|
||||||
|
return result.value || ' ';
|
||||||
|
},
|
||||||
|
/** 复制代码成功 */
|
||||||
|
clipboardSuccess() {
|
||||||
|
this.$modal.msgSuccess("复制成功");
|
||||||
|
},
|
||||||
|
// 多选框选中数据
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.ids = selection.map(item => item.tableId);
|
||||||
|
this.tableNames = selection.map(item => item.tableName);
|
||||||
|
this.single = selection.length != 1;
|
||||||
|
this.multiple = !selection.length;
|
||||||
|
},
|
||||||
|
/** 修改按钮操作 */
|
||||||
|
handleEditTable(row) {
|
||||||
|
const tableId = row.tableId || this.ids[0];
|
||||||
|
const tableName = row.tableName || this.tableNames[0];
|
||||||
|
const params = {pageNum: this.queryParams.pageNum};
|
||||||
|
this.$tab.openPage("修改[" + tableName + "]生成配置", '/tool/gen-edit/index/' + tableId, params);
|
||||||
|
},
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
handleDelete(row) {
|
||||||
|
const tableIds = row.tableId || this.ids;
|
||||||
|
this.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?').then(function () {
|
||||||
|
return delTable(tableIds);
|
||||||
|
}).then(() => {
|
||||||
|
this.getList();
|
||||||
|
this.$modal.msgSuccess("删除成功");
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in New Issue