|
|
@@ -4,42 +4,89 @@
|
|
|
<!-- 左侧字段 -->
|
|
|
<el-aside :width="leftWidth">
|
|
|
<div class="fields-list">
|
|
|
+ <template v-if="customFields && customFields.length > 0">
|
|
|
+ <template v-if="customFields[0].title && customFields[0].list && customFields[0].list.length > 0">
|
|
|
+ <template v-for="(field, index) in customFields">
|
|
|
+ <div class="field-title"
|
|
|
+ :key="'f_' + index">{{field.title}}</div>
|
|
|
+ <draggable tag="ul"
|
|
|
+ :list="field.list"
|
|
|
+ :group="{ name: 'form', pull: 'clone', put: false }"
|
|
|
+ ghost-class="ghost"
|
|
|
+ :sort="false"
|
|
|
+ :key="'d_' + index">
|
|
|
+ <template v-for="(item, cIndex) in field.list">
|
|
|
+ <li class="field-label"
|
|
|
+ :key="'c_' + cIndex">
|
|
|
+ <a @click="handleFieldClick(item)">
|
|
|
+ <i class="icon iconfont"
|
|
|
+ :class="item.icon"></i>
|
|
|
+ <span>{{item.title || item.label}}</span>
|
|
|
+ </a>
|
|
|
+ </li>
|
|
|
+ </template>
|
|
|
+ </draggable>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-link class="field-title"
|
|
|
+ :underline="false"
|
|
|
+ href="https://github.com/sscfaith/avue-form-design/blob/master/CHANGELOG.md#2020-09-22"
|
|
|
+ target="_blank">自定义字段 <i class="el-icon-question"></i></el-link>
|
|
|
+ <draggable tag="ul"
|
|
|
+ :list="customFields"
|
|
|
+ :group="{ name: 'form', pull: 'clone', put: false }"
|
|
|
+ ghost-class="ghost"
|
|
|
+ :sort="false">
|
|
|
+ <template v-for="(item, index) in customFields">
|
|
|
+ <el-tooltip v-if="item.tips"
|
|
|
+ effect="dark"
|
|
|
+ :content="item.tips"
|
|
|
+ :key="index">
|
|
|
+ <li class="field-label"
|
|
|
+ :key="index">
|
|
|
+ <a style="padding: 0 5px;"
|
|
|
+ @click="handleFieldClick(item)">
|
|
|
+ <i :class="item.icon"></i>
|
|
|
+ <span style="margin-left: 5px;">{{item.title || item.label}}</span>
|
|
|
+ </a>
|
|
|
+ </li>
|
|
|
+ </el-tooltip>
|
|
|
+ <li v-else
|
|
|
+ class="field-label"
|
|
|
+ :key="index">
|
|
|
+ <a style="padding: 0 5px;"
|
|
|
+ @click="handleFieldClick(item)">
|
|
|
+ <i :class="item.icon"></i>
|
|
|
+ <span style="margin-left: 5px;">{{item.title || item.label}}</span>
|
|
|
+ </a>
|
|
|
+ </li>
|
|
|
+ </template>
|
|
|
+ </draggable>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
<div v-for="(field, index) in fields"
|
|
|
:key="index">
|
|
|
- <div v-if="!field.disabled">
|
|
|
+ <template v-if="field.list.find(f => includeFields.includes(f.type))">
|
|
|
<div class="field-title">{{field.title}}</div>
|
|
|
<draggable tag="ul"
|
|
|
:list="field.list"
|
|
|
:group="{ name: 'form', pull: 'clone', put: false }"
|
|
|
ghost-class="ghost"
|
|
|
:sort="false">
|
|
|
- <li class="field-label"
|
|
|
- v-for="(item, index) in field.list"
|
|
|
- :key="index">
|
|
|
- <a>
|
|
|
- <i class="icon iconfont"
|
|
|
- :class="item.icon"></i>
|
|
|
- <span>{{item.label}}</span>
|
|
|
- </a>
|
|
|
- </li>
|
|
|
+ <template v-for="(item, cIndex) in field.list">
|
|
|
+ <li class="field-label"
|
|
|
+ v-if="includeFields.includes(item.type)"
|
|
|
+ :key="'c_' + cIndex">
|
|
|
+ <a @click="handleFieldClick(item)">
|
|
|
+ <i class="icon iconfont"
|
|
|
+ :class="item.icon"></i>
|
|
|
+ <span>{{item.title || item.label}}</span>
|
|
|
+ </a>
|
|
|
+ </li>
|
|
|
+ </template>
|
|
|
</draggable>
|
|
|
- </div>
|
|
|
- <div v-else>
|
|
|
- <div class="field-title">{{field.title}}
|
|
|
- <span class="danger">(开发中)</span>
|
|
|
- </div>
|
|
|
- <ul>
|
|
|
- <li class="field-label-disabled"
|
|
|
- v-for="(item, index) in field.list"
|
|
|
- :key="index">
|
|
|
- <a>
|
|
|
- <i class="icon iconfont"
|
|
|
- :class="item.icon"></i>
|
|
|
- <span>{{item.label}}</span>
|
|
|
- </a>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-aside>
|
|
|
@@ -47,43 +94,76 @@
|
|
|
<el-container class="widget-container"
|
|
|
direction="vertical">
|
|
|
<el-header class="widget-container-header">
|
|
|
- <el-button type="text"
|
|
|
- size="medium"
|
|
|
- icon="el-icon-document"
|
|
|
- @click="handleAvueDoc">Avue文档</el-button>
|
|
|
- <el-button type="text"
|
|
|
- size="medium"
|
|
|
- icon="el-icon-upload2"
|
|
|
- @click="importJsonVisible = true">导入JSON</el-button>
|
|
|
- <el-button type="text"
|
|
|
- size="medium"
|
|
|
- icon="el-icon-download"
|
|
|
- @click="handleGenerateJson">生成JSON</el-button>
|
|
|
- <el-button type="text"
|
|
|
- size="medium"
|
|
|
- icon="el-icon-view"
|
|
|
- @click="handlePreview">预览</el-button>
|
|
|
- <el-button class="danger"
|
|
|
- type="text"
|
|
|
- size="medium"
|
|
|
- icon="el-icon-delete"
|
|
|
- @click="handleClear">清空</el-button>
|
|
|
+ <div>
|
|
|
+ <template v-if="undoRedo">
|
|
|
+ <el-button type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-refresh-left"
|
|
|
+ :disabled="historySteps.index == 0"
|
|
|
+ @click="widgetForm = handleUndo()">撤销</el-button>
|
|
|
+ <el-button type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-refresh-right"
|
|
|
+ :disabled="historySteps.index == historySteps.steps.length - 1"
|
|
|
+ @click="widgetForm = handleRedo()">重做</el-button>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <div style="display: flex; align-items: center;">
|
|
|
+ <iframe src="https://ghbtns.com/github-btn.html?user=sscfaith&repo=avue-form-design&type=star&count=true"
|
|
|
+ frameborder="0"
|
|
|
+ scrolling="0"
|
|
|
+ width="100"
|
|
|
+ height="20"
|
|
|
+ title="GitHub"
|
|
|
+ style="margin-left: 10px;"
|
|
|
+ v-if="showGithubStar"></iframe>
|
|
|
+ <slot name="toolbar-left"></slot>
|
|
|
+ <el-button v-if="toolbar.includes('avue-doc')"
|
|
|
+ type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-document"
|
|
|
+ @click="handleAvueDoc">Avue文档</el-button>
|
|
|
+ <el-button v-if="toolbar.includes('import')"
|
|
|
+ type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-upload2"
|
|
|
+ @click="importJsonVisible = true">导入JSON</el-button>
|
|
|
+ <el-button v-if="toolbar.includes('generate')"
|
|
|
+ type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-download"
|
|
|
+ @click="handleGenerateJson">生成JSON</el-button>
|
|
|
+ <el-button v-if="toolbar.includes('preview')"
|
|
|
+ type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-view"
|
|
|
+ @click="handlePreview">预览</el-button>
|
|
|
+ <el-button v-if="toolbar.includes('clear')"
|
|
|
+ class="danger"
|
|
|
+ type="text"
|
|
|
+ size="medium"
|
|
|
+ icon="el-icon-delete"
|
|
|
+ @click="handleClear">清空</el-button>
|
|
|
+ <slot name="toolbar"></slot>
|
|
|
+ </div>
|
|
|
</el-header>
|
|
|
<el-main :style="{background: widgetForm.column.length == 0 ? `url(${widgetEmpty}) no-repeat 50%`: ''}">
|
|
|
<widget-form ref="widgetForm"
|
|
|
:data="widgetForm"
|
|
|
- :select.sync="widgetFormSelect"></widget-form>
|
|
|
+ :select.sync="widgetFormSelect"
|
|
|
+ @change="handleHistoryChange(widgetForm)"></widget-form>
|
|
|
</el-main>
|
|
|
</el-container>
|
|
|
<!-- 右侧配置 -->
|
|
|
<el-aside class="widget-config-container"
|
|
|
- :width="asideRightWidth">
|
|
|
+ :width="rightWidth">
|
|
|
<el-tabs v-model="configTab"
|
|
|
stretch>
|
|
|
<el-tab-pane label="字段属性"
|
|
|
name="widget"
|
|
|
style="padding: 0 10px;">
|
|
|
- <widget-config :data="widgetFormSelect"></widget-config>
|
|
|
+ <widget-config :data="widgetFormSelect"
|
|
|
+ :default-values="defaultValues"></widget-config>
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane label="表单属性"
|
|
|
name="form"
|
|
|
@@ -98,9 +178,11 @@
|
|
|
<el-drawer title="导入JSON"
|
|
|
:visible.sync="importJsonVisible"
|
|
|
size="50%"
|
|
|
+ append-to-body
|
|
|
destroy-on-close>
|
|
|
- <v-json-editor v-model="importJson"
|
|
|
- height="82vh"></v-json-editor>
|
|
|
+ <monaco-editor v-model="importJson"
|
|
|
+ keyIndex="import"
|
|
|
+ height="82%"></monaco-editor>
|
|
|
<div class="drawer-foot">
|
|
|
<el-button size="medium"
|
|
|
type="primary"
|
|
|
@@ -114,57 +196,69 @@
|
|
|
<el-drawer title="生成JSON"
|
|
|
:visible.sync="generateJsonVisible"
|
|
|
size="50%"
|
|
|
+ append-to-body
|
|
|
destroy-on-close>
|
|
|
- <v-json-editor v-model="widgetFormPreview"
|
|
|
- height="82vh"></v-json-editor>
|
|
|
+ <monaco-editor v-model="option"
|
|
|
+ keyIndex="generate"
|
|
|
+ height="82%"
|
|
|
+ :read-only="true"></monaco-editor>
|
|
|
<div class="drawer-foot">
|
|
|
<el-button size="medium"
|
|
|
type="primary"
|
|
|
@click="handleGenerate">生成</el-button>
|
|
|
+
|
|
|
<el-popover placement="top"
|
|
|
trigger="hover"
|
|
|
- popper-class="popper-bo"
|
|
|
- width="250px">
|
|
|
+ width="350px">
|
|
|
+ <el-form v-model="configOption"
|
|
|
+ style="padding: 0 20px"
|
|
|
+ label-suffix=":"
|
|
|
+ label-width="180px"
|
|
|
+ label-position="left">
|
|
|
+ <el-form-item label="类型">
|
|
|
+ <el-popover placement="top-start"
|
|
|
+ trigger="hover"
|
|
|
+ content="复制json对象"
|
|
|
+ style="margin-right: 15px;">
|
|
|
+ <el-radio slot="reference"
|
|
|
+ v-model="configOption.generateType"
|
|
|
+ label="json">json</el-radio>
|
|
|
+ </el-popover>
|
|
|
+ <el-popover placement="top-start"
|
|
|
+ trigger="hover"
|
|
|
+ content="复制string字符串,可直接用于后端保存无需再次处理。">
|
|
|
+ <el-radio slot="reference"
|
|
|
+ v-model="configOption.generateType"
|
|
|
+ label="string">string</el-radio>
|
|
|
+ </el-popover>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="缩进长度-空格数量">
|
|
|
+ <el-slider v-model="configOption.space"
|
|
|
+ show-stops
|
|
|
+ :marks="{ 1: '1', 2: '2', 3: '3', 4: '4' }"
|
|
|
+ :min="1"
|
|
|
+ :max="4"
|
|
|
+ :step="1"></el-slider>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="引号类型">
|
|
|
+ <el-switch v-model="configOption.quoteType"
|
|
|
+ active-value="single"
|
|
|
+ inactive-value="double"
|
|
|
+ active-text="单引号"
|
|
|
+ inactive-text="双引号"></el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="移除key的引号">
|
|
|
+ <el-switch v-model="configOption.dropQuotesOnKeys"></el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="移除数字字符串的引号">
|
|
|
+ <el-switch v-model="configOption.dropQuotesOnNumbers"></el-switch>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
<el-button size="medium"
|
|
|
type="primary"
|
|
|
- slot="reference"
|
|
|
@click="handleCopy"
|
|
|
+ slot="reference"
|
|
|
style="margin-left: 10px;">复制</el-button>
|
|
|
- <div>
|
|
|
- <el-form label-width="180px"
|
|
|
- label-position="left">
|
|
|
- <el-alert :closable="false">
|
|
|
- 在没有开启美化的情况下,当前编辑器内可见的文本,就是复制得到的内容。<br>
|
|
|
- 如有需要,您可以开启美化,然后选取适合自己的美化配置。
|
|
|
- <a href="https://www.npmjs.com/package/csvjson-json_beautifier"
|
|
|
- target="_blank">参考资料</a>
|
|
|
- </el-alert>
|
|
|
- <el-form-item label="是否开启美化">
|
|
|
- <el-switch v-model="beautifierOptions.enabled" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="缩进长度-空格数量">
|
|
|
- <el-slider v-model="beautifierOptions.space"
|
|
|
- show-stops
|
|
|
- :marks="{ 1: '1', 2: '2', 3: '3', 4: '4' }"
|
|
|
- :min="1"
|
|
|
- :max="4"
|
|
|
- :step="1"></el-slider>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="引号类型">
|
|
|
- <el-switch v-model="beautifierOptions.quoteType"
|
|
|
- active-value="single"
|
|
|
- inactive-value="double"
|
|
|
- active-text="单引号"
|
|
|
- inactive-text="双引号"></el-switch>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="移除key的引号">
|
|
|
- <el-switch v-model="beautifierOptions.dropQuotesOnKeys"></el-switch>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="移除数字字符串的引号">
|
|
|
- <el-switch v-model="beautifierOptions.dropQuotesOnNumbers"></el-switch>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </div>
|
|
|
</el-popover>
|
|
|
</div>
|
|
|
</el-drawer>
|
|
|
@@ -172,12 +266,14 @@
|
|
|
<el-drawer title="预览"
|
|
|
:visible.sync="previewVisible"
|
|
|
size="60%"
|
|
|
+ append-to-body
|
|
|
:before-close="handleBeforeClose">
|
|
|
<avue-form v-if="previewVisible"
|
|
|
ref="form"
|
|
|
class="preview-form"
|
|
|
- :option="widgetFormPreview"
|
|
|
- v-model="widgetModels"></avue-form>
|
|
|
+ :option="option"
|
|
|
+ v-model="form"
|
|
|
+ @submit="handlePreviewSubmit"></avue-form>
|
|
|
<div class="drawer-foot">
|
|
|
<el-button size="medium"
|
|
|
type="primary"
|
|
|
@@ -192,21 +288,25 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import Draggable from 'vuedraggable'
|
|
|
-import VJsonEditor from 'v-jsoneditor'
|
|
|
import fields from './fieldsConfig.js'
|
|
|
+import beautifier from './utils/json-beautifier'
|
|
|
+import MonacoEditor from './utils/monaco-editor'
|
|
|
+import widgetEmpty from './assets/widget-empty.png'
|
|
|
+import history from './mixins/history'
|
|
|
+
|
|
|
+import Draggable from 'vuedraggable'
|
|
|
+
|
|
|
import WidgetForm from './WidgetForm'
|
|
|
import FormConfig from './FormConfig'
|
|
|
import WidgetConfig from './WidgetConfig'
|
|
|
-import widgetEmpty from './assets/widget-empty.png'
|
|
|
-import beautifier from 'csvjson-json_beautifier'
|
|
|
|
|
|
export default {
|
|
|
name: "FormDesign",
|
|
|
- components: { Draggable, VJsonEditor, WidgetForm, FormConfig, WidgetConfig },
|
|
|
+ components: { Draggable, MonacoEditor, WidgetForm, FormConfig, WidgetConfig },
|
|
|
+ mixins: [history],
|
|
|
props: {
|
|
|
options: {
|
|
|
- type: Object,
|
|
|
+ type: [Object, String],
|
|
|
default: () => {
|
|
|
return {
|
|
|
column: []
|
|
|
@@ -224,29 +324,53 @@ export default {
|
|
|
asideRightWidth: {
|
|
|
type: [String, Number],
|
|
|
default: '380px'
|
|
|
- }
|
|
|
- },
|
|
|
- watch: {
|
|
|
- widgetForm: {
|
|
|
- handler (val) {
|
|
|
- if (this.storage) {
|
|
|
- if (val.column && val.column.length > 0) localStorage.setItem('avue-form', JSON.stringify(val))
|
|
|
- else localStorage.removeItem('avue-form')
|
|
|
- }
|
|
|
- },
|
|
|
- deep: true
|
|
|
},
|
|
|
- beautifierOptions: {
|
|
|
- handler (val) {
|
|
|
- if (this.storage) {
|
|
|
- localStorage.setItem('avue-form-beautifier-options', JSON.stringify(val))
|
|
|
- }
|
|
|
- },
|
|
|
- deep: true
|
|
|
+ showGithubStar: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ toolbar: {
|
|
|
+ type: Array,
|
|
|
+ default: () => {
|
|
|
+ return ['import', 'generate', 'preview', 'clear']
|
|
|
+ }
|
|
|
+ },
|
|
|
+ undoRedo: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ includeFields: {
|
|
|
+ type: Array,
|
|
|
+ default: () => {
|
|
|
+ const arr = []
|
|
|
+ fields.forEach(f => {
|
|
|
+ f.list.forEach(c => {
|
|
|
+ arr.push(c.type)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return arr
|
|
|
+ }
|
|
|
+ },
|
|
|
+ customFields: {
|
|
|
+ type: Array,
|
|
|
},
|
|
|
+ defaultValues: {
|
|
|
+ type: Object
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
options: {
|
|
|
- handler (val) {
|
|
|
- this.transAvueOptionsToFormDesigner(val).then(res => {
|
|
|
+ handler(val) {
|
|
|
+ let options = val
|
|
|
+ if (typeof options == 'string') {
|
|
|
+ try {
|
|
|
+ options = eval('(' + options + ')')
|
|
|
+ } catch (e) {
|
|
|
+ console.error('非法配置')
|
|
|
+ options = { column: [] }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.transAvueOptionsToFormDesigner(options).then(res => {
|
|
|
this.widgetForm = { ...this.widgetForm, ...res }
|
|
|
})
|
|
|
},
|
|
|
@@ -254,14 +378,14 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
- leftWidth () {
|
|
|
+ leftWidth() {
|
|
|
if (typeof this.asideLeftWidth == 'string') {
|
|
|
return this.asideLeftWidth
|
|
|
} else {
|
|
|
return `${this.asideLeftWidth}px`
|
|
|
}
|
|
|
},
|
|
|
- rightWidth () {
|
|
|
+ rightWidth() {
|
|
|
if (typeof this.asideRightWidth == 'string') {
|
|
|
return this.asideRightWidth
|
|
|
} else {
|
|
|
@@ -269,7 +393,7 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- data () {
|
|
|
+ data() {
|
|
|
return {
|
|
|
widgetEmpty,
|
|
|
fields,
|
|
|
@@ -281,150 +405,214 @@ export default {
|
|
|
gutter: 0,
|
|
|
menuBtn: true,
|
|
|
submitBtn: true,
|
|
|
- submitSize: 'medium',
|
|
|
submitText: '提交',
|
|
|
emptyBtn: true,
|
|
|
- emptySize: 'medium',
|
|
|
emptyText: '清空',
|
|
|
menuPosition: 'center'
|
|
|
},
|
|
|
- widgetFormPreview: {},
|
|
|
+ option: {},
|
|
|
configTab: 'widget',
|
|
|
widgetFormSelect: {},
|
|
|
previewVisible: false,
|
|
|
generateJsonVisible: false,
|
|
|
importJsonVisible: false,
|
|
|
importJson: {},
|
|
|
- widgetModels: {},
|
|
|
- configOption: {},
|
|
|
- beautifierOptions: {
|
|
|
- enabled: false,
|
|
|
+ form: {},
|
|
|
+ configOption: {
|
|
|
+ generateType: 'json',
|
|
|
space: 2,
|
|
|
quoteType: 'single',
|
|
|
- dropQuotesOnKeys: true,
|
|
|
- dropQuotesOnNumbers: false
|
|
|
+ dropQuotesOnKeys: true
|
|
|
+ },
|
|
|
+ history: {
|
|
|
+ index: 0, // 当前下标
|
|
|
+ maxStep: 20, // 最大记录步数
|
|
|
+ steps: [], // 历史步数
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- mounted () {
|
|
|
- this.handleLoadCss();
|
|
|
- this.handleLoadStorage();
|
|
|
- this.loadBeautifierOptions();
|
|
|
+ mounted() {
|
|
|
+ this.handleLoadStorage()
|
|
|
+ this.handleLoadCss()
|
|
|
},
|
|
|
methods: {
|
|
|
// 组件初始化时加载本地存储中的options(需开启storage),若不存在则读取用户配置的options
|
|
|
- handleLoadStorage () {
|
|
|
- if (this.storage) {
|
|
|
- const form = localStorage.getItem('avue-form')
|
|
|
- if (form) this.transAvueOptionsToFormDesigner(JSON.parse(form)).then(data => this.widgetForm = data)
|
|
|
- } else this.transAvueOptionsToFormDesigner({ ...this.widgetForm, ...this.options }).then(data => this.widgetForm = data)
|
|
|
- },
|
|
|
- // 获取JSON格式化属性
|
|
|
- loadBeautifierOptions () {
|
|
|
- const bo = localStorage.getItem('avue-form-beautifier-options')
|
|
|
- if (bo) this.beautifierOptions = JSON.parse(bo)
|
|
|
+ async handleLoadStorage() {
|
|
|
+ let options = this.options
|
|
|
+ if (typeof options == 'string') {
|
|
|
+ try {
|
|
|
+ options = eval('(' + options + ')')
|
|
|
+ } catch (e) {
|
|
|
+ console.error('非法配置')
|
|
|
+ options = { column: [] }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!options.column) options.column = []
|
|
|
+ this.widgetForm = this.initHistory({
|
|
|
+ index: 0,
|
|
|
+ maxStep: 20,
|
|
|
+ steps: [await this.transAvueOptionsToFormDesigner({ ...this.widgetForm, ...options })],
|
|
|
+ storage: this.storage
|
|
|
+ })
|
|
|
+
|
|
|
+ if (this.undoRedo) {
|
|
|
+ window.addEventListener('keydown', (evt) => {
|
|
|
+ // 监听 cmd + z / ctrl + z 撤销
|
|
|
+ if ((evt.metaKey && !evt.shiftKey && evt.keyCode == 90) || (evt.ctrlKey && !evt.shiftKey && evt.keyCode == 90)) {
|
|
|
+ this.widgetForm = this.handleUndo()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听 cmd + shift + z / ctrl + shift + z / ctrl + y 重做
|
|
|
+ if ((evt.metaKey && evt.shiftKey && evt.keyCode == 90) || (evt.ctrlKey && evt.shiftKey && evt.keyCode == 90) || (evt.ctrlKey && evt.keyCode == 89)) {
|
|
|
+ this.widgetForm = this.handleRedo()
|
|
|
+ }
|
|
|
+ }, false)
|
|
|
+ }
|
|
|
+
|
|
|
},
|
|
|
- // 加载阿里iconfront
|
|
|
- handleLoadCss () {
|
|
|
- const url = '//at.alicdn.com/t/font_1254447_rwaizg76pz.css'
|
|
|
- const link = document.createElement('link');
|
|
|
- link.rel = 'stylesheet';
|
|
|
- link.href = url;
|
|
|
- window.document.head.appendChild(link)
|
|
|
+ // 加载icon
|
|
|
+ handleLoadCss() {
|
|
|
+ const head = document.getElementsByTagName('head')[0]
|
|
|
+ const script = document.createElement('link')
|
|
|
+ script.rel = 'stylesheet'
|
|
|
+ script.type = 'text/css'
|
|
|
+ script.href = 'https://at.alicdn.com/t/font_1254447_zc9iezc230c.css'
|
|
|
+ head.appendChild(script)
|
|
|
+ // this.loadScript('css', 'https://at.alicdn.com/t/font_1254447_zc9iezc230c.css')
|
|
|
},
|
|
|
// Avue文档链接
|
|
|
- handleAvueDoc () {
|
|
|
+ handleAvueDoc() {
|
|
|
window.open('https://avuejs.com/doc/form/form-doc', '_blank')
|
|
|
},
|
|
|
+ // 左侧字段点击
|
|
|
+ handleFieldClick(item) {
|
|
|
+ const activeIndex = this.widgetForm.column.findIndex(c => c.prop == this.widgetFormSelect.prop) + 1
|
|
|
+ let newIndex = 0
|
|
|
+ if (activeIndex == -1) {
|
|
|
+ this.widgetForm.column.push(item)
|
|
|
+ newIndex = this.widgetForm.column.length - 1
|
|
|
+ } else {
|
|
|
+ this.widgetForm.column.splice(activeIndex, 0, item)
|
|
|
+ newIndex = activeIndex
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$refs.widgetForm.handleWidgetAdd({ newIndex })
|
|
|
+ },
|
|
|
// 预览 - 弹窗
|
|
|
- handlePreview () {
|
|
|
+ handlePreview() {
|
|
|
if (!this.widgetForm.column || this.widgetForm.column.length == 0) this.$message.error("没有需要展示的内容")
|
|
|
else {
|
|
|
- this.transformToAvueOptions(this.widgetForm).then(data => {
|
|
|
- this.widgetFormPreview = data
|
|
|
+ this.transformToAvueOptions(this.widgetForm, true).then(data => {
|
|
|
+ this.option = data
|
|
|
this.previewVisible = true
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
// 导入JSON - 弹窗 - 确定
|
|
|
- handleImportJsonSubmit () {
|
|
|
+ handleImportJsonSubmit() {
|
|
|
try {
|
|
|
this.transAvueOptionsToFormDesigner(this.importJson).then(res => {
|
|
|
this.widgetForm = res
|
|
|
this.importJsonVisible = false
|
|
|
+ this.handleHistoryChange(this.widgetForm)
|
|
|
})
|
|
|
} catch (e) {
|
|
|
this.$message.error(e.message)
|
|
|
}
|
|
|
},
|
|
|
// 生成JSON - 弹窗
|
|
|
- handleGenerateJson () {
|
|
|
+ handleGenerateJson() {
|
|
|
this.transformToAvueOptions(this.widgetForm).then(data => {
|
|
|
- this.widgetFormPreview = data
|
|
|
+ this.option = data
|
|
|
this.generateJsonVisible = true
|
|
|
})
|
|
|
},
|
|
|
// 生成JSON - 弹窗 - 确定
|
|
|
- handleGenerate () {
|
|
|
+ handleGenerate() {
|
|
|
this.transformToAvueOptions(this.widgetForm).then(data => {
|
|
|
- this.$emit('submit', data)
|
|
|
+ if (this.configOption.generateType && this.configOption.generateType == 'string') this.$emit('submit', beautifier(data, {
|
|
|
+ minify: true,
|
|
|
+ ...this.configOption
|
|
|
+ }))
|
|
|
+ else this.$emit('submit', data)
|
|
|
})
|
|
|
},
|
|
|
// 生成JSON - 弹窗 - 拷贝
|
|
|
- handleCopy () {
|
|
|
- this.$Clipboard({
|
|
|
- text: this.getCopyContent()
|
|
|
- }).then(() => {
|
|
|
- this.$message.success('复制成功')
|
|
|
- }).catch(() => {
|
|
|
- this.$message.error('复制失败')
|
|
|
- });
|
|
|
+ handleCopy() {
|
|
|
+ this.transformToAvueOptions(this.widgetForm).then(data => {
|
|
|
+ this.$Clipboard({
|
|
|
+ text: beautifier(data, {
|
|
|
+ minify: true,
|
|
|
+ ...this.configOption
|
|
|
+ })
|
|
|
+ }).then(() => {
|
|
|
+ this.$message.success('复制成功')
|
|
|
+ }).catch(() => {
|
|
|
+ this.$message.error('复制失败')
|
|
|
+ })
|
|
|
+ })
|
|
|
},
|
|
|
// 预览 - 弹窗 - 确定
|
|
|
- handlePreviewSubmit () {
|
|
|
- this.$refs.form.validate((valid) => {
|
|
|
- if (valid) this.$alert(this.widgetModels).catch(() => {
|
|
|
+ handlePreviewSubmit(form, done) {
|
|
|
+ if (done) {
|
|
|
+ this.$alert(this.form).then(() => {
|
|
|
+ done()
|
|
|
+ }).catch(() => {
|
|
|
+ done()
|
|
|
})
|
|
|
- })
|
|
|
+ } else {
|
|
|
+ this.$refs.form.validate((valid, done) => {
|
|
|
+ if (valid) this.$alert(this.form).then(() => {
|
|
|
+ done()
|
|
|
+ }).catch(() => {
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
},
|
|
|
// 预览 - 弹窗 - 关闭前
|
|
|
- handleBeforeClose () {
|
|
|
- this.$refs.form.resetForm();
|
|
|
+ handleBeforeClose() {
|
|
|
+ this.$refs.form.resetForm()
|
|
|
+ this.form = {}
|
|
|
this.previewVisible = false
|
|
|
},
|
|
|
// 清空
|
|
|
- handleClear () {
|
|
|
+ handleClear() {
|
|
|
if (this.widgetForm && this.widgetForm.column && this.widgetForm.column.length > 0) {
|
|
|
this.$confirm('确定要清空吗?', '警告', {
|
|
|
type: 'warning'
|
|
|
}).then(() => {
|
|
|
- this.widgetForm = { column: [] }
|
|
|
- this.widgetFormSelect = {}
|
|
|
+ this.$set(this.widgetForm, 'column', [])
|
|
|
+ this.$set(this, 'form', {})
|
|
|
+ this.$set(this, 'widgetFormSelect', {})
|
|
|
+ this.handleHistoryChange(this.widgetForm)
|
|
|
}).catch(() => {
|
|
|
})
|
|
|
} else this.$message.error("没有需要清空的内容")
|
|
|
},
|
|
|
- /**
|
|
|
- * 获取需要复制的内容
|
|
|
- * @return {String}
|
|
|
- */
|
|
|
- getCopyContent () {
|
|
|
- if (this.beautifierOptions.enabled) return beautifier(this.widgetFormPreview, this.beautifierOptions)
|
|
|
- else return JSON.stringify(this.widgetFormPreview, null, 2)
|
|
|
- },
|
|
|
// 表单设计器配置项 转化为 Avue配置项
|
|
|
- transformToAvueOptions (obj) {
|
|
|
+ transformToAvueOptions(obj, isPreview = false) {
|
|
|
+ const _this = this
|
|
|
return new Promise((resolve, reject) => {
|
|
|
try {
|
|
|
- const data = this.deepClone(obj)
|
|
|
+ const data = _this.deepClone(obj)
|
|
|
for (let i = 0; i < data.column.length; i++) {
|
|
|
const col = data.column[i]
|
|
|
+
|
|
|
+ if (isPreview) { // 预览调整事件中的this指向
|
|
|
+ let event = ['change', 'blur', 'click', 'focus']
|
|
|
+ event.forEach(e => {
|
|
|
+ if (col[e]) col[e] = eval((col[e] + '').replace(/this/g, '_this'))
|
|
|
+ })
|
|
|
+ if (col.event) Object.keys(col.event).forEach(key => col.event[key] = eval((col.event[key] + '').replace(/this/g, '_this')))
|
|
|
+ }
|
|
|
+
|
|
|
if (col.type == 'dynamic' && col.children && col.children.column && col.children.column.length > 0) {
|
|
|
const c = col.children.column;
|
|
|
c.forEach(item => {
|
|
|
delete item.subfield
|
|
|
})
|
|
|
- this.transformToAvueOptions(col.children).then(res => {
|
|
|
+ this.transformToAvueOptions(col.children, isPreview).then(res => {
|
|
|
col.children = res
|
|
|
})
|
|
|
} else if (col.type == 'group') {
|
|
|
@@ -434,8 +622,11 @@ export default {
|
|
|
label: col.label,
|
|
|
icon: col.icon,
|
|
|
prop: col.prop,
|
|
|
+ arrow: col.arrow,
|
|
|
+ collapse: col.collapse,
|
|
|
+ display: col.display
|
|
|
}
|
|
|
- this.transformToAvueOptions(col.children).then(res => {
|
|
|
+ this.transformToAvueOptions(col.children, isPreview).then(res => {
|
|
|
group.column = res.column
|
|
|
data.group.push(group)
|
|
|
})
|
|
|
@@ -446,33 +637,37 @@ export default {
|
|
|
delete col.dicUrl
|
|
|
delete col.dicMethod
|
|
|
delete col.dicQuery
|
|
|
+ delete col.dicQueryConfig
|
|
|
} else if (col.dicOption == 'remote') {
|
|
|
delete col.dicData
|
|
|
- if (col.dicQuery && col.dicQuery.length > 0) {
|
|
|
+ if (col.dicQueryConfig && col.dicQueryConfig.length > 0) {
|
|
|
const query = {}
|
|
|
- col.dicQuery.forEach(q => {
|
|
|
+ col.dicQueryConfig.forEach(q => {
|
|
|
if (q.key && q.value) query[q.key] = q.value
|
|
|
})
|
|
|
col.dicQuery = query
|
|
|
- } else delete col.dicQuery
|
|
|
+ delete col.dicQueryConfig
|
|
|
+ } else delete col.dicQueryConfig
|
|
|
}
|
|
|
delete col.dicOption
|
|
|
} else if (['upload'].includes(col.type)) {
|
|
|
- if (col.headers && col.headers.length > 0) {
|
|
|
+ if (col.headersConfig && col.headersConfig.length > 0) {
|
|
|
const headers = {}
|
|
|
- col.headers.forEach(h => {
|
|
|
+ col.headersConfig.forEach(h => {
|
|
|
if (h.key && h.value) headers[h.key] = h.value
|
|
|
})
|
|
|
col.headers = headers
|
|
|
- }
|
|
|
+ } else delete col.headers
|
|
|
+ delete col.headersConfig
|
|
|
|
|
|
- if (col.data && col.data.length > 0) {
|
|
|
+ if (col.dataConfig && col.dataConfig.length > 0) {
|
|
|
const data = {}
|
|
|
- col.data.forEach(h => {
|
|
|
+ col.dataConfig.forEach(h => {
|
|
|
if (h.key && h.value) data[h.key] = h.value
|
|
|
})
|
|
|
col.data = data
|
|
|
- }
|
|
|
+ } else delete col.data
|
|
|
+ delete col.dataConfig
|
|
|
}
|
|
|
}
|
|
|
resolve(data)
|
|
|
@@ -482,7 +677,8 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
// Avue配置项 转化为 表单设计器配置项
|
|
|
- transAvueOptionsToFormDesigner (obj) {
|
|
|
+ transAvueOptionsToFormDesigner(obj) {
|
|
|
+ if (typeof obj == 'string') obj = eval('(' + obj + ')')
|
|
|
const data = this.deepClone(obj)
|
|
|
return new Promise((resolve, reject) => {
|
|
|
try {
|
|
|
@@ -506,7 +702,7 @@ export default {
|
|
|
$cellEdit: true
|
|
|
})
|
|
|
}
|
|
|
- col.dicQuery = arr
|
|
|
+ col.dicQueryConfig = arr
|
|
|
}
|
|
|
if (col.dicUrl) col.dicOption = 'remote'
|
|
|
else col.dicOption = 'static'
|
|
|
@@ -521,8 +717,8 @@ export default {
|
|
|
$cellEdit: true
|
|
|
})
|
|
|
}
|
|
|
- col.headers = arr
|
|
|
- }
|
|
|
+ col.headersConfig = arr
|
|
|
+ } else col.headersConfig = []
|
|
|
|
|
|
if (col.data && typeof col.data == 'object') {
|
|
|
const arr = []
|
|
|
@@ -533,8 +729,8 @@ export default {
|
|
|
$cellEdit: true
|
|
|
})
|
|
|
}
|
|
|
- col.data = arr
|
|
|
- }
|
|
|
+ col.dataConfig = arr
|
|
|
+ } else col.dataConfig = []
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
@@ -548,6 +744,9 @@ export default {
|
|
|
label: col.label,
|
|
|
icon: col.icon,
|
|
|
prop: col.prop,
|
|
|
+ arrow: col.arrow,
|
|
|
+ collapse: col.collapse,
|
|
|
+ display: col.display
|
|
|
}
|
|
|
this.transAvueOptionsToFormDesigner(col).then(res => {
|
|
|
group.children = res
|
|
|
@@ -561,49 +760,18 @@ export default {
|
|
|
reject(e)
|
|
|
}
|
|
|
})
|
|
|
+ },
|
|
|
+ async getData(type = 'json', option = {}) {
|
|
|
+ if (type == 'string') return beautifier(await this.transformToAvueOptions(this.widgetForm), {
|
|
|
+ minify: true,
|
|
|
+ ...option
|
|
|
+ })
|
|
|
+ else return await this.transformToAvueOptions(this.widgetForm)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss">
|
|
|
-@import "./styles/index.scss";
|
|
|
-
|
|
|
-.drawer-foot {
|
|
|
- position: absolute;
|
|
|
- bottom: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- padding: 20px;
|
|
|
- display: flex;
|
|
|
-
|
|
|
- button {
|
|
|
- width: 50%;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.drawer-foot > span {
|
|
|
- display: inline-block;
|
|
|
- width: 50%;
|
|
|
- button {
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.popper-bo {
|
|
|
- .el-alert {
|
|
|
- margin-bottom: 10px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.preview-form {
|
|
|
- overflow-y: scroll;
|
|
|
- height: 83vh;
|
|
|
-}
|
|
|
-
|
|
|
-.widget-config-container {
|
|
|
- .avue-group__item {
|
|
|
- padding: 0;
|
|
|
- }
|
|
|
-}
|
|
|
+@import './styles/index.scss';
|
|
|
</style>
|