文件名从 ruoyi-ui/src/utils/generator/drawingDefalut.js 修改 |
| | |
| | | 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: '手机号格式错误'
|
| | | }]
|
| | | }
|
| | | ]
|
| | | 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: '手机号格式错误' |
| | | }] |
| | | } |
| | | ] |
| | |
| | | /* 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 Titile">
|
| | | ${str}
|
| | | <div slot="footer">
|
| | | <el-button @click="close">取消</el-button>
|
| | | <el-button type="primary" @click="handelConfirm">确定</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
|
| | | }
|
| | | /* 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 |
| | | } |
| | |
| | | import { isArray } from 'util'
|
| | | 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)
|
| | | },`,
|
| | | handelConfirm: `handelConfirm() {
|
| | | 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 = isArray(conf.defaultValue) ? 'type: \'array\',' : ''
|
| | | let message = 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 && 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
|
| | | }
|
| | | import { isArray } from 'util' |
| | | 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 = isArray(conf.defaultValue) ? 'type: \'array\',' : '' |
| | | let message = 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 && 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 |
| | | } |
| | |
| | | <template>
|
| | | <div>
|
| | | <el-dialog
|
| | | v-bind="$attrs"
|
| | | width="500px"
|
| | | :close-on-click-modal="false"
|
| | | :modal-append-to-body="false"
|
| | | v-on="$listeners"
|
| | | @open="onOpen"
|
| | | @close="onClose"
|
| | | >
|
| | | <el-row :gutter="15">
|
| | | <el-form
|
| | | ref="elForm"
|
| | | :model="formData"
|
| | | :rules="rules"
|
| | | size="medium"
|
| | | label-width="100px"
|
| | | >
|
| | | <el-col :span="24">
|
| | | <el-form-item label="生成类型" prop="type">
|
| | | <el-radio-group v-model="formData.type">
|
| | | <el-radio-button
|
| | | v-for="(item, index) in typeOptions"
|
| | | :key="index"
|
| | | :label="item.value"
|
| | | :disabled="item.disabled"
|
| | | >
|
| | | {{ item.label }}
|
| | | </el-radio-button>
|
| | | </el-radio-group>
|
| | | </el-form-item>
|
| | | <el-form-item v-if="showFileName" label="文件名" prop="fileName">
|
| | | <el-input v-model="formData.fileName" placeholder="请输入文件名" clearable />
|
| | | </el-form-item>
|
| | | </el-col>
|
| | | </el-form>
|
| | | </el-row>
|
| | |
|
| | | <div slot="footer">
|
| | | <el-button @click="close">
|
| | | 取消
|
| | | </el-button>
|
| | | <el-button type="primary" @click="handelConfirm">
|
| | | 确定
|
| | | </el-button>
|
| | | </div>
|
| | | </el-dialog>
|
| | | </div>
|
| | | </template>
|
| | | <script>
|
| | | export default {
|
| | | inheritAttrs: false,
|
| | | props: ['showFileName'],
|
| | | data() {
|
| | | return {
|
| | | formData: {
|
| | | fileName: undefined,
|
| | | type: 'file'
|
| | | },
|
| | | rules: {
|
| | | fileName: [{
|
| | | required: true,
|
| | | message: '请输入文件名',
|
| | | trigger: 'blur'
|
| | | }],
|
| | | type: [{
|
| | | required: true,
|
| | | message: '生成类型不能为空',
|
| | | trigger: 'change'
|
| | | }]
|
| | | },
|
| | | typeOptions: [{
|
| | | label: '页面',
|
| | | value: 'file'
|
| | | }, {
|
| | | label: '弹窗',
|
| | | value: 'dialog'
|
| | | }]
|
| | | }
|
| | | },
|
| | | computed: {
|
| | | },
|
| | | watch: {},
|
| | | mounted() {},
|
| | | methods: {
|
| | | onOpen() {
|
| | | if (this.showFileName) {
|
| | | this.formData.fileName = `${+new Date()}.vue`
|
| | | }
|
| | | },
|
| | | onClose() {
|
| | | },
|
| | | close(e) {
|
| | | this.$emit('update:visible', false)
|
| | | },
|
| | | handelConfirm() {
|
| | | this.$refs.elForm.validate(valid => {
|
| | | if (!valid) return
|
| | | this.$emit('confirm', { ...this.formData })
|
| | | this.close()
|
| | | })
|
| | | }
|
| | | }
|
| | | }
|
| | | </script>
|
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-bind="$attrs" |
| | | width="500px" |
| | | :close-on-click-modal="false" |
| | | :modal-append-to-body="false" |
| | | v-on="$listeners" |
| | | @open="onOpen" |
| | | @close="onClose" |
| | | > |
| | | <el-row :gutter="15"> |
| | | <el-form |
| | | ref="elForm" |
| | | :model="formData" |
| | | :rules="rules" |
| | | size="medium" |
| | | label-width="100px" |
| | | > |
| | | <el-col :span="24"> |
| | | <el-form-item label="生成类型" prop="type"> |
| | | <el-radio-group v-model="formData.type"> |
| | | <el-radio-button |
| | | v-for="(item, index) in typeOptions" |
| | | :key="index" |
| | | :label="item.value" |
| | | :disabled="item.disabled" |
| | | > |
| | | {{ item.label }} |
| | | </el-radio-button> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item v-if="showFileName" label="文件名" prop="fileName"> |
| | | <el-input v-model="formData.fileName" placeholder="请输入文件名" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-form> |
| | | </el-row> |
| | | |
| | | <div slot="footer"> |
| | | <el-button @click="close"> |
| | | 取消 |
| | | </el-button> |
| | | <el-button type="primary" @click="handleConfirm"> |
| | | 确定 |
| | | </el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | export default { |
| | | inheritAttrs: false, |
| | | props: ['showFileName'], |
| | | data() { |
| | | return { |
| | | formData: { |
| | | fileName: undefined, |
| | | type: 'file' |
| | | }, |
| | | rules: { |
| | | fileName: [{ |
| | | required: true, |
| | | message: '请输入文件名', |
| | | trigger: 'blur' |
| | | }], |
| | | type: [{ |
| | | required: true, |
| | | message: '生成类型不能为空', |
| | | trigger: 'change' |
| | | }] |
| | | }, |
| | | typeOptions: [{ |
| | | label: '页面', |
| | | value: 'file' |
| | | }, { |
| | | label: '弹窗', |
| | | value: 'dialog' |
| | | }] |
| | | } |
| | | }, |
| | | computed: { |
| | | }, |
| | | watch: {}, |
| | | mounted() {}, |
| | | methods: { |
| | | onOpen() { |
| | | if (this.showFileName) { |
| | | this.formData.fileName = `${+new Date()}.vue` |
| | | } |
| | | }, |
| | | onClose() { |
| | | }, |
| | | close(e) { |
| | | this.$emit('update:visible', false) |
| | | }, |
| | | handleConfirm() { |
| | | this.$refs.elForm.validate(valid => { |
| | | if (!valid) return |
| | | this.$emit('confirm', { ...this.formData }) |
| | | this.close() |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | |
| | | <template>
|
| | | <div>
|
| | | <el-dialog
|
| | | v-bind="$attrs"
|
| | | :close-on-click-modal="false"
|
| | | :modal-append-to-body="false"
|
| | | v-on="$listeners"
|
| | | @open="onOpen"
|
| | | @close="onClose"
|
| | | >
|
| | | <el-row :gutter="0">
|
| | | <el-form
|
| | | ref="elForm"
|
| | | :model="formData"
|
| | | :rules="rules"
|
| | | size="small"
|
| | | label-width="100px"
|
| | | >
|
| | | <el-col :span="24">
|
| | | <el-form-item
|
| | | label="选项名"
|
| | | prop="label"
|
| | | >
|
| | | <el-input
|
| | | v-model="formData.label"
|
| | | placeholder="请输入选项名"
|
| | | clearable
|
| | | />
|
| | | </el-form-item>
|
| | | </el-col>
|
| | | <el-col :span="24">
|
| | | <el-form-item
|
| | | label="选项值"
|
| | | prop="value"
|
| | | >
|
| | | <el-input
|
| | | v-model="formData.value"
|
| | | placeholder="请输入选项值"
|
| | | clearable
|
| | | >
|
| | | <el-select
|
| | | slot="append"
|
| | | v-model="dataType"
|
| | | :style="{width: '100px'}"
|
| | | >
|
| | | <el-option
|
| | | v-for="(item, index) in dataTypeOptions"
|
| | | :key="index"
|
| | | :label="item.label"
|
| | | :value="item.value"
|
| | | :disabled="item.disabled"
|
| | | />
|
| | | </el-select>
|
| | | </el-input>
|
| | | </el-form-item>
|
| | | </el-col>
|
| | | </el-form>
|
| | | </el-row>
|
| | | <div slot="footer">
|
| | | <el-button
|
| | | type="primary"
|
| | | @click="handelConfirm"
|
| | | >
|
| | | 确定
|
| | | </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)
|
| | | },
|
| | | handelConfirm() {
|
| | | 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>
|
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-bind="$attrs" |
| | | :close-on-click-modal="false" |
| | | :modal-append-to-body="false" |
| | | v-on="$listeners" |
| | | @open="onOpen" |
| | | @close="onClose" |
| | | > |
| | | <el-row :gutter="0"> |
| | | <el-form |
| | | ref="elForm" |
| | | :model="formData" |
| | | :rules="rules" |
| | | size="small" |
| | | label-width="100px" |
| | | > |
| | | <el-col :span="24"> |
| | | <el-form-item |
| | | label="选项名" |
| | | prop="label" |
| | | > |
| | | <el-input |
| | | v-model="formData.label" |
| | | placeholder="请输入选项名" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-form-item |
| | | label="选项值" |
| | | prop="value" |
| | | > |
| | | <el-input |
| | | v-model="formData.value" |
| | | placeholder="请输入选项值" |
| | | clearable |
| | | > |
| | | <el-select |
| | | slot="append" |
| | | v-model="dataType" |
| | | :style="{width: '100px'}" |
| | | > |
| | | <el-option |
| | | v-for="(item, index) in dataTypeOptions" |
| | | :key="index" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | :disabled="item.disabled" |
| | | /> |
| | | </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> |
| | |
| | | <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
|
| | | class="components-draggable"
|
| | | :list="inputComponents"
|
| | | :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
|
| | | :clone="cloneComponent"
|
| | | draggable=".components-item"
|
| | | :sort="false"
|
| | | @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
|
| | | class="components-draggable"
|
| | | :list="selectComponents"
|
| | | :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
|
| | | :clone="cloneComponent"
|
| | | draggable=".components-item"
|
| | | :sort="false"
|
| | | @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
|
| | | class="components-draggable" :list="layoutComponents"
|
| | | :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
|
| | | draggable=".components-item" :sort="false" @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 class="center-board-row" :gutter="formConf.gutter">
|
| | | <el-form
|
| | | :size="formConf.size"
|
| | | :label-position="formConf.labelPosition"
|
| | | :disabled="formConf.disabled"
|
| | | :label-width="formConf.labelWidth + 'px'"
|
| | | >
|
| | | <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
|
| | | <draggable-item
|
| | | v-for="(element, index) in drawingList"
|
| | | :key="element.renderKey"
|
| | | :drawing-list="drawingList"
|
| | | :element="element"
|
| | | :index="index"
|
| | | :active-id="activeId"
|
| | | :form-conf="formConf"
|
| | | @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
|
| | | :visible.sync="dialogVisible"
|
| | | title="选择生成类型"
|
| | | :show-file-name="showFileName"
|
| | | @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 { inputComponents, selectComponents, layoutComponents, formConf } from '@/utils/generator/config'
|
| | | import { beautifierConf, titleCase } from '@/utils/index'
|
| | | import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html'
|
| | | import { makeUpJs } from '@/utils/generator/js'
|
| | | import { makeUpCss } from '@/utils/generator/css'
|
| | | import drawingDefalut from '@/utils/generator/drawingDefalut'
|
| | | 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: drawingDefalut,
|
| | | drawingData: {},
|
| | | activeId: drawingDefalut[0].formId,
|
| | | drawerVisible: false,
|
| | | formData: {},
|
| | | dialogVisible: false,
|
| | | generateConf: null,
|
| | | showFileName: false,
|
| | | activeData: drawingDefalut[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'>
|
| | | body, html{
|
| | | margin: 0;
|
| | | padding: 0;
|
| | | background: #fff;
|
| | | -moz-osx-font-smoothing: grayscale;
|
| | | -webkit-font-smoothing: antialiased;
|
| | | text-rendering: optimizeLegibility;
|
| | | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
| | | }
|
| | |
|
| | | input, textarea{
|
| | | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
| | | }
|
| | |
|
| | | .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>
|
| | | <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 |
| | | class="components-draggable" |
| | | :list="inputComponents" |
| | | :group="{ name: 'componentsGroup', pull: 'clone', put: false }" |
| | | :clone="cloneComponent" |
| | | draggable=".components-item" |
| | | :sort="false" |
| | | @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 |
| | | class="components-draggable" |
| | | :list="selectComponents" |
| | | :group="{ name: 'componentsGroup', pull: 'clone', put: false }" |
| | | :clone="cloneComponent" |
| | | draggable=".components-item" |
| | | :sort="false" |
| | | @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 |
| | | class="components-draggable" :list="layoutComponents" |
| | | :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent" |
| | | draggable=".components-item" :sort="false" @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 class="center-board-row" :gutter="formConf.gutter"> |
| | | <el-form |
| | | :size="formConf.size" |
| | | :label-position="formConf.labelPosition" |
| | | :disabled="formConf.disabled" |
| | | :label-width="formConf.labelWidth + 'px'" |
| | | > |
| | | <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup"> |
| | | <draggable-item |
| | | v-for="(element, index) in drawingList" |
| | | :key="element.renderKey" |
| | | :drawing-list="drawingList" |
| | | :element="element" |
| | | :index="index" |
| | | :active-id="activeId" |
| | | :form-conf="formConf" |
| | | @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 |
| | | :visible.sync="dialogVisible" |
| | | title="选择生成类型" |
| | | :show-file-name="showFileName" |
| | | @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 { inputComponents, selectComponents, layoutComponents, formConf } from '@/utils/generator/config' |
| | | import { beautifierConf, titleCase } from '@/utils/index' |
| | | import { makeUpHtml, vueTemplate, vueScript, cssStyle } 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'> |
| | | body, html{ |
| | | margin: 0; |
| | | padding: 0; |
| | | background: #fff; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | -webkit-font-smoothing: antialiased; |
| | | text-rendering: optimizeLegibility; |
| | | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; |
| | | } |
| | | |
| | | input, textarea{ |
| | | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; |
| | | } |
| | | |
| | | .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> |