Commit d2420212a68106da906d7d820cc59d61327a76cc
1 parent
00f96865
增加 Form 编辑框
Showing
7 changed files
with
2225 additions
and
1 deletions
yudao-admin-ui/src/router/index.js
| ... | ... | @@ -149,7 +149,20 @@ export const constantRoutes = [ |
| 149 | 149 | meta: { title: '请假表单-审批', icon: 'form' } |
| 150 | 150 | } |
| 151 | 151 | ] |
| 152 | - } | |
| 152 | + }, | |
| 153 | + { | |
| 154 | + path: '/bpm', | |
| 155 | + component: Layout, | |
| 156 | + hidden: true, | |
| 157 | + children: [ | |
| 158 | + { | |
| 159 | + path: 'manager/form/edit', | |
| 160 | + component: (resolve) => require(['@/views/bpm/form/build/index'], resolve), | |
| 161 | + name: '流程表单-编辑', | |
| 162 | + meta: { title: '流程表单-编辑' } | |
| 163 | + } | |
| 164 | + ] | |
| 165 | + }, | |
| 153 | 166 | ] |
| 154 | 167 | |
| 155 | 168 | export default new Router({ | ... | ... |
yudao-admin-ui/src/views/bpm/form/build/CodeTypeDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div> | |
| 3 | + <el-dialog | |
| 4 | + v-bind="$attrs" | |
| 5 | + width="500px" | |
| 6 | + :close-on-click-modal="false" | |
| 7 | + :modal-append-to-body="false" | |
| 8 | + v-on="$listeners" | |
| 9 | + @open="onOpen" | |
| 10 | + @close="onClose" | |
| 11 | + > | |
| 12 | + <el-row :gutter="15"> | |
| 13 | + <el-form | |
| 14 | + ref="elForm" | |
| 15 | + :model="formData" | |
| 16 | + :rules="rules" | |
| 17 | + size="medium" | |
| 18 | + label-width="100px" | |
| 19 | + > | |
| 20 | + <el-col :span="24"> | |
| 21 | + <el-form-item label="生成类型" prop="type"> | |
| 22 | + <el-radio-group v-model="formData.type"> | |
| 23 | + <el-radio-button | |
| 24 | + v-for="(item, index) in typeOptions" | |
| 25 | + :key="index" | |
| 26 | + :label="item.value" | |
| 27 | + :disabled="item.disabled" | |
| 28 | + > | |
| 29 | + {{ item.label }} | |
| 30 | + </el-radio-button> | |
| 31 | + </el-radio-group> | |
| 32 | + </el-form-item> | |
| 33 | + <el-form-item v-if="showFileName" label="文件名" prop="fileName"> | |
| 34 | + <el-input v-model="formData.fileName" placeholder="请输入文件名" clearable /> | |
| 35 | + </el-form-item> | |
| 36 | + </el-col> | |
| 37 | + </el-form> | |
| 38 | + </el-row> | |
| 39 | + | |
| 40 | + <div slot="footer"> | |
| 41 | + <el-button @click="close"> | |
| 42 | + 取消 | |
| 43 | + </el-button> | |
| 44 | + <el-button type="primary" @click="handelConfirm"> | |
| 45 | + 确定 | |
| 46 | + </el-button> | |
| 47 | + </div> | |
| 48 | + </el-dialog> | |
| 49 | + </div> | |
| 50 | +</template> | |
| 51 | +<script> | |
| 52 | +export default { | |
| 53 | + inheritAttrs: false, | |
| 54 | + props: ['showFileName'], | |
| 55 | + data() { | |
| 56 | + return { | |
| 57 | + formData: { | |
| 58 | + fileName: undefined, | |
| 59 | + type: 'file' | |
| 60 | + }, | |
| 61 | + rules: { | |
| 62 | + fileName: [{ | |
| 63 | + required: true, | |
| 64 | + message: '请输入文件名', | |
| 65 | + trigger: 'blur' | |
| 66 | + }], | |
| 67 | + type: [{ | |
| 68 | + required: true, | |
| 69 | + message: '生成类型不能为空', | |
| 70 | + trigger: 'change' | |
| 71 | + }] | |
| 72 | + }, | |
| 73 | + typeOptions: [{ | |
| 74 | + label: '页面', | |
| 75 | + value: 'file' | |
| 76 | + }, { | |
| 77 | + label: '弹窗', | |
| 78 | + value: 'dialog' | |
| 79 | + }] | |
| 80 | + } | |
| 81 | + }, | |
| 82 | + computed: { | |
| 83 | + }, | |
| 84 | + watch: {}, | |
| 85 | + mounted() {}, | |
| 86 | + methods: { | |
| 87 | + onOpen() { | |
| 88 | + if (this.showFileName) { | |
| 89 | + this.formData.fileName = `${+new Date()}.vue` | |
| 90 | + } | |
| 91 | + }, | |
| 92 | + onClose() { | |
| 93 | + }, | |
| 94 | + close(e) { | |
| 95 | + this.$emit('update:visible', false) | |
| 96 | + }, | |
| 97 | + handelConfirm() { | |
| 98 | + this.$refs.elForm.validate(valid => { | |
| 99 | + if (!valid) return | |
| 100 | + this.$emit('confirm', { ...this.formData }) | |
| 101 | + this.close() | |
| 102 | + }) | |
| 103 | + } | |
| 104 | + } | |
| 105 | +} | |
| 106 | +</script> | ... | ... |
yudao-admin-ui/src/views/bpm/form/build/DraggableItem.vue
0 → 100644
| 1 | +<script> | |
| 2 | +import draggable from 'vuedraggable' | |
| 3 | +import render from '@/utils/generator/render' | |
| 4 | + | |
| 5 | +const components = { | |
| 6 | + itemBtns(h, element, index, parent) { | |
| 7 | + const { copyItem, deleteItem } = this.$listeners | |
| 8 | + return [ | |
| 9 | + <span class="drawing-item-copy" title="复制" onClick={event => { | |
| 10 | + copyItem(element, parent); event.stopPropagation() | |
| 11 | + }}> | |
| 12 | + <i class="el-icon-copy-document" /> | |
| 13 | + </span>, | |
| 14 | + <span class="drawing-item-delete" title="删除" onClick={event => { | |
| 15 | + deleteItem(index, parent); event.stopPropagation() | |
| 16 | + }}> | |
| 17 | + <i class="el-icon-delete" /> | |
| 18 | + </span> | |
| 19 | + ] | |
| 20 | + } | |
| 21 | +} | |
| 22 | +const layouts = { | |
| 23 | + colFormItem(h, element, index, parent) { | |
| 24 | + const { activeItem } = this.$listeners | |
| 25 | + let className = this.activeId === element.formId ? 'drawing-item active-from-item' : 'drawing-item' | |
| 26 | + if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered' | |
| 27 | + return ( | |
| 28 | + <el-col span={element.span} class={className} | |
| 29 | + nativeOnClick={event => { activeItem(element); event.stopPropagation() }}> | |
| 30 | + <el-form-item label-width={element.labelWidth ? `${element.labelWidth}px` : null} | |
| 31 | + label={element.label} required={element.required}> | |
| 32 | + <render key={element.renderKey} conf={element} onInput={ event => { | |
| 33 | + this.$set(element, 'defaultValue', event) | |
| 34 | + }} /> | |
| 35 | + </el-form-item> | |
| 36 | + {components.itemBtns.apply(this, arguments)} | |
| 37 | + </el-col> | |
| 38 | + ) | |
| 39 | + }, | |
| 40 | + rowFormItem(h, element, index, parent) { | |
| 41 | + const { activeItem } = this.$listeners | |
| 42 | + const className = this.activeId === element.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item' | |
| 43 | + let child = renderChildren.apply(this, arguments) | |
| 44 | + if (element.type === 'flex') { | |
| 45 | + child = <el-row type={element.type} justify={element.justify} align={element.align}> | |
| 46 | + {child} | |
| 47 | + </el-row> | |
| 48 | + } | |
| 49 | + return ( | |
| 50 | + <el-col span={element.span}> | |
| 51 | + <el-row gutter={element.gutter} class={className} | |
| 52 | + nativeOnClick={event => { activeItem(element); event.stopPropagation() }}> | |
| 53 | + <span class="component-name">{element.componentName}</span> | |
| 54 | + <draggable list={element.children} animation={340} group="componentsGroup" class="drag-wrapper"> | |
| 55 | + {child} | |
| 56 | + </draggable> | |
| 57 | + {components.itemBtns.apply(this, arguments)} | |
| 58 | + </el-row> | |
| 59 | + </el-col> | |
| 60 | + ) | |
| 61 | + } | |
| 62 | +} | |
| 63 | + | |
| 64 | +function renderChildren(h, element, index, parent) { | |
| 65 | + if (!Array.isArray(element.children)) return null | |
| 66 | + return element.children.map((el, i) => { | |
| 67 | + const layout = layouts[el.layout] | |
| 68 | + if (layout) { | |
| 69 | + return layout.call(this, h, el, i, element.children) | |
| 70 | + } | |
| 71 | + return layoutIsNotFound() | |
| 72 | + }) | |
| 73 | +} | |
| 74 | + | |
| 75 | +function layoutIsNotFound() { | |
| 76 | + throw new Error(`没有与${this.element.layout}匹配的layout`) | |
| 77 | +} | |
| 78 | + | |
| 79 | +export default { | |
| 80 | + components: { | |
| 81 | + render, | |
| 82 | + draggable | |
| 83 | + }, | |
| 84 | + props: [ | |
| 85 | + 'element', | |
| 86 | + 'index', | |
| 87 | + 'drawingList', | |
| 88 | + 'activeId', | |
| 89 | + 'formConf' | |
| 90 | + ], | |
| 91 | + render(h) { | |
| 92 | + const layout = layouts[this.element.layout] | |
| 93 | + | |
| 94 | + if (layout) { | |
| 95 | + return layout.call(this, h, this.element, this.index, this.drawingList) | |
| 96 | + } | |
| 97 | + return layoutIsNotFound() | |
| 98 | + } | |
| 99 | +} | |
| 100 | +</script> | ... | ... |
yudao-admin-ui/src/views/bpm/form/build/IconsDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="icon-dialog"> | |
| 3 | + <el-dialog | |
| 4 | + v-bind="$attrs" | |
| 5 | + width="980px" | |
| 6 | + :modal-append-to-body="false" | |
| 7 | + v-on="$listeners" | |
| 8 | + @open="onOpen" | |
| 9 | + @close="onClose" | |
| 10 | + > | |
| 11 | + <div slot="title"> | |
| 12 | + 选择图标 | |
| 13 | + <el-input | |
| 14 | + v-model="key" | |
| 15 | + size="mini" | |
| 16 | + :style="{width: '260px'}" | |
| 17 | + placeholder="请输入图标名称" | |
| 18 | + prefix-icon="el-icon-search" | |
| 19 | + clearable | |
| 20 | + /> | |
| 21 | + </div> | |
| 22 | + <ul class="icon-ul"> | |
| 23 | + <li | |
| 24 | + v-for="icon in iconList" | |
| 25 | + :key="icon" | |
| 26 | + :class="active===icon?'active-item':''" | |
| 27 | + @click="onSelect(icon)" | |
| 28 | + > | |
| 29 | + <i :class="icon" /> | |
| 30 | + <div>{{ icon }}</div> | |
| 31 | + </li> | |
| 32 | + </ul> | |
| 33 | + </el-dialog> | |
| 34 | + </div> | |
| 35 | +</template> | |
| 36 | +<script> | |
| 37 | +import iconList from '@/utils/generator/icon.json' | |
| 38 | + | |
| 39 | +const originList = iconList.map(name => `el-icon-${name}`) | |
| 40 | + | |
| 41 | +export default { | |
| 42 | + inheritAttrs: false, | |
| 43 | + props: ['current'], | |
| 44 | + data() { | |
| 45 | + return { | |
| 46 | + iconList: originList, | |
| 47 | + active: null, | |
| 48 | + key: '' | |
| 49 | + } | |
| 50 | + }, | |
| 51 | + watch: { | |
| 52 | + key(val) { | |
| 53 | + if (val) { | |
| 54 | + this.iconList = originList.filter(name => name.indexOf(val) > -1) | |
| 55 | + } else { | |
| 56 | + this.iconList = originList | |
| 57 | + } | |
| 58 | + } | |
| 59 | + }, | |
| 60 | + methods: { | |
| 61 | + onOpen() { | |
| 62 | + this.active = this.current | |
| 63 | + this.key = '' | |
| 64 | + }, | |
| 65 | + onClose() {}, | |
| 66 | + onSelect(icon) { | |
| 67 | + this.active = icon | |
| 68 | + this.$emit('select', icon) | |
| 69 | + this.$emit('update:visible', false) | |
| 70 | + } | |
| 71 | + } | |
| 72 | +} | |
| 73 | +</script> | |
| 74 | +<style lang="scss" scoped> | |
| 75 | +.icon-ul { | |
| 76 | + margin: 0; | |
| 77 | + padding: 0; | |
| 78 | + font-size: 0; | |
| 79 | + li { | |
| 80 | + list-style-type: none; | |
| 81 | + text-align: center; | |
| 82 | + font-size: 14px; | |
| 83 | + display: inline-block; | |
| 84 | + width: 16.66%; | |
| 85 | + box-sizing: border-box; | |
| 86 | + height: 108px; | |
| 87 | + padding: 15px 6px 6px 6px; | |
| 88 | + cursor: pointer; | |
| 89 | + overflow: hidden; | |
| 90 | + &:hover { | |
| 91 | + background: #f2f2f2; | |
| 92 | + } | |
| 93 | + &.active-item{ | |
| 94 | + background: #e1f3fb; | |
| 95 | + color: #7a6df0 | |
| 96 | + } | |
| 97 | + > i { | |
| 98 | + font-size: 30px; | |
| 99 | + line-height: 50px; | |
| 100 | + } | |
| 101 | + } | |
| 102 | +} | |
| 103 | +.icon-dialog { | |
| 104 | + ::v-deep .el-dialog { | |
| 105 | + border-radius: 8px; | |
| 106 | + margin-bottom: 0; | |
| 107 | + margin-top: 4vh !important; | |
| 108 | + display: flex; | |
| 109 | + flex-direction: column; | |
| 110 | + max-height: 92vh; | |
| 111 | + overflow: hidden; | |
| 112 | + box-sizing: border-box; | |
| 113 | + .el-dialog__header { | |
| 114 | + padding-top: 14px; | |
| 115 | + } | |
| 116 | + .el-dialog__body { | |
| 117 | + margin: 0 20px 20px 20px; | |
| 118 | + padding: 0; | |
| 119 | + overflow: auto; | |
| 120 | + } | |
| 121 | + } | |
| 122 | +} | |
| 123 | +</style> | ... | ... |
yudao-admin-ui/src/views/bpm/form/build/RightPanel.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="right-board"> | |
| 3 | + <el-tabs v-model="currentTab" class="center-tabs"> | |
| 4 | + <el-tab-pane label="组件属性" name="field" /> | |
| 5 | + <el-tab-pane label="表单属性" name="form" /> | |
| 6 | + </el-tabs> | |
| 7 | + <div class="field-box"> | |
| 8 | + <a class="document-link" target="_blank" :href="documentLink" title="查看组件文档"> | |
| 9 | + <i class="el-icon-link" /> | |
| 10 | + </a> | |
| 11 | + <el-scrollbar class="right-scrollbar"> | |
| 12 | + <!-- 组件属性 --> | |
| 13 | + <el-form v-show="currentTab==='field' && showField" size="small" label-width="90px"> | |
| 14 | + <el-form-item v-if="activeData.changeTag" label="组件类型"> | |
| 15 | + <el-select | |
| 16 | + v-model="activeData.tagIcon" | |
| 17 | + placeholder="请选择组件类型" | |
| 18 | + :style="{width: '100%'}" | |
| 19 | + @change="tagChange" | |
| 20 | + > | |
| 21 | + <el-option-group v-for="group in tagList" :key="group.label" :label="group.label"> | |
| 22 | + <el-option | |
| 23 | + v-for="item in group.options" | |
| 24 | + :key="item.label" | |
| 25 | + :label="item.label" | |
| 26 | + :value="item.tagIcon" | |
| 27 | + > | |
| 28 | + <svg-icon class="node-icon" :icon-class="item.tagIcon" /> | |
| 29 | + <span> {{ item.label }}</span> | |
| 30 | + </el-option> | |
| 31 | + </el-option-group> | |
| 32 | + </el-select> | |
| 33 | + </el-form-item> | |
| 34 | + <el-form-item v-if="activeData.vModel!==undefined" label="字段名"> | |
| 35 | + <el-input v-model="activeData.vModel" placeholder="请输入字段名(v-model)" /> | |
| 36 | + </el-form-item> | |
| 37 | + <el-form-item v-if="activeData.componentName!==undefined" label="组件名"> | |
| 38 | + {{ activeData.componentName }} | |
| 39 | + </el-form-item> | |
| 40 | + <el-form-item v-if="activeData.label!==undefined" label="标题"> | |
| 41 | + <el-input v-model="activeData.label" placeholder="请输入标题" /> | |
| 42 | + </el-form-item> | |
| 43 | + <el-form-item v-if="activeData.placeholder!==undefined" label="占位提示"> | |
| 44 | + <el-input v-model="activeData.placeholder" placeholder="请输入占位提示" /> | |
| 45 | + </el-form-item> | |
| 46 | + <el-form-item v-if="activeData['start-placeholder']!==undefined" label="开始占位"> | |
| 47 | + <el-input v-model="activeData['start-placeholder']" placeholder="请输入占位提示" /> | |
| 48 | + </el-form-item> | |
| 49 | + <el-form-item v-if="activeData['end-placeholder']!==undefined" label="结束占位"> | |
| 50 | + <el-input v-model="activeData['end-placeholder']" placeholder="请输入占位提示" /> | |
| 51 | + </el-form-item> | |
| 52 | + <el-form-item v-if="activeData.span!==undefined" label="表单栅格"> | |
| 53 | + <el-slider v-model="activeData.span" :max="24" :min="1" :marks="{12:''}" @change="spanChange" /> | |
| 54 | + </el-form-item> | |
| 55 | + <el-form-item v-if="activeData.layout==='rowFormItem'" label="栅格间隔"> | |
| 56 | + <el-input-number v-model="activeData.gutter" :min="0" placeholder="栅格间隔" /> | |
| 57 | + </el-form-item> | |
| 58 | + <el-form-item v-if="activeData.layout==='rowFormItem'" label="布局模式"> | |
| 59 | + <el-radio-group v-model="activeData.type"> | |
| 60 | + <el-radio-button label="default" /> | |
| 61 | + <el-radio-button label="flex" /> | |
| 62 | + </el-radio-group> | |
| 63 | + </el-form-item> | |
| 64 | + <el-form-item v-if="activeData.justify!==undefined&&activeData.type==='flex'" label="水平排列"> | |
| 65 | + <el-select v-model="activeData.justify" placeholder="请选择水平排列" :style="{width: '100%'}"> | |
| 66 | + <el-option | |
| 67 | + v-for="(item, index) in justifyOptions" | |
| 68 | + :key="index" | |
| 69 | + :label="item.label" | |
| 70 | + :value="item.value" | |
| 71 | + /> | |
| 72 | + </el-select> | |
| 73 | + </el-form-item> | |
| 74 | + <el-form-item v-if="activeData.align!==undefined&&activeData.type==='flex'" label="垂直排列"> | |
| 75 | + <el-radio-group v-model="activeData.align"> | |
| 76 | + <el-radio-button label="top" /> | |
| 77 | + <el-radio-button label="middle" /> | |
| 78 | + <el-radio-button label="bottom" /> | |
| 79 | + </el-radio-group> | |
| 80 | + </el-form-item> | |
| 81 | + <el-form-item v-if="activeData.labelWidth!==undefined" label="标签宽度"> | |
| 82 | + <el-input v-model.number="activeData.labelWidth" type="number" placeholder="请输入标签宽度" /> | |
| 83 | + </el-form-item> | |
| 84 | + <el-form-item v-if="activeData.style&&activeData.style.width!==undefined" label="组件宽度"> | |
| 85 | + <el-input v-model="activeData.style.width" placeholder="请输入组件宽度" clearable /> | |
| 86 | + </el-form-item> | |
| 87 | + <el-form-item v-if="activeData.vModel!==undefined" label="默认值"> | |
| 88 | + <el-input | |
| 89 | + :value="setDefaultValue(activeData.defaultValue)" | |
| 90 | + placeholder="请输入默认值" | |
| 91 | + @input="onDefaultValueInput" | |
| 92 | + /> | |
| 93 | + </el-form-item> | |
| 94 | + <el-form-item v-if="activeData.tag==='el-checkbox-group'" label="至少应选"> | |
| 95 | + <el-input-number | |
| 96 | + :value="activeData.min" | |
| 97 | + :min="0" | |
| 98 | + placeholder="至少应选" | |
| 99 | + @input="$set(activeData, 'min', $event?$event:undefined)" | |
| 100 | + /> | |
| 101 | + </el-form-item> | |
| 102 | + <el-form-item v-if="activeData.tag==='el-checkbox-group'" label="最多可选"> | |
| 103 | + <el-input-number | |
| 104 | + :value="activeData.max" | |
| 105 | + :min="0" | |
| 106 | + placeholder="最多可选" | |
| 107 | + @input="$set(activeData, 'max', $event?$event:undefined)" | |
| 108 | + /> | |
| 109 | + </el-form-item> | |
| 110 | + <el-form-item v-if="activeData.prepend!==undefined" label="前缀"> | |
| 111 | + <el-input v-model="activeData.prepend" placeholder="请输入前缀" /> | |
| 112 | + </el-form-item> | |
| 113 | + <el-form-item v-if="activeData.append!==undefined" label="后缀"> | |
| 114 | + <el-input v-model="activeData.append" placeholder="请输入后缀" /> | |
| 115 | + </el-form-item> | |
| 116 | + <el-form-item v-if="activeData['prefix-icon']!==undefined" label="前图标"> | |
| 117 | + <el-input v-model="activeData['prefix-icon']" placeholder="请输入前图标名称"> | |
| 118 | + <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('prefix-icon')"> | |
| 119 | + 选择 | |
| 120 | + </el-button> | |
| 121 | + </el-input> | |
| 122 | + </el-form-item> | |
| 123 | + <el-form-item v-if="activeData['suffix-icon'] !== undefined" label="后图标"> | |
| 124 | + <el-input v-model="activeData['suffix-icon']" placeholder="请输入后图标名称"> | |
| 125 | + <el-button slot="append" icon="el-icon-thumb" @click="openIconsDialog('suffix-icon')"> | |
| 126 | + 选择 | |
| 127 | + </el-button> | |
| 128 | + </el-input> | |
| 129 | + </el-form-item> | |
| 130 | + <el-form-item v-if="activeData.tag === 'el-cascader'" label="选项分隔符"> | |
| 131 | + <el-input v-model="activeData.separator" placeholder="请输入选项分隔符" /> | |
| 132 | + </el-form-item> | |
| 133 | + <el-form-item v-if="activeData.autosize !== undefined" label="最小行数"> | |
| 134 | + <el-input-number v-model="activeData.autosize.minRows" :min="1" placeholder="最小行数" /> | |
| 135 | + </el-form-item> | |
| 136 | + <el-form-item v-if="activeData.autosize !== undefined" label="最大行数"> | |
| 137 | + <el-input-number v-model="activeData.autosize.maxRows" :min="1" placeholder="最大行数" /> | |
| 138 | + </el-form-item> | |
| 139 | + <el-form-item v-if="activeData.min !== undefined" label="最小值"> | |
| 140 | + <el-input-number v-model="activeData.min" placeholder="最小值" /> | |
| 141 | + </el-form-item> | |
| 142 | + <el-form-item v-if="activeData.max !== undefined" label="最大值"> | |
| 143 | + <el-input-number v-model="activeData.max" placeholder="最大值" /> | |
| 144 | + </el-form-item> | |
| 145 | + <el-form-item v-if="activeData.step !== undefined" label="步长"> | |
| 146 | + <el-input-number v-model="activeData.step" placeholder="步数" /> | |
| 147 | + </el-form-item> | |
| 148 | + <el-form-item v-if="activeData.tag === 'el-input-number'" label="精度"> | |
| 149 | + <el-input-number v-model="activeData.precision" :min="0" placeholder="精度" /> | |
| 150 | + </el-form-item> | |
| 151 | + <el-form-item v-if="activeData.tag === 'el-input-number'" label="按钮位置"> | |
| 152 | + <el-radio-group v-model="activeData['controls-position']"> | |
| 153 | + <el-radio-button label=""> | |
| 154 | + 默认 | |
| 155 | + </el-radio-button> | |
| 156 | + <el-radio-button label="right"> | |
| 157 | + 右侧 | |
| 158 | + </el-radio-button> | |
| 159 | + </el-radio-group> | |
| 160 | + </el-form-item> | |
| 161 | + <el-form-item v-if="activeData.maxlength !== undefined" label="最多输入"> | |
| 162 | + <el-input v-model="activeData.maxlength" placeholder="请输入字符长度"> | |
| 163 | + <template slot="append"> | |
| 164 | + 个字符 | |
| 165 | + </template> | |
| 166 | + </el-input> | |
| 167 | + </el-form-item> | |
| 168 | + <el-form-item v-if="activeData['active-text'] !== undefined" label="开启提示"> | |
| 169 | + <el-input v-model="activeData['active-text']" placeholder="请输入开启提示" /> | |
| 170 | + </el-form-item> | |
| 171 | + <el-form-item v-if="activeData['inactive-text'] !== undefined" label="关闭提示"> | |
| 172 | + <el-input v-model="activeData['inactive-text']" placeholder="请输入关闭提示" /> | |
| 173 | + </el-form-item> | |
| 174 | + <el-form-item v-if="activeData['active-value'] !== undefined" label="开启值"> | |
| 175 | + <el-input | |
| 176 | + :value="setDefaultValue(activeData['active-value'])" | |
| 177 | + placeholder="请输入开启值" | |
| 178 | + @input="onSwitchValueInput($event, 'active-value')" | |
| 179 | + /> | |
| 180 | + </el-form-item> | |
| 181 | + <el-form-item v-if="activeData['inactive-value'] !== undefined" label="关闭值"> | |
| 182 | + <el-input | |
| 183 | + :value="setDefaultValue(activeData['inactive-value'])" | |
| 184 | + placeholder="请输入关闭值" | |
| 185 | + @input="onSwitchValueInput($event, 'inactive-value')" | |
| 186 | + /> | |
| 187 | + </el-form-item> | |
| 188 | + <el-form-item | |
| 189 | + v-if="activeData.type !== undefined && 'el-date-picker' === activeData.tag" | |
| 190 | + label="时间类型" | |
| 191 | + > | |
| 192 | + <el-select | |
| 193 | + v-model="activeData.type" | |
| 194 | + placeholder="请选择时间类型" | |
| 195 | + :style="{ width: '100%' }" | |
| 196 | + @change="dateTypeChange" | |
| 197 | + > | |
| 198 | + <el-option | |
| 199 | + v-for="(item, index) in dateOptions" | |
| 200 | + :key="index" | |
| 201 | + :label="item.label" | |
| 202 | + :value="item.value" | |
| 203 | + /> | |
| 204 | + </el-select> | |
| 205 | + </el-form-item> | |
| 206 | + <el-form-item v-if="activeData.name !== undefined" label="文件字段名"> | |
| 207 | + <el-input v-model="activeData.name" placeholder="请输入上传文件字段名" /> | |
| 208 | + </el-form-item> | |
| 209 | + <el-form-item v-if="activeData.accept !== undefined" label="文件类型"> | |
| 210 | + <el-select | |
| 211 | + v-model="activeData.accept" | |
| 212 | + placeholder="请选择文件类型" | |
| 213 | + :style="{ width: '100%' }" | |
| 214 | + clearable | |
| 215 | + > | |
| 216 | + <el-option label="图片" value="image/*" /> | |
| 217 | + <el-option label="视频" value="video/*" /> | |
| 218 | + <el-option label="音频" value="audio/*" /> | |
| 219 | + <el-option label="excel" value=".xls,.xlsx" /> | |
| 220 | + <el-option label="word" value=".doc,.docx" /> | |
| 221 | + <el-option label="pdf" value=".pdf" /> | |
| 222 | + <el-option label="txt" value=".txt" /> | |
| 223 | + </el-select> | |
| 224 | + </el-form-item> | |
| 225 | + <el-form-item v-if="activeData.fileSize !== undefined" label="文件大小"> | |
| 226 | + <el-input v-model.number="activeData.fileSize" placeholder="请输入文件大小"> | |
| 227 | + <el-select slot="append" v-model="activeData.sizeUnit" :style="{ width: '66px' }"> | |
| 228 | + <el-option label="KB" value="KB" /> | |
| 229 | + <el-option label="MB" value="MB" /> | |
| 230 | + <el-option label="GB" value="GB" /> | |
| 231 | + </el-select> | |
| 232 | + </el-input> | |
| 233 | + </el-form-item> | |
| 234 | + <el-form-item v-if="activeData.action !== undefined" label="上传地址"> | |
| 235 | + <el-input v-model="activeData.action" placeholder="请输入上传地址" clearable /> | |
| 236 | + </el-form-item> | |
| 237 | + <el-form-item v-if="activeData['list-type'] !== undefined" label="列表类型"> | |
| 238 | + <el-radio-group v-model="activeData['list-type']" size="small"> | |
| 239 | + <el-radio-button label="text"> | |
| 240 | + text | |
| 241 | + </el-radio-button> | |
| 242 | + <el-radio-button label="picture"> | |
| 243 | + picture | |
| 244 | + </el-radio-button> | |
| 245 | + <el-radio-button label="picture-card"> | |
| 246 | + picture-card | |
| 247 | + </el-radio-button> | |
| 248 | + </el-radio-group> | |
| 249 | + </el-form-item> | |
| 250 | + <el-form-item | |
| 251 | + v-if="activeData.buttonText !== undefined" | |
| 252 | + v-show="'picture-card' !== activeData['list-type']" | |
| 253 | + label="按钮文字" | |
| 254 | + > | |
| 255 | + <el-input v-model="activeData.buttonText" placeholder="请输入按钮文字" /> | |
| 256 | + </el-form-item> | |
| 257 | + <el-form-item v-if="activeData['range-separator'] !== undefined" label="分隔符"> | |
| 258 | + <el-input v-model="activeData['range-separator']" placeholder="请输入分隔符" /> | |
| 259 | + </el-form-item> | |
| 260 | + <el-form-item v-if="activeData['picker-options'] !== undefined" label="时间段"> | |
| 261 | + <el-input | |
| 262 | + v-model="activeData['picker-options'].selectableRange" | |
| 263 | + placeholder="请输入时间段" | |
| 264 | + /> | |
| 265 | + </el-form-item> | |
| 266 | + <el-form-item v-if="activeData.format !== undefined" label="时间格式"> | |
| 267 | + <el-input | |
| 268 | + :value="activeData.format" | |
| 269 | + placeholder="请输入时间格式" | |
| 270 | + @input="setTimeValue($event)" | |
| 271 | + /> | |
| 272 | + </el-form-item> | |
| 273 | + <template v-if="['el-checkbox-group', 'el-radio-group', 'el-select'].indexOf(activeData.tag) > -1"> | |
| 274 | + <el-divider>选项</el-divider> | |
| 275 | + <draggable | |
| 276 | + :list="activeData.options" | |
| 277 | + :animation="340" | |
| 278 | + group="selectItem" | |
| 279 | + handle=".option-drag" | |
| 280 | + > | |
| 281 | + <div v-for="(item, index) in activeData.options" :key="index" class="select-item"> | |
| 282 | + <div class="select-line-icon option-drag"> | |
| 283 | + <i class="el-icon-s-operation" /> | |
| 284 | + </div> | |
| 285 | + <el-input v-model="item.label" placeholder="选项名" size="small" /> | |
| 286 | + <el-input | |
| 287 | + placeholder="选项值" | |
| 288 | + size="small" | |
| 289 | + :value="item.value" | |
| 290 | + @input="setOptionValue(item, $event)" | |
| 291 | + /> | |
| 292 | + <div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)"> | |
| 293 | + <i class="el-icon-remove-outline" /> | |
| 294 | + </div> | |
| 295 | + </div> | |
| 296 | + </draggable> | |
| 297 | + <div style="margin-left: 20px;"> | |
| 298 | + <el-button | |
| 299 | + style="padding-bottom: 0" | |
| 300 | + icon="el-icon-circle-plus-outline" | |
| 301 | + type="text" | |
| 302 | + @click="addSelectItem" | |
| 303 | + > | |
| 304 | + 添加选项 | |
| 305 | + </el-button> | |
| 306 | + </div> | |
| 307 | + <el-divider /> | |
| 308 | + </template> | |
| 309 | + | |
| 310 | + <template v-if="['el-cascader'].indexOf(activeData.tag) > -1"> | |
| 311 | + <el-divider>选项</el-divider> | |
| 312 | + <el-form-item label="数据类型"> | |
| 313 | + <el-radio-group v-model="activeData.dataType" size="small"> | |
| 314 | + <el-radio-button label="dynamic"> | |
| 315 | + 动态数据 | |
| 316 | + </el-radio-button> | |
| 317 | + <el-radio-button label="static"> | |
| 318 | + 静态数据 | |
| 319 | + </el-radio-button> | |
| 320 | + </el-radio-group> | |
| 321 | + </el-form-item> | |
| 322 | + | |
| 323 | + <template v-if="activeData.dataType === 'dynamic'"> | |
| 324 | + <el-form-item label="标签键名"> | |
| 325 | + <el-input v-model="activeData.labelKey" placeholder="请输入标签键名" /> | |
| 326 | + </el-form-item> | |
| 327 | + <el-form-item label="值键名"> | |
| 328 | + <el-input v-model="activeData.valueKey" placeholder="请输入值键名" /> | |
| 329 | + </el-form-item> | |
| 330 | + <el-form-item label="子级键名"> | |
| 331 | + <el-input v-model="activeData.childrenKey" placeholder="请输入子级键名" /> | |
| 332 | + </el-form-item> | |
| 333 | + </template> | |
| 334 | + | |
| 335 | + <el-tree | |
| 336 | + v-if="activeData.dataType === 'static'" | |
| 337 | + draggable | |
| 338 | + :data="activeData.options" | |
| 339 | + node-key="id" | |
| 340 | + :expand-on-click-node="false" | |
| 341 | + :render-content="renderContent" | |
| 342 | + /> | |
| 343 | + <div v-if="activeData.dataType === 'static'" style="margin-left: 20px"> | |
| 344 | + <el-button | |
| 345 | + style="padding-bottom: 0" | |
| 346 | + icon="el-icon-circle-plus-outline" | |
| 347 | + type="text" | |
| 348 | + @click="addTreeItem" | |
| 349 | + > | |
| 350 | + 添加父级 | |
| 351 | + </el-button> | |
| 352 | + </div> | |
| 353 | + <el-divider /> | |
| 354 | + </template> | |
| 355 | + | |
| 356 | + <el-form-item v-if="activeData.optionType !== undefined" label="选项样式"> | |
| 357 | + <el-radio-group v-model="activeData.optionType"> | |
| 358 | + <el-radio-button label="default"> | |
| 359 | + 默认 | |
| 360 | + </el-radio-button> | |
| 361 | + <el-radio-button label="button"> | |
| 362 | + 按钮 | |
| 363 | + </el-radio-button> | |
| 364 | + </el-radio-group> | |
| 365 | + </el-form-item> | |
| 366 | + <el-form-item v-if="activeData['active-color'] !== undefined" label="开启颜色"> | |
| 367 | + <el-color-picker v-model="activeData['active-color']" /> | |
| 368 | + </el-form-item> | |
| 369 | + <el-form-item v-if="activeData['inactive-color'] !== undefined" label="关闭颜色"> | |
| 370 | + <el-color-picker v-model="activeData['inactive-color']" /> | |
| 371 | + </el-form-item> | |
| 372 | + | |
| 373 | + <el-form-item v-if="activeData['allow-half'] !== undefined" label="允许半选"> | |
| 374 | + <el-switch v-model="activeData['allow-half']" /> | |
| 375 | + </el-form-item> | |
| 376 | + <el-form-item v-if="activeData['show-text'] !== undefined" label="辅助文字"> | |
| 377 | + <el-switch v-model="activeData['show-text']" @change="rateTextChange" /> | |
| 378 | + </el-form-item> | |
| 379 | + <el-form-item v-if="activeData['show-score'] !== undefined" label="显示分数"> | |
| 380 | + <el-switch v-model="activeData['show-score']" @change="rateScoreChange" /> | |
| 381 | + </el-form-item> | |
| 382 | + <el-form-item v-if="activeData['show-stops'] !== undefined" label="显示间断点"> | |
| 383 | + <el-switch v-model="activeData['show-stops']" /> | |
| 384 | + </el-form-item> | |
| 385 | + <el-form-item v-if="activeData.range !== undefined" label="范围选择"> | |
| 386 | + <el-switch v-model="activeData.range" @change="rangeChange" /> | |
| 387 | + </el-form-item> | |
| 388 | + <el-form-item | |
| 389 | + v-if="activeData.border !== undefined && activeData.optionType === 'default'" | |
| 390 | + label="是否带边框" | |
| 391 | + > | |
| 392 | + <el-switch v-model="activeData.border" /> | |
| 393 | + </el-form-item> | |
| 394 | + <el-form-item v-if="activeData.tag === 'el-color-picker'" label="颜色格式"> | |
| 395 | + <el-select | |
| 396 | + v-model="activeData['color-format']" | |
| 397 | + placeholder="请选择颜色格式" | |
| 398 | + :style="{ width: '100%' }" | |
| 399 | + @change="colorFormatChange" | |
| 400 | + > | |
| 401 | + <el-option | |
| 402 | + v-for="(item, index) in colorFormatOptions" | |
| 403 | + :key="index" | |
| 404 | + :label="item.label" | |
| 405 | + :value="item.value" | |
| 406 | + /> | |
| 407 | + </el-select> | |
| 408 | + </el-form-item> | |
| 409 | + <el-form-item | |
| 410 | + v-if="activeData.size !== undefined && | |
| 411 | + (activeData.optionType === 'button' || | |
| 412 | + activeData.border || | |
| 413 | + activeData.tag === 'el-color-picker')" | |
| 414 | + label="选项尺寸" | |
| 415 | + > | |
| 416 | + <el-radio-group v-model="activeData.size"> | |
| 417 | + <el-radio-button label="medium"> | |
| 418 | + 中等 | |
| 419 | + </el-radio-button> | |
| 420 | + <el-radio-button label="small"> | |
| 421 | + 较小 | |
| 422 | + </el-radio-button> | |
| 423 | + <el-radio-button label="mini"> | |
| 424 | + 迷你 | |
| 425 | + </el-radio-button> | |
| 426 | + </el-radio-group> | |
| 427 | + </el-form-item> | |
| 428 | + <el-form-item v-if="activeData['show-word-limit'] !== undefined" label="输入统计"> | |
| 429 | + <el-switch v-model="activeData['show-word-limit']" /> | |
| 430 | + </el-form-item> | |
| 431 | + <el-form-item v-if="activeData.tag === 'el-input-number'" label="严格步数"> | |
| 432 | + <el-switch v-model="activeData['step-strictly']" /> | |
| 433 | + </el-form-item> | |
| 434 | + <el-form-item v-if="activeData.tag === 'el-cascader'" label="是否多选"> | |
| 435 | + <el-switch v-model="activeData.props.props.multiple" /> | |
| 436 | + </el-form-item> | |
| 437 | + <el-form-item v-if="activeData.tag === 'el-cascader'" label="展示全路径"> | |
| 438 | + <el-switch v-model="activeData['show-all-levels']" /> | |
| 439 | + </el-form-item> | |
| 440 | + <el-form-item v-if="activeData.tag === 'el-cascader'" label="可否筛选"> | |
| 441 | + <el-switch v-model="activeData.filterable" /> | |
| 442 | + </el-form-item> | |
| 443 | + <el-form-item v-if="activeData.clearable !== undefined" label="能否清空"> | |
| 444 | + <el-switch v-model="activeData.clearable" /> | |
| 445 | + </el-form-item> | |
| 446 | + <el-form-item v-if="activeData.showTip !== undefined" label="显示提示"> | |
| 447 | + <el-switch v-model="activeData.showTip" /> | |
| 448 | + </el-form-item> | |
| 449 | + <el-form-item v-if="activeData.multiple !== undefined" label="多选文件"> | |
| 450 | + <el-switch v-model="activeData.multiple" /> | |
| 451 | + </el-form-item> | |
| 452 | + <el-form-item v-if="activeData['auto-upload'] !== undefined" label="自动上传"> | |
| 453 | + <el-switch v-model="activeData['auto-upload']" /> | |
| 454 | + </el-form-item> | |
| 455 | + <el-form-item v-if="activeData.readonly !== undefined" label="是否只读"> | |
| 456 | + <el-switch v-model="activeData.readonly" /> | |
| 457 | + </el-form-item> | |
| 458 | + <el-form-item v-if="activeData.disabled !== undefined" label="是否禁用"> | |
| 459 | + <el-switch v-model="activeData.disabled" /> | |
| 460 | + </el-form-item> | |
| 461 | + <el-form-item v-if="activeData.tag === 'el-select'" label="是否可搜索"> | |
| 462 | + <el-switch v-model="activeData.filterable" /> | |
| 463 | + </el-form-item> | |
| 464 | + <el-form-item v-if="activeData.tag === 'el-select'" label="是否多选"> | |
| 465 | + <el-switch v-model="activeData.multiple" @change="multipleChange" /> | |
| 466 | + </el-form-item> | |
| 467 | + <el-form-item v-if="activeData.required !== undefined" label="是否必填"> | |
| 468 | + <el-switch v-model="activeData.required" /> | |
| 469 | + </el-form-item> | |
| 470 | + | |
| 471 | + <template v-if="activeData.layoutTree"> | |
| 472 | + <el-divider>布局结构树</el-divider> | |
| 473 | + <el-tree | |
| 474 | + :data="[activeData]" | |
| 475 | + :props="layoutTreeProps" | |
| 476 | + node-key="renderKey" | |
| 477 | + default-expand-all | |
| 478 | + draggable | |
| 479 | + > | |
| 480 | + <span slot-scope="{ node, data }"> | |
| 481 | + <span class="node-label"> | |
| 482 | + <svg-icon class="node-icon" :icon-class="data.tagIcon" /> | |
| 483 | + {{ node.label }} | |
| 484 | + </span> | |
| 485 | + </span> | |
| 486 | + </el-tree> | |
| 487 | + </template> | |
| 488 | + | |
| 489 | + <template v-if="activeData.layout === 'colFormItem'"> | |
| 490 | + <el-divider>正则校验</el-divider> | |
| 491 | + <div | |
| 492 | + v-for="(item, index) in activeData.regList" | |
| 493 | + :key="index" | |
| 494 | + class="reg-item" | |
| 495 | + > | |
| 496 | + <span class="close-btn" @click="activeData.regList.splice(index, 1)"> | |
| 497 | + <i class="el-icon-close" /> | |
| 498 | + </span> | |
| 499 | + <el-form-item label="表达式"> | |
| 500 | + <el-input v-model="item.pattern" placeholder="请输入正则" /> | |
| 501 | + </el-form-item> | |
| 502 | + <el-form-item label="错误提示" style="margin-bottom:0"> | |
| 503 | + <el-input v-model="item.message" placeholder="请输入错误提示" /> | |
| 504 | + </el-form-item> | |
| 505 | + </div> | |
| 506 | + <div style="margin-left: 20px"> | |
| 507 | + <el-button icon="el-icon-circle-plus-outline" type="text" @click="addReg"> | |
| 508 | + 添加规则 | |
| 509 | + </el-button> | |
| 510 | + </div> | |
| 511 | + </template> | |
| 512 | + </el-form> | |
| 513 | + <!-- 表单属性 --> | |
| 514 | + <el-form v-show="currentTab === 'form'" size="small" label-width="90px"> | |
| 515 | + <el-form-item label="表单名"> | |
| 516 | + <el-input v-model="formConf.formRef" placeholder="请输入表单名(ref)" /> | |
| 517 | + </el-form-item> | |
| 518 | + <el-form-item label="表单模型"> | |
| 519 | + <el-input v-model="formConf.formModel" placeholder="请输入数据模型" /> | |
| 520 | + </el-form-item> | |
| 521 | + <el-form-item label="校验模型"> | |
| 522 | + <el-input v-model="formConf.formRules" placeholder="请输入校验模型" /> | |
| 523 | + </el-form-item> | |
| 524 | + <el-form-item label="表单尺寸"> | |
| 525 | + <el-radio-group v-model="formConf.size"> | |
| 526 | + <el-radio-button label="medium"> | |
| 527 | + 中等 | |
| 528 | + </el-radio-button> | |
| 529 | + <el-radio-button label="small"> | |
| 530 | + 较小 | |
| 531 | + </el-radio-button> | |
| 532 | + <el-radio-button label="mini"> | |
| 533 | + 迷你 | |
| 534 | + </el-radio-button> | |
| 535 | + </el-radio-group> | |
| 536 | + </el-form-item> | |
| 537 | + <el-form-item label="标签对齐"> | |
| 538 | + <el-radio-group v-model="formConf.labelPosition"> | |
| 539 | + <el-radio-button label="left"> | |
| 540 | + 左对齐 | |
| 541 | + </el-radio-button> | |
| 542 | + <el-radio-button label="right"> | |
| 543 | + 右对齐 | |
| 544 | + </el-radio-button> | |
| 545 | + <el-radio-button label="top"> | |
| 546 | + 顶部对齐 | |
| 547 | + </el-radio-button> | |
| 548 | + </el-radio-group> | |
| 549 | + </el-form-item> | |
| 550 | + <el-form-item label="标签宽度"> | |
| 551 | + <el-input-number v-model="formConf.labelWidth" placeholder="标签宽度" /> | |
| 552 | + </el-form-item> | |
| 553 | + <el-form-item label="栅格间隔"> | |
| 554 | + <el-input-number v-model="formConf.gutter" :min="0" placeholder="栅格间隔" /> | |
| 555 | + </el-form-item> | |
| 556 | + <el-form-item label="禁用表单"> | |
| 557 | + <el-switch v-model="formConf.disabled" /> | |
| 558 | + </el-form-item> | |
| 559 | + <el-form-item label="表单按钮"> | |
| 560 | + <el-switch v-model="formConf.formBtns" /> | |
| 561 | + </el-form-item> | |
| 562 | + <el-form-item label="显示未选中组件边框"> | |
| 563 | + <el-switch v-model="formConf.unFocusedComponentBorder" /> | |
| 564 | + </el-form-item> | |
| 565 | + </el-form> | |
| 566 | + </el-scrollbar> | |
| 567 | + </div> | |
| 568 | + | |
| 569 | + <treeNode-dialog :visible.sync="dialogVisible" title="添加选项" @commit="addNode" /> | |
| 570 | + <icons-dialog :visible.sync="iconsVisible" :current="activeData[currentIconModel]" @select="setIcon" /> | |
| 571 | + </div> | |
| 572 | +</template> | |
| 573 | + | |
| 574 | +<script> | |
| 575 | +import { isArray } from 'util' | |
| 576 | +import TreeNodeDialog from './TreeNodeDialog' | |
| 577 | +import { isNumberStr } from '@/utils/index' | |
| 578 | +import IconsDialog from './IconsDialog' | |
| 579 | +import { | |
| 580 | + inputComponents, | |
| 581 | + selectComponents, | |
| 582 | + layoutComponents | |
| 583 | +} from '@/utils/generator/config' | |
| 584 | + | |
| 585 | +const dateTimeFormat = { | |
| 586 | + date: 'yyyy-MM-dd', | |
| 587 | + week: 'yyyy 第 WW 周', | |
| 588 | + month: 'yyyy-MM', | |
| 589 | + year: 'yyyy', | |
| 590 | + datetime: 'yyyy-MM-dd HH:mm:ss', | |
| 591 | + daterange: 'yyyy-MM-dd', | |
| 592 | + monthrange: 'yyyy-MM', | |
| 593 | + datetimerange: 'yyyy-MM-dd HH:mm:ss' | |
| 594 | +} | |
| 595 | + | |
| 596 | +export default { | |
| 597 | + components: { | |
| 598 | + TreeNodeDialog, | |
| 599 | + IconsDialog | |
| 600 | + }, | |
| 601 | + props: ['showField', 'activeData', 'formConf'], | |
| 602 | + data() { | |
| 603 | + return { | |
| 604 | + currentTab: 'field', | |
| 605 | + currentNode: null, | |
| 606 | + dialogVisible: false, | |
| 607 | + iconsVisible: false, | |
| 608 | + currentIconModel: null, | |
| 609 | + dateTypeOptions: [ | |
| 610 | + { | |
| 611 | + label: '日(date)', | |
| 612 | + value: 'date' | |
| 613 | + }, | |
| 614 | + { | |
| 615 | + label: '周(week)', | |
| 616 | + value: 'week' | |
| 617 | + }, | |
| 618 | + { | |
| 619 | + label: '月(month)', | |
| 620 | + value: 'month' | |
| 621 | + }, | |
| 622 | + { | |
| 623 | + label: '年(year)', | |
| 624 | + value: 'year' | |
| 625 | + }, | |
| 626 | + { | |
| 627 | + label: '日期时间(datetime)', | |
| 628 | + value: 'datetime' | |
| 629 | + } | |
| 630 | + ], | |
| 631 | + dateRangeTypeOptions: [ | |
| 632 | + { | |
| 633 | + label: '日期范围(daterange)', | |
| 634 | + value: 'daterange' | |
| 635 | + }, | |
| 636 | + { | |
| 637 | + label: '月范围(monthrange)', | |
| 638 | + value: 'monthrange' | |
| 639 | + }, | |
| 640 | + { | |
| 641 | + label: '日期时间范围(datetimerange)', | |
| 642 | + value: 'datetimerange' | |
| 643 | + } | |
| 644 | + ], | |
| 645 | + colorFormatOptions: [ | |
| 646 | + { | |
| 647 | + label: 'hex', | |
| 648 | + value: 'hex' | |
| 649 | + }, | |
| 650 | + { | |
| 651 | + label: 'rgb', | |
| 652 | + value: 'rgb' | |
| 653 | + }, | |
| 654 | + { | |
| 655 | + label: 'rgba', | |
| 656 | + value: 'rgba' | |
| 657 | + }, | |
| 658 | + { | |
| 659 | + label: 'hsv', | |
| 660 | + value: 'hsv' | |
| 661 | + }, | |
| 662 | + { | |
| 663 | + label: 'hsl', | |
| 664 | + value: 'hsl' | |
| 665 | + } | |
| 666 | + ], | |
| 667 | + justifyOptions: [ | |
| 668 | + { | |
| 669 | + label: 'start', | |
| 670 | + value: 'start' | |
| 671 | + }, | |
| 672 | + { | |
| 673 | + label: 'end', | |
| 674 | + value: 'end' | |
| 675 | + }, | |
| 676 | + { | |
| 677 | + label: 'center', | |
| 678 | + value: 'center' | |
| 679 | + }, | |
| 680 | + { | |
| 681 | + label: 'space-around', | |
| 682 | + value: 'space-around' | |
| 683 | + }, | |
| 684 | + { | |
| 685 | + label: 'space-between', | |
| 686 | + value: 'space-between' | |
| 687 | + } | |
| 688 | + ], | |
| 689 | + layoutTreeProps: { | |
| 690 | + label(data, node) { | |
| 691 | + return data.componentName || `${data.label}: ${data.vModel}` | |
| 692 | + } | |
| 693 | + } | |
| 694 | + } | |
| 695 | + }, | |
| 696 | + computed: { | |
| 697 | + documentLink() { | |
| 698 | + return ( | |
| 699 | + this.activeData.document | |
| 700 | + || 'https://element.eleme.cn/#/zh-CN/component/installation' | |
| 701 | + ) | |
| 702 | + }, | |
| 703 | + dateOptions() { | |
| 704 | + if ( | |
| 705 | + this.activeData.type !== undefined | |
| 706 | + && this.activeData.tag === 'el-date-picker' | |
| 707 | + ) { | |
| 708 | + if (this.activeData['start-placeholder'] === undefined) { | |
| 709 | + return this.dateTypeOptions | |
| 710 | + } | |
| 711 | + return this.dateRangeTypeOptions | |
| 712 | + } | |
| 713 | + return [] | |
| 714 | + }, | |
| 715 | + tagList() { | |
| 716 | + return [ | |
| 717 | + { | |
| 718 | + label: '输入型组件', | |
| 719 | + options: inputComponents | |
| 720 | + }, | |
| 721 | + { | |
| 722 | + label: '选择型组件', | |
| 723 | + options: selectComponents | |
| 724 | + } | |
| 725 | + ] | |
| 726 | + } | |
| 727 | + }, | |
| 728 | + methods: { | |
| 729 | + addReg() { | |
| 730 | + this.activeData.regList.push({ | |
| 731 | + pattern: '', | |
| 732 | + message: '' | |
| 733 | + }) | |
| 734 | + }, | |
| 735 | + addSelectItem() { | |
| 736 | + this.activeData.options.push({ | |
| 737 | + label: '', | |
| 738 | + value: '' | |
| 739 | + }) | |
| 740 | + }, | |
| 741 | + addTreeItem() { | |
| 742 | + ++this.idGlobal | |
| 743 | + this.dialogVisible = true | |
| 744 | + this.currentNode = this.activeData.options | |
| 745 | + }, | |
| 746 | + renderContent(h, { node, data, store }) { | |
| 747 | + return ( | |
| 748 | + <div class="custom-tree-node"> | |
| 749 | + <span>{node.label}</span> | |
| 750 | + <span class="node-operation"> | |
| 751 | + <i on-click={() => this.append(data)} | |
| 752 | + class="el-icon-plus" | |
| 753 | + title="添加" | |
| 754 | + ></i> | |
| 755 | + <i on-click={() => this.remove(node, data)} | |
| 756 | + class="el-icon-delete" | |
| 757 | + title="删除" | |
| 758 | + ></i> | |
| 759 | + </span> | |
| 760 | + </div> | |
| 761 | + ) | |
| 762 | + }, | |
| 763 | + append(data) { | |
| 764 | + if (!data.children) { | |
| 765 | + this.$set(data, 'children', []) | |
| 766 | + } | |
| 767 | + this.dialogVisible = true | |
| 768 | + this.currentNode = data.children | |
| 769 | + }, | |
| 770 | + remove(node, data) { | |
| 771 | + const { parent } = node | |
| 772 | + const children = parent.data.children || parent.data | |
| 773 | + const index = children.findIndex(d => d.id === data.id) | |
| 774 | + children.splice(index, 1) | |
| 775 | + }, | |
| 776 | + addNode(data) { | |
| 777 | + this.currentNode.push(data) | |
| 778 | + }, | |
| 779 | + setOptionValue(item, val) { | |
| 780 | + item.value = isNumberStr(val) ? +val : val | |
| 781 | + }, | |
| 782 | + setDefaultValue(val) { | |
| 783 | + if (Array.isArray(val)) { | |
| 784 | + return val.join(',') | |
| 785 | + } | |
| 786 | + if (['string', 'number'].indexOf(val) > -1) { | |
| 787 | + return val | |
| 788 | + } | |
| 789 | + if (typeof val === 'boolean') { | |
| 790 | + return `${val}` | |
| 791 | + } | |
| 792 | + return val | |
| 793 | + }, | |
| 794 | + onDefaultValueInput(str) { | |
| 795 | + if (isArray(this.activeData.defaultValue)) { | |
| 796 | + // 数组 | |
| 797 | + this.$set( | |
| 798 | + this.activeData, | |
| 799 | + 'defaultValue', | |
| 800 | + str.split(',').map(val => (isNumberStr(val) ? +val : val)) | |
| 801 | + ) | |
| 802 | + } else if (['true', 'false'].indexOf(str) > -1) { | |
| 803 | + // 布尔 | |
| 804 | + this.$set(this.activeData, 'defaultValue', JSON.parse(str)) | |
| 805 | + } else { | |
| 806 | + // 字符串和数字 | |
| 807 | + this.$set( | |
| 808 | + this.activeData, | |
| 809 | + 'defaultValue', | |
| 810 | + isNumberStr(str) ? +str : str | |
| 811 | + ) | |
| 812 | + } | |
| 813 | + }, | |
| 814 | + onSwitchValueInput(val, name) { | |
| 815 | + if (['true', 'false'].indexOf(val) > -1) { | |
| 816 | + this.$set(this.activeData, name, JSON.parse(val)) | |
| 817 | + } else { | |
| 818 | + this.$set(this.activeData, name, isNumberStr(val) ? +val : val) | |
| 819 | + } | |
| 820 | + }, | |
| 821 | + setTimeValue(val, type) { | |
| 822 | + const valueFormat = type === 'week' ? dateTimeFormat.date : val | |
| 823 | + this.$set(this.activeData, 'defaultValue', null) | |
| 824 | + this.$set(this.activeData, 'value-format', valueFormat) | |
| 825 | + this.$set(this.activeData, 'format', val) | |
| 826 | + }, | |
| 827 | + spanChange(val) { | |
| 828 | + this.formConf.span = val | |
| 829 | + }, | |
| 830 | + multipleChange(val) { | |
| 831 | + this.$set(this.activeData, 'defaultValue', val ? [] : '') | |
| 832 | + }, | |
| 833 | + dateTypeChange(val) { | |
| 834 | + this.setTimeValue(dateTimeFormat[val], val) | |
| 835 | + }, | |
| 836 | + rangeChange(val) { | |
| 837 | + this.$set( | |
| 838 | + this.activeData, | |
| 839 | + 'defaultValue', | |
| 840 | + val ? [this.activeData.min, this.activeData.max] : this.activeData.min | |
| 841 | + ) | |
| 842 | + }, | |
| 843 | + rateTextChange(val) { | |
| 844 | + if (val) this.activeData['show-score'] = false | |
| 845 | + }, | |
| 846 | + rateScoreChange(val) { | |
| 847 | + if (val) this.activeData['show-text'] = false | |
| 848 | + }, | |
| 849 | + colorFormatChange(val) { | |
| 850 | + this.activeData.defaultValue = null | |
| 851 | + this.activeData['show-alpha'] = val.indexOf('a') > -1 | |
| 852 | + this.activeData.renderKey = +new Date() // 更新renderKey,重新渲染该组件 | |
| 853 | + }, | |
| 854 | + openIconsDialog(model) { | |
| 855 | + this.iconsVisible = true | |
| 856 | + this.currentIconModel = model | |
| 857 | + }, | |
| 858 | + setIcon(val) { | |
| 859 | + this.activeData[this.currentIconModel] = val | |
| 860 | + }, | |
| 861 | + tagChange(tagIcon) { | |
| 862 | + let target = inputComponents.find(item => item.tagIcon === tagIcon) | |
| 863 | + if (!target) target = selectComponents.find(item => item.tagIcon === tagIcon) | |
| 864 | + this.$emit('tag-change', target) | |
| 865 | + } | |
| 866 | + } | |
| 867 | +} | |
| 868 | +</script> | |
| 869 | + | |
| 870 | +<style lang="scss" scoped> | |
| 871 | +.right-board { | |
| 872 | + width: 350px; | |
| 873 | + position: absolute; | |
| 874 | + right: 0; | |
| 875 | + top: 0; | |
| 876 | + padding-top: 3px; | |
| 877 | + .field-box { | |
| 878 | + position: relative; | |
| 879 | + height: calc(100vh - 42px); | |
| 880 | + box-sizing: border-box; | |
| 881 | + overflow: hidden; | |
| 882 | + } | |
| 883 | + .el-scrollbar { | |
| 884 | + height: 100%; | |
| 885 | + } | |
| 886 | +} | |
| 887 | +.select-item { | |
| 888 | + display: flex; | |
| 889 | + border: 1px dashed #fff; | |
| 890 | + box-sizing: border-box; | |
| 891 | + & .close-btn { | |
| 892 | + cursor: pointer; | |
| 893 | + color: #f56c6c; | |
| 894 | + } | |
| 895 | + & .el-input + .el-input { | |
| 896 | + margin-left: 4px; | |
| 897 | + } | |
| 898 | +} | |
| 899 | +.select-item + .select-item { | |
| 900 | + margin-top: 4px; | |
| 901 | +} | |
| 902 | +.select-item.sortable-chosen { | |
| 903 | + border: 1px dashed #409eff; | |
| 904 | +} | |
| 905 | +.select-line-icon { | |
| 906 | + line-height: 32px; | |
| 907 | + font-size: 22px; | |
| 908 | + padding: 0 4px; | |
| 909 | + color: #777; | |
| 910 | +} | |
| 911 | +.option-drag { | |
| 912 | + cursor: move; | |
| 913 | +} | |
| 914 | +.time-range { | |
| 915 | + .el-date-editor { | |
| 916 | + width: 227px; | |
| 917 | + } | |
| 918 | + ::v-deep .el-icon-time { | |
| 919 | + display: none; | |
| 920 | + } | |
| 921 | +} | |
| 922 | +.document-link { | |
| 923 | + position: absolute; | |
| 924 | + display: block; | |
| 925 | + width: 26px; | |
| 926 | + height: 26px; | |
| 927 | + top: 0; | |
| 928 | + left: 0; | |
| 929 | + cursor: pointer; | |
| 930 | + background: #409eff; | |
| 931 | + z-index: 1; | |
| 932 | + border-radius: 0 0 6px 0; | |
| 933 | + text-align: center; | |
| 934 | + line-height: 26px; | |
| 935 | + color: #fff; | |
| 936 | + font-size: 18px; | |
| 937 | +} | |
| 938 | +.node-label{ | |
| 939 | + font-size: 14px; | |
| 940 | +} | |
| 941 | +.node-icon{ | |
| 942 | + color: #bebfc3; | |
| 943 | +} | |
| 944 | +</style> | ... | ... |
yudao-admin-ui/src/views/bpm/form/build/TreeNodeDialog.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div> | |
| 3 | + <el-dialog | |
| 4 | + v-bind="$attrs" | |
| 5 | + :close-on-click-modal="false" | |
| 6 | + :modal-append-to-body="false" | |
| 7 | + v-on="$listeners" | |
| 8 | + @open="onOpen" | |
| 9 | + @close="onClose" | |
| 10 | + > | |
| 11 | + <el-row :gutter="0"> | |
| 12 | + <el-form | |
| 13 | + ref="elForm" | |
| 14 | + :model="formData" | |
| 15 | + :rules="rules" | |
| 16 | + size="small" | |
| 17 | + label-width="100px" | |
| 18 | + > | |
| 19 | + <el-col :span="24"> | |
| 20 | + <el-form-item | |
| 21 | + label="选项名" | |
| 22 | + prop="label" | |
| 23 | + > | |
| 24 | + <el-input | |
| 25 | + v-model="formData.label" | |
| 26 | + placeholder="请输入选项名" | |
| 27 | + clearable | |
| 28 | + /> | |
| 29 | + </el-form-item> | |
| 30 | + </el-col> | |
| 31 | + <el-col :span="24"> | |
| 32 | + <el-form-item | |
| 33 | + label="选项值" | |
| 34 | + prop="value" | |
| 35 | + > | |
| 36 | + <el-input | |
| 37 | + v-model="formData.value" | |
| 38 | + placeholder="请输入选项值" | |
| 39 | + clearable | |
| 40 | + > | |
| 41 | + <el-select | |
| 42 | + slot="append" | |
| 43 | + v-model="dataType" | |
| 44 | + :style="{width: '100px'}" | |
| 45 | + > | |
| 46 | + <el-option | |
| 47 | + v-for="(item, index) in dataTypeOptions" | |
| 48 | + :key="index" | |
| 49 | + :label="item.label" | |
| 50 | + :value="item.value" | |
| 51 | + :disabled="item.disabled" | |
| 52 | + /> | |
| 53 | + </el-select> | |
| 54 | + </el-input> | |
| 55 | + </el-form-item> | |
| 56 | + </el-col> | |
| 57 | + </el-form> | |
| 58 | + </el-row> | |
| 59 | + <div slot="footer"> | |
| 60 | + <el-button | |
| 61 | + type="primary" | |
| 62 | + @click="handelConfirm" | |
| 63 | + > | |
| 64 | + 确定 | |
| 65 | + </el-button> | |
| 66 | + <el-button @click="close"> | |
| 67 | + 取消 | |
| 68 | + </el-button> | |
| 69 | + </div> | |
| 70 | + </el-dialog> | |
| 71 | + </div> | |
| 72 | +</template> | |
| 73 | +<script> | |
| 74 | +import { isNumberStr } from '@/utils/index' | |
| 75 | + | |
| 76 | +export default { | |
| 77 | + components: {}, | |
| 78 | + inheritAttrs: false, | |
| 79 | + props: [], | |
| 80 | + data() { | |
| 81 | + return { | |
| 82 | + id: 100, | |
| 83 | + formData: { | |
| 84 | + label: undefined, | |
| 85 | + value: undefined | |
| 86 | + }, | |
| 87 | + rules: { | |
| 88 | + label: [ | |
| 89 | + { | |
| 90 | + required: true, | |
| 91 | + message: '请输入选项名', | |
| 92 | + trigger: 'blur' | |
| 93 | + } | |
| 94 | + ], | |
| 95 | + value: [ | |
| 96 | + { | |
| 97 | + required: true, | |
| 98 | + message: '请输入选项值', | |
| 99 | + trigger: 'blur' | |
| 100 | + } | |
| 101 | + ] | |
| 102 | + }, | |
| 103 | + dataType: 'string', | |
| 104 | + dataTypeOptions: [ | |
| 105 | + { | |
| 106 | + label: '字符串', | |
| 107 | + value: 'string' | |
| 108 | + }, | |
| 109 | + { | |
| 110 | + label: '数字', | |
| 111 | + value: 'number' | |
| 112 | + } | |
| 113 | + ] | |
| 114 | + } | |
| 115 | + }, | |
| 116 | + computed: {}, | |
| 117 | + watch: { | |
| 118 | + // eslint-disable-next-line func-names | |
| 119 | + 'formData.value': function (val) { | |
| 120 | + this.dataType = isNumberStr(val) ? 'number' : 'string' | |
| 121 | + } | |
| 122 | + }, | |
| 123 | + created() {}, | |
| 124 | + mounted() {}, | |
| 125 | + methods: { | |
| 126 | + onOpen() { | |
| 127 | + this.formData = { | |
| 128 | + label: undefined, | |
| 129 | + value: undefined | |
| 130 | + } | |
| 131 | + }, | |
| 132 | + onClose() {}, | |
| 133 | + close() { | |
| 134 | + this.$emit('update:visible', false) | |
| 135 | + }, | |
| 136 | + handelConfirm() { | |
| 137 | + this.$refs.elForm.validate(valid => { | |
| 138 | + if (!valid) return | |
| 139 | + if (this.dataType === 'number') { | |
| 140 | + this.formData.value = parseFloat(this.formData.value) | |
| 141 | + } | |
| 142 | + this.formData.id = this.id++ | |
| 143 | + this.$emit('commit', this.formData) | |
| 144 | + this.close() | |
| 145 | + }) | |
| 146 | + } | |
| 147 | + } | |
| 148 | +} | |
| 149 | +</script> | ... | ... |
yudao-admin-ui/src/views/bpm/form/build/index.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="container"> | |
| 3 | + <div class="left-board"> | |
| 4 | + <div class="logo-wrapper"> | |
| 5 | + <div class="logo"> | |
| 6 | + <img :src="logo" alt="logo"> Form Generator | |
| 7 | + </div> | |
| 8 | + </div> | |
| 9 | + <el-scrollbar class="left-scrollbar"> | |
| 10 | + <div class="components-list"> | |
| 11 | + <div class="components-title"> | |
| 12 | + <svg-icon icon-class="component" />输入型组件 | |
| 13 | + </div> | |
| 14 | + <draggable | |
| 15 | + class="components-draggable" | |
| 16 | + :list="inputComponents" | |
| 17 | + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" | |
| 18 | + :clone="cloneComponent" | |
| 19 | + draggable=".components-item" | |
| 20 | + :sort="false" | |
| 21 | + @end="onEnd" | |
| 22 | + > | |
| 23 | + <div | |
| 24 | + v-for="(element, index) in inputComponents" :key="index" class="components-item" | |
| 25 | + @click="addComponent(element)" | |
| 26 | + > | |
| 27 | + <div class="components-body"> | |
| 28 | + <svg-icon :icon-class="element.tagIcon" /> | |
| 29 | + {{ element.label }} | |
| 30 | + </div> | |
| 31 | + </div> | |
| 32 | + </draggable> | |
| 33 | + <div class="components-title"> | |
| 34 | + <svg-icon icon-class="component" />选择型组件 | |
| 35 | + </div> | |
| 36 | + <draggable | |
| 37 | + class="components-draggable" | |
| 38 | + :list="selectComponents" | |
| 39 | + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" | |
| 40 | + :clone="cloneComponent" | |
| 41 | + draggable=".components-item" | |
| 42 | + :sort="false" | |
| 43 | + @end="onEnd" | |
| 44 | + > | |
| 45 | + <div | |
| 46 | + v-for="(element, index) in selectComponents" | |
| 47 | + :key="index" | |
| 48 | + class="components-item" | |
| 49 | + @click="addComponent(element)" | |
| 50 | + > | |
| 51 | + <div class="components-body"> | |
| 52 | + <svg-icon :icon-class="element.tagIcon" /> | |
| 53 | + {{ element.label }} | |
| 54 | + </div> | |
| 55 | + </div> | |
| 56 | + </draggable> | |
| 57 | + <div class="components-title"> | |
| 58 | + <svg-icon icon-class="component" /> 布局型组件 | |
| 59 | + </div> | |
| 60 | + <draggable | |
| 61 | + class="components-draggable" :list="layoutComponents" | |
| 62 | + :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent" | |
| 63 | + draggable=".components-item" :sort="false" @end="onEnd" | |
| 64 | + > | |
| 65 | + <div | |
| 66 | + v-for="(element, index) in layoutComponents" :key="index" class="components-item" | |
| 67 | + @click="addComponent(element)" | |
| 68 | + > | |
| 69 | + <div class="components-body"> | |
| 70 | + <svg-icon :icon-class="element.tagIcon" /> | |
| 71 | + {{ element.label }} | |
| 72 | + </div> | |
| 73 | + </div> | |
| 74 | + </draggable> | |
| 75 | + </div> | |
| 76 | + </el-scrollbar> | |
| 77 | + </div> | |
| 78 | + | |
| 79 | + <div class="center-board"> | |
| 80 | + <div class="action-bar"> | |
| 81 | + <el-button icon="el-icon-download" type="text" @click="download"> | |
| 82 | + 导出vue文件 | |
| 83 | + </el-button> | |
| 84 | + <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy"> | |
| 85 | + 复制代码 | |
| 86 | + </el-button> | |
| 87 | + <el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty"> | |
| 88 | + 清空 | |
| 89 | + </el-button> | |
| 90 | + </div> | |
| 91 | + <el-scrollbar class="center-scrollbar"> | |
| 92 | + <el-row class="center-board-row" :gutter="formConf.gutter"> | |
| 93 | + <el-form | |
| 94 | + :size="formConf.size" | |
| 95 | + :label-position="formConf.labelPosition" | |
| 96 | + :disabled="formConf.disabled" | |
| 97 | + :label-width="formConf.labelWidth + 'px'" | |
| 98 | + > | |
| 99 | + <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup"> | |
| 100 | + <draggable-item | |
| 101 | + v-for="(element, index) in drawingList" | |
| 102 | + :key="element.renderKey" | |
| 103 | + :drawing-list="drawingList" | |
| 104 | + :element="element" | |
| 105 | + :index="index" | |
| 106 | + :active-id="activeId" | |
| 107 | + :form-conf="formConf" | |
| 108 | + @activeItem="activeFormItem" | |
| 109 | + @copyItem="drawingItemCopy" | |
| 110 | + @deleteItem="drawingItemDelete" | |
| 111 | + /> | |
| 112 | + </draggable> | |
| 113 | + <div v-show="!drawingList.length" class="empty-info"> | |
| 114 | + 从左侧拖入或点选组件进行表单设计 | |
| 115 | + </div> | |
| 116 | + </el-form> | |
| 117 | + </el-row> | |
| 118 | + </el-scrollbar> | |
| 119 | + </div> | |
| 120 | + | |
| 121 | + <right-panel | |
| 122 | + :active-data="activeData" | |
| 123 | + :form-conf="formConf" | |
| 124 | + :show-field="!!drawingList.length" | |
| 125 | + @tag-change="tagChange" | |
| 126 | + /> | |
| 127 | + | |
| 128 | + <code-type-dialog | |
| 129 | + :visible.sync="dialogVisible" | |
| 130 | + title="选择生成类型" | |
| 131 | + :show-file-name="showFileName" | |
| 132 | + @confirm="generate" | |
| 133 | + /> | |
| 134 | + <input id="copyNode" type="hidden"> | |
| 135 | + </div> | |
| 136 | +</template> | |
| 137 | + | |
| 138 | +<script> | |
| 139 | +import draggable from 'vuedraggable' | |
| 140 | +import { saveAs } from 'file-saver' | |
| 141 | +import beautifier from 'js-beautify' | |
| 142 | +import ClipboardJS from 'clipboard' | |
| 143 | +import render from '@/utils/generator/render' | |
| 144 | +import RightPanel from './RightPanel' | |
| 145 | +import { | |
| 146 | + inputComponents, | |
| 147 | + selectComponents, | |
| 148 | + layoutComponents, | |
| 149 | + formConf | |
| 150 | +} from '@/utils/generator/config' | |
| 151 | +import { | |
| 152 | + exportDefault, beautifierConf, isNumberStr, titleCase | |
| 153 | +} from '@/utils/index' | |
| 154 | +import { | |
| 155 | + makeUpHtml, vueTemplate, vueScript, cssStyle | |
| 156 | +} from '@/utils/generator/html' | |
| 157 | +import { makeUpJs } from '@/utils/generator/js' | |
| 158 | +import { makeUpCss } from '@/utils/generator/css' | |
| 159 | +import drawingDefalut from '@/utils/generator/drawingDefalut' | |
| 160 | +import logo from '@/assets/logo/logo.png' | |
| 161 | +import CodeTypeDialog from './CodeTypeDialog' | |
| 162 | +import DraggableItem from './DraggableItem' | |
| 163 | + | |
| 164 | +const emptyActiveData = { style: {}, autosize: {} } | |
| 165 | +let oldActiveId | |
| 166 | +let tempActiveData | |
| 167 | + | |
| 168 | +export default { | |
| 169 | + components: { | |
| 170 | + draggable, | |
| 171 | + render, | |
| 172 | + RightPanel, | |
| 173 | + CodeTypeDialog, | |
| 174 | + DraggableItem | |
| 175 | + }, | |
| 176 | + data() { | |
| 177 | + return { | |
| 178 | + logo, | |
| 179 | + idGlobal: 100, | |
| 180 | + formConf, | |
| 181 | + inputComponents, | |
| 182 | + selectComponents, | |
| 183 | + layoutComponents, | |
| 184 | + labelWidth: 100, | |
| 185 | + drawingList: drawingDefalut, | |
| 186 | + drawingData: {}, | |
| 187 | + activeId: drawingDefalut[0].formId, | |
| 188 | + drawerVisible: false, | |
| 189 | + formData: {}, | |
| 190 | + dialogVisible: false, | |
| 191 | + generateConf: null, | |
| 192 | + showFileName: false, | |
| 193 | + activeData: drawingDefalut[0] | |
| 194 | + } | |
| 195 | + }, | |
| 196 | + computed: { | |
| 197 | + }, | |
| 198 | + watch: { | |
| 199 | + // eslint-disable-next-line func-names | |
| 200 | + 'activeData.label': function (val, oldVal) { | |
| 201 | + if ( | |
| 202 | + this.activeData.placeholder === undefined | |
| 203 | + || !this.activeData.tag | |
| 204 | + || oldActiveId !== this.activeId | |
| 205 | + ) { | |
| 206 | + return | |
| 207 | + } | |
| 208 | + this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val | |
| 209 | + }, | |
| 210 | + activeId: { | |
| 211 | + handler(val) { | |
| 212 | + oldActiveId = val | |
| 213 | + }, | |
| 214 | + immediate: true | |
| 215 | + } | |
| 216 | + }, | |
| 217 | + mounted() { | |
| 218 | + const clipboard = new ClipboardJS('#copyNode', { | |
| 219 | + text: trigger => { | |
| 220 | + const codeStr = this.generateCode() | |
| 221 | + this.$notify({ | |
| 222 | + title: '成功', | |
| 223 | + message: '代码已复制到剪切板,可粘贴。', | |
| 224 | + type: 'success' | |
| 225 | + }) | |
| 226 | + return codeStr | |
| 227 | + } | |
| 228 | + }) | |
| 229 | + clipboard.on('error', e => { | |
| 230 | + this.$message.error('代码复制失败') | |
| 231 | + }) | |
| 232 | + }, | |
| 233 | + methods: { | |
| 234 | + activeFormItem(element) { | |
| 235 | + this.activeData = element | |
| 236 | + this.activeId = element.formId | |
| 237 | + }, | |
| 238 | + onEnd(obj, a) { | |
| 239 | + if (obj.from !== obj.to) { | |
| 240 | + this.activeData = tempActiveData | |
| 241 | + this.activeId = this.idGlobal | |
| 242 | + } | |
| 243 | + }, | |
| 244 | + addComponent(item) { | |
| 245 | + const clone = this.cloneComponent(item) | |
| 246 | + this.drawingList.push(clone) | |
| 247 | + this.activeFormItem(clone) | |
| 248 | + }, | |
| 249 | + cloneComponent(origin) { | |
| 250 | + const clone = JSON.parse(JSON.stringify(origin)) | |
| 251 | + clone.formId = ++this.idGlobal | |
| 252 | + clone.span = formConf.span | |
| 253 | + clone.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件 | |
| 254 | + if (!clone.layout) clone.layout = 'colFormItem' | |
| 255 | + if (clone.layout === 'colFormItem') { | |
| 256 | + clone.vModel = `field${this.idGlobal}` | |
| 257 | + clone.placeholder !== undefined && (clone.placeholder += clone.label) | |
| 258 | + tempActiveData = clone | |
| 259 | + } else if (clone.layout === 'rowFormItem') { | |
| 260 | + delete clone.label | |
| 261 | + clone.componentName = `row${this.idGlobal}` | |
| 262 | + clone.gutter = this.formConf.gutter | |
| 263 | + tempActiveData = clone | |
| 264 | + } | |
| 265 | + return tempActiveData | |
| 266 | + }, | |
| 267 | + AssembleFormData() { | |
| 268 | + this.formData = { | |
| 269 | + fields: JSON.parse(JSON.stringify(this.drawingList)), | |
| 270 | + ...this.formConf | |
| 271 | + } | |
| 272 | + }, | |
| 273 | + generate(data) { | |
| 274 | + const func = this[`exec${titleCase(this.operationType)}`] | |
| 275 | + this.generateConf = data | |
| 276 | + func && func(data) | |
| 277 | + }, | |
| 278 | + execRun(data) { | |
| 279 | + this.AssembleFormData() | |
| 280 | + this.drawerVisible = true | |
| 281 | + }, | |
| 282 | + execDownload(data) { | |
| 283 | + const codeStr = this.generateCode() | |
| 284 | + const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' }) | |
| 285 | + saveAs(blob, data.fileName) | |
| 286 | + }, | |
| 287 | + execCopy(data) { | |
| 288 | + document.getElementById('copyNode').click() | |
| 289 | + }, | |
| 290 | + empty() { | |
| 291 | + this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then( | |
| 292 | + () => { | |
| 293 | + this.drawingList = [] | |
| 294 | + } | |
| 295 | + ) | |
| 296 | + }, | |
| 297 | + drawingItemCopy(item, parent) { | |
| 298 | + let clone = JSON.parse(JSON.stringify(item)) | |
| 299 | + clone = this.createIdAndKey(clone) | |
| 300 | + parent.push(clone) | |
| 301 | + this.activeFormItem(clone) | |
| 302 | + }, | |
| 303 | + createIdAndKey(item) { | |
| 304 | + item.formId = ++this.idGlobal | |
| 305 | + item.renderKey = +new Date() | |
| 306 | + if (item.layout === 'colFormItem') { | |
| 307 | + item.vModel = `field${this.idGlobal}` | |
| 308 | + } else if (item.layout === 'rowFormItem') { | |
| 309 | + item.componentName = `row${this.idGlobal}` | |
| 310 | + } | |
| 311 | + if (Array.isArray(item.children)) { | |
| 312 | + item.children = item.children.map(childItem => this.createIdAndKey(childItem)) | |
| 313 | + } | |
| 314 | + return item | |
| 315 | + }, | |
| 316 | + drawingItemDelete(index, parent) { | |
| 317 | + parent.splice(index, 1) | |
| 318 | + this.$nextTick(() => { | |
| 319 | + const len = this.drawingList.length | |
| 320 | + if (len) { | |
| 321 | + this.activeFormItem(this.drawingList[len - 1]) | |
| 322 | + } | |
| 323 | + }) | |
| 324 | + }, | |
| 325 | + generateCode() { | |
| 326 | + const { type } = this.generateConf | |
| 327 | + this.AssembleFormData() | |
| 328 | + const script = vueScript(makeUpJs(this.formData, type)) | |
| 329 | + const html = vueTemplate(makeUpHtml(this.formData, type)) | |
| 330 | + const css = cssStyle(makeUpCss(this.formData)) | |
| 331 | + return beautifier.html(html + script + css, beautifierConf.html) | |
| 332 | + }, | |
| 333 | + download() { | |
| 334 | + this.dialogVisible = true | |
| 335 | + this.showFileName = true | |
| 336 | + this.operationType = 'download' | |
| 337 | + }, | |
| 338 | + run() { | |
| 339 | + this.dialogVisible = true | |
| 340 | + this.showFileName = false | |
| 341 | + this.operationType = 'run' | |
| 342 | + }, | |
| 343 | + copy() { | |
| 344 | + this.dialogVisible = true | |
| 345 | + this.showFileName = false | |
| 346 | + this.operationType = 'copy' | |
| 347 | + }, | |
| 348 | + tagChange(newTag) { | |
| 349 | + newTag = this.cloneComponent(newTag) | |
| 350 | + newTag.vModel = this.activeData.vModel | |
| 351 | + newTag.formId = this.activeId | |
| 352 | + newTag.span = this.activeData.span | |
| 353 | + delete this.activeData.tag | |
| 354 | + delete this.activeData.tagIcon | |
| 355 | + delete this.activeData.document | |
| 356 | + Object.keys(newTag).forEach(key => { | |
| 357 | + if (this.activeData[key] !== undefined | |
| 358 | + && typeof this.activeData[key] === typeof newTag[key]) { | |
| 359 | + newTag[key] = this.activeData[key] | |
| 360 | + } | |
| 361 | + }) | |
| 362 | + this.activeData = newTag | |
| 363 | + this.updateDrawingList(newTag, this.drawingList) | |
| 364 | + }, | |
| 365 | + updateDrawingList(newTag, list) { | |
| 366 | + const index = list.findIndex(item => item.formId === this.activeId) | |
| 367 | + if (index > -1) { | |
| 368 | + list.splice(index, 1, newTag) | |
| 369 | + } else { | |
| 370 | + list.forEach(item => { | |
| 371 | + if (Array.isArray(item.children)) this.updateDrawingList(newTag, item.children) | |
| 372 | + }) | |
| 373 | + } | |
| 374 | + } | |
| 375 | + } | |
| 376 | +} | |
| 377 | +</script> | |
| 378 | + | |
| 379 | +<style lang='scss'> | |
| 380 | +body, html{ | |
| 381 | + margin: 0; | |
| 382 | + padding: 0; | |
| 383 | + background: #fff; | |
| 384 | + -moz-osx-font-smoothing: grayscale; | |
| 385 | + -webkit-font-smoothing: antialiased; | |
| 386 | + text-rendering: optimizeLegibility; | |
| 387 | + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; | |
| 388 | +} | |
| 389 | + | |
| 390 | +input, textarea{ | |
| 391 | + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; | |
| 392 | +} | |
| 393 | + | |
| 394 | +.editor-tabs{ | |
| 395 | + background: #121315; | |
| 396 | + .el-tabs__header{ | |
| 397 | + margin: 0; | |
| 398 | + border-bottom-color: #121315; | |
| 399 | + .el-tabs__nav{ | |
| 400 | + border-color: #121315; | |
| 401 | + } | |
| 402 | + } | |
| 403 | + .el-tabs__item{ | |
| 404 | + height: 32px; | |
| 405 | + line-height: 32px; | |
| 406 | + color: #888a8e; | |
| 407 | + border-left: 1px solid #121315 !important; | |
| 408 | + background: #363636; | |
| 409 | + margin-right: 5px; | |
| 410 | + user-select: none; | |
| 411 | + } | |
| 412 | + .el-tabs__item.is-active{ | |
| 413 | + background: #1e1e1e; | |
| 414 | + border-bottom-color: #1e1e1e!important; | |
| 415 | + color: #fff; | |
| 416 | + } | |
| 417 | + .el-icon-edit{ | |
| 418 | + color: #f1fa8c; | |
| 419 | + } | |
| 420 | + .el-icon-document{ | |
| 421 | + color: #a95812; | |
| 422 | + } | |
| 423 | +} | |
| 424 | + | |
| 425 | +// home | |
| 426 | +.right-scrollbar { | |
| 427 | + .el-scrollbar__view { | |
| 428 | + padding: 12px 18px 15px 15px; | |
| 429 | + } | |
| 430 | +} | |
| 431 | +.left-scrollbar .el-scrollbar__wrap { | |
| 432 | + box-sizing: border-box; | |
| 433 | + overflow-x: hidden !important; | |
| 434 | + margin-bottom: 0 !important; | |
| 435 | +} | |
| 436 | +.center-tabs{ | |
| 437 | + .el-tabs__header{ | |
| 438 | + margin-bottom: 0!important; | |
| 439 | + } | |
| 440 | + .el-tabs__item{ | |
| 441 | + width: 50%; | |
| 442 | + text-align: center; | |
| 443 | + } | |
| 444 | + .el-tabs__nav{ | |
| 445 | + width: 100%; | |
| 446 | + } | |
| 447 | +} | |
| 448 | +.reg-item{ | |
| 449 | + padding: 12px 6px; | |
| 450 | + background: #f8f8f8; | |
| 451 | + position: relative; | |
| 452 | + border-radius: 4px; | |
| 453 | + .close-btn{ | |
| 454 | + position: absolute; | |
| 455 | + right: -6px; | |
| 456 | + top: -6px; | |
| 457 | + display: block; | |
| 458 | + width: 16px; | |
| 459 | + height: 16px; | |
| 460 | + line-height: 16px; | |
| 461 | + background: rgba(0, 0, 0, 0.2); | |
| 462 | + border-radius: 50%; | |
| 463 | + color: #fff; | |
| 464 | + text-align: center; | |
| 465 | + z-index: 1; | |
| 466 | + cursor: pointer; | |
| 467 | + font-size: 12px; | |
| 468 | + &:hover{ | |
| 469 | + background: rgba(210, 23, 23, 0.5) | |
| 470 | + } | |
| 471 | + } | |
| 472 | + & + .reg-item{ | |
| 473 | + margin-top: 18px; | |
| 474 | + } | |
| 475 | +} | |
| 476 | +.action-bar{ | |
| 477 | + & .el-button+.el-button { | |
| 478 | + margin-left: 15px; | |
| 479 | + } | |
| 480 | + & i { | |
| 481 | + font-size: 20px; | |
| 482 | + vertical-align: middle; | |
| 483 | + position: relative; | |
| 484 | + top: -1px; | |
| 485 | + } | |
| 486 | +} | |
| 487 | + | |
| 488 | +.custom-tree-node{ | |
| 489 | + width: 100%; | |
| 490 | + font-size: 14px; | |
| 491 | + .node-operation{ | |
| 492 | + float: right; | |
| 493 | + } | |
| 494 | + i[class*="el-icon"] + i[class*="el-icon"]{ | |
| 495 | + margin-left: 6px; | |
| 496 | + } | |
| 497 | + .el-icon-plus{ | |
| 498 | + color: #409EFF; | |
| 499 | + } | |
| 500 | + .el-icon-delete{ | |
| 501 | + color: #157a0c; | |
| 502 | + } | |
| 503 | +} | |
| 504 | + | |
| 505 | +.left-scrollbar .el-scrollbar__view{ | |
| 506 | + overflow-x: hidden; | |
| 507 | +} | |
| 508 | + | |
| 509 | +.el-rate{ | |
| 510 | + display: inline-block; | |
| 511 | + vertical-align: text-top; | |
| 512 | +} | |
| 513 | +.el-upload__tip{ | |
| 514 | + line-height: 1.2; | |
| 515 | +} | |
| 516 | + | |
| 517 | +$selectedColor: #f6f7ff; | |
| 518 | +$lighterBlue: #409EFF; | |
| 519 | + | |
| 520 | +.container { | |
| 521 | + position: relative; | |
| 522 | + width: 100%; | |
| 523 | + height: 100%; | |
| 524 | +} | |
| 525 | + | |
| 526 | +.components-list { | |
| 527 | + padding: 8px; | |
| 528 | + box-sizing: border-box; | |
| 529 | + height: 100%; | |
| 530 | + .components-item { | |
| 531 | + display: inline-block; | |
| 532 | + width: 48%; | |
| 533 | + margin: 1%; | |
| 534 | + transition: transform 0ms !important; | |
| 535 | + } | |
| 536 | +} | |
| 537 | +.components-draggable{ | |
| 538 | + padding-bottom: 20px; | |
| 539 | +} | |
| 540 | +.components-title{ | |
| 541 | + font-size: 14px; | |
| 542 | + color: #222; | |
| 543 | + margin: 6px 2px; | |
| 544 | + .svg-icon{ | |
| 545 | + color: #666; | |
| 546 | + font-size: 18px; | |
| 547 | + } | |
| 548 | +} | |
| 549 | + | |
| 550 | +.components-body { | |
| 551 | + padding: 8px 10px; | |
| 552 | + background: $selectedColor; | |
| 553 | + font-size: 12px; | |
| 554 | + cursor: move; | |
| 555 | + border: 1px dashed $selectedColor; | |
| 556 | + border-radius: 3px; | |
| 557 | + .svg-icon{ | |
| 558 | + color: #777; | |
| 559 | + font-size: 15px; | |
| 560 | + } | |
| 561 | + &:hover { | |
| 562 | + border: 1px dashed #787be8; | |
| 563 | + color: #787be8; | |
| 564 | + .svg-icon { | |
| 565 | + color: #787be8; | |
| 566 | + } | |
| 567 | + } | |
| 568 | +} | |
| 569 | + | |
| 570 | +.left-board { | |
| 571 | + width: 260px; | |
| 572 | + position: absolute; | |
| 573 | + left: 0; | |
| 574 | + top: 0; | |
| 575 | + height: 100vh; | |
| 576 | +} | |
| 577 | +.left-scrollbar{ | |
| 578 | + height: calc(100vh - 42px); | |
| 579 | + overflow: hidden; | |
| 580 | +} | |
| 581 | +.center-scrollbar { | |
| 582 | + height: calc(100vh - 42px); | |
| 583 | + overflow: hidden; | |
| 584 | + border-left: 1px solid #f1e8e8; | |
| 585 | + border-right: 1px solid #f1e8e8; | |
| 586 | + box-sizing: border-box; | |
| 587 | +} | |
| 588 | +.center-board { | |
| 589 | + height: 100vh; | |
| 590 | + width: auto; | |
| 591 | + margin: 0 350px 0 260px; | |
| 592 | + box-sizing: border-box; | |
| 593 | +} | |
| 594 | +.empty-info{ | |
| 595 | + position: absolute; | |
| 596 | + top: 46%; | |
| 597 | + left: 0; | |
| 598 | + right: 0; | |
| 599 | + text-align: center; | |
| 600 | + font-size: 18px; | |
| 601 | + color: #ccb1ea; | |
| 602 | + letter-spacing: 4px; | |
| 603 | +} | |
| 604 | +.action-bar{ | |
| 605 | + position: relative; | |
| 606 | + height: 42px; | |
| 607 | + text-align: right; | |
| 608 | + padding: 0 15px; | |
| 609 | + box-sizing: border-box;; | |
| 610 | + border: 1px solid #f1e8e8; | |
| 611 | + border-top: none; | |
| 612 | + border-left: none; | |
| 613 | + .delete-btn{ | |
| 614 | + color: #F56C6C; | |
| 615 | + } | |
| 616 | +} | |
| 617 | +.logo-wrapper{ | |
| 618 | + position: relative; | |
| 619 | + height: 42px; | |
| 620 | + background: #fff; | |
| 621 | + border-bottom: 1px solid #f1e8e8; | |
| 622 | + box-sizing: border-box; | |
| 623 | +} | |
| 624 | +.logo{ | |
| 625 | + position: absolute; | |
| 626 | + left: 12px; | |
| 627 | + top: 6px; | |
| 628 | + line-height: 30px; | |
| 629 | + color: #00afff; | |
| 630 | + font-weight: 600; | |
| 631 | + font-size: 17px; | |
| 632 | + white-space: nowrap; | |
| 633 | + > img{ | |
| 634 | + width: 30px; | |
| 635 | + height: 30px; | |
| 636 | + vertical-align: top; | |
| 637 | + } | |
| 638 | + .github{ | |
| 639 | + display: inline-block; | |
| 640 | + vertical-align: sub; | |
| 641 | + margin-left: 15px; | |
| 642 | + > img{ | |
| 643 | + height: 22px; | |
| 644 | + } | |
| 645 | + } | |
| 646 | +} | |
| 647 | + | |
| 648 | +.center-board-row { | |
| 649 | + padding: 12px 12px 15px 12px; | |
| 650 | + box-sizing: border-box; | |
| 651 | + & > .el-form { | |
| 652 | + // 69 = 12+15+42 | |
| 653 | + height: calc(100vh - 69px); | |
| 654 | + } | |
| 655 | +} | |
| 656 | +.drawing-board { | |
| 657 | + height: 100%; | |
| 658 | + position: relative; | |
| 659 | + .components-body { | |
| 660 | + padding: 0; | |
| 661 | + margin: 0; | |
| 662 | + font-size: 0; | |
| 663 | + } | |
| 664 | + .sortable-ghost { | |
| 665 | + position: relative; | |
| 666 | + display: block; | |
| 667 | + overflow: hidden; | |
| 668 | + &::before { | |
| 669 | + content: " "; | |
| 670 | + position: absolute; | |
| 671 | + left: 0; | |
| 672 | + right: 0; | |
| 673 | + top: 0; | |
| 674 | + height: 3px; | |
| 675 | + background: rgb(89, 89, 223); | |
| 676 | + z-index: 2; | |
| 677 | + } | |
| 678 | + } | |
| 679 | + .components-item.sortable-ghost { | |
| 680 | + width: 100%; | |
| 681 | + height: 60px; | |
| 682 | + background-color: $selectedColor; | |
| 683 | + } | |
| 684 | + .active-from-item { | |
| 685 | + & > .el-form-item{ | |
| 686 | + background: $selectedColor; | |
| 687 | + border-radius: 6px; | |
| 688 | + } | |
| 689 | + & > .drawing-item-copy, & > .drawing-item-delete{ | |
| 690 | + display: initial; | |
| 691 | + } | |
| 692 | + & > .component-name{ | |
| 693 | + color: $lighterBlue; | |
| 694 | + } | |
| 695 | + } | |
| 696 | + .el-form-item{ | |
| 697 | + margin-bottom: 15px; | |
| 698 | + } | |
| 699 | +} | |
| 700 | +.drawing-item{ | |
| 701 | + position: relative; | |
| 702 | + cursor: move; | |
| 703 | + &.unfocus-bordered:not(.activeFromItem) > div:first-child { | |
| 704 | + border: 1px dashed #ccc; | |
| 705 | + } | |
| 706 | + .el-form-item{ | |
| 707 | + padding: 12px 10px; | |
| 708 | + } | |
| 709 | +} | |
| 710 | +.drawing-row-item{ | |
| 711 | + position: relative; | |
| 712 | + cursor: move; | |
| 713 | + box-sizing: border-box; | |
| 714 | + border: 1px dashed #ccc; | |
| 715 | + border-radius: 3px; | |
| 716 | + padding: 0 2px; | |
| 717 | + margin-bottom: 15px; | |
| 718 | + .drawing-row-item { | |
| 719 | + margin-bottom: 2px; | |
| 720 | + } | |
| 721 | + .el-col{ | |
| 722 | + margin-top: 22px; | |
| 723 | + } | |
| 724 | + .el-form-item{ | |
| 725 | + margin-bottom: 0; | |
| 726 | + } | |
| 727 | + .drag-wrapper{ | |
| 728 | + min-height: 80px; | |
| 729 | + } | |
| 730 | + &.active-from-item{ | |
| 731 | + border: 1px dashed $lighterBlue; | |
| 732 | + } | |
| 733 | + .component-name{ | |
| 734 | + position: absolute; | |
| 735 | + top: 0; | |
| 736 | + left: 0; | |
| 737 | + font-size: 12px; | |
| 738 | + color: #bbb; | |
| 739 | + display: inline-block; | |
| 740 | + padding: 0 6px; | |
| 741 | + } | |
| 742 | +} | |
| 743 | +.drawing-item, .drawing-row-item{ | |
| 744 | + &:hover { | |
| 745 | + & > .el-form-item{ | |
| 746 | + background: $selectedColor; | |
| 747 | + border-radius: 6px; | |
| 748 | + } | |
| 749 | + & > .drawing-item-copy, & > .drawing-item-delete{ | |
| 750 | + display: initial; | |
| 751 | + } | |
| 752 | + } | |
| 753 | + & > .drawing-item-copy, & > .drawing-item-delete{ | |
| 754 | + display: none; | |
| 755 | + position: absolute; | |
| 756 | + top: -10px; | |
| 757 | + width: 22px; | |
| 758 | + height: 22px; | |
| 759 | + line-height: 22px; | |
| 760 | + text-align: center; | |
| 761 | + border-radius: 50%; | |
| 762 | + font-size: 12px; | |
| 763 | + border: 1px solid; | |
| 764 | + cursor: pointer; | |
| 765 | + z-index: 1; | |
| 766 | + } | |
| 767 | + & > .drawing-item-copy{ | |
| 768 | + right: 56px; | |
| 769 | + border-color: $lighterBlue; | |
| 770 | + color: $lighterBlue; | |
| 771 | + background: #fff; | |
| 772 | + &:hover{ | |
| 773 | + background: $lighterBlue; | |
| 774 | + color: #fff; | |
| 775 | + } | |
| 776 | + } | |
| 777 | + & > .drawing-item-delete{ | |
| 778 | + right: 24px; | |
| 779 | + border-color: #F56C6C; | |
| 780 | + color: #F56C6C; | |
| 781 | + background: #fff; | |
| 782 | + &:hover{ | |
| 783 | + background: #F56C6C; | |
| 784 | + color: #fff; | |
| 785 | + } | |
| 786 | + } | |
| 787 | +} | |
| 788 | + | |
| 789 | +</style> | ... | ... |