commit e7a012f0eb7e9c055bdc7c5f87ee4209c7af8651
Author: 李志强 <2997723368@qq.com>
Date: Mon Mar 20 14:43:23 2023 +0800
可视化表单
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8dcab82
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+.DS_Store
+node_modules
+/dist
+/dist0/
+/dist2
+.VSCodeCounter
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+/package-lock.json
+/VariantForm.iml
+yarn.lock
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0aaf98f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,195 @@
+# Variant Form
+#### 一款高效的Vue低代码表单,可视化设计,一键生成源码,享受更多摸鱼时间。
+
+
+
+
+
+### 立即体验
+[在线Demo](http://120.92.142.115/)
+
+### 立即体验VForm Pro高级版(提供商业支持)
+[Pro Demo](https://vform666.com/pages/pro/)
+
+### 视频教程集合:
+[B站观看](https://space.bilibili.com/626932375)
+
+### Vue 3正式版已发布
+[立即进入](https://gitee.com/vdpadmin/variant-form3-vite)
+
+### 🎉🎉基于Vant组件库的Mobile版本已发布🎉🎉
+[立即进入](https://vform666.com/vform-mobile.html)
+
+
+### 友情链接
+[Fantastic-admin](https://hooray.gitee.io/fantastic-admin/) —— 一款开箱即用的 Vue 中后台管理系统框架(支持Vue2/Vue3)
+
+[REBUILD](https://getrebuild.com/) —— 高度可定制化的企业管理系统
+
+
+
+### 功能一览
+```
+> 拖拽式可视化表单设计;
+> 支持PC、Pad、H5三种布局;
+> 支持运行时动态加载表单;
+> 支持表单复杂交互控制;
+> 支持自定义CSS样式;
+> 支持自定义校验逻辑;
+> 支持国际化多语言;
+> 兼容IE 11浏览器;
+> 可导出Vue组件、HTML源码;
+> 可导出Vue的SFC单文件组件;
+> 支持开发自定义组件;
+> 支持响应式自适应布局;
+> 支持VS Code插件;
+> 更多功能等你探究...;
+```
+
+### 安装依赖
+```
+npm install --registry=https://registry.npm.taobao.org
+```
+
+### 开发调试
+```
+npm run serve
+```
+
+### 生产打包
+```
+npm run build
+```
+
+### 表单设计器 + 表单渲染器打包
+```
+npm run lib
+```
+
+### 表单渲染器打包
+```
+npm run lib-render
+```
+
+### 浏览器兼容性
+```Chrome(及同内核的浏览器如QQ浏览器、360浏览器等等),Edge, Firefox,Safari,IE 11```
+
+
+
+### 跟Vue项目集成
+
+
+
+#### 1. 安装包
+ ```bash
+ npm i vform-builds
+ ```
+或
+ ```bash
+ yarn add vform-builds
+ ```
+
+
+
+#### 2. 引入并全局注册VForm组件
+```javascript
+import Vue from 'vue'
+import App from './App.vue'
+
+import ElementUI from 'element-ui' //引入element-ui库
+import VForm from 'vform-builds' //引入VForm库
+
+import 'element-ui/lib/theme-chalk/index.css' //引入element-ui样式
+import 'vform-builds/dist/VFormDesigner.css' //引入VForm样式
+
+Vue.config.productionTip = false
+
+Vue.use(ElementUI) //全局注册element-ui
+Vue.use(VForm) //全局注册VForm(同时注册了v-form-designer和v-form-render组件)
+
+new Vue({
+ render: h => h(App),
+}).$mount('#app')
+```
+
+
+
+#### 3. 在Vue模板中使用表单设计器组件
+```html
+
+
+
+
+
+
+
+```
+
+
+
+#### 4. 在Vue模板中使用表单渲染器组件
+```html
+
+
+
+
+ Submit
+
+
+
+```
+
+
+
+### 资源链接
+
+
+文档官网:https://www.vform666.com/
+
+在线演示:http://120.92.142.115/
+
+Gitee仓库:https://gitee.com/vdpadmin/variant-form
+
+Github仓库:https://github.com/vform666/variant-form
+
+VS Code插件:https://www.vform666.com/pages/plugin/
+
+更新日志:https://www.vform666.com/changelog.html
+
+订阅Pro版:https://www.vform666.com/pages/pro/
+
+技术交流群:扫如下二维码加群
+
+
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..e955840
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: [
+ '@vue/cli-plugin-babel/preset'
+ ]
+}
diff --git a/index_template/index_dev.html b/index_template/index_dev.html
new file mode 100644
index 0000000..d6f067b
--- /dev/null
+++ b/index_template/index_dev.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
+
+
diff --git a/index_template/index_prod.html b/index_template/index_prod.html
new file mode 100644
index 0000000..2e95736
--- /dev/null
+++ b/index_template/index_prod.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/install-render.js b/install-render.js
new file mode 100644
index 0000000..da60086
--- /dev/null
+++ b/install-render.js
@@ -0,0 +1,29 @@
+import VFormRender from '@/components/form-render/index.vue'
+import {loadExtension} from "@/extension/extension-loader"
+import axios from "axios"
+
+loadExtension()
+
+VFormRender.install = function (Vue) {
+ Vue.component(VFormRender.name, VFormRender)
+}
+
+const components = [
+ VFormRender
+]
+
+const install = (Vue) => {
+ window.axios = axios
+ components.forEach(component => {
+ Vue.component(component.name, component)
+ })
+}
+
+if (typeof window !== 'undefined' && window.Vue) { /* script方式引入时主动调用install方法!! */
+ install(window.Vue);
+}
+
+export default {
+ install,
+ VFormRender
+}
diff --git a/install.js b/install.js
new file mode 100644
index 0000000..71df886
--- /dev/null
+++ b/install.js
@@ -0,0 +1,41 @@
+import axios from 'axios'
+
+import VFormDesigner from '@/components/form-designer/index.vue'
+import VFormRender from '@/components/form-render/index.vue'
+import {loadExtension} from "@/extension/extension-loader"
+
+import '@/utils/directive'
+import '@/icons'
+import '@/iconfont/iconfont.css'
+
+loadExtension()
+
+VFormDesigner.install = function (Vue) {
+ Vue.component(VFormDesigner.name, VFormDesigner)
+}
+
+VFormRender.install = function (Vue) {
+ Vue.component(VFormRender.name, VFormRender)
+}
+
+const components = [
+ VFormDesigner,
+ VFormRender
+]
+
+const install = (Vue) => {
+ window.axios = axios
+ components.forEach(component => {
+ Vue.component(component.name, component)
+ })
+}
+
+if (typeof window !== 'undefined' && window.Vue) { /* script方式引入时主动调用install方法!! */
+ install(window.Vue);
+}
+
+export default {
+ install,
+ VFormDesigner,
+ VFormRender
+}
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..7d63aa6
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,10 @@
+{
+ "######": "本文件用于解决IDEA无法识别Vue项目@符号的问题",
+ "compilerOptions": {
+ "baseUrl": "./",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..01f25e7
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,8 @@
+# Variant Form 许可条款 1.0
+
+1. 免责声明:任何情况下根据任何法律,本作者不对用户因使用VariantForm产生的侵权、数据损坏丢失、软硬件故障和违法犯罪等问题承担任何责任;
+2. 禁止任何用户对VariantForm进行简单包装后,即声称为自己的产品、销售源码获利;
+3. VariantForm为开源项目,获取到源代码的用户可自由修改源码供自身开发使用,可分发build构建后的库代码,也可分发VariantForm源代码(需保留文件头部的作者声明),本作者保留VariantForm的原始著作权;
+4. 个人或公司用户均可将VariantForm项目应用于商业项目开发,为支持本项目持续开发,请尽量购买VariantForm Pro版源码订阅更新服务;
+5. 如果你不同意本许可条款,请勿使用VariantForm;任何情况下,一旦实际使用VariantForm,则代表你已确定完全同意本许可条款;
+6. 条款内容结束。
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6742540
--- /dev/null
+++ b/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "variant-form",
+ "version": "2.2.9",
+ "private": false,
+ "scripts": {
+ "serve": "vue-cli-service serve --open src/main.js",
+ "build": "vue-cli-service build --report --dest dist/build",
+ "lib": "vue-cli-service build --report --target lib --dest dist/lib --name VFormDesigner install.js",
+ "lib-render": "vue-cli-service build --report --target lib --dest dist/lib-render --name VFormRender install-render.js",
+ "lint": "vue-cli-service lint"
+ },
+ "dependencies": {
+ "axios": "^0.21.1",
+ "clipboard": "^2.0.8",
+ "core-js": "^3.6.5",
+ "element-ui": "^2.15.1",
+ "file-saver": "^2.0.5",
+ "vue": "^2.6.11",
+ "vue2-editor": "^2.10.2",
+ "vuedraggable": "^2.24.3"
+ },
+ "devDependencies": {
+ "@vue/cli-plugin-babel": "~4.5.0",
+ "@vue/cli-plugin-eslint": "~4.5.0",
+ "@vue/cli-service": "~4.5.0",
+ "ace-builds": "^1.4.12",
+ "babel-eslint": "^10.1.0",
+ "babel-polyfill": "^6.26.0",
+ "eslint": "^6.7.2",
+ "eslint-plugin-vue": "^6.2.2",
+ "mvdir": "^1.0.21",
+ "sass": "^1.45.1",
+ "sass-loader": "^8.0.2",
+ "svg-sprite-loader": "^5.2.1",
+ "vue-template-compiler": "^2.6.11"
+ },
+ "eslintConfig": {
+ "root": true,
+ "env": {
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/essential",
+ "eslint:recommended"
+ ],
+ "parserOptions": {
+ "parser": "babel-eslint"
+ },
+ "rules": {}
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not dead"
+ ]
+}
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..84ed3f9
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..d6f067b
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
+
+
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..6fb0056
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,45 @@
+
+
+
+
+ 测试按钮
+
+
+
+
+
+
+
+
diff --git a/src/assets/ft-images/t1.png b/src/assets/ft-images/t1.png
new file mode 100644
index 0000000..620f536
Binary files /dev/null and b/src/assets/ft-images/t1.png differ
diff --git a/src/assets/ft-images/t2.png b/src/assets/ft-images/t2.png
new file mode 100644
index 0000000..a968ef7
Binary files /dev/null and b/src/assets/ft-images/t2.png differ
diff --git a/src/assets/ft-images/t3.png b/src/assets/ft-images/t3.png
new file mode 100644
index 0000000..0239058
Binary files /dev/null and b/src/assets/ft-images/t3.png differ
diff --git a/src/assets/ft-images/t4.png b/src/assets/ft-images/t4.png
new file mode 100644
index 0000000..7996852
Binary files /dev/null and b/src/assets/ft-images/t4.png differ
diff --git a/src/assets/ft-images/t5.png b/src/assets/ft-images/t5.png
new file mode 100644
index 0000000..35e03d2
Binary files /dev/null and b/src/assets/ft-images/t5.png differ
diff --git a/src/assets/ft-images/t6.png b/src/assets/ft-images/t6.png
new file mode 100644
index 0000000..22aeb50
Binary files /dev/null and b/src/assets/ft-images/t6.png differ
diff --git a/src/assets/ft-images/t7.png b/src/assets/ft-images/t7.png
new file mode 100644
index 0000000..7128e37
Binary files /dev/null and b/src/assets/ft-images/t7.png differ
diff --git a/src/assets/ft-images/t8.png b/src/assets/ft-images/t8.png
new file mode 100644
index 0000000..37baf96
Binary files /dev/null and b/src/assets/ft-images/t8.png differ
diff --git a/src/assets/vform-logo.png b/src/assets/vform-logo.png
new file mode 100644
index 0000000..f8cc5df
Binary files /dev/null and b/src/assets/vform-logo.png differ
diff --git a/src/components/code-editor/index.vue b/src/components/code-editor/index.vue
new file mode 100644
index 0000000..11e47a5
--- /dev/null
+++ b/src/components/code-editor/index.vue
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/designer.js b/src/components/form-designer/designer.js
new file mode 100644
index 0000000..c9e29c6
--- /dev/null
+++ b/src/components/form-designer/designer.js
@@ -0,0 +1,962 @@
+/**
+ * author: vformAdmin
+ * email: vdpadmin@163.com
+ * website: https://www.vform666.com
+ * date: 2021.08.18
+ * remark: 如果要分发VForm源码,需在本文件顶部保留此文件头信息!!
+ */
+
+import {deepClone, generateId, getDefaultFormConfig, overwriteObj} from "@/utils/util"
+import {containers, advancedFields, basicFields, customFields} from "@/components/form-designer/widget-panel/widgetsConfig.js"
+import {VARIANT_FORM_VERSION} from "@/utils/config"
+
+export function createDesigner(vueInstance) {
+ let defaultFormConfig = deepClone( getDefaultFormConfig() )
+
+ return {
+ widgetList: [],
+ formConfig: {cssCode: ''},
+
+ selectedId: null,
+ selectedWidget: null,
+ selectedWidgetName: null, //选中组件名称(唯一)
+ vueInstance: vueInstance,
+
+ formWidget: null, //表单设计容器
+
+ cssClassList: [], //自定义样式列表
+
+ historyData: {
+ index: -1, //index: 0,
+ maxStep: 20,
+ steps: [],
+ },
+
+ initDesigner(resetFormJson) {
+ this.widgetList = []
+ this.formConfig = deepClone(defaultFormConfig)
+
+ //输出版本信息和语雀链接
+ console.info(`%cVariantForm %cVer${VARIANT_FORM_VERSION} %chttps://www.yuque.com/visualdev/vform`,
+ "color:#409EFF;font-size: 22px;font-weight:bolder",
+ "color:#999;font-size: 12px",
+ "color:#333"
+ )
+
+ if (!resetFormJson) {
+ this.initHistoryData()
+ }
+ },
+
+ clearDesigner(skipHistoryChange) {
+ let emptyWidgetListFlag = (this.widgetList.length === 0)
+ this.widgetList = []
+ this.selectedId = null
+ this.selectedWidgetName = null
+ this.selectedWidget = {} //this.selectedWidget = null
+ overwriteObj(this.formConfig, defaultFormConfig) //
+
+ if (!!skipHistoryChange) {
+ //什么也不做!!
+ } else if (!emptyWidgetListFlag) {
+ this.emitHistoryChange()
+ } else {
+ this.saveCurrentHistoryStep()
+ }
+ },
+
+ loadPresetCssCode(preCssCode) {
+ if ((this.formConfig.cssCode === '') && !!preCssCode) {
+ this.formConfig.cssCode = preCssCode
+ }
+ },
+
+ getLayoutType() {
+ return this.formConfig.layoutType || 'PC'
+ },
+
+ changeLayoutType(newType) {
+ this.formConfig.layoutType = newType
+ },
+
+ getImportTemplate() {
+ return {
+ widgetList: [],
+ // formConfig: deepClone(this.formConfig)
+ formConfig: deepClone(defaultFormConfig)
+ }
+ },
+
+ loadFormJson(formJson) {
+ let modifiedFlag = false
+
+ if (!!formJson && !!formJson.widgetList) {
+ this.formWidget.clearWidgetRefList()
+ this.widgetList = formJson.widgetList
+ modifiedFlag = true
+ }
+ if (!!formJson && !!formJson.formConfig) {
+ //this.formConfig = importObj.formConfig
+ overwriteObj(this.formConfig, formJson.formConfig) /* 用=赋值,会导致inject依赖注入的formConfig属性变成非响应式 */
+ modifiedFlag = true
+ }
+
+ if (modifiedFlag) {
+ this.emitEvent('form-json-imported', []) // 通知其他组件
+ }
+
+ return modifiedFlag
+ },
+
+ setSelected(selected) {
+ if (!selected) {
+ this.clearSelected()
+ return
+ }
+
+ this.selectedWidget = selected
+ if (!!selected.id) {
+ this.selectedId = selected.id
+ this.selectedWidgetName = selected.options.name
+ }
+ },
+
+ updateSelectedWidgetNameAndLabel(selectedWidget, newName, newLabel) {
+ this.selectedWidgetName = newName
+ //selectedWidget.options.name = newName //此行多余
+ if (!!newLabel && (Object.keys(selectedWidget.options).indexOf('label') > -1)) {
+ selectedWidget.options.label = newLabel
+ }
+ },
+
+ clearSelected() {
+ this.selectedId = null
+ this.selectedWidgetName = null
+ this.selectedWidget = {} //this.selectedWidget = null
+ },
+
+ checkWidgetMove(evt) { /* Only field widget can be dragged into sub-form */
+ if (!!evt.draggedContext && !!evt.draggedContext.element) {
+ let wgCategory = evt.draggedContext.element.category
+ let wgType = evt.draggedContext.element.type
+ if (!!evt.to) {
+ if ((evt.to.className === 'sub-form-table') && (wgCategory === 'container')) {
+ //this.$message.info(this.vueInstance.i18nt('designer.hint.onlyFieldWidgetAcceptable'))
+ return false
+ }
+ }
+ }
+
+ return true
+ },
+
+ checkFieldMove(evt) {
+ if (!!evt.draggedContext && !!evt.draggedContext.element) {
+ let wgCategory = evt.draggedContext.element.category
+ let wgType = evt.draggedContext.element.type + ''
+ //console.log('wgType======', wgType)
+ if (!!evt.to) {
+ if ((evt.to.className === 'sub-form-table') && (wgType === 'slot')) {
+ //this.$message.info(this.vueInstance.i18nt('designer.hint.onlyFieldWidgetAcceptable'))
+ return false
+ }
+ }
+ }
+
+ return true
+ },
+
+ /**
+ * 追加表格新行
+ * @param widget
+ */
+ appendTableRow(widget) {
+ let rowIdx = widget.rows.length//确定插入行位置
+ let newRow = deepClone(widget.rows[widget.rows.length - 1])
+ newRow.id = 'table-row-' + generateId()
+ newRow.merged = false
+ newRow.cols.forEach(col => {
+ col.id = 'table-cell-' + generateId()
+ col.options.name = col.id
+ col.merged = false
+ col.options.colspan = 1
+ col.options.rowspan = 1
+ col.widgetList.length = 0
+ })
+ widget.rows.splice(rowIdx, 0, newRow)
+
+ this.emitHistoryChange()
+ },
+
+ /**
+ * 追加表格新列
+ * @param widget
+ */
+ appendTableCol(widget) {
+ let colIdx = widget.rows[0].cols.length //确定插入列位置
+ widget.rows.forEach(row => {
+ let newCol = deepClone(this.getContainerByType('table-cell'))
+ newCol.id = 'table-cell-' + generateId()
+ newCol.options.name = newCol.id
+ newCol.merged = false
+ newCol.options.colspan = 1
+ newCol.options.rowspan = 1
+ newCol.widgetList.length = 0
+ row.cols.splice(colIdx, 0, newCol)
+ })
+
+ this.emitHistoryChange()
+ },
+
+ insertTableRow(widget, insertPos, cloneRowIdx, curCol, aboveFlag) {
+ let newRowIdx = !!aboveFlag ? insertPos : (insertPos + 1) //初步确定插入行位置
+ if (!aboveFlag) { //继续向下寻找同列第一个未被合并的单元格
+ let tmpRowIdx = newRowIdx
+ let rowFoundFlag = false
+ while (tmpRowIdx < widget.rows.length) {
+ if (!widget.rows[tmpRowIdx].cols[curCol].merged) {
+ newRowIdx = tmpRowIdx
+ rowFoundFlag = true
+ break
+ } else {
+ tmpRowIdx++
+ }
+ }
+
+ if (!rowFoundFlag) {
+ newRowIdx = widget.rows.length
+ }
+ }
+
+ let newRow = deepClone( widget.rows[cloneRowIdx] )
+ newRow.id = 'table-row-' + generateId()
+ newRow.merged = false
+ newRow.cols.forEach(col => {
+ col.id = 'table-cell-' + generateId()
+ col.options.name = col.id
+ col.merged = false
+ col.options.colspan = 1
+ col.options.rowspan = 1
+ col.widgetList.length = 0
+ })
+ widget.rows.splice(newRowIdx, 0, newRow)
+
+ let colNo = 0
+ while ((newRowIdx < widget.rows.length - 1) && (colNo < widget.rows[0].cols.length)) { //越界判断
+ const cellOfNextRow = widget.rows[newRowIdx + 1].cols[colNo]
+ const rowMerged = cellOfNextRow.merged //确定插入位置下一行的单元格是否为合并单元格
+ if (!!rowMerged) {
+ let rowArray = widget.rows
+ let unMergedCell = {}
+ let startRowIndex = null
+ for (let i = newRowIdx; i >= 0; i--) { //查找该行已合并的主单元格
+ if (!rowArray[i].cols[colNo].merged && (rowArray[i].cols[colNo].options.rowspan > 1)) {
+ startRowIndex = i
+ unMergedCell = rowArray[i].cols[colNo]
+ break
+ }
+ }
+
+ if (!!unMergedCell.options) { //如果有符合条件的unMergedCell
+ let newRowspan = unMergedCell.options.rowspan + 1
+ this.setPropsOfMergedRows(widget.rows, startRowIndex, colNo, unMergedCell.options.colspan, newRowspan)
+ colNo += unMergedCell.options.colspan
+ } else {
+ colNo += 1
+ }
+ } else {
+ //colNo += 1
+ colNo += cellOfNextRow.options.colspan || 1
+ }
+ }
+
+ this.emitHistoryChange()
+ },
+
+ insertTableCol(widget, insertPos, curRow, leftFlag) {
+ let newColIdx = !!leftFlag ? insertPos : (insertPos + 1) //初步确定插入列位置
+ if (!leftFlag) { //继续向右寻找同行第一个未被合并的单元格
+ let tmpColIdx = newColIdx
+ let colFoundFlag = false
+ while (tmpColIdx < widget.rows[curRow].cols.length) {
+ if (!widget.rows[curRow].cols[tmpColIdx].merged) {
+ newColIdx = tmpColIdx
+ colFoundFlag = true
+ break
+ } else {
+ tmpColIdx++
+ }
+
+ if (!colFoundFlag) {
+ newColIdx = widget.rows[curRow].cols.length
+ }
+ }
+ }
+
+ widget.rows.forEach(row => {
+ let newCol = deepClone(this.getContainerByType('table-cell'))
+ newCol.id = 'table-cell-' + generateId()
+ newCol.options.name = newCol.id
+ newCol.merged = false
+ newCol.options.colspan = 1
+ newCol.options.rowspan = 1
+ newCol.widgetList.length = 0
+ row.cols.splice(newColIdx, 0, newCol)
+ })
+
+ let rowNo = 0
+ while((newColIdx < widget.rows[0].cols.length - 1) && (rowNo < widget.rows.length)) { //越界判断
+ const cellOfNextCol = widget.rows[rowNo].cols[newColIdx + 1]
+ const colMerged = cellOfNextCol.merged //确定插入位置右侧列的单元格是否为合并单元格
+ if (!!colMerged) {
+ let colArray = widget.rows[rowNo].cols
+ let unMergedCell = {}
+ let startColIndex = null
+ for (let i = newColIdx; i >= 0; i--) { //查找该行已合并的主单元格
+ if (!colArray[i].merged && (colArray[i].options.colspan > 1)) {
+ startColIndex = i
+ unMergedCell = colArray[i]
+ break
+ }
+ }
+
+ if (!!unMergedCell.options) { //如果有符合条件的unMergedCell
+ let newColspan = unMergedCell.options.colspan + 1
+ this.setPropsOfMergedCols(widget.rows, rowNo, startColIndex, newColspan, unMergedCell.options.rowspan)
+ rowNo += unMergedCell.options.rowspan
+ } else {
+ rowNo += 1
+ }
+ } else {
+ //rowNo += 1
+ rowNo += cellOfNextCol.options.rowspan || 1
+ }
+ }
+
+ this.emitHistoryChange()
+ },
+
+ setPropsOfMergedCols(rowArray, startRowIndex, startColIndex, newColspan, rowspan) {
+ for (let i = startRowIndex; i < startRowIndex + rowspan; i++) {
+ for (let j = startColIndex; j < startColIndex + newColspan; j++) {
+ if ((i === startRowIndex) && (j === startColIndex)) {
+ rowArray[i].cols[j].options.colspan = newColspan //合并后的主单元格
+ continue
+ }
+
+ rowArray[i].cols[j].merged = true
+ rowArray[i].cols[j].options.colspan = newColspan
+ rowArray[i].cols[j].widgetList = []
+ }
+ }
+ },
+
+ setPropsOfMergedRows(rowArray, startRowIndex, startColIndex, colspan, newRowspan) {
+ for (let i = startRowIndex; i < startRowIndex + newRowspan; i++) {
+ for (let j = startColIndex; j < startColIndex + colspan; j++) {
+ if ((i === startRowIndex) && (j === startColIndex)) {
+ rowArray[i].cols[j].options.rowspan = newRowspan
+ continue
+ }
+
+ rowArray[i].cols[j].merged = true
+ rowArray[i].cols[j].options.rowspan = newRowspan
+ rowArray[i].cols[j].widgetList = []
+ }
+ }
+ },
+
+ setPropsOfSplitCol(rowArray, startRowIndex, startColIndex, colspan, rowspan) {
+ for (let i = startRowIndex; i < startRowIndex + rowspan; i++) {
+ for (let j = startColIndex; j < startColIndex + colspan; j++) {
+ rowArray[i].cols[j].merged = false;
+ rowArray[i].cols[j].options.rowspan = 1
+ rowArray[i].cols[j].options.colspan = 1
+ }
+ }
+ },
+
+ setPropsOfSplitRow(rowArray, startRowIndex, startColIndex, colspan, rowspan) {
+ for (let i = startRowIndex; i < startRowIndex + rowspan; i++) {
+ for (let j = startColIndex; j < startColIndex + colspan; j++) {
+ rowArray[i].cols[j].merged = false;
+ rowArray[i].cols[j].options.rowspan = 1
+ rowArray[i].cols[j].options.colspan = 1
+ }
+ }
+ },
+
+ mergeTableCol(rowArray, colArray, curRow, curCol, leftFlag, cellWidget) {
+ let mergedColIdx = !!leftFlag ? curCol : curCol + colArray[curCol].options.colspan
+
+ // let remainedColIdx = !!leftFlag ? curCol - colArray[curCol - 1].options.colspan : curCol
+ let remainedColIdx = !!leftFlag ? curCol - 1 : curCol
+ if (!!leftFlag) { //继续向左寻找同行未被合并的第一个单元格
+ let tmpColIdx = remainedColIdx
+ while (tmpColIdx >= 0) {
+ if (!rowArray[curRow].cols[tmpColIdx].merged) {
+ remainedColIdx = tmpColIdx
+ break;
+ } else {
+ tmpColIdx--
+ }
+ }
+ }
+
+ if (!!colArray[mergedColIdx].widgetList && (colArray[mergedColIdx].widgetList.length > 0)) { //保留widgetList
+ if (!colArray[remainedColIdx].widgetList || (colArray[remainedColIdx].widgetList.length === 0)) {
+ colArray[remainedColIdx].widgetList = deepClone(colArray[mergedColIdx].widgetList)
+ }
+ }
+
+ let newColspan = colArray[mergedColIdx].options.colspan * 1 + colArray[remainedColIdx].options.colspan * 1
+ this.setPropsOfMergedCols(rowArray, curRow, remainedColIdx, newColspan, cellWidget.options.rowspan)
+
+ this.emitHistoryChange()
+ },
+
+ mergeTableWholeRow(rowArray, colArray, rowIndex, colIndex) { //需要考虑操作的行存在已合并的单元格!!
+ //整行所有单元格行高不一致不可合并!!
+ let startRowspan = rowArray[rowIndex].cols[0].options.rowspan
+ let unmatchedFlag = false
+ for (let i = 1; i < rowArray[rowIndex].cols.length; i++) {
+ if (rowArray[rowIndex].cols[i].options.rowspan !== startRowspan) {
+ unmatchedFlag = true
+ break;
+ }
+ }
+ if (unmatchedFlag) {
+ this.vueInstance.$message.info(this.vueInstance.i18nt('designer.hint.rowspanNotConsistentForMergeEntireRow'))
+ return
+ }
+
+ let widgetListCols = colArray.filter((colItem) => {
+ return !colItem.merged && !!colItem.widgetList && (colItem.widgetList.length > 0)
+ })
+ if (!!widgetListCols && (widgetListCols.length > 0)) { //保留widgetList
+ if ((widgetListCols[0].id !== colArray[0].id) && (!colArray[0].widgetList ||
+ colArray[0].widgetList.length <= 0)) {
+ colArray[0].widgetList = deepClone( widgetListCols[0].widgetList )
+ }
+ }
+
+ this.setPropsOfMergedCols(rowArray, rowIndex, 0, colArray.length, colArray[colIndex].options.rowspan)
+
+ this.emitHistoryChange()
+ },
+
+ mergeTableRow(rowArray, curRow, curCol, aboveFlag, cellWidget) {
+ let mergedRowIdx = !!aboveFlag ? curRow : curRow + cellWidget.options.rowspan
+
+ //let remainedRowIdx = !!aboveFlag ? curRow - cellWidget.options.rowspan : curRow
+ let remainedRowIdx = !!aboveFlag ? curRow - 1 : curRow
+ if (!!aboveFlag) { //继续向上寻找同列未被合并的第一个单元格
+ let tmpRowIdx = remainedRowIdx
+ while (tmpRowIdx >= 0) {
+ if (!rowArray[tmpRowIdx].cols[curCol].merged) {
+ remainedRowIdx = tmpRowIdx
+ break;
+ } else {
+ tmpRowIdx--
+ }
+ }
+ }
+
+ if (!!rowArray[mergedRowIdx].cols[curCol].widgetList && (rowArray[mergedRowIdx].cols[curCol].widgetList.length > 0)) { //保留widgetList
+ if (!rowArray[remainedRowIdx].cols[curCol].widgetList || (rowArray[remainedRowIdx].cols[curCol].widgetList.length === 0)) {
+ rowArray[remainedRowIdx].cols[curCol].widgetList = deepClone(rowArray[mergedRowIdx].cols[curCol].widgetList)
+ }
+ }
+
+ let newRowspan = rowArray[mergedRowIdx].cols[curCol].options.rowspan * 1 + rowArray[remainedRowIdx].cols[curCol].options.rowspan * 1
+ this.setPropsOfMergedRows(rowArray, remainedRowIdx, curCol, cellWidget.options.colspan, newRowspan)
+
+ this.emitHistoryChange()
+ },
+
+ mergeTableWholeCol(rowArray, colArray, rowIndex, colIndex) { //需要考虑操作的列存在已合并的单元格!!
+ //整列所有单元格列宽不一致不可合并!!
+ let startColspan = rowArray[0].cols[colIndex].options.colspan
+ let unmatchedFlag = false
+ for (let i = 1; i < rowArray.length; i++) {
+ if (rowArray[i].cols[colIndex].options.colspan !== startColspan) {
+ unmatchedFlag = true
+ break;
+ }
+ }
+ if (unmatchedFlag) {
+ this.vueInstance.$message.info(this.vueInstance.i18nt('designer.hint.colspanNotConsistentForMergeEntireColumn'))
+ return
+ }
+
+ let widgetListCols = []
+ rowArray.forEach(rowItem => {
+ let tempCell = rowItem.cols[colIndex]
+ if (!tempCell.merged && !!tempCell.widgetList && (tempCell.widgetList.length > 0)) {
+ widgetListCols.push(tempCell)
+ }
+ })
+
+ let firstCellOfCol = rowArray[0].cols[colIndex]
+ if (!!widgetListCols && (widgetListCols.length > 0)) { //保留widgetList
+ if ((widgetListCols[0].id !== firstCellOfCol.id) && (!firstCellOfCol.widgetList ||
+ firstCellOfCol.widgetList.length <= 0)) {
+ firstCellOfCol.widgetList = deepClone( widgetListCols[0].widgetList )
+ }
+ }
+
+ this.setPropsOfMergedRows(rowArray, 0, colIndex, firstCellOfCol.options.colspan, rowArray.length)
+
+ this.emitHistoryChange()
+ },
+
+ undoMergeTableCol(rowArray, rowIndex, colIndex, colspan, rowspan) {
+ this.setPropsOfSplitCol(rowArray, rowIndex, colIndex, colspan, rowspan)
+
+ this.emitHistoryChange()
+ },
+
+ undoMergeTableRow(rowArray, rowIndex, colIndex, colspan, rowspan) {
+ this.setPropsOfSplitRow(rowArray, rowIndex, colIndex, colspan, rowspan)
+
+ this.emitHistoryChange()
+ },
+
+ deleteTableWholeCol(rowArray, colIndex) { //需考虑删除的是合并列!!
+ let onlyOneColFlag = true
+ rowArray.forEach(ri => {
+ if (ri.cols[0].options.colspan !== rowArray[0].cols.length) {
+ onlyOneColFlag = false
+ }
+ })
+ //仅剩一列则不可删除!!
+ if (onlyOneColFlag) {
+ this.vueInstance.$message.info(this.vueInstance.i18nt('designer.hint.lastColCannotBeDeleted'))
+ return
+ }
+
+ //整列所有单元格列宽不一致不可删除!!
+ let startColspan = rowArray[0].cols[colIndex].options.colspan
+ let unmatchedFlag = false
+ for (let i = 1; i < rowArray.length; i++) {
+ if (rowArray[i].cols[colIndex].options.colspan !== startColspan) {
+ unmatchedFlag = true
+ break;
+ }
+ }
+ if (unmatchedFlag) {
+ this.vueInstance.$message.info(this.vueInstance.i18nt('designer.hint.colspanNotConsistentForDeleteEntireColumn'))
+ return
+ }
+
+ rowArray.forEach((rItem) => {
+ rItem.cols.splice(colIndex, startColspan)
+ })
+
+ this.emitHistoryChange()
+ },
+
+ deleteTableWholeRow(rowArray, rowIndex) { //需考虑删除的是合并行!!
+ let onlyOneRowFlag = true
+ rowArray[0].cols.forEach(ci => {
+ if (ci.options.rowspan !== rowArray.length) {
+ onlyOneRowFlag = false
+ }
+ })
+ //仅剩一行则不可删除!!
+ if (onlyOneRowFlag) {
+ this.vueInstance.$message.info(this.vueInstance.i18nt('designer.hint.lastRowCannotBeDeleted'))
+ return
+ }
+
+ //整行所有单元格行高不一致不可删除!!
+ let startRowspan = rowArray[rowIndex].cols[0].options.rowspan
+ let unmatchedFlag = false
+ for (let i = 1; i < rowArray[rowIndex].cols.length; i++) {
+ if (rowArray[rowIndex].cols[i].options.rowspan !== startRowspan) {
+ unmatchedFlag = true
+ break;
+ }
+ }
+ if (unmatchedFlag) {
+ this.vueInstance.$message.info(this.vueInstance.i18nt('designer.hint.rowspanNotConsistentForDeleteEntireRow'))
+ return
+ }
+
+ rowArray.splice(rowIndex, startRowspan)
+
+ this.emitHistoryChange()
+ },
+
+ getContainerByType(typeName) {
+ let allWidgets = [...containers, ...basicFields, ...advancedFields, ...customFields]
+ let foundCon = null
+ allWidgets.forEach(con => {
+ if (!!con.category && !!con.type && (con.type === typeName)) {
+ foundCon = con
+ }
+ })
+
+ return foundCon
+ },
+
+ getFieldWidgetByType(typeName) {
+ let allWidgets = [...containers, ...basicFields, ...advancedFields, ...customFields]
+ let foundWidget = null
+ allWidgets.forEach(widget => {
+ if (!!!widget.category && !!widget.type && (widget.type === typeName)) {
+ foundWidget = widget
+ }
+ })
+
+ return foundWidget
+ },
+
+ hasConfig(widget, configName) {
+ let originalWidget = null
+ if (!!widget.category) {
+ originalWidget = this.getContainerByType(widget.type)
+ } else {
+ originalWidget = this.getFieldWidgetByType(widget.type)
+ }
+
+ if (!originalWidget || !originalWidget.options) {
+ return false
+ }
+
+ return Object.keys(originalWidget.options).indexOf(configName) > -1
+ },
+
+ upgradeWidgetConfig(oldWidget) {
+ let newWidget = null
+ if (!!oldWidget.category) {
+ newWidget = this.getContainerByType(oldWidget.type)
+ } else {
+ newWidget = this.getFieldWidgetByType(oldWidget.type)
+ }
+
+ if (!newWidget || !newWidget.options) {
+ return
+ }
+
+ Object.keys(newWidget.options).forEach(ck => {
+ if (!oldWidget.hasOwnProperty(ck)) {
+ oldWidget.options[ck] = deepClone(newWidget.options[ck])
+ }
+ })
+ },
+
+ upgradeFormConfig(oldFormConfig) {
+ Object.keys(this.formConfig).forEach(fc => {
+ if (!oldFormConfig.hasOwnProperty(fc)) {
+ oldFormConfig[fc] = deepClone(this.formConfig[fc])
+ }
+ })
+ },
+
+ cloneGridCol(widget, parentWidget) {
+ let newGridCol = deepClone(this.getContainerByType('grid-col'))
+ newGridCol.options.span = widget.options.span
+ let tmpId = generateId()
+ newGridCol.id = 'grid-col-' + tmpId
+ newGridCol.options.name = 'gridCol' + tmpId
+
+ parentWidget.cols.push(newGridCol)
+ },
+
+ cloneContainer(containWidget) {
+ if (containWidget.type === 'grid') {
+ let newGrid = deepClone(this.getContainerByType('grid'))
+ newGrid.id = newGrid.type + generateId()
+ newGrid.options.name = newGrid.id
+ containWidget.cols.forEach(gridCol => {
+ let newGridCol = deepClone(this.getContainerByType('grid-col'))
+ let tmpId = generateId()
+ newGridCol.id = 'grid-col-' + tmpId
+ newGridCol.options.name = 'gridCol' + tmpId
+ newGridCol.options.span = gridCol.options.span
+ newGrid.cols.push(newGridCol)
+ })
+
+ return newGrid
+ } else if (containWidget.type === 'table') {
+ let newTable = deepClone(this.getContainerByType('table'))
+ newTable.id = newTable.type + generateId()
+ newTable.options.name = newTable.id
+ containWidget.rows.forEach(tRow => {
+ let newRow = deepClone(tRow)
+ newRow.id = 'table-row-' + generateId()
+ newRow.cols.forEach(col => {
+ col.id = 'table-cell-' + generateId()
+ col.options.name = col.id
+ col.widgetList = [] //清空组件列表
+ })
+ newTable.rows.push(newRow)
+ })
+
+ return newTable
+ } else { //其他容器组件不支持clone操作
+ return null
+ }
+ },
+
+ moveUpWidget(parentList, indexOfParentList) {
+ if (!!parentList) {
+ if (indexOfParentList === 0) {
+ this.vueInstance.$message(this.vueInstance.i18nt('designer.hint.moveUpFirstChildHint'))
+ return
+ }
+
+ let tempWidget = parentList[indexOfParentList]
+ parentList.splice(indexOfParentList, 1)
+ parentList.splice(indexOfParentList - 1, 0, tempWidget)
+ }
+ },
+
+ moveDownWidget(parentList, indexOfParentList) {
+ if (!!parentList) {
+ if (indexOfParentList === parentList.length - 1) {
+ this.vueInstance.$message(this.vueInstance.i18nt('designer.hint.moveDownLastChildHint'))
+ return
+ }
+
+ let tempWidget = parentList[indexOfParentList]
+ parentList.splice(indexOfParentList, 1)
+ parentList.splice(indexOfParentList + 1, 0, tempWidget)
+ }
+ },
+
+ copyNewFieldWidget(origin) {
+ let newWidget = deepClone(origin)
+ let tempId = generateId()
+ newWidget.id = newWidget.type.replace(/-/g, '') + tempId
+ newWidget.options.name = newWidget.id
+ newWidget.options.label = newWidget.options.label || newWidget.type.toLowerCase()
+
+ delete newWidget.displayName
+ return newWidget
+ },
+
+ copyNewContainerWidget(origin) {
+ let newCon = deepClone(origin)
+ newCon.id = newCon.type.replace(/-/g, '') + generateId()
+ newCon.options.name = newCon.id
+ if (newCon.type === 'grid') {
+ let newCol = deepClone( this.getContainerByType('grid-col') )
+ let tmpId = generateId()
+ newCol.id = 'grid-col-' + tmpId
+ newCol.options.name = 'gridCol' + tmpId
+ newCon.cols.push(newCol)
+ //
+ newCol = deepClone(newCol)
+ tmpId = generateId()
+ newCol.id = 'grid-col-' + tmpId
+ newCol.options.name = 'gridCol' + tmpId
+ newCon.cols.push(newCol)
+ } else if (newCon.type === 'table') {
+ let newRow = {cols: []}
+ newRow.id = 'table-row-' + generateId()
+ newRow.merged = false
+ let newCell = deepClone( this.getContainerByType('table-cell') )
+ newCell.id = 'table-cell-' + generateId()
+ newCell.options.name = newCell.id
+ newCell.merged = false
+ newCell.options.colspan = 1
+ newCell.options.rowspan = 1
+ newRow.cols.push(newCell)
+ newCon.rows.push(newRow)
+ } else if (newCon.type === 'tab') {
+ let newTabPane = deepClone( this.getContainerByType('tab-pane') )
+ newTabPane.id = 'tab-pane-' + generateId()
+ newTabPane.options.name = 'tab1'
+ newTabPane.options.label = 'tab 1'
+ newCon.tabs.push(newTabPane)
+ }
+ //newCon.options.customClass = []
+
+ delete newCon.displayName
+ return newCon
+ },
+
+ addContainerByDbClick(container) {
+ let newCon = this.copyNewContainerWidget(container)
+ this.widgetList.push(newCon)
+ this.setSelected(newCon)
+ },
+
+ addFieldByDbClick(widget) {
+ let newWidget = this.copyNewFieldWidget(widget)
+ if (!!this.selectedWidget && this.selectedWidget.type === 'tab') {
+ //获取当前激活的tabPane
+ let activeTab = this.selectedWidget.tabs[0]
+ this.selectedWidget.tabs.forEach(tabPane => {
+ if (!!tabPane.options.active) {
+ activeTab = tabPane
+ }
+ })
+
+ !!activeTab && activeTab.widgetList.push(newWidget)
+ } else if (!!this.selectedWidget && !!this.selectedWidget.widgetList) {
+ this.selectedWidget.widgetList.push(newWidget)
+ } else {
+ this.widgetList.push(newWidget)
+ }
+
+ this.setSelected(newWidget)
+ this.emitHistoryChange()
+ },
+
+ deleteColOfGrid(gridWidget, colIdx) {
+ if (!!gridWidget && !!gridWidget.cols) {
+ gridWidget.cols.splice(colIdx, 1)
+ }
+ },
+
+ addNewColOfGrid(gridWidget) {
+ const cols = gridWidget.cols
+ let newGridCol = deepClone(this.getContainerByType('grid-col'))
+ let tmpId = generateId()
+ newGridCol.id = 'grid-col-' + tmpId
+ newGridCol.options.name = 'gridCol' + tmpId
+ if ((!!cols) && (cols.length > 0)) {
+ let spanSum = 0
+ cols.forEach((col) => {
+ spanSum += col.options.span
+ })
+
+ if (spanSum >= 24) {
+ //this.$message.info('列栅格之和超出24')
+ console.log('列栅格之和超出24')
+ gridWidget.cols.push(newGridCol)
+ } else {
+ newGridCol.options.span = (24 - spanSum) > 12 ? 12 : (24 - spanSum)
+ gridWidget.cols.push(newGridCol)
+ }
+ } else {
+ gridWidget.cols = [newGridCol]
+ }
+ },
+
+ addTabPaneOfTabs(tabsWidget) {
+ const tabPanes = tabsWidget.tabs
+ let newTabPane = deepClone( this.getContainerByType('tab-pane') )
+ newTabPane.id = 'tab-pane-' + generateId()
+ newTabPane.options.name = newTabPane.id
+ newTabPane.options.label = 'tab ' + (tabPanes.length + 1)
+ tabPanes.push(newTabPane)
+ },
+
+ deleteTabPaneOfTabs(tabsWidget, tpIdx) {
+ tabsWidget.tabs.splice(tpIdx, 1)
+ },
+
+ emitEvent(evtName, evtData) { //用于兄弟组件发射事件
+ this.vueInstance.$emit(evtName, evtData)
+ },
+
+ handleEvent(evtName, callback) { //用于兄弟组件接收事件
+ this.vueInstance.$on(evtName, (data) => callback(data))
+ },
+
+ setCssClassList(cssClassList) {
+ this.cssClassList = cssClassList
+ },
+
+ getCssClassList() {
+ return this.cssClassList
+ },
+
+ registerFormWidget(formWidget) {
+ this.formWidget = formWidget
+ },
+
+ initHistoryData() {
+ this.loadFormContentFromStorage()
+ this.historyData.index++
+ this.historyData.steps[this.historyData.index] = ({
+ widgetList: deepClone(this.widgetList),
+ formConfig: deepClone(this.formConfig)
+ })
+ },
+
+ emitHistoryChange() {
+ //console.log('------------', 'Form history changed!')
+
+ if (this.historyData.index === this.historyData.maxStep - 1) {
+ this.historyData.steps.shift()
+ } else {
+ this.historyData.index++
+ }
+
+ this.historyData.steps[this.historyData.index] = ({
+ widgetList: deepClone(this.widgetList),
+ formConfig: deepClone(this.formConfig)
+ })
+
+ this.saveFormContentToStorage()
+
+ if (this.historyData.index < this.historyData.steps.length - 1) {
+ this.historyData.steps = this.historyData.steps.slice(0, this.historyData.index + 1)
+ }
+
+ //console.log('history', this.historyData.index)
+ },
+
+ saveCurrentHistoryStep() {
+ this.historyData.steps[this.historyData.index] = deepClone({
+ widgetList: this.widgetList,
+ formConfig: this.formConfig
+ })
+
+ this.saveFormContentToStorage()
+ },
+
+ undoHistoryStep() {
+ if (this.historyData.index !== 0) {
+ this.historyData.index--
+ }
+ //console.log('undo', this.historyData.index)
+
+ this.widgetList = deepClone(this.historyData.steps[this.historyData.index].widgetList)
+ this.formConfig = deepClone(this.historyData.steps[this.historyData.index].formConfig)
+ },
+
+ redoHistoryStep() {
+ if (this.historyData.index !== (this.historyData.steps.length - 1)) {
+ this.historyData.index++
+ }
+ //console.log('redo', this.historyData.index)
+
+ this.widgetList = deepClone(this.historyData.steps[this.historyData.index].widgetList)
+ this.formConfig = deepClone(this.historyData.steps[this.historyData.index].formConfig)
+ },
+
+ undoEnabled() {
+ return (this.historyData.index > 0) && (this.historyData.steps.length > 0)
+ },
+
+ redoEnabled() {
+ return this.historyData.index < (this.historyData.steps.length - 1)
+ },
+
+ saveFormContentToStorage() {
+ window.localStorage.setItem('widget__list__backup', JSON.stringify(this.widgetList))
+ window.localStorage.setItem('form__config__backup', JSON.stringify(this.formConfig))
+ },
+
+ loadFormContentFromStorage() {
+ let widgetListBackup = window.localStorage.getItem('widget__list__backup')
+ if (!!widgetListBackup) {
+ this.widgetList = JSON.parse(widgetListBackup)
+ }
+
+ let formConfigBackup = window.localStorage.getItem('form__config__backup')
+ if (!!formConfigBackup) {
+ //this.formConfig = JSON.parse(formConfigBackup)
+ overwriteObj(this.formConfig, JSON.parse(formConfigBackup)) /* 用=赋值,会导致inject依赖注入的formConfig属性变成非响应式 */
+ }
+ },
+
+
+ }
+}
diff --git a/src/components/form-designer/form-widget/container-widget/container-wrapper.vue b/src/components/form-designer/form-widget/container-widget/container-wrapper.vue
new file mode 100644
index 0000000..813f8bf
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/container-wrapper.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${widget.type}`, `extension.widgetLabel.${widget.type}`)}}
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/container-widget/containerMixin.js b/src/components/form-designer/form-widget/container-widget/containerMixin.js
new file mode 100644
index 0000000..2426325
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/containerMixin.js
@@ -0,0 +1,93 @@
+export default {
+ inject: ['getGlobalDsv'],
+ methods: {
+ appendTableRow(widget) {
+ this.designer.appendTableRow(widget)
+ },
+
+ appendTableCol(widget) {
+ this.designer.appendTableCol(widget)
+ },
+
+ onContainerDragAdd(evt, subList) {
+ const newIndex = evt.newIndex
+ if (!!subList[newIndex]) {
+ this.designer.setSelected( subList[newIndex] )
+ }
+
+ this.designer.emitHistoryChange()
+ this.designer.emitEvent('field-selected', this.widget)
+ },
+
+ onContainerDragUpdate() {
+ this.designer.emitHistoryChange()
+ },
+
+ checkContainerMove(evt) {
+ return this.designer.checkWidgetMove(evt)
+ },
+
+ selectWidget(widget) {
+ this.designer.setSelected(widget)
+ },
+
+ selectParentWidget() {
+ if (this.parentWidget) {
+ this.designer.setSelected(this.parentWidget)
+ } else {
+ this.designer.clearSelected()
+ }
+ },
+
+ moveUpWidget() {
+ this.designer.moveUpWidget(this.parentList, this.indexOfParentList)
+ this.designer.emitHistoryChange()
+ },
+
+ moveDownWidget() {
+ this.designer.moveDownWidget(this.parentList, this.indexOfParentList)
+ this.designer.emitHistoryChange()
+ },
+
+ cloneContainer(widget) {
+ if (!!this.parentList) {
+ let newCon = this.designer.cloneContainer(widget)
+ this.parentList.splice(this.indexOfParentList + 1, 0, newCon)
+ this.designer.setSelected(newCon)
+
+ this.designer.emitHistoryChange()
+ }
+ },
+
+ removeWidget() {
+ if (!!this.parentList) {
+ const widgetRefName = this.designer.selectedWidgetName
+ let nextSelected = null
+ if (this.parentList.length === 1) {
+ if (!!this.parentWidget) {
+ nextSelected = this.parentWidget
+ }
+ } else if (this.parentList.length === (1 + this.indexOfParentList)) {
+ nextSelected = this.parentList[this.indexOfParentList - 1]
+ } else {
+ nextSelected = this.parentList[this.indexOfParentList + 1]
+ }
+
+ this.$nextTick(() => {
+ this.parentList.splice(this.indexOfParentList, 1)
+ this.designer.setSelected(nextSelected)
+
+ this.designer.formWidget.deleteWidgetRef(widgetRefName) //删除组件ref!!!
+ this.designer.emitHistoryChange()
+ })
+ }
+ },
+
+ setWidgetOption(optionName, optionValue) { //通用组件选项修改API
+ if (this.widget.options.hasOwnProperty(optionName)) {
+ this.widget.options[optionName] = optionValue
+ }
+ },
+
+ }
+}
diff --git a/src/components/form-designer/form-widget/container-widget/grid-col-widget.vue b/src/components/form-designer/form-widget/container-widget/grid-col-widget.vue
new file mode 100644
index 0000000..37d7fad
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/grid-col-widget.vue
@@ -0,0 +1,314 @@
+
+
+ onGridDragEnd(evt, widget.widgetList)"
+ @add="(evt) => onGridDragAdd(evt, widget.widgetList)"
+ @update="onGridDragUpdate" :move="checkContainerMove">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.widgetLabel.' + widget.type)}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/container-widget/grid-widget.vue b/src/components/form-designer/form-widget/container-widget/grid-widget.vue
new file mode 100644
index 0000000..f75f91e
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/grid-widget.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/container-widget/index.js b/src/components/form-designer/form-widget/container-widget/index.js
new file mode 100644
index 0000000..79f6d00
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/index.js
@@ -0,0 +1,24 @@
+import Vue from 'vue'
+
+const requireComponent = require.context('./', false, /\w+\.vue$/)
+
+/**
+ * 容器组件时递归组件,且内部可以嵌套其他容器,局部注册会找不到组件,必须注册为全局组件,原因不明?!
+ * begin
+ *
+let comps = {}
+
+requireComponent.keys().map(fileName => {
+ let comp = requireComponent(fileName).default;
+ comps[comp.name] = comp
+})
+
+export default comps;
+
+end */
+
+/* 全局注册!! */
+requireComponent.keys().map(fileName => {
+ let comp = requireComponent(fileName).default;
+ Vue.component(comp.name, comp)
+})
diff --git a/src/components/form-designer/form-widget/container-widget/tab-widget.vue b/src/components/form-designer/form-widget/container-widget/tab-widget.vue
new file mode 100644
index 0000000..e966aff
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/tab-widget.vue
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+ onContainerDragAdd(evt, tab.widgetList)"
+ @update="onContainerDragUpdate" :move="checkContainerMove">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/container-widget/table-cell-widget.vue b/src/components/form-designer/form-widget/container-widget/table-cell-widget.vue
new file mode 100644
index 0000000..8dccee5
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/table-cell-widget.vue
@@ -0,0 +1,347 @@
+
+
+ onTableDragEnd(evt, widget.widgetList)"
+ @add="(evt) => onTableDragAdd(evt, widget.widgetList)"
+ @update="onTableDragUpdate" :move="checkContainerMove">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.setting.insertColumnToLeft')}}
+ {{i18nt('designer.setting.insertColumnToRight')}}
+ {{i18nt('designer.setting.insertRowAbove')}}
+ {{i18nt('designer.setting.insertRowBelow')}}
+ {{i18nt('designer.setting.mergeLeftColumn')}}
+ {{i18nt('designer.setting.mergeRightColumn')}}
+ {{i18nt('designer.setting.mergeEntireRow')}}
+ {{i18nt('designer.setting.mergeRowAbove')}}
+ {{i18nt('designer.setting.mergeRowBelow')}}
+ {{i18nt('designer.setting.mergeEntireColumn')}}
+ {{i18nt('designer.setting.undoMergeRow')}}
+ {{i18nt('designer.setting.undoMergeCol')}}
+ {{i18nt('designer.setting.deleteEntireCol')}}
+ {{i18nt('designer.setting.deleteEntireRow')}}
+
+
+
+
+
+ {{i18nt('designer.widgetLabel.' + widget.type)}}
+
+ |
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/container-widget/table-widget.vue b/src/components/form-designer/form-widget/container-widget/table-widget.vue
new file mode 100644
index 0000000..4119c03
--- /dev/null
+++ b/src/components/form-designer/form-widget/container-widget/table-widget.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/button-widget.vue b/src/components/form-designer/form-widget/field-widget/button-widget.vue
new file mode 100644
index 0000000..758117f
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/button-widget.vue
@@ -0,0 +1,87 @@
+
+
+
+ {{field.options.label}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/cascader-widget.vue b/src/components/form-designer/form-widget/field-widget/cascader-widget.vue
new file mode 100644
index 0000000..5fa2a0b
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/cascader-widget.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/checkbox-widget.vue b/src/components/form-designer/form-widget/field-widget/checkbox-widget.vue
new file mode 100644
index 0000000..b7e21b5
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/checkbox-widget.vue
@@ -0,0 +1,105 @@
+
+
+
+
+ {{item.label}}
+
+
+ {{item.label}}
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/color-widget.vue b/src/components/form-designer/form-widget/field-widget/color-widget.vue
new file mode 100644
index 0000000..b02789c
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/color-widget.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/date-range-widget.vue b/src/components/form-designer/form-widget/field-widget/date-range-widget.vue
new file mode 100644
index 0000000..9c26ec1
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/date-range-widget.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/date-widget.vue b/src/components/form-designer/form-widget/field-widget/date-widget.vue
new file mode 100644
index 0000000..5fd071b
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/date-widget.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/divider-widget.vue b/src/components/form-designer/form-widget/field-widget/divider-widget.vue
new file mode 100644
index 0000000..a09117a
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/divider-widget.vue
@@ -0,0 +1,83 @@
+
+
+
+ {{field.options.label}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/fieldMixin.js b/src/components/form-designer/form-widget/field-widget/fieldMixin.js
new file mode 100644
index 0000000..6e400ff
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/fieldMixin.js
@@ -0,0 +1,613 @@
+import {deepClone} from "@/utils/util"
+import FormValidators from '@/utils/validators'
+
+export default {
+ inject: ['refList', 'formConfig', 'getGlobalDsv', 'globalOptionData', 'globalModel', 'getOptionData'],
+
+ computed: {
+ subFormName() {
+ return !!this.parentWidget ? this.parentWidget.options.name : ''
+ },
+
+ subFormItemFlag() {
+ return !!this.parentWidget ? this.parentWidget.type === 'sub-form' : false
+ },
+
+ formModel: {
+ cache: false,
+ get() {
+ return this.globalModel.formModel
+ }
+ },
+
+ },
+
+ methods: {
+
+ //--------------------- 组件内部方法 begin ------------------//
+ getPropName() {
+ if (this.subFormItemFlag && !this.designState) {
+ return this.subFormName + "." + this.subFormRowIndex + "." + this.field.options.name + ""
+ } else {
+ return this.field.options.name
+ }
+ },
+
+ initFieldModel() {
+ if (!this.field.formItemFlag) {
+ return
+ }
+
+ if (!!this.subFormItemFlag && !this.designState) { //SubForm子表单组件需要特殊处理!!
+ let subFormData = this.formModel[this.subFormName]
+ if (((subFormData === undefined) || (subFormData[this.subFormRowIndex] === undefined) ||
+ (subFormData[this.subFormRowIndex][this.field.options.name] === undefined)) &&
+ (this.field.options.defaultValue !== undefined)) {
+ this.fieldModel = this.field.options.defaultValue
+ subFormData[this.subFormRowIndex][this.field.options.name] = this.field.options.defaultValue
+ } else if (subFormData[this.subFormRowIndex][this.field.options.name] === undefined) {
+ this.fieldModel = null
+ subFormData[this.subFormRowIndex][this.field.options.name] = null
+ } else {
+ this.fieldModel = subFormData[this.subFormRowIndex][this.field.options.name]
+ }
+
+ /* 主动触发子表单内field-widget的onChange事件!! */
+ setTimeout(() => { //延时触发onChange事件, 便于更新计算字段!!
+ this.handleOnChangeForSubForm(this.fieldModel, this.oldFieldValue, subFormData, this.subFormRowId)
+ }, 800)
+ this.oldFieldValue = deepClone(this.fieldModel)
+
+ this.initFileList() //处理图片上传、文件上传字段
+
+ return
+ }
+
+ if ((this.formModel[this.field.options.name] === undefined) &&
+ (this.field.options.defaultValue !== undefined)) {
+ this.fieldModel = this.field.options.defaultValue
+ } else if (this.formModel[this.field.options.name] === undefined) { //如果formModel为空对象,则初始化字段值为null!!
+ this.formModel[this.field.options.name] = null
+ } else {
+ this.fieldModel = this.formModel[this.field.options.name]
+ }
+ this.oldFieldValue = deepClone(this.fieldModel)
+ this.initFileList() //处理图片上传、文件上传字段
+ },
+
+ initFileList() { //初始化上传组件的已上传文件列表
+ if ( ((this.field.type !== 'picture-upload') && (this.field.type !== 'file-upload')) || (this.designState === true) ) {
+ return
+ }
+
+ if (!!this.fieldModel) {
+ if (Array.isArray(this.fieldModel)) {
+ this.fileList = deepClone(this.fieldModel)
+ } else {
+ this.fileList.splice(0, 0, deepClone(this.fieldModel))
+ }
+ }
+ },
+
+ initEventHandler() {
+ this.$on('setFormData', (newFormData) => {
+ //console.log('formModel of globalModel----------', this.globalModel.formModel)
+ if (!this.subFormItemFlag) {
+ this.setValue(newFormData[this.field.options.name])
+ }
+ })
+
+ this.$on('field-value-changed', (values) => {
+ if (!!this.subFormItemFlag) {
+ let subFormData = this.formModel[this.subFormName]
+ this.handleOnChangeForSubForm(values[0], values[1], subFormData, this.subFormRowId)
+ } else {
+ this.handleOnChange(values[0], values[1])
+ }
+ })
+
+ /* 监听重新加载选项事件 */
+ this.$on('reloadOptionItems', (widgetNames) => {
+ if ((widgetNames.length === 0) || (widgetNames.indexOf(this.field.options.name) > -1)) {
+ this.initOptionItems(true)
+ }
+ })
+ },
+
+ handleOnCreated() {
+ if (!!this.field.options.onCreated) {
+ let customFunc = new Function(this.field.options.onCreated)
+ customFunc.call(this)
+ }
+ },
+
+ handleOnMounted() {
+ if (!!this.field.options.onMounted) {
+ let mountFunc = new Function(this.field.options.onMounted)
+ mountFunc.call(this)
+ }
+ },
+
+ registerToRefList(oldRefName) {
+ if ((this.refList !== null) && !!this.field.options.name) {
+ if (this.subFormItemFlag && !this.designState) { //处理子表单元素(且非设计状态)
+ if (!!oldRefName) {
+ delete this.refList[oldRefName + '@row' + this.subFormRowId]
+ }
+ this.refList[this.field.options.name + '@row' + this.subFormRowId] = this
+ } else {
+ if (!!oldRefName) {
+ delete this.refList[oldRefName]
+ }
+ this.refList[this.field.options.name] = this
+ }
+ }
+ },
+
+ unregisterFromRefList() { //销毁组件时注销组件ref
+ if ((this.refList !== null) && !!this.field.options.name) {
+ let oldRefName = this.field.options.name
+ if (this.subFormItemFlag && !this.designState) { //处理子表单元素(且非设计状态)
+ delete this.refList[oldRefName + '@row' + this.subFormRowId]
+ } else {
+ delete this.refList[oldRefName]
+ }
+ }
+ },
+
+ initOptionItems(keepSelected) {
+ if (this.designState) {
+ return
+ }
+
+ if ((this.field.type === 'radio') || (this.field.type === 'checkbox')
+ || (this.field.type === 'select') || (this.field.type === 'cascader')) {
+ /* 异步更新option-data之后globalOptionData不能获取到最新值,改用provide的getOptionData()方法 */
+ const newOptionItems = this.getOptionData()
+ if (!!newOptionItems && newOptionItems.hasOwnProperty(this.field.options.name)) {
+ if (!!keepSelected) {
+ this.reloadOptions(newOptionItems[this.field.options.name])
+ } else {
+ this.loadOptions(newOptionItems[this.field.options.name])
+ }
+ }
+ }
+ },
+
+ refreshDefaultValue() {
+ if ((this.designState === true) && (this.field.options.defaultValue !== undefined)) {
+ this.fieldModel = this.field.options.defaultValue
+ }
+ },
+
+ clearFieldRules() {
+ if (!this.field.formItemFlag) {
+ return
+ }
+
+ this.rules.splice(0, this.rules.length) //清空已有
+ },
+
+ buildFieldRules() {
+ if (!this.field.formItemFlag && this.field.options.hidden) {
+ return
+ }
+
+ this.rules.splice(0, this.rules.length) //清空已有
+ if (!!this.field.options.required) {
+ this.rules.push({
+ required: true,
+ //trigger: ['blur', 'change'],
+ trigger: ['blur'], /* 去掉change事件触发校验,change事件触发时formModel数据尚未更新,导致radio/checkbox必填校验出错!! */
+ message: this.field.options.requiredHint || this.i18nt('render.hint.fieldRequired'),
+ })
+ }
+
+ if (!!this.field.options.validation) {
+ let vldName = this.field.options.validation
+ if (!!FormValidators[vldName]) {
+ this.rules.push({
+ validator: FormValidators[vldName],
+ trigger: ['blur', 'change'],
+ label: this.field.options.label,
+ errorMsg: this.field.options.validationHint
+ })
+ } else {
+ this.rules.push({
+ validator: FormValidators['regExp'],
+ trigger: ['blur', 'change'],
+ regExp: vldName,
+ label: this.field.options.label,
+ errorMsg: this.field.options.validationHint
+ })
+ }
+ }
+
+ if (!!this.field.options.onValidate) {
+ let customFn = (rule, value, callback) => {
+ let tmpFunc = new Function('rule', 'value', 'callback', this.field.options.onValidate)
+ return tmpFunc.call(this, rule, value, callback)
+ }
+ this.rules.push({
+ validator: customFn,
+ trigger: ['blur', 'change'],
+ label: this.field.options.label
+ })
+ }
+ },
+
+ /**
+ * 禁用字段值变动触发表单校验
+ */
+ disableChangeValidate() {
+ if (!this.rules) {
+ return
+ }
+
+ this.rules.forEach(rule => {
+ if (!!rule.trigger) {
+ rule.trigger.splice(0, rule.trigger.length)
+ }
+ })
+ },
+
+ /**
+ * 启用字段值变动触发表单校验
+ */
+ enableChangeValidate() {
+ if (!this.rules) {
+ return
+ }
+
+ this.rules.forEach(rule => {
+ if (!!rule.trigger) {
+ rule.trigger.push('blur')
+ rule.trigger.push('change')
+ }
+ })
+ },
+
+ disableOptionOfList(optionList, optionValue) {
+ if (!!optionList && (optionList.length > 0)) {
+ optionList.forEach(opt => {
+ if (opt.value === optionValue) {
+ opt.disabled = true
+ }
+ })
+ }
+ },
+
+ enableOptionOfList(optionList, optionValue) {
+ if (!!optionList && (optionList.length > 0)) {
+ optionList.forEach(opt => {
+ if (opt.value === optionValue) {
+ opt.disabled = false
+ }
+ })
+ }
+ },
+
+ //--------------------- 组件内部方法 end ------------------//
+
+ //--------------------- 事件处理 begin ------------------//
+
+ emitFieldDataChange(newValue, oldValue) {
+ this.$emit('field-value-changed', [newValue, oldValue])
+
+ /* 必须用dispatch向指定父组件派发消息!! */
+ this.dispatch('VFormRender', 'fieldChange',
+ [this.field.options.name, newValue, oldValue, this.subFormName, this.subFormRowIndex])
+ },
+
+ syncUpdateFormModel(value) {
+ if (!!this.designState) {
+ return
+ }
+
+ if (!!this.subFormItemFlag) {
+ let subFormData = this.formModel[this.subFormName] || [{}]
+ let subFormDataRow = subFormData[this.subFormRowIndex]
+ subFormDataRow[this.field.options.name] = value
+ } else {
+ this.formModel[this.field.options.name] = value
+ }
+ },
+
+ handleChangeEvent(value) { /* input的清除输入小按钮会同时触发handleChangeEvent、handleInputCustomEvent!! */
+ this.syncUpdateFormModel(value)
+ this.emitFieldDataChange(value, this.oldFieldValue)
+
+ //number组件一般不会触发focus事件,故此处需要手工赋值oldFieldValue!!
+ this.oldFieldValue = deepClone(value) /* oldFieldValue需要在initFieldModel()方法中赋初值!! */
+
+ /* 主动触发表单的单个字段校验,用于清除字段可能存在的校验错误提示 */
+ this.dispatch('VFormRender', 'fieldValidation', [this.getPropName()])
+ },
+
+ handleFocusCustomEvent(event) {
+ this.oldFieldValue = deepClone(this.fieldModel) //保存修改change之前的值
+
+ if (!!this.field.options.onFocus) {
+ let customFn = new Function('event', this.field.options.onFocus)
+ customFn.call(this, event)
+ }
+ },
+
+ handleBlurCustomEvent(event) {
+ if (!!this.field.options.onBlur) {
+ let customFn = new Function('event', this.field.options.onBlur)
+ customFn.call(this, event)
+ }
+ },
+
+ handleInputCustomEvent(value) {
+ this.syncUpdateFormModel(value)
+
+ /* 主动触发表单的单个字段校验,用于清除字段可能存在的校验错误提示 */
+ this.dispatch('VFormRender', 'fieldValidation', [this.getPropName()])
+
+ if (!!this.field.options.onInput) {
+ let customFn = new Function('value', this.field.options.onInput)
+ customFn.call(this, value)
+ }
+ },
+
+ emitAppendButtonClick() {
+ if (!!this.designState) { //设计状态不触发点击事件
+ return
+ }
+
+ if (!!this.field.options.onAppendButtonClick) {
+ let customFn = new Function(this.field.options.onAppendButtonClick)
+ customFn.call(this)
+ } else {
+ /* 必须调用mixins中的dispatch方法逐级向父组件发送消息!! */
+ this.dispatch('VFormRender', 'appendButtonClick', [this])
+ }
+ },
+
+ handleOnChange(val, oldVal) { //自定义onChange事件
+ if (!!this.field.options.onChange) {
+ let changeFn = new Function('value', 'oldValue', this.field.options.onChange)
+ changeFn.call(this, val, oldVal)
+ }
+ },
+
+ handleOnChangeForSubForm(val, oldVal, subFormData, rowId) { //子表单自定义onChange事件
+ if (!!this.field.options.onChange) {
+ let changeFn = new Function('value', 'oldValue', 'subFormData', 'rowId', this.field.options.onChange)
+ changeFn.call(this, val, oldVal, subFormData, rowId)
+ }
+ },
+
+ handleButtonWidgetClick() {
+ if (!!this.designState) { //设计状态不触发点击事件
+ return
+ }
+
+ if (!!this.field.options.onClick) {
+ let changeFn = new Function(this.field.options.onClick)
+ changeFn.call(this)
+ } else {
+ this.dispatch('VFormRender', 'buttonClick', [this]);
+ }
+ },
+
+ remoteQuery(keyword) {
+ if (!!this.field.options.onRemoteQuery) {
+ let remoteFn = new Function('keyword', this.field.options.onRemoteQuery)
+ remoteFn.call(this, keyword)
+ }
+ },
+
+ //--------------------- 事件处理 end ------------------//
+
+ //--------------------- 以下为组件支持外部调用的API方法 begin ------------------//
+ /* 提示:用户可自行扩充这些方法!!! */
+
+ getFormRef() { /* 获取VFrom引用,必须在VForm组件created之后方可调用 */
+ return this.refList['v_form_ref']
+ },
+
+ getWidgetRef(widgetName, showError) {
+ let foundRef = this.refList[widgetName]
+ if (!foundRef && !!showError) {
+ this.$message.error(this.i18nt('render.hint.refNotFound') + widgetName)
+ }
+ return foundRef
+ },
+
+ getFieldEditor() { //获取内置的el表单组件
+ return this.$refs['fieldEditor']
+ },
+
+ /*
+ 注意:VFormRender的setFormData方法不会触发子表单内field-widget的setValue方法,
+ 因为setFormData方法调用后,子表单内所有field-widget组件已被清空,接收不到setFormData事件!!
+ * */
+ setValue(newValue) {
+ /* if ((this.field.type === 'picture-upload') || (this.field.type === 'file-upload')) {
+ this.fileList = newValue
+ } else */ if (!!this.field.formItemFlag) {
+ let oldValue = deepClone(this.fieldModel)
+ this.fieldModel = newValue
+ this.initFileList()
+
+ this.syncUpdateFormModel(newValue)
+ this.emitFieldDataChange(newValue, oldValue)
+ }
+ },
+
+ getValue() {
+ /* if ((this.field.type === 'picture-upload') || (this.field.type === 'file-upload')) {
+ return this.fileList
+ } else */ {
+ return this.fieldModel
+ }
+ },
+
+ resetField() {
+ if (!!this.subFormItemFlag) { //跳过子表单组件
+ return
+ }
+
+ let defaultValue = this.field.options.defaultValue
+ this.setValue(defaultValue)
+
+ //清空上传组件文件列表
+ if ((this.field.type === 'picture-upload') || (this.field.type === 'file-upload')) {
+ this.$refs['fieldEditor'].clearFiles()
+ this.fileList.splice(0, this.fileList.length)
+ }
+ },
+
+ setWidgetOption(optionName, optionValue) { //通用组件选项修改API
+ if (this.field.options.hasOwnProperty(optionName)) {
+ this.field.options[optionName] = optionValue
+ //TODO: 是否重新构建组件??有些属性修改后必须重新构建组件才能生效,比如字段校验规则。
+ }
+ },
+
+ setReadonly(flag) {
+ this.field.options.readonly = flag
+ },
+
+ setDisabled(flag) {
+ this.field.options.disabled = flag
+ },
+
+ setAppendButtonVisible(flag) {
+ this.field.options.appendButton = flag
+ },
+
+ setAppendButtonDisabled(flag) {
+ this.field.options.appendButtonDisabled = flag
+ },
+
+ setHidden(flag) {
+ this.field.options.hidden = flag
+
+ if (!!flag) { //清除组件校验规则
+ this.clearFieldRules()
+ } else { //重建组件校验规则
+ this.buildFieldRules()
+ }
+ },
+
+ setRequired(flag) {
+ this.field.options.required = flag
+ this.buildFieldRules()
+ },
+
+ setLabel(newLabel) {
+ this.field.options.label = newLabel
+ },
+
+ focus() {
+ if (!!this.getFieldEditor() && !!this.getFieldEditor().focus) {
+ this.getFieldEditor().focus()
+ }
+ },
+
+ clearSelectedOptions() { //清空已选选项
+ if ((this.field.type !== 'checkbox') && (this.field.type !== 'radio') && (this.field.type !== 'select')) {
+ return
+ }
+
+ if ((this.field.type === 'checkbox') ||
+ ((this.field.type === 'select') && this.field.options.multiple)) {
+ this.fieldModel = []
+ } else {
+ this.fieldModel = ''
+ }
+ },
+
+ /**
+ * 加载选项,并清空字段值
+ * @param options
+ */
+ loadOptions(options) {
+ this.field.options.optionItems = deepClone(options)
+ //this.clearSelectedOptions() //清空已选选项
+ },
+
+ /**
+ * 重新加载选项,不清空字段值
+ * @param options
+ */
+ reloadOptions(options) {
+ this.field.options.optionItems = deepClone(options)
+ },
+
+ /**
+ * 返回radio/checkbox/select/cascader的选项数据
+ * @returns 选择项数组
+ */
+ getOptions() {
+ return this.field.options.optionItems
+ },
+
+ disableOption(optionValue) {
+ this.disableOptionOfList(this.field.options.optionItems, optionValue)
+ },
+
+ enableOption(optionValue) {
+ this.enableOptionOfList(this.field.options.optionItems, optionValue)
+ },
+
+ setUploadHeader(name, value) {
+ this.$set(this.uploadHeaders, name, value)
+ },
+
+ setUploadData(name, value) {
+ this.$set(this.uploadData, name, value)
+ },
+
+ setToolbar(customToolbar) {
+ this.customToolbar = customToolbar
+ },
+
+ /**
+ * 是否子表单内嵌的组件
+ * @returns {boolean}
+ */
+ isSubFormItem() {
+ return !!this.parentWidget ? this.parentWidget.type === 'sub-form' : false
+ },
+
+ /**
+ * 动态增加自定义css样式
+ * @param className
+ */
+ addCssClass(className) {
+ if (!this.field.options.customClass) {
+ this.field.options.customClass = [className]
+ } else {
+ this.field.options.customClass.push(className)
+ }
+ },
+
+ /**
+ * 动态移除自定义css样式
+ * @param className
+ */
+ removeCssClass(className) {
+ if (!this.field.options.customClass) {
+ return
+ }
+
+ let foundIdx = -1
+ this.field.options.customClass.map((cc, idx) => {
+ if (cc === className) {
+ foundIdx = idx
+ }
+ })
+ if (foundIdx > -1) {
+ this.field.options.customClass.splice(foundIdx, 1)
+ }
+ },
+
+ //--------------------- 以上为组件支持外部调用的API方法 end ------------------//
+
+ }
+}
diff --git a/src/components/form-designer/form-widget/field-widget/file-upload-widget.vue b/src/components/form-designer/form-widget/field-widget/file-upload-widget.vue
new file mode 100644
index 0000000..332923e
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/file-upload-widget.vue
@@ -0,0 +1,303 @@
+
+
+
+
+ {{field.options.uploadTip}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/form-item-wrapper.vue b/src/components/form-designer/form-widget/field-widget/form-item-wrapper.vue
new file mode 100644
index 0000000..9b4ec8d
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/form-item-wrapper.vue
@@ -0,0 +1,331 @@
+
+
+
+
+
+
+
+
+
+
+ {{label}}
+
+ {{label}}
+
+
+
+ {{label}}
+
+
+ {{label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${field.type}`, `extension.widgetLabel.${field.type}`)}}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/html-text-widget.vue b/src/components/form-designer/form-widget/field-widget/html-text-widget.vue
new file mode 100644
index 0000000..6046b65
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/html-text-widget.vue
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/index.js b/src/components/form-designer/form-widget/field-widget/index.js
new file mode 100644
index 0000000..9e0f6ae
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/index.js
@@ -0,0 +1,10 @@
+const requireComponent = require.context('./', false, /\w+\.vue$/)
+
+let comps = {}
+
+requireComponent.keys().map(fileName => {
+ let comp = requireComponent(fileName).default;
+ comps[comp.name] = comp
+})
+
+export default comps;
diff --git a/src/components/form-designer/form-widget/field-widget/input-widget.vue b/src/components/form-designer/form-widget/field-widget/input-widget.vue
new file mode 100644
index 0000000..dac4785
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/input-widget.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/number-widget.vue b/src/components/form-designer/form-widget/field-widget/number-widget.vue
new file mode 100644
index 0000000..a685192
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/number-widget.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/picture-upload-widget.vue b/src/components/form-designer/form-widget/field-widget/picture-upload-widget.vue
new file mode 100644
index 0000000..9705220
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/picture-upload-widget.vue
@@ -0,0 +1,270 @@
+
+
+
+
+ {{field.options.uploadTip}}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/radio-widget.vue b/src/components/form-designer/form-widget/field-widget/radio-widget.vue
new file mode 100644
index 0000000..8dc04f7
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/radio-widget.vue
@@ -0,0 +1,105 @@
+
+
+
+
+ {{item.label}}
+
+
+ {{item.label}}
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/rate-widget.vue b/src/components/form-designer/form-widget/field-widget/rate-widget.vue
new file mode 100644
index 0000000..aab9897
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/rate-widget.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/rich-editor-widget.vue b/src/components/form-designer/form-widget/field-widget/rich-editor-widget.vue
new file mode 100644
index 0000000..c6d70bd
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/rich-editor-widget.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/select-widget.vue b/src/components/form-designer/form-widget/field-widget/select-widget.vue
new file mode 100644
index 0000000..150faf0
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/select-widget.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/slider-widget.vue b/src/components/form-designer/form-widget/field-widget/slider-widget.vue
new file mode 100644
index 0000000..ecbe13c
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/slider-widget.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/slot-widget.vue b/src/components/form-designer/form-widget/field-widget/slot-widget.vue
new file mode 100644
index 0000000..7fd1545
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/slot-widget.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
{{field.options.label}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/static-content-wrapper.vue b/src/components/form-designer/form-widget/field-widget/static-content-wrapper.vue
new file mode 100644
index 0000000..9aaf3da
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/static-content-wrapper.vue
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${field.type}`, `extension.widgetLabel.${field.type}`)}}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/static-text-widget.vue b/src/components/form-designer/form-widget/field-widget/static-text-widget.vue
new file mode 100644
index 0000000..8cddc7f
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/static-text-widget.vue
@@ -0,0 +1,83 @@
+
+
+
+
{{field.options.textContent}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/switch-widget.vue b/src/components/form-designer/form-widget/field-widget/switch-widget.vue
new file mode 100644
index 0000000..5b433d5
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/switch-widget.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/textarea-widget.vue b/src/components/form-designer/form-widget/field-widget/textarea-widget.vue
new file mode 100644
index 0000000..4e1f6dd
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/textarea-widget.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/time-range-widget.vue b/src/components/form-designer/form-widget/field-widget/time-range-widget.vue
new file mode 100644
index 0000000..8a83704
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/time-range-widget.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/field-widget/time-widget.vue b/src/components/form-designer/form-widget/field-widget/time-widget.vue
new file mode 100644
index 0000000..b4f15aa
--- /dev/null
+++ b/src/components/form-designer/form-widget/field-widget/time-widget.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/form-widget/index.vue b/src/components/form-designer/form-widget/index.vue
new file mode 100644
index 0000000..90ecafb
--- /dev/null
+++ b/src/components/form-designer/form-widget/index.vue
@@ -0,0 +1,272 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/index.vue b/src/components/form-designer/index.vue
new file mode 100644
index 0000000..8ee68d3
--- /dev/null
+++ b/src/components/form-designer/index.vue
@@ -0,0 +1,500 @@
+
+
+
+
+
+
+

+
VForm {{i18nt('application.productTitle')}}
Ver {{vFormVersion}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/refMixinDesign.js b/src/components/form-designer/refMixinDesign.js
new file mode 100644
index 0000000..0b027f1
--- /dev/null
+++ b/src/components/form-designer/refMixinDesign.js
@@ -0,0 +1,28 @@
+export default {
+ methods: {
+ initRefList() {
+ if ((this.refList !== null) && !!this.widget.options.name) {
+ this.refList[this.widget.options.name] = this
+ }
+ },
+
+ getWidgetRef(widgetName, showError = false) {
+ let foundRef = this.refList[widgetName]
+ if (!foundRef && !!showError) {
+ this.$message.error(this.i18nt('render.hint.refNotFound') + widgetName)
+ }
+ return foundRef
+ },
+
+ /* 该方法用于组件重名检查!! */
+ registerToRefList(oldRefName) {
+ if ((this.refList !== null) && !!this.widget.options.name) {
+ if (!!oldRefName) {
+ delete this.refList[oldRefName]
+ }
+ this.refList[this.widget.options.name] = this
+ }
+ },
+
+ }
+}
diff --git a/src/components/form-designer/setting-panel/form-setting.vue b/src/components/form-designer/setting-panel/form-setting.vue
new file mode 100644
index 0000000..0bc6d93
--- /dev/null
+++ b/src/components/form-designer/setting-panel/form-setting.vue
@@ -0,0 +1,354 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.setting.leftPosition')}}
+ {{i18nt('designer.setting.topPosition')}}
+
+
+
+
+ {{i18nt('designer.setting.leftAlign')}}
+ {{i18nt('designer.setting.centerAlign')}}
+ {{i18nt('designer.setting.rightAlign')}}
+
+
+
+
+
+
+ {{i18nt('designer.setting.addCss')}}
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+ {{i18nt('designer.setting.formSFCSetting')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/index.vue b/src/components/form-designer/setting-panel/index.vue
new file mode 100644
index 0000000..d065f3b
--- /dev/null
+++ b/src/components/form-designer/setting-panel/index.vue
@@ -0,0 +1,367 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/option-items-setting.vue b/src/components/form-designer/setting-panel/option-items-setting.vue
new file mode 100644
index 0000000..9076356
--- /dev/null
+++ b/src/components/form-designer/setting-panel/option-items-setting.vue
@@ -0,0 +1,234 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.setting.importOptions')}}
+ {{i18nt('designer.setting.resetDefault')}}
+
+
+
+ {{i18nt('designer.setting.addOption')}}
+ {{i18nt('designer.setting.importOptions')}}
+ {{i18nt('designer.setting.resetDefault')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor-factory.js b/src/components/form-designer/setting-panel/property-editor-factory.js
new file mode 100644
index 0000000..8838555
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor-factory.js
@@ -0,0 +1,173 @@
+import {translate} from "@/utils/i18n"
+import emitter from '@/utils/emitter'
+
+export const createInputTextEditor = function (propName, propLabelKey) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ render(h) {
+ return (
+
+
+
+ )
+ }
+ }
+}
+
+export const createInputNumberEditor = function (propName, propLabelKey) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ methods: {
+ updateValue(newValue) {
+ if ((newValue === undefined) || (newValue === null) || isNaN(newValue)) {
+ this.optionModel[propName] = null
+ } else {
+ this.optionModel[propName] = Number(newValue)
+ }
+ },
+ },
+ render(h) {
+ return (
+
+
+
+ )
+ }
+ }
+}
+
+export const createBooleanEditor = function (propName, propLabelKey) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ render(h) {
+ return (
+
+
+
+ )
+ }
+ }
+}
+
+export const createCheckboxGroupEditor = function (propName, propLabelKey, configs) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ render(h) {
+ return (
+
+
+ {
+ configs.optionItems.map(item => {
+ return {item.label}
+ })
+ }
+
+
+ )
+ }
+ }
+}
+
+
+export const createRadioGroupEditor = function (propName, propLabelKey, configs) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ render(h) {
+ return (
+
+
+ {
+ configs.optionItems.map(item => {
+ return {item.label}
+ })
+ }
+
+
+ )
+ }
+ }
+}
+
+export const createRadioButtonGroupEditor = function (propName, propLabelKey, configs) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ render(h) {
+ return (
+
+
+ {
+ configs.optionItems.map(item => {
+ return {item.label}
+ })
+ }
+
+
+ )
+ }
+ }
+}
+
+export const createSelectEditor = function (propName, propLabelKey, configs) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ render(h) {
+ return (
+
+
+ {
+ configs.optionItems.map(item => {
+ return
+ })
+ }
+
+
+ )
+ }
+ }
+}
+
+export const createEventHandlerEditor = function (eventPropName, eventParams) {
+ return {
+ props: {
+ optionModel: Object,
+ },
+ mixins: [emitter],
+ methods: {
+ editEventHandler() {
+ this.dispatch('SettingPanel', 'editEventHandler', [eventPropName, [...eventParams]])
+ },
+ },
+ render(h) {
+ return (
+
+
+ {translate('designer.setting.addEventHandler')}
+
+ )
+ }
+ }
+}
+
+export const createEmptyEditor = function () {
+ return {
+ render() {
+ return
+ }
+ }
+}
+
diff --git a/src/components/form-designer/setting-panel/property-editor/allowCreate-editor.vue b/src/components/form-designer/setting-panel/property-editor/allowCreate-editor.vue
new file mode 100644
index 0000000..299fcb2
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/allowCreate-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/appendButton-editor.vue b/src/components/form-designer/setting-panel/property-editor/appendButton-editor.vue
new file mode 100644
index 0000000..aa9d15e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/appendButton-editor.vue
@@ -0,0 +1,28 @@
+
+
+
+ {{i18nt('designer.setting.inputButton')}}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/appendButtonDisabled-editor.vue b/src/components/form-designer/setting-panel/property-editor/appendButtonDisabled-editor.vue
new file mode 100644
index 0000000..dd5c4f0
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/appendButtonDisabled-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/automaticDropdown-editor.vue b/src/components/form-designer/setting-panel/property-editor/automaticDropdown-editor.vue
new file mode 100644
index 0000000..b5d5b40
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/automaticDropdown-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/border-editor.vue b/src/components/form-designer/setting-panel/property-editor/border-editor.vue
new file mode 100644
index 0000000..8f76f15
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/border-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/buttonIcon-editor.vue b/src/components/form-designer/setting-panel/property-editor/buttonIcon-editor.vue
new file mode 100644
index 0000000..93848a4
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/buttonIcon-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/buttonStyle-editor.vue b/src/components/form-designer/setting-panel/property-editor/buttonStyle-editor.vue
new file mode 100644
index 0000000..060c0df
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/buttonStyle-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/clearable-editor.vue b/src/components/form-designer/setting-panel/property-editor/clearable-editor.vue
new file mode 100644
index 0000000..bae7dc0
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/clearable-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/columnWidth-editor.vue b/src/components/form-designer/setting-panel/property-editor/columnWidth-editor.vue
new file mode 100644
index 0000000..64552be
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/columnWidth-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-offset-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-offset-editor.vue
new file mode 100644
index 0000000..046818c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-offset-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-pull-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-pull-editor.vue
new file mode 100644
index 0000000..3cfb0c4
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-pull-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-push-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-push-editor.vue
new file mode 100644
index 0000000..23d0815
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-push-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-responsive-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-responsive-editor.vue
new file mode 100644
index 0000000..820802c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-responsive-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-span-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-span-editor.vue
new file mode 100644
index 0000000..a7f0eef
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid-col/grid-col-span-editor.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid/colHeight-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid/colHeight-editor.vue
new file mode 100644
index 0000000..c2648a2
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid/colHeight-editor.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-grid/gutter-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-grid/gutter-editor.vue
new file mode 100644
index 0000000..0be4106
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-grid/gutter-editor.vue
@@ -0,0 +1,81 @@
+
+
+
+ {{i18nt('designer.setting.columnSetting')}}
+
+
+
+
+
+
+
+ {{i18nt('designer.setting.colSpanTitle')}}{{colIdx + 1}}
+ spanChanged(selectedWidget, colItem, colIdx, newValue, oldValue)"
+ class="cell-span-input">
+
+
+
+ {{i18nt('designer.setting.addColumn')}}
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-sub-form/showBlankRow-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-sub-form/showBlankRow-editor.vue
new file mode 100644
index 0000000..629cdae
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-sub-form/showBlankRow-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-sub-form/showRowNumber-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-sub-form/showRowNumber-editor.vue
new file mode 100644
index 0000000..3395796
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-sub-form/showRowNumber-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-sub-form/sub-form-labelAlign-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-sub-form/sub-form-labelAlign-editor.vue
new file mode 100644
index 0000000..aea845b
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-sub-form/sub-form-labelAlign-editor.vue
@@ -0,0 +1,36 @@
+
+
+
+
+ {{i18nt('designer.setting.leftAlign')}}
+
+ {{i18nt('designer.setting.centerAlign')}}
+
+ {{i18nt('designer.setting.rightAlign')}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-tab/tab-customClass-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-tab/tab-customClass-editor.vue
new file mode 100644
index 0000000..89fc1dc
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-tab/tab-customClass-editor.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onTabPaneActiveChange(evt, tpItem)"
+ style="margin-right: 8px">{{i18nt('designer.setting.paneActive')}}
+
+
+
+
+
+ {{i18nt('designer.setting.addTabPane')}}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-table-cell/cellHeight-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-table-cell/cellHeight-editor.vue
new file mode 100644
index 0000000..0fab2bf
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-table-cell/cellHeight-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-table-cell/cellWidth-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-table-cell/cellWidth-editor.vue
new file mode 100644
index 0000000..f2fad36
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-table-cell/cellWidth-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/container-table-cell/wordBreak-editor.vue b/src/components/form-designer/setting-panel/property-editor/container-table-cell/wordBreak-editor.vue
new file mode 100644
index 0000000..0417fee
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/container-table-cell/wordBreak-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/customClass-editor.vue b/src/components/form-designer/setting-panel/property-editor/customClass-editor.vue
new file mode 100644
index 0000000..d0042a7
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/customClass-editor.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/defaultValue-editor.vue
new file mode 100644
index 0000000..3122cf0
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/defaultValue-editor.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/disabled-editor.vue b/src/components/form-designer/setting-panel/property-editor/disabled-editor.vue
new file mode 100644
index 0000000..baba3a0
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/disabled-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/displayStyle-editor.vue b/src/components/form-designer/setting-panel/property-editor/displayStyle-editor.vue
new file mode 100644
index 0000000..fef5866
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/displayStyle-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+ {{i18nt('designer.setting.inlineLayout')}}
+ {{i18nt('designer.setting.blockLayout')}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/editable-editor.vue b/src/components/form-designer/setting-panel/property-editor/editable-editor.vue
new file mode 100644
index 0000000..105aa51
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/editable-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/endPlaceholder-editor.vue b/src/components/form-designer/setting-panel/property-editor/endPlaceholder-editor.vue
new file mode 100644
index 0000000..bfb6a50
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/endPlaceholder-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/eventMixin.js b/src/components/form-designer/setting-panel/property-editor/event-handler/eventMixin.js
new file mode 100644
index 0000000..8792dd1
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/eventMixin.js
@@ -0,0 +1,12 @@
+import emitter from '@/utils/emitter'
+
+export default {
+ mixins: [emitter],
+ created() {},
+ methods: {
+ editEventHandler(eventName, eventParams) {
+ this.dispatch('SettingPanel', 'editEventHandler', [eventName, [...eventParams]])
+ },
+
+ }
+}
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onAppendButtonClick-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onAppendButtonClick-editor.vue
new file mode 100644
index 0000000..99c7cf9
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onAppendButtonClick-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onBeforeUpload-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onBeforeUpload-editor.vue
new file mode 100644
index 0000000..e4355dd
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onBeforeUpload-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onBlur-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onBlur-editor.vue
new file mode 100644
index 0000000..393f7e6
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onBlur-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onChange-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onChange-editor.vue
new file mode 100644
index 0000000..8010610
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onChange-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onClick-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onClick-editor.vue
new file mode 100644
index 0000000..c154bcf
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onClick-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onCreated-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onCreated-editor.vue
new file mode 100644
index 0000000..afc1e8c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onCreated-editor.vue
@@ -0,0 +1,31 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onFileRemove.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onFileRemove.vue
new file mode 100644
index 0000000..08946b3
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onFileRemove.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onFocus-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onFocus-editor.vue
new file mode 100644
index 0000000..1c24dad
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onFocus-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onInput-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onInput-editor.vue
new file mode 100644
index 0000000..35d4982
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onInput-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onMounted-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onMounted-editor.vue
new file mode 100644
index 0000000..bbb8bc6
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onMounted-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onRemoteQuery-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onRemoteQuery-editor.vue
new file mode 100644
index 0000000..5275c61
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onRemoteQuery-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowAdd-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowAdd-editor.vue
new file mode 100644
index 0000000..26abcfe
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowAdd-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowChange-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowChange-editor.vue
new file mode 100644
index 0000000..254638a
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowChange-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowDelete-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowDelete-editor.vue
new file mode 100644
index 0000000..8666b9e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowDelete-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowInsert-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowInsert-editor.vue
new file mode 100644
index 0000000..ed71c3d
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onSubFormRowInsert-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onUploadError-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onUploadError-editor.vue
new file mode 100644
index 0000000..7562f96
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onUploadError-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onUploadSuccess-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onUploadSuccess-editor.vue
new file mode 100644
index 0000000..925f791
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onUploadSuccess-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/event-handler/onValidate-editor.vue b/src/components/form-designer/setting-panel/property-editor/event-handler/onValidate-editor.vue
new file mode 100644
index 0000000..44cc2a1
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/event-handler/onValidate-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{i18nt('designer.setting.addEventHandler')}}
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-button/button-type-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-button/button-type-editor.vue
new file mode 100644
index 0000000..4bb13de
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-button/button-type-editor.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-button/circle-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-button/circle-editor.vue
new file mode 100644
index 0000000..a5c0f6a
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-button/circle-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-button/icon-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-button/icon-editor.vue
new file mode 100644
index 0000000..177ce68
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-button/icon-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-button/plain-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-button/plain-editor.vue
new file mode 100644
index 0000000..f825841
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-button/plain-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-button/round-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-button/round-editor.vue
new file mode 100644
index 0000000..33c5612
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-button/round-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-cascader/cascader-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-cascader/cascader-defaultValue-editor.vue
new file mode 100644
index 0000000..8ccc44e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-cascader/cascader-defaultValue-editor.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-cascader/cascader-multiple-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-cascader/cascader-multiple-editor.vue
new file mode 100644
index 0000000..7446820
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-cascader/cascader-multiple-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-cascader/checkStrictly-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-cascader/checkStrictly-editor.vue
new file mode 100644
index 0000000..3216dad
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-cascader/checkStrictly-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-cascader/showAllLevels-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-cascader/showAllLevels-editor.vue
new file mode 100644
index 0000000..68c91fc
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-cascader/showAllLevels-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-checkbox/checkbox-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-checkbox/checkbox-defaultValue-editor.vue
new file mode 100644
index 0000000..9660985
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-checkbox/checkbox-defaultValue-editor.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-color/color-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-color/color-defaultValue-editor.vue
new file mode 100644
index 0000000..21523ee
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-color/color-defaultValue-editor.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-defaultValue-editor.vue
new file mode 100644
index 0000000..d0df950
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-defaultValue-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-format-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-format-editor.vue
new file mode 100644
index 0000000..35a0353
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-format-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-type-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-type-editor.vue
new file mode 100644
index 0000000..e31e968
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-type-editor.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-valueFormat-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-valueFormat-editor.vue
new file mode 100644
index 0000000..bdca28c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date-range/date-range-valueFormat-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date/date-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date/date-defaultValue-editor.vue
new file mode 100644
index 0000000..0b292e5
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date/date-defaultValue-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date/date-format-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date/date-format-editor.vue
new file mode 100644
index 0000000..0a28e69
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date/date-format-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date/date-type-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date/date-type-editor.vue
new file mode 100644
index 0000000..2dcb301
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date/date-type-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-date/date-valueFormat-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-date/date-valueFormat-editor.vue
new file mode 100644
index 0000000..ae5c6f6
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-date/date-valueFormat-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-divider/contentPosition-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-divider/contentPosition-editor.vue
new file mode 100644
index 0000000..ca21786
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-divider/contentPosition-editor.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-file-upload/file-upload-fileTypes-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-file-upload/file-upload-fileTypes-editor.vue
new file mode 100644
index 0000000..a18c22a
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-file-upload/file-upload-fileTypes-editor.vue
@@ -0,0 +1,44 @@
+
+
+ {{i18nt('designer.setting.fileTypes')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-html-text/htmlContent-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-html-text/htmlContent-editor.vue
new file mode 100644
index 0000000..7de014f
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-html-text/htmlContent-editor.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-number/controlsPosition-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-number/controlsPosition-editor.vue
new file mode 100644
index 0000000..6cc39e5
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-number/controlsPosition-editor.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-picture-upload/picture-upload-fileTypes-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-picture-upload/picture-upload-fileTypes-editor.vue
new file mode 100644
index 0000000..697d3df
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-picture-upload/picture-upload-fileTypes-editor.vue
@@ -0,0 +1,44 @@
+
+
+ {{i18nt('designer.setting.fileTypes')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-radio/radio-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-radio/radio-defaultValue-editor.vue
new file mode 100644
index 0000000..0f846a9
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-radio/radio-defaultValue-editor.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/allowHalf-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/allowHalf-editor.vue
new file mode 100644
index 0000000..929a36c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/allowHalf-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/highThreshold-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/highThreshold-editor.vue
new file mode 100644
index 0000000..c5fe412
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/highThreshold-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/lowThreshold-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/lowThreshold-editor.vue
new file mode 100644
index 0000000..00464b2
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/lowThreshold-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/rate-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/rate-defaultValue-editor.vue
new file mode 100644
index 0000000..d4dd66b
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/rate-defaultValue-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/rate-max-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/rate-max-editor.vue
new file mode 100644
index 0000000..a43c192
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/rate-max-editor.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/showScore-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/showScore-editor.vue
new file mode 100644
index 0000000..f929132
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/showScore-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-rate/showText-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-rate/showText-editor.vue
new file mode 100644
index 0000000..85625d5
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-rate/showText-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-select/select-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-select/select-defaultValue-editor.vue
new file mode 100644
index 0000000..56612ee
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-select/select-defaultValue-editor.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-slider/range-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-slider/range-editor.vue
new file mode 100644
index 0000000..7b1ef04
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-slider/range-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-slider/showStops-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-slider/showStops-editor.vue
new file mode 100644
index 0000000..ae79fe8
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-slider/showStops-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-slider/vertical-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-slider/vertical-editor.vue
new file mode 100644
index 0000000..42cb5e3
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-slider/vertical-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-static-text/fontSize-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-static-text/fontSize-editor.vue
new file mode 100644
index 0000000..b2c1758
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-static-text/fontSize-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-static-text/preWrap-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-static-text/preWrap-editor.vue
new file mode 100644
index 0000000..61ee8a3
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-static-text/preWrap-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-static-text/textContent-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-static-text/textContent-editor.vue
new file mode 100644
index 0000000..5c6e145
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-static-text/textContent-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-switch/activeColor-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-switch/activeColor-editor.vue
new file mode 100644
index 0000000..641bc94
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-switch/activeColor-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-switch/activeText-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-switch/activeText-editor.vue
new file mode 100644
index 0000000..5c77879
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-switch/activeText-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-switch/inactiveColor-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-switch/inactiveColor-editor.vue
new file mode 100644
index 0000000..dd4cb5c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-switch/inactiveColor-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-switch/inactiveText-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-switch/inactiveText-editor.vue
new file mode 100644
index 0000000..c37698a
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-switch/inactiveText-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-switch/switch-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-switch/switch-defaultValue-editor.vue
new file mode 100644
index 0000000..08012b6
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-switch/switch-defaultValue-editor.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-switch/switchWidth-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-switch/switchWidth-editor.vue
new file mode 100644
index 0000000..969f1c9
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-switch/switchWidth-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-time-range/time-range-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-time-range/time-range-defaultValue-editor.vue
new file mode 100644
index 0000000..36a0dd3
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-time-range/time-range-defaultValue-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-time-range/time-range-format-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-time-range/time-range-format-editor.vue
new file mode 100644
index 0000000..d461dcc
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-time-range/time-range-format-editor.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-time/time-defaultValue-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-time/time-defaultValue-editor.vue
new file mode 100644
index 0000000..d9d1e43
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-time/time-defaultValue-editor.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/field-time/time-format-editor.vue b/src/components/form-designer/setting-panel/property-editor/field-time/time-format-editor.vue
new file mode 100644
index 0000000..6ffe0bc
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/field-time/time-format-editor.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/fileMaxSize-editor.vue b/src/components/form-designer/setting-panel/property-editor/fileMaxSize-editor.vue
new file mode 100644
index 0000000..52df908
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/fileMaxSize-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/filterable-editor.vue b/src/components/form-designer/setting-panel/property-editor/filterable-editor.vue
new file mode 100644
index 0000000..3c72077
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/filterable-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/hidden-editor.vue b/src/components/form-designer/setting-panel/property-editor/hidden-editor.vue
new file mode 100644
index 0000000..a954304
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/hidden-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/index.js b/src/components/form-designer/setting-panel/property-editor/index.js
new file mode 100644
index 0000000..918fbdf
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/index.js
@@ -0,0 +1,10 @@
+const requireComponent = require.context('./', true, /\w+\.vue$/) //遍历当前目录和子目录
+
+let comps = {}
+
+requireComponent.keys().map(fileName => {
+ let comp = requireComponent(fileName).default;
+ comps[comp.name] = comp
+})
+
+export default comps;
diff --git a/src/components/form-designer/setting-panel/property-editor/label-editor.vue b/src/components/form-designer/setting-panel/property-editor/label-editor.vue
new file mode 100644
index 0000000..cfa112e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/label-editor.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/labelAlign-editor.vue b/src/components/form-designer/setting-panel/property-editor/labelAlign-editor.vue
new file mode 100644
index 0000000..a704ab8
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/labelAlign-editor.vue
@@ -0,0 +1,42 @@
+
+
+
+
+ {{i18nt('designer.setting.leftAlign')}}
+
+ {{i18nt('designer.setting.centerAlign')}}
+
+ {{i18nt('designer.setting.rightAlign')}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/labelHidden-editor.vue b/src/components/form-designer/setting-panel/property-editor/labelHidden-editor.vue
new file mode 100644
index 0000000..e512ab4
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/labelHidden-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/labelIconClass-editor.vue b/src/components/form-designer/setting-panel/property-editor/labelIconClass-editor.vue
new file mode 100644
index 0000000..5e32f65
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/labelIconClass-editor.vue
@@ -0,0 +1,28 @@
+
+
+
+ {{i18nt('designer.setting.customLabelIcon')}}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/labelIconPosition-editor.vue b/src/components/form-designer/setting-panel/property-editor/labelIconPosition-editor.vue
new file mode 100644
index 0000000..805c793
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/labelIconPosition-editor.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/labelTooltip-editor.vue b/src/components/form-designer/setting-panel/property-editor/labelTooltip-editor.vue
new file mode 100644
index 0000000..3ee1308
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/labelTooltip-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/labelWidth-editor.vue b/src/components/form-designer/setting-panel/property-editor/labelWidth-editor.vue
new file mode 100644
index 0000000..96cedad
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/labelWidth-editor.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/limit-editor.vue b/src/components/form-designer/setting-panel/property-editor/limit-editor.vue
new file mode 100644
index 0000000..b9a64f2
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/limit-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/max-editor.vue b/src/components/form-designer/setting-panel/property-editor/max-editor.vue
new file mode 100644
index 0000000..ab5f320
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/max-editor.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/maxLength-editor.vue b/src/components/form-designer/setting-panel/property-editor/maxLength-editor.vue
new file mode 100644
index 0000000..9545cf4
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/maxLength-editor.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/min-editor.vue b/src/components/form-designer/setting-panel/property-editor/min-editor.vue
new file mode 100644
index 0000000..513fd24
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/min-editor.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/minLength-editor.vue b/src/components/form-designer/setting-panel/property-editor/minLength-editor.vue
new file mode 100644
index 0000000..1b0e2e4
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/minLength-editor.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/multiple-editor.vue b/src/components/form-designer/setting-panel/property-editor/multiple-editor.vue
new file mode 100644
index 0000000..487e4ad
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/multiple-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/multipleLimit-editor.vue b/src/components/form-designer/setting-panel/property-editor/multipleLimit-editor.vue
new file mode 100644
index 0000000..3fae0c7
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/multipleLimit-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/multipleSelect-editor.vue b/src/components/form-designer/setting-panel/property-editor/multipleSelect-editor.vue
new file mode 100644
index 0000000..8303980
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/multipleSelect-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/name-editor.vue b/src/components/form-designer/setting-panel/property-editor/name-editor.vue
new file mode 100644
index 0000000..4ffb2f5
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/name-editor.vue
@@ -0,0 +1,89 @@
+
+
+ {{i18nt('designer.setting.uniqueName')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/optionItems-editor.vue b/src/components/form-designer/setting-panel/property-editor/optionItems-editor.vue
new file mode 100644
index 0000000..d911453
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/optionItems-editor.vue
@@ -0,0 +1,28 @@
+
+
+ {{i18nt('designer.setting.optionsSetting')}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/placeholder-editor.vue b/src/components/form-designer/setting-panel/property-editor/placeholder-editor.vue
new file mode 100644
index 0000000..e44ff8e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/placeholder-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/precision-editor.vue b/src/components/form-designer/setting-panel/property-editor/precision-editor.vue
new file mode 100644
index 0000000..4e64878
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/precision-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/prefixIcon-editor.vue b/src/components/form-designer/setting-panel/property-editor/prefixIcon-editor.vue
new file mode 100644
index 0000000..361ff3a
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/prefixIcon-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/propertyMixin.js b/src/components/form-designer/setting-panel/property-editor/propertyMixin.js
new file mode 100644
index 0000000..550e119
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/propertyMixin.js
@@ -0,0 +1,55 @@
+export default {
+ methods: {
+ hasConfig(configName) {
+ if (!this.designer || !this.designer.selectedWidget) {
+ return false
+ }
+
+ return this.designer.hasConfig(this.selectedWidget, configName)
+ },
+
+ emitDefaultValueChange() {
+ if (!!this.designer) {
+ if (!!this.designer.formWidget) {
+ let fieldWidget = this.designer.formWidget.getWidgetRef(this.designer.selectedWidget.options.name)
+ if (!!fieldWidget && !!fieldWidget.refreshDefaultValue) {
+ fieldWidget.refreshDefaultValue()
+ }
+ }
+ }
+ },
+
+ inputNumberHandler({target}) {
+ target.value = target.value.replace(/[^0-9]/gi, '')
+ },
+
+ onRemoteChange(val) {
+ if (!!val) {
+ this.optionModel.filterable = true
+ this.optionModel.allowCreate = false
+ }
+ },
+
+ onMultipleSelected(val) {
+ if (val) {
+ //debugger
+
+ // 清空已选项,否则console会报错!!
+ let foundRef = this.designer.formWidget.getWidgetRef(this.optionModel.name)
+ if (!!foundRef && !!foundRef.clearSelectedOptions) {
+ foundRef.clearSelectedOptions()
+ }
+
+ this.optionModel.defaultValue = [] //清空原默认值!!
+ } else {
+ if (!!this.optionModel.defaultValue && (this.optionModel.defaultValue.length > 0)) {
+ this.optionModel.defaultValue = this.optionModel.defaultValue[0]
+ } else {
+ this.optionModel.defaultValue = ''
+ }
+ }
+ },
+
+
+ }
+}
diff --git a/src/components/form-designer/setting-panel/property-editor/readonly-editor.vue b/src/components/form-designer/setting-panel/property-editor/readonly-editor.vue
new file mode 100644
index 0000000..b13462e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/readonly-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/remote-editor.vue b/src/components/form-designer/setting-panel/property-editor/remote-editor.vue
new file mode 100644
index 0000000..def1ff2
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/remote-editor.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/required-editor.vue b/src/components/form-designer/setting-panel/property-editor/required-editor.vue
new file mode 100644
index 0000000..6925b12
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/required-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/requiredHint-editor.vue b/src/components/form-designer/setting-panel/property-editor/requiredHint-editor.vue
new file mode 100644
index 0000000..d44c442
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/requiredHint-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/rows-editor.vue b/src/components/form-designer/setting-panel/property-editor/rows-editor.vue
new file mode 100644
index 0000000..dafd688
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/rows-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/showFileList-editor.vue b/src/components/form-designer/setting-panel/property-editor/showFileList-editor.vue
new file mode 100644
index 0000000..04e2def
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/showFileList-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/showPassword-editor.vue b/src/components/form-designer/setting-panel/property-editor/showPassword-editor.vue
new file mode 100644
index 0000000..902818d
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/showPassword-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/showWordLimit-editor.vue b/src/components/form-designer/setting-panel/property-editor/showWordLimit-editor.vue
new file mode 100644
index 0000000..07fab83
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/showWordLimit-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/size-editor.vue b/src/components/form-designer/setting-panel/property-editor/size-editor.vue
new file mode 100644
index 0000000..671ceb5
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/size-editor.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/startPlaceholder-editor.vue b/src/components/form-designer/setting-panel/property-editor/startPlaceholder-editor.vue
new file mode 100644
index 0000000..c1832a8
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/startPlaceholder-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/step-editor.vue b/src/components/form-designer/setting-panel/property-editor/step-editor.vue
new file mode 100644
index 0000000..ac64aff
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/step-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/suffixIcon-editor.vue b/src/components/form-designer/setting-panel/property-editor/suffixIcon-editor.vue
new file mode 100644
index 0000000..6f45b71
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/suffixIcon-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/textAlign-editor.vue b/src/components/form-designer/setting-panel/property-editor/textAlign-editor.vue
new file mode 100644
index 0000000..0d49890
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/textAlign-editor.vue
@@ -0,0 +1,35 @@
+
+
+
+
+ {{i18nt('designer.setting.leftAlign')}}
+
+ {{i18nt('designer.setting.centerAlign')}}
+
+ {{i18nt('designer.setting.rightAlign')}}
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/type-editor.vue b/src/components/form-designer/setting-panel/property-editor/type-editor.vue
new file mode 100644
index 0000000..7f4ee50
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/type-editor.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/uploadTip-editor.vue b/src/components/form-designer/setting-panel/property-editor/uploadTip-editor.vue
new file mode 100644
index 0000000..f58b6fd
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/uploadTip-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/uploadURL-editor.vue b/src/components/form-designer/setting-panel/property-editor/uploadURL-editor.vue
new file mode 100644
index 0000000..6b651ae
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/uploadURL-editor.vue
@@ -0,0 +1,28 @@
+
+
+
+ {{i18nt('designer.setting.uploadSetting')}}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/validation-editor.vue b/src/components/form-designer/setting-panel/property-editor/validation-editor.vue
new file mode 100644
index 0000000..8a5c425
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/validation-editor.vue
@@ -0,0 +1,48 @@
+
+
+ {{i18nt('designer.setting.validation')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/validationHint-editor.vue b/src/components/form-designer/setting-panel/property-editor/validationHint-editor.vue
new file mode 100644
index 0000000..006696e
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/validationHint-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/property-editor/withCredentials-editor.vue b/src/components/form-designer/setting-panel/property-editor/withCredentials-editor.vue
new file mode 100644
index 0000000..b965627
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/withCredentials-editor.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/propertyRegister.js b/src/components/form-designer/setting-panel/propertyRegister.js
new file mode 100644
index 0000000..3e9536c
--- /dev/null
+++ b/src/components/form-designer/setting-panel/propertyRegister.js
@@ -0,0 +1,215 @@
+import Vue from 'vue'
+
+/**
+ * 格式说明:属性名称==对应属性编辑器的组件名称
+ */
+const COMMON_PROPERTIES = {
+ //字段
+ 'name' : 'name-editor',
+ 'label' : 'label-editor',
+ 'labelAlign' : 'labelAlign-editor',
+ 'type' : 'type-editor',
+ 'defaultValue' : 'defaultValue-editor',
+ 'placeholder' : 'placeholder-editor',
+ 'startPlaceholder' : 'startPlaceholder-editor',
+ 'endPlaceholder' : 'endPlaceholder-editor',
+ 'columnWidth' : 'columnWidth-editor',
+ 'size' : 'size-editor',
+ 'showStops' : 'showStops-editor',
+ 'displayStyle' : 'displayStyle-editor',
+ 'buttonStyle' : 'buttonStyle-editor',
+ 'border' : 'border-editor',
+ 'labelWidth' : 'labelWidth-editor',
+ 'labelHidden' : 'labelHidden-editor',
+ 'rows' : 'rows-editor',
+ 'required' : 'required-editor',
+ 'requiredHint' : 'requiredHint-editor',
+ 'validation' : 'validation-editor',
+ 'validationHint' : 'validationHint-editor',
+ 'readonly' : 'readonly-editor',
+ 'disabled' : 'disabled-editor',
+ 'hidden' : 'hidden-editor',
+ 'clearable' : 'clearable-editor',
+ 'editable' : 'editable-editor',
+ 'showPassword' : 'showPassword-editor',
+ 'textContent' : 'textContent-editor',
+ 'textAlign' : 'textAlign-editor',
+ 'fontSize' : 'fontSize-editor',
+ 'preWrap' : 'preWrap-editor',
+ 'htmlContent' : 'htmlContent-editor',
+ 'format' : 'format-editor',
+ 'valueFormat' : 'valueFormat-editor',
+ 'filterable' : 'filterable-editor',
+ 'allowCreate' : 'allowCreate-editor',
+ 'remote' : 'remote-editor',
+ 'automaticDropdown' : 'automaticDropdown-editor',
+ 'checkStrictly' : 'checkStrictly-editor',
+ 'showAllLevels' : 'showAllLevels-editor',
+ 'multiple' : 'multiple-editor',
+ 'multipleLimit' : 'multipleLimit-editor',
+ 'contentPosition' : 'contentPosition-editor',
+ 'optionItems' : 'optionItems-editor',
+ 'uploadURL' : 'uploadURL-editor',
+ 'uploadTip' : 'uploadTip-editor',
+ 'withCredentials' : 'withCredentials-editor',
+ 'multipleSelect' : 'multipleSelect-editor',
+ 'limit' : 'limit-editor',
+ 'fileMaxSize' : 'fileMaxSize-editor',
+ 'fileTypes' : 'fileTypes-editor',
+ 'customClass' : 'customClass-editor',
+
+ //容器
+ 'showBlankRow' : 'showBlankRow-editor',
+ 'showRowNumber' : 'showRowNumber-editor',
+ 'cellWidth' : 'cellWidth-editor',
+ 'cellHeight' : 'cellHeight-editor',
+ 'colHeight' : 'colHeight-editor',
+ 'wordBreak' : 'wordBreak-editor',
+ 'gutter' : 'gutter-editor',
+ 'responsive' : 'responsive-editor',
+ 'span' : 'span-editor',
+ 'offset' : 'offset-editor',
+ 'push' : 'push-editor',
+ 'pull' : 'pull-editor',
+
+}
+
+const ADVANCED_PROPERTIES = {
+ 'min' : 'min-editor',
+ 'max' : 'max-editor',
+ 'precision' : 'precision-editor',
+ 'step' : 'step-editor',
+ 'controlsPosition' : 'controlsPosition-editor',
+ 'minLength' : 'minLength-editor',
+ 'maxLength' : 'maxLength-editor',
+ 'showWordLimit' : 'showWordLimit-editor',
+ 'prefixIcon' : 'prefixIcon-editor',
+ 'suffixIcon' : 'suffixIcon-editor',
+ 'switchWidth' : 'switchWidth-editor',
+ 'activeText' : 'activeText-editor',
+ 'inactiveText' : 'inactiveText-editor',
+ 'activeColor' : 'activeColor-editor',
+ 'inactiveColor' : 'inactiveColor-editor',
+ 'lowThreshold' : 'lowThreshold-editor',
+ 'highThreshold' : 'highThreshold-editor',
+ 'allowHalf' : 'allowHalf-editor',
+ 'showText' : 'showText-editor',
+ 'showScore' : 'showScore-editor',
+ 'range' : 'range-editor',
+ 'vertical' : 'vertical-editor',
+ 'plain' : 'plain-editor',
+ 'round' : 'round-editor',
+ 'circle' : 'circle-editor',
+ 'icon' : 'icon-editor',
+ 'labelIconClass' : 'labelIconClass-editor',
+ 'labelIconPosition' : 'labelIconPosition-editor',
+ 'labelTooltip' : 'labelTooltip-editor',
+ 'appendButton' : 'appendButton-editor',
+ 'appendButtonDisabled': 'appendButtonDisabled-editor',
+ 'buttonIcon' : 'buttonIcon-editor',
+
+}
+
+const EVENT_PROPERTIES = {
+ //字段
+ 'onCreated' : 'onCreated-editor',
+ 'onMounted' : 'onMounted-editor',
+ 'onClick' : 'onClick-editor',
+ 'onInput' : 'onInput-editor',
+ 'onChange' : 'onChange-editor',
+ 'onFocus' : 'onFocus-editor',
+ 'onBlur' : 'onBlur-editor',
+ 'onRemoteQuery' : 'onRemoteQuery-editor',
+ 'onBeforeUpload' : 'onBeforeUpload-editor',
+ 'onUploadSuccess' : 'onUploadSuccess-editor',
+ 'onUploadError' : 'onUploadError-editor',
+ 'onFileRemove' : 'onFileRemove-editor',
+ 'onValidate' : 'onValidate-editor',
+ 'onAppendButtonClick': 'onAppendButtonClick-editor',
+
+ //容器
+ 'onSubFormRowAdd' : 'onSubFormRowAdd-editor',
+ 'onSubFormRowInsert': 'onSubFormRowInsert-editor',
+ 'onSubFormRowDelete': 'onSubFormRowDelete-editor',
+ 'onSubFormRowChange': 'onSubFormRowChange-editor',
+
+}
+
+/**
+ * 注册组件常见属性
+ * 如属性编辑器的组件名称propEditorName设置为null,则不显示该属性编辑器!!
+ * @param uniquePropName 属性名称(保证名称唯一,不跟其他组件属性冲突)
+ * @param propEditorName 对应属性编辑器的组件名称
+ */
+export function registerCommonProperty(uniquePropName, propEditorName) {
+ COMMON_PROPERTIES[uniquePropName] = propEditorName
+}
+
+/**
+ * 注册组件高级属性
+ * 如属性编辑器的组件名称propEditorName设置为null,则不显示该属性编辑器!!
+ * @param uniquePropName 属性名称(保证名称唯一,不跟其他组件属性冲突)
+ * @param propEditorName 对应属性编辑器的组件名称
+ */
+export function registerAdvancedProperty(uniquePropName, propEditorName) {
+ ADVANCED_PROPERTIES[uniquePropName] = propEditorName
+}
+
+/**
+ * 注册组件事件属性
+ * 如属性编辑器的组件名称propEditorName设置为null,则不显示该属性编辑器!!
+ * @param uniquePropName 属性名称(保证名称唯一,不跟其他组件属性冲突)
+ * @param propEditorName 对应属性编辑器的组件名称
+ */
+export function registerEventProperty(uniquePropName, propEditorName) {
+ EVENT_PROPERTIES[uniquePropName] = propEditorName
+}
+
+/**
+ * 判断属性是否已注册
+ * @param uniquePropName 属性名称(保证名称唯一,不跟其他组件属性冲突)
+ */
+export function propertyRegistered(uniquePropName) {
+ return !!COMMON_PROPERTIES[uniquePropName] || !!ADVANCED_PROPERTIES[uniquePropName] || !!EVENT_PROPERTIES[uniquePropName]
+}
+
+/**
+ * 注册常见属性对应的属性编辑器
+ * @param uniquePropName
+ * @param propEditorName
+ * @param editorComponent
+ */
+export function registerCPEditor(uniquePropName, propEditorName, editorComponent) {
+ Vue.component(propEditorName, editorComponent)
+ registerCommonProperty(uniquePropName, propEditorName)
+}
+
+/**
+ * 注册高级属性对应的属性编辑器
+ * @param uniquePropName
+ * @param propEditorName
+ * @param editorComponent
+ */
+export function registerAPEditor(uniquePropName, propEditorName, editorComponent) {
+ Vue.component(propEditorName, editorComponent)
+ registerAdvancedProperty(uniquePropName, propEditorName)
+}
+
+/**
+ * 注册事件属性对应的属性编辑器
+ * @param uniquePropName
+ * @param propEditorName
+ * @param editorComponent
+ */
+export function registerEPEditor(uniquePropName, propEditorName, editorComponent) {
+ Vue.component(propEditorName, editorComponent)
+ registerEventProperty(uniquePropName, propEditorName)
+}
+
+export default {
+ COMMON_PROPERTIES,
+ ADVANCED_PROPERTIES,
+ EVENT_PROPERTIES
+}
+
+
diff --git a/src/components/form-designer/toolbar-panel/index.vue b/src/components/form-designer/toolbar-panel/index.vue
new file mode 100644
index 0000000..42b93a2
--- /dev/null
+++ b/src/components/form-designer/toolbar-panel/index.vue
@@ -0,0 +1,886 @@
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/widget-panel/index.vue b/src/components/form-designer/widget-panel/index.vue
new file mode 100644
index 0000000..7b6dc89
--- /dev/null
+++ b/src/components/form-designer/widget-panel/index.vue
@@ -0,0 +1,386 @@
+
+
+
+
+
+
+ {{i18nt('designer.componentLib')}}
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${ctn.type}`, `extension.widgetLabel.${ctn.type}`)}}
+
+
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)}}
+
+
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)}}
+
+
+
+
+
+
+
+
+
+ {{i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)}}
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('designer.formLib')}}
+
+
+
+
+
+
+
+
+ #{{idx+1}} {{ft.title}}
+
+ {{i18nt('designer.hint.loadFormTemplate')}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/widget-panel/templatesConfig.js b/src/components/form-designer/widget-panel/templatesConfig.js
new file mode 100644
index 0000000..bf67a61
--- /dev/null
+++ b/src/components/form-designer/widget-panel/templatesConfig.js
@@ -0,0 +1,58 @@
+export const formTemplates = [
+ {
+ title: '单列表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t1.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json1.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '多列表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t2.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json2.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '分组表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t3.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json3.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '标签页表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t4.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json4.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '主从表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t5.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json5.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '响应式表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t6.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json6.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '问卷调查表',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t7.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json7.txt',
+ description: '表单模板详细说明...'
+ },
+
+ {
+ title: '固定表格表单',
+ imgUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/t8.png',
+ jsonUrl: 'https://ks3-cn-beijing.ksyuncs.com/vform-static/form-samples/json8.txt',
+ description: '表单模板详细说明...'
+ },
+
+]
diff --git a/src/components/form-designer/widget-panel/widgetsConfig.js b/src/components/form-designer/widget-panel/widgetsConfig.js
new file mode 100644
index 0000000..5c986e6
--- /dev/null
+++ b/src/components/form-designer/widget-panel/widgetsConfig.js
@@ -0,0 +1,934 @@
+
+export const containers = [
+ {
+ type: 'grid',
+ category: 'container',
+ icon: 'grid',
+ cols: [],
+ options: {
+ name: '',
+ hidden: false,
+ gutter: 12,
+ colHeight: null, //栅格列统一高度属性,用于解决栅格列设置响应式布局浮动后被挂住的问题!!
+ customClass: '', //自定义css类名
+ }
+ },
+
+ {
+ type: 'table',
+ category: 'container',
+ icon: 'table',
+ rows: [],
+ options: {
+ name: '',
+ hidden: false,
+ customClass: '', //自定义css类名
+ }
+ },
+
+ {
+ type: 'tab',
+ category: 'container',
+ icon: 'tab',
+ displayType: 'border-card',
+ tabs: [],
+ options: {
+ name: '',
+ hidden: false,
+ customClass: '', //自定义css类名
+ }
+ },
+
+ {
+ type: 'grid-col',
+ category: 'container',
+ icon: 'grid-col',
+ internal: true,
+ widgetList: [],
+ options: {
+ name: '',
+ hidden: false,
+ span: 12,
+ offset: 0,
+ push: 0,
+ pull: 0,
+ responsive: false, //是否开启响应式布局
+ md: 12,
+ sm: 12,
+ xs: 12,
+ customClass: '', //自定义css类名
+ }
+ },
+
+ {
+ type: 'table-cell',
+ category: 'container',
+ icon: 'table-cell',
+ internal: true,
+ widgetList: [],
+ merged: false,
+ options: {
+ name: '',
+ cellWidth: '',
+ cellHeight: '',
+ colspan: 1,
+ rowspan: 1,
+ wordBreak: false, //是否自动换行
+ customClass: '', //自定义css类名
+ }
+ },
+
+ {
+ type: 'tab-pane',
+ category: 'container',
+ icon: 'tab-pane',
+ internal: true,
+ widgetList: [],
+ options: {
+ name: '',
+ label: '',
+ hidden: false,
+ active: false,
+ disabled: false,
+ customClass: '', //自定义css类名
+ }
+ },
+
+
+]
+
+export const basicFields = [
+ {
+ type: 'input',
+ icon: 'text-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ type: 'text',
+ defaultValue: '',
+ placeholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ readonly: false,
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ showPassword: false,
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ minLength: null,
+ maxLength: null,
+ showWordLimit: false,
+ prefixIcon: '',
+ suffixIcon: '',
+ appendButton: false,
+ appendButtonDisabled: false,
+ buttonIcon: 'el-icon-search',
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onInput: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ onAppendButtonClick: '',
+ },
+ },
+
+ {
+ type: 'textarea',
+ icon: 'textarea-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ rows: 3,
+ defaultValue: '',
+ placeholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ readonly: false,
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ minLength: null,
+ maxLength: null,
+ showWordLimit: false,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onInput: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'number',
+ icon: 'number-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: 0,
+ placeholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ min: -100000000000,
+ max: 100000000000,
+ precision: 0,
+ step: 1,
+ controlsPosition: 'right',
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'radio',
+ icon: 'radio-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: null,
+ columnWidth: '200px',
+ size: '',
+ displayStyle: 'inline',
+ buttonStyle: false,
+ border: false,
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ optionItems: [
+ {label: 'radio 1', value: 1},
+ {label: 'radio 2', value: 2},
+ {label: 'radio 3', value: 3},
+ ],
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'checkbox',
+ icon: 'checkbox-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: [],
+ columnWidth: '200px',
+ size: '',
+ displayStyle: 'inline',
+ buttonStyle: false,
+ border: false,
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ optionItems: [
+ {label: 'check 1', value: 1},
+ {label: 'check 2', value: 2},
+ {label: 'check 3', value: 3},
+ ],
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'select',
+ icon: 'select-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: '',
+ placeholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ filterable: false,
+ allowCreate: false,
+ remote: false,
+ automaticDropdown: false, //自动下拉
+ multiple: false,
+ multipleLimit: 0,
+ optionItems: [
+ {label: 'select 1', value: 1},
+ {label: 'select 2', value: 2},
+ {label: 'select 3', value: 3},
+ ],
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onRemoteQuery: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'time',
+ icon: 'time-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: null,
+ placeholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ readonly: false,
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ editable: false,
+ format: 'HH:mm:ss', //时间格式
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'time-range',
+ icon: 'time-range-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: null,
+ startPlaceholder: '',
+ endPlaceholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ readonly: false,
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ editable: false,
+ format: 'HH:mm:ss', //时间格式
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'date',
+ icon: 'date-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ type: 'date',
+ defaultValue: null,
+ placeholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ readonly: false,
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ editable: false,
+ format: 'yyyy-MM-dd', //日期显示格式
+ valueFormat: 'yyyy-MM-dd', //日期对象格式
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'date-range',
+ icon: 'date-range-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ type: 'daterange',
+ defaultValue: null,
+ startPlaceholder: '',
+ endPlaceholder: '',
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ readonly: false,
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ editable: false,
+ format: 'yyyy-MM-dd', //日期显示格式
+ valueFormat: 'yyyy-MM-dd', //日期对象格式
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'switch',
+ icon: 'switch-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: null,
+ columnWidth: '200px',
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ switchWidth: 40,
+ activeText: '',
+ inactiveText: '',
+ activeColor: null,
+ inactiveColor: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'rate',
+ icon: 'rate-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: null,
+ columnWidth: '200px',
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ max: 5,
+ lowThreshold: 2,
+ highThreshold: 4,
+ allowHalf: false,
+ showText: false,
+ showScore: false,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'color',
+ icon: 'color-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: null,
+ columnWidth: '200px',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'slider',
+ icon: 'slider-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ columnWidth: '200px',
+ showStops: true,
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ validation: '',
+ validationHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ min: 0,
+ max: 100,
+ step: 10,
+ range: false,
+ //vertical: false,
+ height: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'static-text',
+ icon: 'static-text',
+ formItemFlag: false,
+ options: {
+ name: '',
+ columnWidth: '200px',
+ hidden: false,
+ textContent: 'static text',
+ textAlign: 'left',
+ fontSize: '13px',
+ preWrap: false, //是否自动换行
+ //-------------------
+ customClass: '', //自定义css类名
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ },
+ },
+
+ {
+ type: 'html-text',
+ icon: 'html-text',
+ formItemFlag: false,
+ options: {
+ name: '',
+ columnWidth: '200px',
+ hidden: false,
+ htmlContent: 'html text',
+ //-------------------
+ customClass: '', //自定义css类名
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ },
+ },
+
+ {
+ type: 'button',
+ icon: 'button',
+ formItemFlag: false,
+ options: {
+ name: '',
+ label: '',
+ columnWidth: '200px',
+ size: '',
+ displayStyle: 'block',
+ disabled: false,
+ hidden: false,
+ type: '',
+ plain: false,
+ round: false,
+ circle: false,
+ icon: null,
+ //-------------------
+ customClass: '', //自定义css类名
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onClick: '',
+ },
+ },
+
+ {
+ type: 'divider',
+ icon: 'divider',
+ formItemFlag: false,
+ options: {
+ name: '',
+ label: '',
+ columnWidth: '200px',
+ direction: 'horizontal',
+ contentPosition: 'center',
+ hidden: false,
+ //-------------------
+ customClass: '', //自定义css类名
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ },
+ },
+
+ //
+
+]
+
+export const advancedFields = [
+ {
+ type: 'picture-upload',
+ icon: 'picture-upload-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ labelWidth: null,
+ labelHidden: false,
+ columnWidth: '200px',
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ customRule: '',
+ customRuleHint: '',
+ //-------------------
+ uploadURL: '',
+ uploadTip: '',
+ withCredentials: false,
+ multipleSelect: false,
+ showFileList: true,
+ limit: 3,
+ fileMaxSize: 5, //MB
+ fileTypes: ['jpg', 'jpeg', 'png'],
+ //headers: [],
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onBeforeUpload: '',
+ onUploadSuccess: '',
+ onUploadError: '',
+ onFileRemove: '',
+ onValidate: '',
+ //onFileChange: '',
+ },
+
+ },
+
+ {
+ type: 'file-upload',
+ icon: 'file-upload-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ labelWidth: null,
+ labelHidden: false,
+ columnWidth: '200px',
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ customRule: '',
+ customRuleHint: '',
+ //-------------------
+ uploadURL: '',
+ uploadTip: '',
+ withCredentials: false,
+ multipleSelect: false,
+ showFileList: true,
+ limit: 3,
+ fileMaxSize: 5, //MB
+ fileTypes: ['doc', 'docx', 'xls', 'xlsx'],
+ //headers: [],
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onBeforeUpload: '',
+ onUploadSuccess: '',
+ onUploadError: '',
+ onFileRemove: '',
+ onValidate: '',
+ //onFileChange: '',
+ },
+ },
+
+ {
+ type: 'rich-editor',
+ icon: 'rich-editor-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ placeholder: '',
+ labelWidth: null,
+ labelHidden: false,
+ columnWidth: '200px',
+ disabled: false,
+ hidden: false,
+ required: false,
+ requiredHint: '',
+ customRule: '',
+ customRuleHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ minLength: null,
+ maxLength: null,
+ showWordLimit: false,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onValidate: '',
+ },
+ },
+
+ {
+ type: 'cascader',
+ icon: 'cascader-field',
+ formItemFlag: true,
+ options: {
+ name: '',
+ label: '',
+ labelAlign: '',
+ defaultValue: '',
+ placeholder: '',
+ size: '',
+ labelWidth: null,
+ labelHidden: false,
+ columnWidth: '200px',
+ disabled: false,
+ hidden: false,
+ clearable: true,
+ filterable: false,
+ multiple: false,
+ checkStrictly: false, //可选择任意一级选项,默认不开启
+ showAllLevels: true, //显示完整路径
+ optionItems: [
+ {label: 'select 1', value: 1, children: [{label: 'child 1', value: 11}]},
+ {label: 'select 2', value: 2},
+ {label: 'select 3', value: 3},
+ ],
+ required: false,
+ requiredHint: '',
+ customRule: '',
+ customRuleHint: '',
+ //-------------------
+ customClass: '', //自定义css类名
+ labelIconClass: null,
+ labelIconPosition: 'rear',
+ labelTooltip: null,
+ //-------------------
+ onCreated: '',
+ onMounted: '',
+ onChange: '',
+ onFocus: '',
+ onBlur: '',
+ onValidate: '',
+ },
+ },
+
+]
+
+export const customFields = [
+
+]
+
+export function addContainerWidgetSchema(containerSchema) {
+ containers.push(containerSchema)
+}
+
+export function addBasicFieldSchema(fieldSchema) {
+ basicFields.push(fieldSchema)
+}
+
+export function addAdvancedFieldSchema(fieldSchema) {
+ advancedFields.push(fieldSchema)
+}
+
+export function addCustomWidgetSchema(widgetSchema) {
+ customFields.push(widgetSchema)
+}
diff --git a/src/components/form-render/container-item/container-item-wrapper.vue b/src/components/form-render/container-item/container-item-wrapper.vue
new file mode 100644
index 0000000..288f792
--- /dev/null
+++ b/src/components/form-render/container-item/container-item-wrapper.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/container-item/containerItemMixin.js b/src/components/form-render/container-item/containerItemMixin.js
new file mode 100644
index 0000000..c73e45d
--- /dev/null
+++ b/src/components/form-render/container-item/containerItemMixin.js
@@ -0,0 +1,221 @@
+import { traverseFieldWidgetsOfContainer } from "@/utils/util";
+
+export default {
+ inject: ['getGlobalDsv'],
+ computed: {
+ customClass() {
+ return this.widget.options.customClass || ''
+ },
+
+ formModel: {
+ cache: false,
+ get() {
+ return this.globalModel.formModel
+ }
+ },
+
+ },
+
+ mounted() {
+ this.callSetHidden()
+ },
+
+ methods: {
+ unregisterFromRefList() { //销毁容器组件时注销组件ref
+ if ((this.refList !== null) && !!this.widget.options.name) {
+ let oldRefName = this.widget.options.name
+ delete this.refList[oldRefName]
+ }
+ },
+
+ /* 主动触发setHidden()方法,以清空被隐藏容器内字段组件的校验规则!! */
+ callSetHidden() {
+ if (this.widget.options.hidden === true) {
+ this.setHidden(true)
+ }
+ },
+
+ //--------------------- 以下为组件支持外部调用的API方法 begin ------------------//
+ /* 提示:用户可自行扩充这些方法!!! */
+
+ setHidden(flag) {
+ this.widget.options.hidden = flag
+
+ /* 容器被隐藏后,需要同步清除容器内部字段组件的校验规则 */
+ let clearRulesFn = (fieldWidget) => {
+ let fwName = fieldWidget.options.name
+ let fwRef = this.getWidgetRef(fwName)
+ if (flag && !!fwRef && !!fwRef.clearFieldRules) {
+ fwRef.clearFieldRules()
+ }
+
+ if (!flag && !!fwRef && !!fwRef.buildFieldRules) {
+ fwRef.buildFieldRules()
+ }
+ }
+
+ traverseFieldWidgetsOfContainer(this.widget, clearRulesFn)
+ },
+
+ activeTab(tabIndex) { //tabIndex从0计数
+ if ((tabIndex >= 0) && (tabIndex < this.widget.tabs.length)) {
+ this.widget.tabs.forEach((tp, idx) => {
+ tp.options.active = idx === tabIndex
+ if (idx === tabIndex) {
+ this.activeTabName = tp.options.name
+ }
+ })
+ }
+ },
+
+ disableTab(tabIndex) {
+ if ((tabIndex >= 0) && (tabIndex < this.widget.tabs.length)) {
+ this.widget.tabs[tabIndex].options.disabled = true
+ }
+ },
+
+ enableTab(tabIndex) {
+ if ((tabIndex >= 0) && (tabIndex < this.widget.tabs.length)) {
+ this.widget.tabs[tabIndex].options.disabled = false
+ }
+ },
+
+ hideTab(tabIndex) {
+ if ((tabIndex >= 0) && (tabIndex < this.widget.tabs.length)) {
+ this.widget.tabs[tabIndex].options.hidden = true
+ }
+ },
+
+ showTab(tabIndex) {
+ if ((tabIndex >= 0) && (tabIndex < this.widget.tabs.length)) {
+ this.widget.tabs[tabIndex].options.hidden = false
+ }
+ },
+
+ setWidgetOption(optionName, optionValue) { //通用组件选项修改API
+ if (this.widget.options.hasOwnProperty(optionName)) {
+ this.widget.options[optionName] = optionValue
+ }
+ },
+
+ /**
+ * 获取子表单的行数
+ */
+ getSubFormRowCount() {
+ return !this.rowIdData ? 0 : this.rowIdData.length
+ },
+
+ disableSubFormRow(rowIndex) {
+ this.widget.widgetList.forEach(subWidget => {
+ let swRefName = subWidget.options.name + '@row' + this.rowIdData[rowIndex]
+ let foundSW = this.getWidgetRef(swRefName)
+ if (!!foundSW) {
+ foundSW.setDisabled(true)
+ }
+ })
+ },
+
+ enableSubFormRow(rowIndex) {
+ this.widget.widgetList.forEach(subWidget => {
+ let swRefName = subWidget.options.name + '@row' + this.rowIdData[rowIndex]
+ let foundSW = this.getWidgetRef(swRefName)
+ if (!!foundSW) {
+ foundSW.setDisabled(false)
+ }
+ })
+ },
+
+ disableSubForm() {
+ if (this.rowIdData.length > 0) {
+ this.rowIdData.forEach((dataRow, rIdx) => {
+ this.disableSubFormRow(rIdx)
+ })
+ }
+
+ //禁用3个操作按钮
+ this.actionDisabled = true
+ },
+
+ enableSubForm() {
+ if (this.rowIdData.length > 0) {
+ this.rowIdData.forEach((dataRow, rIdx) => {
+ this.enableSubFormRow(rIdx)
+ })
+ }
+
+ //启用3个操作按钮
+ this.actionDisabled = false
+ },
+
+ resetSubForm() { //重置subForm数据为空
+ if (this.widget.type === 'sub-form') {
+ let subFormModel = this.formModel[this.widget.options.name]
+ if (!!subFormModel) {
+ subFormModel.splice(0, subFormModel.length)
+ this.rowIdData.splice(0, this.rowIdData.length)
+ }
+
+ if (this.widget.options.showBlankRow) {
+ this.addSubFormRow()
+ }
+ }
+ },
+
+ getSubFormValues(needValidation = true) {
+ if (this.widget.type === 'sub-form') {
+ //TODO: 逐行校验子表单!暂未实现!!
+ return this.formModel[this.widget.options.name]
+ } else {
+ this.$message.error(this.i18nt('render.hint.nonSubFormType'))
+ }
+ },
+
+ setSubFormValues(subFormValues) {
+ //TODO: 待实现!!
+ },
+
+ // validateField(fieldName) { //逐行校验子表单字段
+ // //TODO:
+ // },
+ //
+ // validateSubForm() { //逐行校验子表单全部字段
+ // //TODO:
+ // },
+
+ /**
+ * 动态增加自定义css样式
+ * @param className
+ */
+ addCssClass(className) {
+ if (!this.widget.options.customClass) {
+ this.widget.options.customClass = [className]
+ } else {
+ this.widget.options.customClass.push(className)
+ }
+ },
+
+ /**
+ * 动态移除自定义css样式
+ * @param className
+ */
+ removeCssClass(className) {
+ if (!this.widget.options.customClass) {
+ return
+ }
+
+ let foundIdx = -1
+ this.widget.options.customClass.map((cc, idx) => {
+ if (cc === className) {
+ foundIdx = idx
+ }
+ })
+ if (foundIdx > -1) {
+ this.widget.options.customClass.splice(foundIdx, 1)
+ }
+ },
+
+ //--------------------- 以上为组件支持外部调用的API方法 end ------------------//
+
+ },
+
+}
diff --git a/src/components/form-render/container-item/grid-col-item.vue b/src/components/form-render/container-item/grid-col-item.vue
new file mode 100644
index 0000000..280138e
--- /dev/null
+++ b/src/components/form-render/container-item/grid-col-item.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{i18nt('render.hint.blankCellContent')}}
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/container-item/grid-item.vue b/src/components/form-render/container-item/grid-item.vue
new file mode 100644
index 0000000..5488fb9
--- /dev/null
+++ b/src/components/form-render/container-item/grid-item.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/container-item/index.js b/src/components/form-render/container-item/index.js
new file mode 100644
index 0000000..ac67666
--- /dev/null
+++ b/src/components/form-render/container-item/index.js
@@ -0,0 +1,24 @@
+import Vue from "vue"
+
+const requireComponent = require.context('./', false, /\w+\.vue$/)
+
+/**
+ * 容器组件时递归组件,且内部可以嵌套其他容器,局部注册会找不到组件,必须注册为全局组件,原因不明?!
+ * begin
+ *
+let comps = {}
+
+requireComponent.keys().map(fileName => {
+ let comp = requireComponent(fileName).default;
+ comps[comp.name] = comp
+})
+
+export default comps;
+
+end */
+
+/* 全局注册!! */
+requireComponent.keys().map(fileName => {
+ let comp = requireComponent(fileName).default;
+ Vue.component(comp.name, comp)
+})
diff --git a/src/components/form-render/container-item/sub-form-item.vue b/src/components/form-render/container-item/sub-form-item.vue
new file mode 100644
index 0000000..12193de
--- /dev/null
+++ b/src/components/form-render/container-item/sub-form-item.vue
@@ -0,0 +1,417 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/container-item/tab-item.vue b/src/components/form-render/container-item/tab-item.vue
new file mode 100644
index 0000000..2a5267b
--- /dev/null
+++ b/src/components/form-render/container-item/tab-item.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/container-item/table-cell-item.vue b/src/components/form-render/container-item/table-cell-item.vue
new file mode 100644
index 0000000..300b344
--- /dev/null
+++ b/src/components/form-render/container-item/table-cell-item.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/src/components/form-render/container-item/table-item.vue b/src/components/form-render/container-item/table-item.vue
new file mode 100644
index 0000000..a694839
--- /dev/null
+++ b/src/components/form-render/container-item/table-item.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/index.vue b/src/components/form-render/index.vue
new file mode 100644
index 0000000..d43b790
--- /dev/null
+++ b/src/components/form-render/index.vue
@@ -0,0 +1,721 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-render/refMixin.js b/src/components/form-render/refMixin.js
new file mode 100644
index 0000000..e129ea1
--- /dev/null
+++ b/src/components/form-render/refMixin.js
@@ -0,0 +1,30 @@
+export default {
+ methods: {
+ initRefList() {
+ if ((this.refList !== null) && !!this.widget.options.name) {
+ this.refList[this.widget.options.name] = this
+ }
+ },
+
+ getWidgetRef(widgetName, showError = false) {
+ let foundRef = this.refList[widgetName]
+ if (!foundRef && !!showError) {
+ this.$message.error(this.i18nt('render.hint.refNotFound') + widgetName)
+ }
+ return foundRef
+ },
+
+ getFormRef() { /* 获取VFrom引用,必须在VForm组件created之后方可调用 */
+ return this.refList['v_form_ref']
+ },
+
+ getComponentByContainer(con) {
+ if (con.type === 'grid') { //grid-item跟VueGridLayout全局注册组件重名,故特殊处理!!
+ return 'vf-grid-item'
+ }
+
+ return con.type + '-item'
+ },
+
+ }
+}
diff --git a/src/components/svg-icon/index.vue b/src/components/svg-icon/index.vue
new file mode 100644
index 0000000..8fc1294
--- /dev/null
+++ b/src/components/svg-icon/index.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
diff --git a/src/components/v-table/index.vue b/src/components/v-table/index.vue
new file mode 100644
index 0000000..371330d
--- /dev/null
+++ b/src/components/v-table/index.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/extension/extension-helper.js b/src/extension/extension-helper.js
new file mode 100644
index 0000000..6f6c1f4
--- /dev/null
+++ b/src/extension/extension-helper.js
@@ -0,0 +1,23 @@
+import {
+ addContainerWidgetSchema,
+ addBasicFieldSchema,
+ addAdvancedFieldSchema,
+ addCustomWidgetSchema
+} from '@/components/form-designer/widget-panel/widgetsConfig'
+import {
+ registerCommonProperty,
+ registerAdvancedProperty,
+ registerEventProperty
+} from '@/components/form-designer/setting-panel/propertyRegister'
+
+
+export default {
+ addContainerWidgetSchema,
+ addBasicFieldSchema,
+ addAdvancedFieldSchema,
+ addCustomWidgetSchema,
+
+ registerCommonProperty,
+ registerAdvancedProperty,
+ registerEventProperty,
+}
diff --git a/src/extension/extension-loader.js b/src/extension/extension-loader.js
new file mode 100644
index 0000000..7b67c8c
--- /dev/null
+++ b/src/extension/extension-loader.js
@@ -0,0 +1,116 @@
+import Vue from 'vue'
+
+import {
+ addContainerWidgetSchema,
+ addCustomWidgetSchema
+} from '@/components/form-designer/widget-panel/widgetsConfig'
+import * as PERegister from '@/components/form-designer/setting-panel/propertyRegister'
+import * as PEFactory from '@/components/form-designer/setting-panel/property-editor-factory'
+
+import {cardSchema} from "@/extension/samples/extension-schema"
+import CardWidget from '@/extension/samples/card/card-widget'
+import CardItem from '@/extension/samples/card/card-item'
+import {registerCWGenerator} from '@/utils/sfc-generator'
+import {cardTemplateGenerator} from '@/extension/samples/extension-sfc-generator'
+
+import {alertSchema} from "@/extension/samples/extension-schema"
+import AlertWidget from '@/extension/samples/alert/alert-widget'
+import {registerFWGenerator} from '@/utils/sfc-generator'
+import {alertTemplateGenerator} from '@/extension/samples/extension-sfc-generator'
+
+export const loadExtension = function () {
+
+ /**
+ * 加载容器组件步骤:
+ * 1. 加载组件Json Schema;
+ * 2. 全局注册容器组件,容器组件有两种状态——设计期和运行期,故需要注册两个组件;
+ * 3. 全局注册属性编辑器组件(基本属性、高级属性、事件属性);
+ * 4. 注册容器组件的代码生成器;
+ * 5. 加载完毕。
+ */
+ addContainerWidgetSchema(cardSchema) //加载组件Json Schema
+ /* -------------------------------------------------- */
+ Vue.component(CardWidget.name, CardWidget) //注册设计期的容器组件
+ Vue.component(CardItem.name, CardItem) //注册运行期的容器组件
+ /* -------------------------------------------------- */
+ PERegister.registerCPEditor('card-folded', 'card-folded-editor',
+ PEFactory.createBooleanEditor('folded', 'extension.setting.cardFolded'))
+
+ PERegister.registerCPEditor('card-showFold', 'card-showFold-editor',
+ PEFactory.createBooleanEditor('showFold', 'extension.setting.cardShowFold'))
+
+ PERegister.registerCPEditor('card-cardWidth', 'card-cardWidth-editor',
+ PEFactory.createInputTextEditor('cardWidth', 'extension.setting.cardWidth'))
+
+ let shadowOptions = [
+ {label: 'never', value: 'never'},
+ {label: 'hover', value: 'hover'},
+ {label: 'always', value: 'always'},
+ ]
+ PERegister.registerCPEditor('card-shadow', 'card-shadow-editor',
+ PEFactory.createSelectEditor('shadow', 'extension.setting.cardShadow',
+ {optionItems: shadowOptions}))
+ /* -------------------------------------------------- */
+ registerCWGenerator('card', cardTemplateGenerator) //注册容器组件的代码生成器
+ /* -------------------------------------------------- */
+ /* 容器组件加载完毕 end */
+
+ /**
+ * 加载字段组件步骤:
+ * 1. 加载组件Json Schema;
+ * 2. 全局注册字段组件,字段组件设计期和运行期共用,故需要仅需注册一个组件;
+ * 3. 全局注册属性编辑器组件(基本属性、高级属性、事件属性);
+ * 4. 注册字段组件的代码生成器;
+ * 5. 加载完毕。
+ */
+ addCustomWidgetSchema(alertSchema) //加载组件Json Schema
+ /* -------------------------------------------------- */
+ Vue.component(AlertWidget.name, AlertWidget) //注册组件
+ /* -------------------------------------------------- */
+ PERegister.registerCPEditor('alert-title', 'alert-title-editor',
+ PEFactory.createInputTextEditor('title', 'extension.setting.alertTitle'))
+
+ let typeOptions = [
+ {label: 'success', value: 'success'},
+ {label: 'warning', value: 'warning'},
+ {label: 'info', value: 'info'},
+ {label: 'error', value: 'error'},
+ ]
+ // PERegister.registerCPEditor('alert-type', 'alert-type-editor',
+ // PEFactory.createSelectEditor('type', 'extension.setting.alertType',
+ // {optionItems: typeOptions}))
+ /* type属性映射已存在,无须再注册,故只需注册属性编辑器即可!! */
+ Vue.component('alert-type-editor',
+ PEFactory.createSelectEditor('type', 'extension.setting.alertType',
+ {optionItems: typeOptions}))
+
+ PERegister.registerCPEditor('alert-description', 'alert-description-editor',
+ PEFactory.createInputTextEditor('description', 'extension.setting.description'))
+
+ PERegister.registerCPEditor('alert-closable', 'alert-closable-editor',
+ PEFactory.createBooleanEditor('closable', 'extension.setting.closable'))
+
+ PERegister.registerCPEditor('alert-closeText', 'alert-closeText-editor',
+ PEFactory.createInputTextEditor('closeText', 'extension.setting.closeText'))
+
+ PERegister.registerCPEditor('alert-center', 'alert-center-editor',
+ PEFactory.createBooleanEditor('center', 'extension.setting.center'))
+
+ PERegister.registerCPEditor('alert-showIcon', 'alert-showIcon-editor',
+ PEFactory.createBooleanEditor('showIcon', 'extension.setting.showIcon'))
+
+ let effectOptions = [
+ {label: 'light', value: 'light'},
+ {label: 'dark', value: 'dark'},
+ ]
+ PERegister.registerCPEditor('alert-effect', 'alert-effect-editor',
+ PEFactory.createRadioButtonGroupEditor('effect', 'extension.setting.effect',
+ {optionItems: effectOptions}))
+
+ PERegister.registerEPEditor('alert-onClose', 'alert-onClose-editor',
+ PEFactory.createEventHandlerEditor('onClose', []))
+ /* -------------------------------------------------- */
+ registerFWGenerator('alert', alertTemplateGenerator) //注册字段组件的代码生成器
+ /* -------------------------------------------------- */
+ /* 字段组件加载完毕 end */
+}
diff --git a/src/extension/samples/alert/alert-widget.vue b/src/extension/samples/alert/alert-widget.vue
new file mode 100644
index 0000000..f13eb6e
--- /dev/null
+++ b/src/extension/samples/alert/alert-widget.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/extension/samples/card/card-item.vue b/src/extension/samples/card/card-item.vue
new file mode 100644
index 0000000..e80dbf8
--- /dev/null
+++ b/src/extension/samples/card/card-item.vue
@@ -0,0 +1,100 @@
+
+
+
+
+ {{widget.options.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/extension/samples/card/card-widget.vue b/src/extension/samples/card/card-widget.vue
new file mode 100644
index 0000000..f090af5
--- /dev/null
+++ b/src/extension/samples/card/card-widget.vue
@@ -0,0 +1,132 @@
+
+
+
+
+ {{widget.options.label}}
+
+
+ onContainerDragAdd(evt, widget.widgetList)"
+ @update="onContainerDragUpdate" :move="checkContainerMove">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/extension/samples/extension-schema.js b/src/extension/samples/extension-schema.js
new file mode 100644
index 0000000..abdae48
--- /dev/null
+++ b/src/extension/samples/extension-schema.js
@@ -0,0 +1,36 @@
+export const cardSchema = {
+ type: 'card',
+ category: 'container',
+ icon: 'card',
+ widgetList: [],
+ options: {
+ name: '',
+ label: 'card',
+ hidden: false,
+ folded: false,
+ showFold: true,
+ cardWidth: '100%',
+ shadow: 'never',
+ customClass: '',
+ }
+}
+
+export const alertSchema = {
+ type: 'alert',
+ icon: 'alert',
+ formItemFlag: false,
+ options: {
+ name: '',
+ title: 'Good things are coming...',
+ type: 'info',
+ description: '',
+ closable: true,
+ closeText: '',
+ center: true,
+ showIcon: false,
+ effect: 'light',
+ hidden: false,
+ onClose: '',
+ customClass: '',
+ }
+}
diff --git a/src/extension/samples/extension-sfc-generator.js b/src/extension/samples/extension-sfc-generator.js
new file mode 100644
index 0000000..4625c03
--- /dev/null
+++ b/src/extension/samples/extension-sfc-generator.js
@@ -0,0 +1,50 @@
+import {buildClassAttr, buildContainerWidget, buildFieldWidget} from '@/utils/sfc-generator'
+
+export const cardTemplateGenerator = function (cw, formConfig) {
+ const wop = cw.options
+ //const headerAttr = `header="${wop.label}"`
+ const classAttr = buildClassAttr(cw)
+ const styleAttr = !!wop.cardWidth ? `style="{width: ${wop.cardWidth} !important}"` : ''
+ const shadowAttr = `shadow="${wop.shadow}"`
+ const vShowAttr = !!wop.hidden ? `v-show="false"` : ''
+
+ const cardTemplate =
+`
+
+
+ ${wop.label}
+ ${!!wop.showFold ? `` : ''}
+
+ ${
+ cw.widgetList.map(wItem => {
+ if (wItem.category === 'container') {
+ return buildContainerWidget(wItem, formConfig)
+ } else {
+ return buildFieldWidget(wItem, formConfig)
+ }
+ }).join('')
+ }
+
+
`
+
+ return cardTemplate
+}
+
+export const alertTemplateGenerator = function(fw, formConfig) {
+ const wop = fw.options
+ const titleAttr = `title="${wop.title}"`
+ const typeAttr = `type=${wop.type}`
+ const descriptionAttr = !!wop.description ? `description="${wop.description}"` : ''
+ const closableAttr = `:closable="${wop.closable}"`
+ const closeTextAttr = !!wop.closeText ? `close-text="${wop.closeText}"` : ''
+ const centerAttr = `:center="${wop.center}"`
+ const showIconAttr = `:show-icon="${wop.showIcon}"`
+ const effectAttr = `effect="${wop.effect}"`
+
+ const alertTemplate =
+`
+`
+
+ return alertTemplate
+}
diff --git a/src/iconfont/iconfont.css b/src/iconfont/iconfont.css
new file mode 100644
index 0000000..32f87eb
--- /dev/null
+++ b/src/iconfont/iconfont.css
@@ -0,0 +1,29 @@
+@font-face {
+ font-family: "iconfont"; /* Project id */
+ src: url('iconfont.ttf?t=1620643511304') format('truetype');
+}
+
+.iconfont {
+ font-family: "iconfont" !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-insertcolumn:before {
+ content: "\e753";
+}
+
+.icon-insertrow:before {
+ content: "\e754";
+}
+
+.icon-hide:before {
+ content: "\e76b";
+}
+
+.icon-drag:before {
+ content: "\e61d";
+}
+
diff --git a/src/iconfont/iconfont.eot b/src/iconfont/iconfont.eot
new file mode 100644
index 0000000..fc442ca
Binary files /dev/null and b/src/iconfont/iconfont.eot differ
diff --git a/src/iconfont/iconfont.js b/src/iconfont/iconfont.js
new file mode 100644
index 0000000..577219f
--- /dev/null
+++ b/src/iconfont/iconfont.js
@@ -0,0 +1 @@
+!function(e){var t,n,o,l,c,i,a='',d=(d=document.getElementsByTagName("script"))[d.length-1].getAttribute("data-injectcss");if(d&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(e){console&&console.log(e)}}function s(){c||(c=!0,o())}t=function(){var e,t,n,o;(o=document.createElement("div")).innerHTML=a,a=null,(n=o.getElementsByTagName("svg")[0])&&(n.setAttribute("aria-hidden","true"),n.style.position="absolute",n.style.width=0,n.style.height=0,n.style.overflow="hidden",e=n,(t=document.body).firstChild?(o=e,(n=t.firstChild).parentNode.insertBefore(o,n)):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),t()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(o=t,l=e.document,c=!1,(i=function(){try{l.documentElement.doScroll("left")}catch(e){return void setTimeout(i,50)}s()})(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,s())})}(window);
\ No newline at end of file
diff --git a/src/iconfont/iconfont.json b/src/iconfont/iconfont.json
new file mode 100644
index 0000000..92af6b1
--- /dev/null
+++ b/src/iconfont/iconfont.json
@@ -0,0 +1,37 @@
+{
+ "id": "",
+ "name": "",
+ "font_family": "iconfont",
+ "css_prefix_text": "icon-",
+ "description": "",
+ "glyphs": [
+ {
+ "icon_id": "586931",
+ "name": "insert-column",
+ "font_class": "insertcolumn",
+ "unicode": "e753",
+ "unicode_decimal": 59219
+ },
+ {
+ "icon_id": "586932",
+ "name": "insert-row",
+ "font_class": "insertrow",
+ "unicode": "e754",
+ "unicode_decimal": 59220
+ },
+ {
+ "icon_id": "1030072",
+ "name": "hide",
+ "font_class": "hide",
+ "unicode": "e76b",
+ "unicode_decimal": 59243
+ },
+ {
+ "icon_id": "14772710",
+ "name": "drag",
+ "font_class": "drag",
+ "unicode": "e61d",
+ "unicode_decimal": 58909
+ }
+ ]
+}
diff --git a/src/iconfont/iconfont.svg b/src/iconfont/iconfont.svg
new file mode 100644
index 0000000..207c0c9
--- /dev/null
+++ b/src/iconfont/iconfont.svg
@@ -0,0 +1,35 @@
+
+
+
+
diff --git a/src/iconfont/iconfont.ttf b/src/iconfont/iconfont.ttf
new file mode 100644
index 0000000..d7a08f5
Binary files /dev/null and b/src/iconfont/iconfont.ttf differ
diff --git a/src/iconfont/iconfont.woff b/src/iconfont/iconfont.woff
new file mode 100644
index 0000000..330d984
Binary files /dev/null and b/src/iconfont/iconfont.woff differ
diff --git a/src/iconfont/iconfont.woff2 b/src/iconfont/iconfont.woff2
new file mode 100644
index 0000000..aea096e
Binary files /dev/null and b/src/iconfont/iconfont.woff2 differ
diff --git a/src/icons/index.js b/src/icons/index.js
new file mode 100644
index 0000000..4c4c36e
--- /dev/null
+++ b/src/icons/index.js
@@ -0,0 +1,3 @@
+const requireAll = requireContext => requireContext.keys().map(requireContext)
+const req = require.context('./svg', false, /\.svg$/)
+requireAll(req)
diff --git a/src/icons/svg/alert.svg b/src/icons/svg/alert.svg
new file mode 100644
index 0000000..f1182f0
--- /dev/null
+++ b/src/icons/svg/alert.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/button.svg b/src/icons/svg/button.svg
new file mode 100644
index 0000000..174a34a
--- /dev/null
+++ b/src/icons/svg/button.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/card.svg b/src/icons/svg/card.svg
new file mode 100644
index 0000000..0e5404b
--- /dev/null
+++ b/src/icons/svg/card.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/cascader-field.svg b/src/icons/svg/cascader-field.svg
new file mode 100644
index 0000000..ac4ef8e
--- /dev/null
+++ b/src/icons/svg/cascader-field.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/checkbox-field.svg b/src/icons/svg/checkbox-field.svg
new file mode 100644
index 0000000..0f14ae4
--- /dev/null
+++ b/src/icons/svg/checkbox-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/color-field.svg b/src/icons/svg/color-field.svg
new file mode 100644
index 0000000..8dca8c5
--- /dev/null
+++ b/src/icons/svg/color-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/custom-component.svg b/src/icons/svg/custom-component.svg
new file mode 100644
index 0000000..2633507
--- /dev/null
+++ b/src/icons/svg/custom-component.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/data-table.svg b/src/icons/svg/data-table.svg
new file mode 100644
index 0000000..d4e7caf
--- /dev/null
+++ b/src/icons/svg/data-table.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/date-field.svg b/src/icons/svg/date-field.svg
new file mode 100644
index 0000000..54c51f1
--- /dev/null
+++ b/src/icons/svg/date-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/date-range-field.svg b/src/icons/svg/date-range-field.svg
new file mode 100644
index 0000000..66526ba
--- /dev/null
+++ b/src/icons/svg/date-range-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/divider.svg b/src/icons/svg/divider.svg
new file mode 100644
index 0000000..286c16b
--- /dev/null
+++ b/src/icons/svg/divider.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/document.svg b/src/icons/svg/document.svg
new file mode 100644
index 0000000..93668d5
--- /dev/null
+++ b/src/icons/svg/document.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/drag.svg b/src/icons/svg/drag.svg
new file mode 100644
index 0000000..990dce4
--- /dev/null
+++ b/src/icons/svg/drag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/file-upload-field.svg b/src/icons/svg/file-upload-field.svg
new file mode 100644
index 0000000..614405e
--- /dev/null
+++ b/src/icons/svg/file-upload-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/github.svg b/src/icons/svg/github.svg
new file mode 100644
index 0000000..42f596d
--- /dev/null
+++ b/src/icons/svg/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/grid.svg b/src/icons/svg/grid.svg
new file mode 100644
index 0000000..f1b9f1d
--- /dev/null
+++ b/src/icons/svg/grid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/html-text.svg b/src/icons/svg/html-text.svg
new file mode 100644
index 0000000..44b9963
--- /dev/null
+++ b/src/icons/svg/html-text.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/node-tree.svg b/src/icons/svg/node-tree.svg
new file mode 100644
index 0000000..37f45fc
--- /dev/null
+++ b/src/icons/svg/node-tree.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/number-field.svg b/src/icons/svg/number-field.svg
new file mode 100644
index 0000000..19a53a6
--- /dev/null
+++ b/src/icons/svg/number-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/picture-upload-field.svg b/src/icons/svg/picture-upload-field.svg
new file mode 100644
index 0000000..dde3bb4
--- /dev/null
+++ b/src/icons/svg/picture-upload-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/radio-field.svg b/src/icons/svg/radio-field.svg
new file mode 100644
index 0000000..274cb17
--- /dev/null
+++ b/src/icons/svg/radio-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/rate-field.svg b/src/icons/svg/rate-field.svg
new file mode 100644
index 0000000..5852671
--- /dev/null
+++ b/src/icons/svg/rate-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/redo.svg b/src/icons/svg/redo.svg
new file mode 100644
index 0000000..e5ea4a7
--- /dev/null
+++ b/src/icons/svg/redo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/rich-editor-field.svg b/src/icons/svg/rich-editor-field.svg
new file mode 100644
index 0000000..bb540f7
--- /dev/null
+++ b/src/icons/svg/rich-editor-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/section.svg b/src/icons/svg/section.svg
new file mode 100644
index 0000000..9249c99
--- /dev/null
+++ b/src/icons/svg/section.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/select-field.svg b/src/icons/svg/select-field.svg
new file mode 100644
index 0000000..59f83f1
--- /dev/null
+++ b/src/icons/svg/select-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/slider-field.svg b/src/icons/svg/slider-field.svg
new file mode 100644
index 0000000..f8f47dc
--- /dev/null
+++ b/src/icons/svg/slider-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/slot-component.svg b/src/icons/svg/slot-component.svg
new file mode 100644
index 0000000..6adea10
--- /dev/null
+++ b/src/icons/svg/slot-component.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/slot-field.svg b/src/icons/svg/slot-field.svg
new file mode 100644
index 0000000..16e953a
--- /dev/null
+++ b/src/icons/svg/slot-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/static-text.svg b/src/icons/svg/static-text.svg
new file mode 100644
index 0000000..fccdc45
--- /dev/null
+++ b/src/icons/svg/static-text.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/sub-form.svg b/src/icons/svg/sub-form.svg
new file mode 100644
index 0000000..13475d0
--- /dev/null
+++ b/src/icons/svg/sub-form.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/switch-field.svg b/src/icons/svg/switch-field.svg
new file mode 100644
index 0000000..a5ccb46
--- /dev/null
+++ b/src/icons/svg/switch-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/tab.svg b/src/icons/svg/tab.svg
new file mode 100644
index 0000000..b171999
--- /dev/null
+++ b/src/icons/svg/tab.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/table.svg b/src/icons/svg/table.svg
new file mode 100644
index 0000000..8876696
--- /dev/null
+++ b/src/icons/svg/table.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/text-field.svg b/src/icons/svg/text-field.svg
new file mode 100644
index 0000000..7a411c0
--- /dev/null
+++ b/src/icons/svg/text-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/textarea-field.svg b/src/icons/svg/textarea-field.svg
new file mode 100644
index 0000000..967a753
--- /dev/null
+++ b/src/icons/svg/textarea-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/time-field.svg b/src/icons/svg/time-field.svg
new file mode 100644
index 0000000..9ad81e9
--- /dev/null
+++ b/src/icons/svg/time-field.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/svg/time-range-field.svg b/src/icons/svg/time-range-field.svg
new file mode 100644
index 0000000..c5d9413
--- /dev/null
+++ b/src/icons/svg/time-range-field.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/undo.svg b/src/icons/svg/undo.svg
new file mode 100644
index 0000000..032c7ee
--- /dev/null
+++ b/src/icons/svg/undo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/icons/svg/vue-sfc.svg b/src/icons/svg/vue-sfc.svg
new file mode 100644
index 0000000..4bd20bb
--- /dev/null
+++ b/src/icons/svg/vue-sfc.svg
@@ -0,0 +1 @@
+
diff --git a/src/lang/en-US.js b/src/lang/en-US.js
new file mode 100644
index 0000000..d7f2969
--- /dev/null
+++ b/src/lang/en-US.js
@@ -0,0 +1,354 @@
+export default {
+ application: {
+ 'zh-CN': '简体中文',
+ 'en-US': 'English',
+ productTitle: 'Online Form Designer',
+ github: 'GitHub',
+ document: 'Docs',
+ qqGroup: 'WeChat Group',
+ deployment: 'Deployment',
+ subscription: 'Subscription',
+ },
+
+ designer: {
+ componentLib: 'Components',
+ formLib: 'Templates',
+ containerTitle: 'Container',
+ dragHandlerHint: 'drag container or field to layout center',
+ dragAction: 'drag',
+ basicFieldTitle: 'Basic Field',
+ advancedFieldTitle: 'Advanced Field',
+ customFieldTitle: 'Customized Field',
+
+ noWidgetHint: 'Please select a widget from the left list, drag and drop to this container.',
+
+ widgetLabel: {
+ grid: 'Grid',
+ table: 'Table',
+ tab: 'Tab',
+ section: 'Section',
+ 'sub-form': 'SubForm',
+ 'grid-col': 'GridCol',
+ 'table-cell': 'TableCell',
+ 'tab-pane': 'TabPane',
+ 'data-table': 'DataTable',
+
+ input: 'Input',
+ textarea: 'Textarea',
+ number: 'InputNumber',
+ radio: 'Radio',
+ checkbox: 'Checkbox',
+ select: 'Select',
+ time: 'Time',
+ 'time-range': 'Time range',
+ date: 'Date',
+ 'date-range': 'Date range',
+ switch: 'Switch',
+ rate: 'Rate',
+ color: 'ColorPicker',
+ slider: 'Slider',
+ 'static-text': 'Text',
+ 'html-text': 'HTML',
+ button: 'Button',
+ divider: 'Divider',
+
+ 'picture-upload': 'Picture',
+ 'file-upload': 'File',
+ 'rich-editor': 'Rich Editor',
+ cascader: 'Cascader',
+ slot: 'Slot',
+
+ custom: 'Custom Component',
+ },
+
+ hint: {
+ selectParentWidget: 'Select parent of this widget',
+ moveUpWidget: 'Move up this widget',
+ moveDownWidget: 'Move down this widget',
+ cloneWidget: 'Clone this widget',
+ insertRow: 'Insert new row',
+ insertColumn: 'Insert new column',
+ remove: 'Remove this widget',
+ cellSetting: 'Cell setting',
+ dragHandler: 'Drag handler',
+ copyField: 'Copy field widget',
+ onlyFieldWidgetAcceptable: 'Only field widget can be dragged into sub-form',
+ moveUpFirstChildHint: 'First child can not be move up',
+ moveDownLastChildHint: 'Last child can not be move down',
+
+ closePreview: 'Close',
+ copyJson: 'Copy',
+ saveFormJson: 'Save As File',
+ copyVueCode: 'Copy Vue Code',
+ copyHtmlCode: 'Copy HTML Code',
+ copyJsonSuccess: 'Copy succeed',
+ importJsonSuccess: 'Import succeed',
+ copyJsonFail: 'Copy failed',
+ copyVueCodeSuccess: 'Copy succeed',
+ copyVueCodeFail: 'Copy failed',
+ copyHtmlCodeSuccess: 'Copy succeed',
+ copyHtmlCodeFail: 'Copy failed',
+ saveVueCode: 'Save Vue File',
+ saveHtmlCode: 'Save Html File',
+ getFormData: 'Get Data',
+ resetForm: 'Reset',
+ disableForm: 'Disable',
+ enableForm: 'Enable',
+ exportFormData: 'Form Data',
+ copyFormData: 'Copy',
+ saveFormData: 'Save As File',
+ copyVue2SFC: 'Copy Vue2',
+ copyVue3SFC: 'Copy Vue3',
+ copySFCFail: 'Copy failed',
+ copySFCSuccess: 'Copy succeed',
+ saveVue2SFC: 'Save As Vue2',
+ saveVue3SFC: 'Save As Vue3',
+ fileNameForSave: 'File name:',
+ saveFileTitle: 'Save as File',
+ fileNameInputPlaceholder: 'Enter the file name',
+ sampleLoadedSuccess: 'Example loaded successfully',
+ sampleLoadedFail: 'Sample load failed',
+ loadFormTemplate: 'Load This',
+ loadFormTemplateHint: 'Are you sure to load this template?',
+ loadFormTemplateSuccess: 'Load form template success!',
+ loadFormTemplateFailed: 'Load form template failed.',
+ currentNodeCannotBeSelected: 'The current node cannot be selected.',
+
+ widgetSetting: 'Widget Config',
+ formSetting: 'Form Config',
+
+ prompt: 'Prompt',
+ confirm: 'OK',
+ cancel: 'Cancel',
+ import: 'Import',
+ importJsonHint: 'The code to be imported should have the following JSON format.',
+ invalidOptionsData: 'Invalid data of options:',
+ lastPaneCannotBeDeleted: 'The last pane cannot be deleted.',
+ duplicateName: 'Duplicate name: ',
+ nameRequired: 'Name required.',
+
+ numberValidator: 'Number',
+ letterValidator: 'Letter',
+ letterAndNumberValidator: 'LetterAndNumber',
+ mobilePhoneValidator: 'MobilePhone',
+ emailValidator: 'Email',
+ urlValidator: 'URL',
+ noChineseValidator: 'Non-Chinese',
+ chineseValidator: 'Chinese',
+
+ rowspanNotConsistentForMergeEntireRow: 'Cells in this row don\'t have the same rowspan, operation failed.',
+ colspanNotConsistentForMergeEntireColumn: 'Cells in this column don\'t have the same colspan, operation failed.',
+ rowspanNotConsistentForDeleteEntireRow: 'Cells in this row don\'t have the same rowspan, operation failed.',
+ colspanNotConsistentForDeleteEntireColumn: 'Cells in this column don\'t have the same colspan, operation failed.',
+ lastColCannotBeDeleted: 'The last col cannot be deleted.',
+ lastRowCannotBeDeleted: 'The last row cannot be deleted.',
+ },
+
+ toolbar: {
+ undoHint: 'Undo',
+ redoHint: 'Redo',
+ pcLayout: 'PC',
+ padLayout: 'Pad',
+ mobileLayout: 'H5',
+ nodeTreeHint: 'Tree View Of Component Hierarchy',
+ nodeTreeTitle: 'Tree View Of Component Hierarchy',
+ clear: 'Clear',
+ preview: 'Preview',
+ importJson: 'Import',
+ exportJson: 'Export',
+ exportCode: 'Codes',
+ generateCode: 'Generate Code',
+ generateSFC: 'Generate SFC',
+ },
+
+ setting: {
+ basicSetting: 'Basic Setting',
+ attributeSetting: 'Attribute Setting',
+ commonSetting: 'Common Setting',
+ advancedSetting: 'Advanced Setting',
+ eventSetting: 'Event Setting',
+ uniqueName: 'Unique Name',
+ editNameHelp: 'Press enter to confirm the modification',
+ label: 'Label',
+ displayType: 'Type',
+ defaultValue: 'Default Value',
+ placeholder: 'Placeholder',
+ startPlaceholder: 'Start Placeholder',
+ endPlaceholder: 'End Placeholder',
+ widgetColumnWidth: 'Width',
+ widgetSize: 'Size',
+ fontSize: 'Font Size',
+ textAlign: 'Text Align',
+ showStops: 'Show Stops',
+ displayStyle: 'Display Style',
+ inlineLayout: 'inline',
+ blockLayout: 'block',
+ buttonStyle: 'Show As Button',
+ border: 'Show Border',
+ labelWidth: 'Width Of Label',
+ rows: 'Rows',
+ labelHidden: 'Hide Label',
+ required: 'Required',
+ requiredHint: 'Failure Hint',
+ validation: 'Validation',
+ validationHelp: 'Regular expressions supported',
+ validationHint: 'Validation Hint',
+ readonly: 'Readonly',
+ disabled: 'Disabled',
+ hidden: 'Hidden',
+ textContent: 'Text',
+ preWrap: 'Line Wrap',
+ htmlContent: 'HTML',
+ clearable: 'Clearable',
+ editable: 'Editable',
+ format: 'Format',
+ valueFormat: 'Value Format',
+ showPassword: 'Show Reveal',
+ filterable: 'Filterable',
+ allowCreate: 'Allow Create',
+ remote: 'Remote Query',
+ automaticDropdown: 'Automatic Dropdown',
+ multiple: 'Multiple',
+ multipleLimit: 'Multiple Limit',
+ checkStrictly: 'Any Level Selectable',
+ showAllLevels: 'Show All Levels',
+ contentPosition: 'Content Position',
+ plain: 'Plain',
+ round: 'Round',
+ circle: 'Circle',
+ icon: 'Icon',
+ optionsSetting: 'Options Setting',
+ addOption: 'Add Option',
+ importOptions: 'Import Options',
+ resetDefault: 'Reset Default',
+ uploadSetting: 'Upload Setting',
+ uploadURL: 'Upload URL',
+ uploadTip: 'Tip Content',
+ withCredentials: 'Send Cookie',
+ multipleSelect: 'File Multi-select',
+ showFileList: 'Show File List',
+ limit: 'Max Upload Number',
+ fileMaxSize: 'Max Size(MB)',
+ fileTypes: 'Upload File Types',
+ fileTypesHelp: 'Allows to add more file types',
+ headers: 'Request Headers',
+
+ cellWidth: 'Width',
+ cellHeight: 'Height',
+ wordBreak: 'Line Wrap',
+ gridColHeight: 'Height Of Col(px)',
+ gutter: 'Gutter(px)',
+ columnSetting: 'Cols Setting',
+ colsOfGrid: 'Cols Of Grid:',
+ colSpanTitle: 'Spans Of Col',
+ colOffsetTitle: 'Offset Of Col',
+ colPushTitle: 'Push Of Col',
+ colPullTitle: 'Pull Of Col',
+ addColumn: 'Add Column',
+ responsive: 'Responsive',
+
+ tabPaneSetting: 'Tab Panes',
+ addTabPane: 'Add Tab Pane',
+ paneActive: 'Active',
+
+ customLabelIcon: 'Custom Label',
+ labelIconClass: 'Label Icon Class',
+ labelIconPosition: 'Label Icon Position',
+ labelTooltip: 'Label Tooltip',
+ minValue: 'Min Value',
+ maxValue: 'Max Value',
+ precision: 'Precision',
+ step: 'Step',
+ controlsPosition: 'Controls Position',
+ minLength: 'Min Length',
+ maxLength: 'Max Length',
+ showWordLimit: 'Show Word Limit',
+ prefixIcon: 'Prefix Icon',
+ suffixIcon: 'Suffix Icon',
+ inputButton: 'Input Button Setting',
+ appendButton: 'Append Button',
+ appendButtonDisabled: 'Button Disabled',
+ appendButtonIcon: 'Append Button Icon',
+ buttonIcon: 'Button Icon',
+ switchWidth: 'Width of Switch(px)',
+ activeText: 'Active Text',
+ inactiveText: 'Inactive Text',
+ activeColor: 'Active Color',
+ inactiveColor: 'Inactive Color',
+ maxStars: 'Stars Max Number',
+ lowThreshold: 'Low Threshold',
+ highThreshold: 'High Threshold',
+ allowHalf: 'Allow Half',
+ showText: 'Show Text',
+ showScore: 'Show Score',
+ range: 'Range',
+ vertical: 'Vertical',
+ showBlankRow: 'Show Blank Row',
+ showRowNumber: 'Show Row Number',
+
+ insertColumnToLeft: 'insert column to left',
+ insertColumnToRight: 'insert column to right',
+ insertRowAbove: 'insert row above',
+ insertRowBelow: 'insert row below',
+ mergeLeftColumn: 'merge left cell',
+ mergeRightColumn: 'merge right cell',
+ mergeEntireRow: 'merge entire row',
+ mergeRowAbove: 'merge cell above',
+ mergeRowBelow: 'merge cell below',
+ mergeEntireColumn: 'merge entire column',
+ undoMergeCol: 'undo merge column',
+ undoMergeRow: 'undo merge row',
+ deleteEntireCol: 'delete entire column',
+ deleteEntireRow: 'delete entire row',
+
+ widgetName: 'Unique Name',
+ formSize: 'Size',
+ labelPosition: 'Position Of Label',
+ topPosition: 'Top',
+ leftPosition: 'Left',
+ labelAlign: 'Label Align',
+ leftAlign: 'Left',
+ centerAlign: 'Center',
+ rightAlign: 'Right',
+ formCss: 'Form CSS',
+ addCss: 'Edit',
+ customClass: 'Custom Class',
+ globalFunctions: 'Global Functions',
+ addEventHandler: 'Edit',
+ editWidgetEventHandler: 'Edit Widget Event Handler',
+ editFormEventHandler: 'Edit Form Event Handler',
+ formSFCSetting: 'SFC Setting',
+ formModelName: 'Model Name',
+ formRefName: 'Ref Name',
+ formRulesName: 'Rules Name',
+ syntaxCheckWarning: 'Syntax error in the javascript codes, please check again!',
+
+ //data-table
+ tableWidth: 'Width(px/%)',
+ tableHeight: 'Height(px/%)',
+ showCheckBox: 'Show CheckBox',
+ showIndex: 'Show Row Number',
+ showPagination: 'Show Pagination',
+ smallPagination: 'Small Pagination',
+ tableColEdit: 'Edit Cols',
+ tableDataEdit: 'Edit Data',
+ stripe: 'Stripe',
+ showSummary: 'Show Summary',
+ rowSpacing: 'Row Spacing(px)',
+ editAction: 'Edit...',
+ columnName: 'Name',
+ columnLabel: 'Label',
+ columnWidth: 'Width(px/%)',
+ visibleColumn: 'Visible',
+ sortableColumn: 'Sortable',
+ fixedColumn: 'Fixed',
+ alignTypeOfColumn: 'Align',
+ formatOfColumn: 'Format',
+ actionColumn: 'Action',
+ addTableColumn: 'Add New Column',
+ deleteTableColumn: 'Delete This Column',
+ OnlyOneColumnCannotBeDeleted: 'The last column cannot be deleted.',
+ }
+
+ }
+}
diff --git a/src/lang/en-US_extension.js b/src/lang/en-US_extension.js
new file mode 100644
index 0000000..2149320
--- /dev/null
+++ b/src/lang/en-US_extension.js
@@ -0,0 +1,26 @@
+export default {
+ extension: {
+ widgetLabel: {
+ card: 'Card',
+ alert: 'Alert',
+ },
+
+ setting: {
+ cardFolded: 'Folded',
+ cardShowFold: 'Show Fold',
+ cardWidth: 'Width Of Card',
+ cardShadow: 'Shadow',
+
+ alertTitle: 'Title',
+ alertType: 'Type',
+ description: 'Description',
+ closable: 'Closable',
+ closeText: 'Text On Close Btn',
+ center: 'Center',
+ showIcon: 'Show Icon',
+ effect: 'Effect',
+
+ },
+
+ }
+}
diff --git a/src/lang/en-US_render.js b/src/lang/en-US_render.js
new file mode 100644
index 0000000..8582386
--- /dev/null
+++ b/src/lang/en-US_render.js
@@ -0,0 +1,38 @@
+export default {
+ render: {
+
+ hint: {
+ prompt: 'Prompt',
+ confirm: 'OK',
+ cancel: 'Cancel',
+
+ selectPlaceholder: 'Pick some item',
+ timePlaceholder: 'Select time',
+ startTimePlaceholder: 'Start time',
+ endTimePlaceholder: 'End time',
+ datePlaceholder: 'Select date',
+ startDatePlaceholder: 'Start date',
+ endDatePlaceholder: 'End date',
+ blankCellContent: '--',
+
+ uploadError: 'Upload error: ',
+ uploadExceed: 'The maximum number(${uploadLimit}) of file uploads has been exceeded.',
+ unsupportedFileType: 'Unsupported format: ',
+ fileSizeExceed: 'File size out of limit: ',
+ refNotFound: 'Ref not found: ',
+ fieldRequired: 'Input value should be not null.',
+ invalidNumber: 'Invalid number format',
+ selectFile: ' File...',
+ downloadFile: 'Download',
+ removeFile: 'Remove',
+ validationFailed: 'Form validation failed',
+
+ subFormAction: 'Action',
+ subFormAddAction: 'Add',
+ subFormAddActionHint: 'add new row',
+ insertSubFormRow: 'insert new row',
+ deleteSubFormRow: 'delete this row',
+ nonSubFormType: 'The type of widget don\'t match sub-form',
+ }
+ }
+}
diff --git a/src/lang/zh-CN.js b/src/lang/zh-CN.js
new file mode 100644
index 0000000..cdaeb98
--- /dev/null
+++ b/src/lang/zh-CN.js
@@ -0,0 +1,354 @@
+export default {
+ application: {
+ 'zh-CN': '简体中文',
+ 'en-US': 'English',
+ productTitle: '表单设计器',
+ github: 'GitHub',
+ document: '文档',
+ qqGroup: '技术WX群',
+ deployment: '私有部署',
+ subscription: '订阅Pro',
+ },
+
+ designer: {
+ componentLib: '组件库',
+ formLib: '表单模板',
+ containerTitle: '容器',
+ dragHandlerHint: '鼠标拖拽容器组件或字段组件并放置于表单中',
+ dragAction: '拖动',
+ basicFieldTitle: '基础字段',
+ advancedFieldTitle: '高级字段',
+ customFieldTitle: '自定义扩展字段',
+
+ noWidgetHint: '请从左侧列表中选择一个组件, 然后用鼠标拖动组件放置于此处.',
+
+ widgetLabel: {
+ grid: '栅格',
+ table: '表格',
+ tab: '标签页',
+ section: '区块',
+ 'sub-form': '子表单',
+ 'grid-col': '栅格列',
+ 'table-cell': '单元格',
+ 'tab-pane': '选项卡页',
+ 'data-table': '数据表格',
+
+ input: '单行输入',
+ textarea: '多行输入',
+ number: '计数器',
+ radio: '单选项',
+ checkbox: '多选项',
+ select: '下拉选项',
+ time: '时间',
+ 'time-range': '时间范围',
+ date: '日期',
+ 'date-range': '日期范围',
+ switch: '开关',
+ rate: '评分',
+ color: '颜色选择器',
+ slider: '滑块',
+ 'static-text': '静态文字',
+ 'html-text': 'HTML',
+ button: '按钮',
+ divider: '分隔线',
+
+ 'picture-upload': '图片',
+ 'file-upload': '文件',
+ 'rich-editor': '富文本',
+ cascader: '级联选择',
+ slot: '插槽',
+
+ custom: 'Custom Component',
+ },
+
+ hint: {
+ selectParentWidget: '选中父组件',
+ moveUpWidget: '上移组件',
+ moveDownWidget: '下移组件',
+ cloneWidget: '复制组件',
+ insertRow: '插入新行',
+ insertColumn: '插入新列',
+ remove: '移除组件',
+ cellSetting: '单元格操作',
+ dragHandler: '拖拽手柄',
+ copyField: '复制字段组件',
+ onlyFieldWidgetAcceptable: '子表单只能接收字段组件',
+ moveUpFirstChildHint: '已经移动到最上面',
+ moveDownLastChildHint: '已经移动到最下面',
+
+ closePreview: '关闭',
+ copyJson: '复制JSON',
+ saveFormJson: '保存为文件',
+ copyVueCode: '复制Vue代码',
+ copyHtmlCode: '复制HTML代码',
+ copyJsonSuccess: '复制JSON成功',
+ importJsonSuccess: '导入JSON成功',
+ copyJsonFail: '复制JSON失败',
+ copyVueCodeSuccess: '复制Vue代码成功',
+ copyVueCodeFail: '复制Vue代码失败',
+ copyHtmlCodeSuccess: '复制HTML代码成功',
+ copyHtmlCodeFail: '复制HTML代码失败',
+ saveVueCode: '保存Vue文件',
+ saveHtmlCode: '保存Html文件',
+ getFormData: '获取数据',
+ resetForm: '重置表单',
+ disableForm: '禁用编辑',
+ enableForm: '恢复编辑',
+ exportFormData: '表单数据',
+ copyFormData: '复制JSON',
+ saveFormData: '保存为文件',
+ copyVue2SFC: '复制Vue2代码',
+ copyVue3SFC: '复制Vue3代码',
+ copySFCFail: '复制SFC代码失败',
+ copySFCSuccess: '复制SFC代码成功',
+ saveVue2SFC: '保存为Vue2组件',
+ saveVue3SFC: '保存为Vue3组件',
+ fileNameForSave: '文件名:',
+ saveFileTitle: '保存为文件',
+ fileNameInputPlaceholder: '请输入文件名',
+ sampleLoadedSuccess: '表单示例加载成功',
+ sampleLoadedFail: '表单示例加载失败',
+ loadFormTemplate: '加载此模板',
+ loadFormTemplateHint: '是否加载这个模板?加载后会覆盖设计器当前表单,你可以使用“撤销”功能恢复。',
+ loadFormTemplateSuccess: '表单模板加载成功',
+ loadFormTemplateFailed: '表单模板加载失败',
+ currentNodeCannotBeSelected: '当前组件节点不可选择',
+
+ widgetSetting: '组件设置',
+ formSetting: '表单设置',
+
+ prompt: '提示',
+ confirm: '确定',
+ cancel: '取消',
+ import: '导入',
+ importJsonHint: '导入的JSON内容须符合下述格式,以保证顺利导入.',
+ invalidOptionsData: '无效的选项数据:',
+ lastPaneCannotBeDeleted: '仅剩一个选项卡页不可删除.',
+ duplicateName: '组件名称已存在: ',
+ nameRequired: '组件名称不可为空',
+
+ numberValidator: '数字',
+ letterValidator: '字母',
+ letterAndNumberValidator: '数字字母',
+ mobilePhoneValidator: '手机号码',
+ emailValidator: '邮箱',
+ urlValidator: '网址',
+ noChineseValidator: '非中文字符',
+ chineseValidator: '仅中文字符',
+
+ rowspanNotConsistentForMergeEntireRow: '存在行高不一致的单元格, 无法合并整行.',
+ colspanNotConsistentForMergeEntireColumn: '存在列宽不一致的单元格, 无法合并整列.',
+ rowspanNotConsistentForDeleteEntireRow: '存在行高不一致的单元格, 不可删除整行.',
+ colspanNotConsistentForDeleteEntireColumn: '存在列宽不一致的单元格, 不可删除整列.',
+ lastColCannotBeDeleted: '最后一列不可删除.',
+ lastRowCannotBeDeleted: '最后一行不可删除.',
+ },
+
+ toolbar: {
+ undoHint: '撤销',
+ redoHint: '重做',
+ pcLayout: 'PC',
+ padLayout: 'Pad',
+ mobileLayout: 'H5',
+ nodeTreeHint: '组件层次结构树',
+ nodeTreeTitle: '组件层次结构树',
+ clear: '清空',
+ preview: '预览',
+ importJson: '导入JSON',
+ exportJson: '导出JSON',
+ exportCode: '导出代码',
+ generateCode: '生成代码',
+ generateSFC: '生成SFC',
+ },
+
+ setting: {
+ basicSetting: '基本属性',
+ attributeSetting: '属性设置',
+ commonSetting: '常见属性',
+ advancedSetting: '高级属性',
+ eventSetting: '事件属性',
+ uniqueName: '唯一名称',
+ editNameHelp: '修改名称后需按回车确认',
+ label: '标签',
+ displayType: '显示类型',
+ defaultValue: '默认值',
+ placeholder: '占位内容',
+ startPlaceholder: '起始占位内容',
+ endPlaceholder: '截止占位内容',
+ widgetColumnWidth: '组件列宽',
+ widgetSize: '组件大小',
+ fontSize: '字体大小',
+ textAlign: '文字对齐',
+ showStops: '显示间断点',
+ displayStyle: '显示样式',
+ inlineLayout: '行内',
+ blockLayout: '块',
+ buttonStyle: '显示为按钮',
+ border: '带有边框',
+ labelWidth: '标签宽度',
+ rows: '行数',
+ labelHidden: '隐藏字段标签',
+ required: '必填字段',
+ validation: '字段校验',
+ requiredHint: '必填校验提示',
+ validationHelp: '支持输入正则表达式',
+ validationHint: '校验失败提示',
+ readonly: '只读',
+ disabled: '禁用',
+ hidden: '隐藏',
+ textContent: '静态文字',
+ preWrap: '自动换行',
+ htmlContent: 'HTML',
+ clearable: '可清除',
+ editable: '可输入',
+ format: '显示格式',
+ valueFormat: '绑定值格式',
+ showPassword: '可显示密码',
+ filterable: '可搜索选项',
+ allowCreate: '允许创建选项',
+ remote: '可远程搜索',
+ automaticDropdown: '自动弹出选项',
+ multiple: '选项可多选',
+ multipleLimit: '多选数量限制',
+ checkStrictly: '任意级节点可选',
+ showAllLevels: '显示完整路径',
+ contentPosition: '文字位置',
+ plain: '朴素按钮',
+ round: '圆角按钮',
+ circle: '圆形按钮',
+ icon: '图标',
+ optionsSetting: '选项设置',
+ addOption: '增加选项',
+ importOptions: '导入选项',
+ resetDefault: '重设选中项',
+ uploadSetting: '上传参数设置',
+ uploadURL: '上传地址',
+ uploadTip: '上传提示内容',
+ withCredentials: '发送cookie凭证',
+ multipleSelect: '文件可多选',
+ showFileList: '显示文件列表',
+ limit: '最大上传数量',
+ fileMaxSize: '文件大小限制(MB)',
+ fileTypes: '上传文件类型',
+ fileTypesHelp: '支持添加其他文件类型',
+ headers: '上传请求头',
+
+ cellWidth: '宽度',
+ cellHeight: '高度',
+ wordBreak: '文字自动换行',
+ gridColHeight: '栅格列统一高度(px)',
+ gutter: '栅格间隔(px)',
+ columnSetting: '栅格属性设置',
+ colsOfGrid: '当前栅格列:',
+ colSpanTitle: '栅格宽度',
+ colOffsetTitle: '左侧间隔格数',
+ colPushTitle: '右移栅格数',
+ colPullTitle: '左移栅格数',
+ addColumn: '增加栅格',
+ responsive: '响应式布局',
+
+ tabPaneSetting: '选项卡设置',
+ addTabPane: '增加选项卡页',
+ paneActive: '激活',
+
+ customLabelIcon: '定制字段标签',
+ labelIconClass: '标签Icon样式',
+ labelIconPosition: '标签Icon位置',
+ labelTooltip: '标签文字提示',
+ minValue: '最小值',
+ maxValue: '最大值',
+ precision: '精度',
+ step: '增减步长',
+ controlsPosition: '控制按钮位置',
+ minLength: '最小长度',
+ maxLength: '最大长度',
+ showWordLimit: '显示字数统计',
+ prefixIcon: '头部Icon',
+ suffixIcon: '尾部Icon',
+ inputButton: '输入框按钮设置',
+ appendButton: '添加后置按钮',
+ appendButtonDisabled: '后置按钮禁用',
+ appendButtonIcon: '后置按钮Icon',
+ buttonIcon: '按钮Icon',
+ switchWidth: '开关宽度(像素)',
+ activeText: '开启时文字描述',
+ inactiveText: '关闭时文字描述',
+ activeColor: '开启时背景色',
+ inactiveColor: '关闭时背景色',
+ maxStars: '最大评分值',
+ lowThreshold: '低分界限值',
+ highThreshold: '高分界限值',
+ allowHalf: '允许半选',
+ showText: '显示辅助文字',
+ showScore: '显示当前分数',
+ range: '是否为范围选择',
+ vertical: '是否竖向显示',
+ showBlankRow: '默认显示新行',
+ showRowNumber: '显示行号',
+
+ insertColumnToLeft: '插入左侧列',
+ insertColumnToRight: '插入右侧列',
+ insertRowAbove: '插入上方行',
+ insertRowBelow: '插入下方行',
+ mergeLeftColumn: '合并左侧单元格',
+ mergeRightColumn: '合并右侧单元格',
+ mergeEntireRow: '合并整行',
+ mergeRowAbove: '合并上方单元格',
+ mergeRowBelow: '合并下方单元格',
+ mergeEntireColumn: '合并整列',
+ undoMergeCol: '撤销列合并',
+ undoMergeRow: '撤销行合并',
+ deleteEntireCol: '删除整列',
+ deleteEntireRow: '删除整行',
+
+ widgetName: '组件唯一名称',
+ formSize: '全局组件大小',
+ labelPosition: '字段标签位置',
+ topPosition: '顶部',
+ leftPosition: '左边',
+ labelAlign: '标签对齐',
+ leftAlign: '居左',
+ centerAlign: '居中',
+ rightAlign: '居右',
+ formCss: '表单全局CSS',
+ addCss: '编写CSS',
+ customClass: '自定义CSS样式',
+ globalFunctions: '表单全局函数',
+ addEventHandler: '编写代码',
+ editWidgetEventHandler: '组件事件处理',
+ editFormEventHandler: '表单事件处理',
+ formSFCSetting: '生成SFC设置',
+ formModelName: '数据对象名称',
+ formRefName: '引用名称',
+ formRulesName: '验证规则名称',
+ syntaxCheckWarning: 'JS代码存在语法错误,请仔细检查!',
+
+ //data-table
+ tableWidth: '宽度(px/%)',
+ tableHeight: '高度(px/%)',
+ showCheckBox: '是否显示复选框列',
+ showIndex: '是否显示行号',
+ showPagination: '是否显示分页',
+ smallPagination: '小型分页',
+ tableColEdit: '表格列编辑',
+ tableDataEdit: '表格数据编辑',
+ showSummary: '是否合计',
+ stripe: '是否斑马线',
+ rowSpacing: '行距(px)',
+ editAction: '编辑...',
+ columnName: '字段名称',
+ columnLabel: '显示名称',
+ columnWidth: '列宽(px/%)',
+ visibleColumn: '是否显示',
+ sortableColumn: '是否排序',
+ fixedColumn: '是否固定',
+ alignTypeOfColumn: '对齐方式',
+ formatOfColumn: '格式化',
+ actionColumn: '操作',
+ addTableColumn: '增加列',
+ deleteTableColumn: '删除列',
+ OnlyOneColumnCannotBeDeleted: '表格只有一列时不可删除.',
+ }
+
+ }
+}
diff --git a/src/lang/zh-CN_extension.js b/src/lang/zh-CN_extension.js
new file mode 100644
index 0000000..907e38e
--- /dev/null
+++ b/src/lang/zh-CN_extension.js
@@ -0,0 +1,26 @@
+export default {
+ extension: {
+ widgetLabel: {
+ card: '卡片',
+ alert: '提示',
+ },
+
+ setting: {
+ cardFolded: '是否收起',
+ cardShowFold: '显示折叠按钮',
+ cardWidth: '卡片宽度',
+ cardShadow: '显示阴影',
+
+ alertTitle: '标题',
+ alertType: '类型',
+ description: '辅助性文字',
+ closable: '是否可关闭',
+ closeText: '关闭按钮文字',
+ center: '文字居中',
+ showIcon: '显示图标',
+ effect: '显示效果',
+
+ },
+
+ }
+}
diff --git a/src/lang/zh-CN_render.js b/src/lang/zh-CN_render.js
new file mode 100644
index 0000000..1dd74c4
--- /dev/null
+++ b/src/lang/zh-CN_render.js
@@ -0,0 +1,38 @@
+export default {
+ render: {
+
+ hint: {
+ prompt: '提示',
+ confirm: '确定',
+ cancel: '取消',
+
+ selectPlaceholder: '请选择',
+ timePlaceholder: '选择时间',
+ startTimePlaceholder: '起始时间',
+ endTimePlaceholder: '截止时间',
+ datePlaceholder: '选择日期',
+ startDatePlaceholder: '起始日期',
+ endDatePlaceholder: '截止日期',
+ blankCellContent: '--',
+
+ uploadError: '上传错误: ',
+ uploadExceed: '最大上传数量(${uploadLimit})已超出.',
+ unsupportedFileType: '不支持格式: ',
+ fileSizeExceed: '文件大小已超出: ',
+ refNotFound: '组件未找到: ',
+ fieldRequired: '字段值不可为空',
+ invalidNumber: '数据格式错误',
+ selectFile: ' 选择文件',
+ downloadFile: '下载',
+ removeFile: '移除',
+ validationFailed: '表单数据校验失败',
+
+ subFormAction: '操作',
+ subFormAddAction: '新增',
+ subFormAddActionHint: '新增行',
+ insertSubFormRow: '插入行',
+ deleteSubFormRow: '删除行',
+ nonSubFormType: '组件类型不是子表单',
+ }
+ }
+}
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..6b28b0b
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,29 @@
+import 'babel-polyfill'
+import './utils/debug-console'
+import Vue from 'vue'
+import axios from "axios";
+import App from './App.vue'
+import ElementUI from 'element-ui'
+import './utils/directive'
+import './icons'
+
+import 'element-ui/lib/theme-chalk/index.css'
+import '@/styles/index.scss'
+import '@/iconfont/iconfont.css'
+
+import {loadExtension} from '@/extension/extension-loader'
+
+loadExtension()
+
+Vue.use(ElementUI, { size: 'small' })
+
+if (typeof window !== 'undefined') {
+ window.axios = axios
+}
+
+Vue.config.productionTip = false
+
+new Vue({
+ el: "#app",
+ render: h => h(App),
+})
diff --git a/src/styles/global.scss b/src/styles/global.scss
new file mode 100644
index 0000000..76f2814
--- /dev/null
+++ b/src/styles/global.scss
@@ -0,0 +1,108 @@
+/* 全局css变量 */
+$--color-primary: #409EFF;
+
+.primary-color {
+ color: $--color-primary;
+}
+
+.background-opacity {
+ background: rgba(64, 158, 255, 0.6);
+}
+
+.form-widget-list {
+
+ .ghost{
+ content: '';
+ font-size: 0;
+ height: 3px;
+ box-sizing: border-box;
+ background: $--color-primary;
+ border: 2px solid $--color-primary;
+ outline-width: 0;
+ padding: 0;
+ overflow: hidden;
+ }
+}
+
+.el-form-item--medium {
+ .el-radio {
+ line-height: 36px !important;
+ }
+
+ .el-rate{
+ margin-top: 8px;
+ }
+}
+
+.el-form-item--small {
+ .el-radio {
+ line-height: 32px !important;
+ }
+
+ .el-rate{
+ margin-top: 6px;
+ }
+}
+
+.el-form-item--mini {
+ .el-radio {
+ line-height: 28px !important;
+ }
+
+ .el-rate{
+ margin-top: 4px;
+ }
+}
+
+.el-card {
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+
+input[type="password"]::-ms-reveal { /* 隐藏IE/Edge原生的密码查看按钮 */
+ display: none;
+}
+
+/* 滚动条样式 begin */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ width: 8px;
+ background: rgba(#101F1C, 0.1);
+ -webkit-border-radius: 2em;
+ -moz-border-radius: 2em;
+ border-radius: 2em;
+}
+
+::-webkit-scrollbar-thumb {
+ background-color: rgba(#101F1C, 0.35);
+ background-clip: padding-box;
+ min-height: 28px;
+ -webkit-border-radius: 2em;
+ -moz-border-radius: 2em;
+ border-radius: 2em;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background-color: rgba(#101F1C, 0.85);
+}
+
+* {//Firefox浏览器滚动条样式
+ scrollbar-color: #e5e5e5 #f7f7f9; //滚动条轨道颜色、滚动条滑块的颜色
+ scrollbar-width: thin; //thin模式下滚动条两端的三角按钮会消失
+}
+
+
+/*
+body {//IE浏览器滚动条样式
+ scrollbar-shadow-color: #e5e5e5;
+ scrollbar-face-color: #e5e5e5;
+ scrollbar-base-color: #ffffff;
+ scrollbar-arrow-color: #444040;
+}
+*/
+
+/* 滚动条样式 end */
diff --git a/src/styles/index.scss b/src/styles/index.scss
new file mode 100644
index 0000000..ebb3bc7
--- /dev/null
+++ b/src/styles/index.scss
@@ -0,0 +1,15 @@
+
+html {
+ height: 100%;
+ box-sizing: border-box;
+}
+
+body {
+ height: 100%;
+ margin: 0; /* */
+ background-color: #ffffff;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+}
diff --git a/src/utils/beautifierLoader.js b/src/utils/beautifierLoader.js
new file mode 100644
index 0000000..3736b8f
--- /dev/null
+++ b/src/utils/beautifierLoader.js
@@ -0,0 +1,77 @@
+import {loadRemoteScript} from "@/utils/util";
+import {BEAUTIFIER_PATH} from "@/utils/config";
+
+let beautifierObj
+
+export const beautifierOpts = {
+ html: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'separate',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: false,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ },
+ js: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'normal',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: true,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ },
+ css: {
+ indent_size: '2',
+ indent_char: ' ',
+ max_preserve_newlines: '-1',
+ preserve_newlines: false,
+ keep_array_indentation: false,
+ break_chained_methods: false,
+ indent_scripts: 'normal',
+ brace_style: 'end-expand',
+ space_before_conditional: true,
+ unescape_strings: false,
+ jslint_happy: true,
+ end_with_newline: true,
+ wrap_line_length: '110',
+ indent_inner_html: true,
+ comma_first: false,
+ e4x: true,
+ indent_empty_lines: true
+ }
+}
+
+export default function loadBeautifier(callback) {
+ if (beautifierObj) {
+ callback(beautifierObj)
+ return
+ }
+
+ loadRemoteScript(BEAUTIFIER_PATH, () => {
+ // eslint-disable-next-line no-undef
+ beautifierObj = beautifier //beautifier为全局对象
+ callback(beautifierObj)
+ })
+}
diff --git a/src/utils/code-generator.js b/src/utils/code-generator.js
new file mode 100644
index 0000000..221743a
--- /dev/null
+++ b/src/utils/code-generator.js
@@ -0,0 +1,91 @@
+export const generateCode = function(formJson, codeType= 'vue') {
+ let formJsonStr = JSON.stringify(formJson)
+
+ if (codeType === 'html') {
+ return `
+
+
+
+
+ VForm Demo
+
+
+
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+
+
+`
+
+ } else {
+ return `
+
+
+
+ Submit
+
+
+`
+
+ }
+}
diff --git a/src/utils/config.js b/src/utils/config.js
new file mode 100644
index 0000000..373f3f0
--- /dev/null
+++ b/src/utils/config.js
@@ -0,0 +1,9 @@
+export const VARIANT_FORM_VERSION = '2.2.9'
+
+//export const MOCK_CASE_URL = 'https://www.fastmock.site/mock/2de212e0dc4b8e0885fea44ab9f2e1d0/vform/'
+export const MOCK_CASE_URL = 'https://ks3-cn-beijing.ksyuncs.com/vform-static/vcase/'
+
+//export const ACE_BASE_PATH = 'public/lib/ace/src-min-noconflict'
+export const ACE_BASE_PATH = 'https://ks3-cn-beijing.ksyun.com/vform2021/ace-mini'
+
+export const BEAUTIFIER_PATH= 'https://ks3-cn-beijing.ksyun.com/vform2021/js-beautify/1.14.0/beautifier.min.js'
diff --git a/src/utils/debug-console.js b/src/utils/debug-console.js
new file mode 100644
index 0000000..f4e6063
--- /dev/null
+++ b/src/utils/debug-console.js
@@ -0,0 +1,7 @@
+console.log = (function(logFunc) {
+ return function () {
+ if (process.env.NODE_ENV === 'development') {
+ logFunc.call(console, ...arguments);
+ }
+ }
+})(console.log)
diff --git a/src/utils/directive.js b/src/utils/directive.js
new file mode 100644
index 0000000..4f3a671
--- /dev/null
+++ b/src/utils/directive.js
@@ -0,0 +1,74 @@
+import Vue from 'vue'
+
+// v-dialogDrag: 弹窗拖拽
+Vue.directive('dialogDrag', {
+ bind(el, binding, vnode, oldVnode) {
+ const dialogHeaderEl = el.querySelector('.el-dialog__header')
+ const dragDom = el.querySelector('.el-dialog')
+ dialogHeaderEl.style.cursor = 'move'
+
+ // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+ const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
+
+ dialogHeaderEl.onmousedown = (e) => {
+ // 鼠标按下,计算当前元素距离可视区的距离
+ const disX = e.clientX - dialogHeaderEl.offsetLeft
+ const disY = e.clientY - dialogHeaderEl.offsetTop
+
+ // 获取到的值带px 正则匹配替换
+ let styL, styT
+
+ // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+ if (sty.left.includes('%')) {
+ styL = +document.body.clientWidth * (+sty.left.replace(/%/g, '') / 100)
+ styT = +document.body.clientHeight * (+sty.top.replace(/%/g, '') / 100)
+ } else {
+ styL = +sty.left.replace(/px/g, '')
+ styT = +sty.top.replace(/px/g, '')
+ }
+
+ document.onmousemove = function(e) {
+ // 通过事件委托,计算移动的距离
+ const l = e.clientX - disX
+ const t = e.clientY - disY
+
+ // 移动当前元素
+ dragDom.style.left = `${l + styL}px`
+ dragDom.style.top = `${t + styT}px`
+
+ // 将此时的位置传出去
+ // binding.value({x:e.pageX,y:e.pageY})
+ }
+
+ document.onmouseup = function(e) {
+ document.onmousemove = null
+ document.onmouseup = null
+ }
+ }
+ }
+})
+
+// v-dialogDragWidth: 弹窗宽度拖大 拖小
+Vue.directive('dialogDragWidth', {
+ bind(el, binding, vnode, oldVnode) {
+ const dragDom = binding.value.$el.querySelector('.el-dialog')
+
+ el.onmousedown = (e) => {
+ // 鼠标按下,计算当前元素距离可视区的距离
+ const disX = e.clientX - el.offsetLeft
+
+ document.onmousemove = function(e) {
+ e.preventDefault() // 移动时禁用默认事件
+
+ // 通过事件委托,计算移动的距离
+ const l = e.clientX - disX
+ dragDom.style.width = `${l}px`
+ }
+
+ document.onmouseup = function(e) {
+ document.onmousemove = null
+ document.onmouseup = null
+ }
+ }
+ }
+})
diff --git a/src/utils/emitter.js b/src/utils/emitter.js
new file mode 100644
index 0000000..5547e80
--- /dev/null
+++ b/src/utils/emitter.js
@@ -0,0 +1,34 @@
+function broadcast(componentName, eventName, params) {
+ this.$children.forEach(child => {
+ var name = child.$options.componentName;
+
+ if (name === componentName) {
+ child.$emit.apply(child, [eventName].concat(params));
+ broadcast.apply(child, [componentName, eventName].concat([params])); //继续遍历子节点!!
+ } else {
+ broadcast.apply(child, [componentName, eventName].concat([params]));
+ }
+ });
+}
+export default {
+ methods: {
+ dispatch(componentName, eventName, params) {
+ var parent = this.$parent || this.$root;
+ var name = parent.$options.componentName;
+
+ while (parent && (!name || name !== componentName)) {
+ parent = parent.$parent;
+
+ if (parent) {
+ name = parent.$options.componentName;
+ }
+ }
+ if (parent) {
+ parent.$emit.apply(parent, [eventName].concat(params));
+ }
+ },
+ broadcast(componentName, eventName, params) {
+ broadcast.call(this, componentName, eventName, params);
+ }
+ }
+};
diff --git a/src/utils/format.js b/src/utils/format.js
new file mode 100644
index 0000000..9698368
--- /dev/null
+++ b/src/utils/format.js
@@ -0,0 +1,214 @@
+
+export function formatDate1(date) {
+ if (new Date(Date.parse(date.replace(/-/g, "/"))) === "Invalid Date") {
+ return date;
+ }
+ date = new Date(Date.parse(date.replace(/-/g, "/"))); //转换成Date
+ let y = date.getFullYear();
+ let m = date.getMonth() + 1;
+ m = m < 10 ? '0' + m : m;
+ let d = date.getDate();
+ d = d < 10 ? ('0' + d) : d;
+ return y + '-' + m + '-' + d;
+}
+
+export function formatDate2(date) {
+ if (new Date(Date.parse(date.replace(/-/g, "/"))) === "Invalid Date") {
+ return date;
+ }
+ date = new Date(Date.parse(date.replace(/-/g, "/"))); //转换成Date
+ let y = date.getFullYear();
+ let m = date.getMonth() + 1;
+ m = m < 10 ? '0' + m : m;
+ let d = date.getDate();
+ d = d < 10 ? ('0' + d) : d;
+ return y + '/' + m + '/' + d;
+}
+
+export function formatDate3(date) {
+ if (new Date(Date.parse(date.replace(/-/g, "/"))) === "Invalid Date") {
+ return date;
+ }
+ date = new Date(Date.parse(date.replace(/-/g, "/"))); //转换成Date
+ let y = date.getFullYear();
+ let m = date.getMonth() + 1;
+ m = m < 10 ? '0' + m : m;
+ let d = date.getDate();
+ d = d < 10 ? ('0' + d) : d;
+ return y + '年' + m + '月' + d +'日';
+}
+
+export function formatDate4(date) {
+ if (new Date(Date.parse(date.replace(/-/g, "/"))) === "Invalid Date"){
+ return date;
+ }
+ date = new Date(Date.parse(date.replace(/-/g, "/"))); //转换成Date
+ return date.toLocaleString()
+}
+
+export function formatDate5(date) {
+ if (new Date(Date.parse(date.replace(/-/g, "/"))) === "Invalid Date") {
+ return date;
+ }
+ date = new Date(Date.parse(date.replace(/-/g, "/"))); //转换成Data();
+ return date.toLocaleString('chinese', { hour12: false })
+}
+
+// ###,###,###,##0.######
+export function formatNumber1(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ let length = v.toString().split(".")[1].length;
+ switch(length){
+ case 0:
+ v = v.toFixed(0)
+ break;
+ case 1:
+ v = v.toFixed(1)
+ break;
+ case 2:
+ v = v.toFixed(2)
+ break;
+ case 3:
+ v = v.toFixed(3)
+ break;
+ case 4:
+ v = v.toFixed(4)
+ break;
+ case 5:
+ v = v.toFixed(5)
+ break;
+ default:
+ v = v.toFixed(6)
+ }
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res;
+}
+
+//###,###,###,##0.00####
+export function formatNumber2(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ let length = v.toString().split(".")[1].length;
+ switch(length){
+ case 0:
+ case 1:
+ case 2:
+ v = v.toFixed(2)
+ break;
+ case 3:
+ v = v.toFixed(3)
+ break;
+ case 4:
+ v = v.toFixed(4)
+ break;
+ case 5:
+ v = v.toFixed(5)
+ break;
+ default:
+ v = v.toFixed(6)
+ }
+
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res;
+}
+
+// ###,###,###,##0.000000
+export function formatNumber3(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ v = v.toFixed(6)
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res;
+}
+// ###,###,###,##0.000
+export function formatNumber4(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ v = v.toFixed(3)
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res;
+}
+
+// ###,###,###,##0.00
+export function formatNumber5(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ v = v.toFixed(2)
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res;
+}
+
+// ###,###,###,##0
+export function formatNumber6(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ v = v.toFixed(0)
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res;
+}
+
+// ###,##0.00##%
+export function formatNumber7(v) {
+ if (typeof(v) != "number") {
+ return v;
+ }
+
+ let length = v.toString().split(".")[1].length;
+ v = v*100
+ switch(length){
+ case 0:
+ case 1:
+ case 2:
+ v = v.toFixed(2)
+ break;
+ case 3:
+ v = v.toFixed(3)
+ break;
+ default:
+ v = v.toFixed(4)
+ }
+
+ let res = v.toString().replace(/\d+/, function(n){ // 先提取整数部分
+ return n.replace(/(\d)(?=(\d{3})+$)/g,function($1){
+ return $1+",";
+ });
+ })
+ return res+'%';
+}
diff --git a/src/utils/i18n.js b/src/utils/i18n.js
new file mode 100644
index 0000000..cf890be
--- /dev/null
+++ b/src/utils/i18n.js
@@ -0,0 +1,95 @@
+import Vue from 'vue'
+import si18n from './smart-vue-i18n/index'
+
+import enLocaleElement from "element-ui/lib/locale/lang/en";
+import zhLocaleElement from "element-ui/lib/locale/lang/zh-CN";
+import locale from "element-ui/lib/locale"
+
+import enLocale from "@/lang/en-US";
+import zhLocale from "@/lang/zh-CN";
+import enLocale_render from "@/lang/en-US_render";
+import zhLocale_render from "@/lang/zh-CN_render";
+import enLocale_extension from "@/lang/en-US_extension";
+import zhLocale_extension from "@/lang/zh-CN_extension";
+
+const langResources = {
+ 'en-US': {
+ something: {
+ //...
+ },
+ ...enLocaleElement,
+ ...enLocale,
+ ...enLocale_render,
+ ...enLocale_extension
+ },
+
+ 'zh-CN': {
+ something: {
+ //...
+ },
+ ...zhLocaleElement,
+ ...zhLocale,
+ ...zhLocale_render,
+ ...zhLocale_extension
+ }
+}
+
+// ********************* 下述代码参考element-ui/lib/locale/format.js begin *****************//
+
+const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
+function hasOwn(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+}
+
+const elLocalFormatter = function template(string, args) {
+ return string.replace(RE_NARGS, (match, prefix, i, index) => {
+ let result;
+
+ if (string[index - 1] === '{' &&
+ string[index + match.length] === '}') {
+ return i;
+ } else {
+ result = hasOwn(args, i) ? args[i] : null;
+ if (result === null || result === undefined) {
+ return '';
+ }
+
+ return result;
+ }
+ })
+}
+
+// ********************* 下述代码参考element-ui/lib/locale/format.js end ******************//
+
+Vue.use(si18n, {
+ lang: localStorage.getItem('v_form_locale') || 'zh-CN',
+ messages: langResources
+})
+
+locale.i18n((key, value) => {
+ let result = Vue.prototype.$st(key)
+ return elLocalFormatter(result, value)
+})
+
+export const changeLocale = function(langName) {
+ Vue.prototype.$si18n.setLang(langName)
+ localStorage.setItem('v_form_locale', langName)
+}
+
+export const translate = function(key) {
+ return Vue.prototype.$st(key)
+}
+
+export default {
+ methods: {
+ i18nt(key) {
+ return this.$st(key)
+ },
+
+ /* 如果key1不存在,则查找key2 */
+ i18n2t(key1, key2) {
+ return this.$st2(key1, key2)
+ },
+
+ }
+}
diff --git a/src/utils/sfc-generator.js b/src/utils/sfc-generator.js
new file mode 100644
index 0000000..314d055
--- /dev/null
+++ b/src/utils/sfc-generator.js
@@ -0,0 +1,583 @@
+import {isNotNull} from "@/utils/util";
+import {genVue2JS} from "@/utils/vue2js-generator";
+import {beautifierOpts} from "@/utils/beautifierLoader";
+import {genVue3JS} from "@/utils/vue3js-generator";
+
+export function buildClassAttr(ctn, defaultClass) {
+ const cop = ctn.options
+ let gridClassArray = []
+ !!defaultClass && gridClassArray.push(defaultClass)
+ !!cop.customClass && (cop.customClass.length > 0) && gridClassArray.push(cop.customClass.join(' '))
+ return gridClassArray.length > 0 ? `class="${gridClassArray.join(' ')}"` : ''
+}
+
+const containerTemplates = { //容器组件属性
+ 'grid': (ctn, formConfig) => {
+ const gridClassAttr = buildClassAttr(ctn)
+ const gridTemplate =
+`
+${ctn.cols.map(col => {
+ const colOpt = col.options
+ const spanAttr = !!colOpt.responsive ? '' : `:span="${colOpt.span}"`
+ const mdAttr = !colOpt.responsive ? '' : `:md="${colOpt.md}"`
+ const smAttr = !colOpt.responsive ? '' : `:sm="${colOpt.sm}"`
+ const xsAttr = !colOpt.responsive ? '' : `:xs="${colOpt.xs}"`
+ const offsetAttr = !!colOpt.offset ? `:offset="${colOpt.offset}"` : ''
+ const pushAttr = !!colOpt.push ? `:push="${colOpt.push}"` : ''
+ const pullAttr = !!colOpt.pull ? `:pull="${colOpt.pull}"` : ''
+ const colClassAttr = buildClassAttr(col, 'grid-cell')
+ return `
+ ${col.widgetList.map(cw => {
+ if (cw.category === 'container') {
+ return buildContainerWidget(cw, formConfig)
+ } else {
+ return buildFieldWidget(cw, formConfig)
+ }
+ }).join('')
+ }
+ `
+ }).join('')
+}
+`
+
+ return gridTemplate
+ },
+
+ 'table': (ctn, formConfig) => {
+ const tableClassAttr = buildClassAttr(ctn, 'table-layout')
+ const tableTemplate =
+`
+
+ ${ctn.rows.map(tr => {
+ return `${
+ tr.cols.filter(td => !td.merged).map(td => {
+ const tdOpt = td.options
+ const tdClassAttr = buildClassAttr(td, 'table-cell')
+ const colspanAttr = (!isNaN(tdOpt.colspan) && (tdOpt.colspan !== 1)) ? `colspan="${tdOpt.colspan}"` : ''
+ const rowspanAttr = (!isNaN(tdOpt.rowspan) && (tdOpt.rowspan !== 1)) ? `rowspan="${tdOpt.rowspan}"` : ''
+
+ let tdStyleArray = []
+ !!tdOpt.cellWidth && tdStyleArray.push('width: ' + tdOpt.cellWidth + ' !important')
+ !!tdOpt.cellHeight && tdStyleArray.push('height: ' + tdOpt.cellHeight + ' !important')
+ let tdStyleAttr = (tdStyleArray.length > 0) ? `style="${tdStyleArray.join(';')}"` : ''
+
+ return `${td.widgetList.map(tw => {
+ if (tw.category === 'container') {
+ return buildContainerWidget(tw, formConfig)
+ } else {
+ return buildFieldWidget(tw, formConfig)
+ }
+ }).join('')
+ }
+ | `
+ }).join('')
+ }
`
+ }).join('')
+ }
+
+
`
+ return tableTemplate
+ },
+
+ 'tab': (ctn, formConfig) => {
+ const tabClassAttr = buildClassAttr(ctn)
+ const vModel = ctn.tabs && (ctn.tabs.length > 0) ? `v-model="${ctn.options.name}ActiveTab"` : ''
+ const tabTemplate =
+`
+
+ ${ctn.tabs.map(tab => {
+ const tabOpt = tab.options
+ const disabledAttr = (tabOpt.disabled === true) ? `disabled` : ''
+ return `
+ ${tab.widgetList.map(tw => {
+ if (tw.category === 'container') {
+ return buildContainerWidget(tw, formConfig)
+ } else {
+ return buildFieldWidget(tw, formConfig)
+ }
+ }).join('')
+ }`
+ }).join('')}
+
+
`
+
+ return tabTemplate
+ },
+
+ 'sub-form': (ctn, formConfig) => {
+ //TODO:
+ },
+
+}
+
+export function buildContainerWidget(widget, formConfig) {
+ return containerTemplates[widget.type] ? containerTemplates[widget.type](widget, formConfig) : null
+}
+
+function getElAttrs(widget, formConfig) { //获取El组件属性
+ let wop = widget.options
+ return {
+ vModel: `v-model="${formConfig.modelName}.${wop.name}"`,
+ readonly: wop.readonly ? `readonly="true"` : '',
+ disabled: wop.disabled ? `:disabled="true"` : '',
+ size: !!wop.size ? `size="${wop.size}"` : '',
+ type: !!wop.type ? `type="${wop.type === 'number' ? 'text' : wop.type}"` : '',
+ showPassword: !!wop.showPassword ? `:show-password="${wop.showPassword}"` : '',
+ placeholder: !!wop.placeholder ? `placeholder="${wop.placeholder}"` : '',
+ rows: (isNotNull(wop.rows) && !isNaN(wop.rows)) ? `rows="${wop.rows}"` : '',
+ clearable: !!wop.clearable ? 'clearable' : '',
+ minlength: (isNotNull(wop.minLength) && !isNaN(wop.minLength)) ? `:minlength="${wop.minLength}"` : '',
+ maxlength: (isNotNull(wop.maxLength) && !isNaN(wop.maxLength)) ? `:maxlength="${wop.maxLength}"` : '',
+ showWordLimit: !!wop.showWordLimit ? `:show-word-limit="true"`: '',
+ prefixIcon: !!wop.prefixIcon ? `prefix-icon="${wop.prefixIcon}"` : '',
+ suffixIcon: !!wop.suffixIcon ? `suffix-icon="${wop.suffixIcon}"` : '',
+ controlsPosition: wop.controlsPosition === 'right' ? `controls-position="right"` : '',
+ min: (isNotNull(wop.min) && !isNaN(wop.min)) ? `:min="${wop.min}"` : '',
+ max: (isNotNull(wop.max) && !isNaN(wop.max)) ? `:max="${wop.max}"` : '',
+ precision: (isNotNull(wop.precision) && !isNaN(wop.precision)) ? `:precision="${wop.precision}"` : '',
+ step: (isNotNull(wop.step) && !isNaN(wop.step)) ? `:step="${wop.step}"` : '',
+ filterable: !!wop.filterable ? `filterable` : '',
+ allowCreate: !!wop.allowCreate ? `allow-create` : '',
+ defaultFirstOption: (!!wop.filterable && !!wop.allowCreate) ? `default-first-option` : '',
+ multiple: !!wop.multiple ? `multiple` : '',
+ multipleLimit: (!isNaN(wop.multipleLimit) && (wop.multipleLimit > 0)) ? `:multiple-limit="${wop.multipleLimit}"` : '',
+ automaticDropdown: !!wop.automaticDropdown ? `automatic-dropdown` : '',
+ remote: !!wop.remote ? `remote` : '',
+ format: !!wop.format ? `format="${wop.format}"` : '',
+ valueFormat: !!wop.valueFormat ? `value-format="${wop.valueFormat}"` : '',
+ editable: !!wop.editable ? `:editable="${wop.editable}"` : '',
+ startPlaceholder: !!wop.startPlaceholder ? `start-placeholder="${wop.startPlaceholder}"` : '',
+ endPlaceholder: !!wop.endPlaceholder ? `end-placeholder="${wop.endPlaceholder}"` : '',
+
+ activeText: !!wop.activeText ? `active-text="${wop.activeText}"` : '',
+ inactiveText: !!wop.inactiveText ? `inactive-text="${wop.inactiveText}"` : '',
+ activeColor: !!wop.activeColor ? `active-color="${wop.activeColor}"` : '',
+ inactiveColor: !!wop.inactiveColor ? `inactive-color="${wop.inactiveColor}"` : '',
+ switchWidth: (!isNaN(wop.switchWidth) && (wop.switchWidth !== 40)) ? `:width="${wop.switchWidth}"` : '',
+
+ rateMax: (!isNaN(wop.max) && (wop.max !== 5)) ? `:max="${wop.max}"` : '',
+ lowThreshold: (!isNaN(wop.lowThreshold) && (wop.lowThreshold !== 2)) ? `:low-threshold="${wop.lowThreshold}"` : '',
+ highThreshold: (!isNaN(wop.highThreshold) && (wop.highThreshold !== 4)) ? `:high-threshold="${wop.highThreshold}"` : '',
+ allowHalf: !!wop.allowHalf ? `allow-half` : '',
+ showText: !!wop.showText ? `show-text` : '',
+ showScore: !!wop.showScore ? `show-score` : '',
+
+ sliderMin: (!isNaN(wop.min) && (wop.min !== 0)) ? `:min="${wop.min}"` : '',
+ sliderMax: (!isNaN(wop.max) && (wop.max !== 100)) ? `:max="${wop.max}"` : '',
+ sliderStep: (!isNaN(wop.step) && (wop.step !== 1)) ? `:step="${wop.step}"` : '',
+ sliderRange: !!wop.range ? `range` : '',
+ sliderVertical: !!wop.vertical ? `vertical` : '',
+
+ uploadAction: !!wop.uploadURL ? `action="${wop.uploadURL}"` : '',
+ withCredentials: !!wop.withCredentials ? `with-credentials` : '',
+ multipleSelect: !!wop.multipleSelect ? `multiple` : '',
+ showFileList: !!wop.showFileList ? `show-file-list` : '',
+ limit: !isNaN(wop.limit) ? `:limit="${wop.limit}"` : '',
+ uploadTipSlotChild: !!wop.uploadTip ? `${wop.uploadTip}
` : '',
+ pictureUploadIconChild: ``,
+ fileUploadIconChild: ``,
+
+ buttonType: !!wop.type ? `type="${wop.type}"` : '',
+ buttonPlain: !!wop.plain ? `plain` : '',
+ buttonRound: !!wop.round ? `round` : '',
+ buttonCircle: !!wop.circle ? `circle` : '',
+ buttonIcon: !!wop.icon ? `icon="${wop.icon}"` : '',
+
+ contentPosition: (!!wop.contentPosition && (wop.contentPosition !== 'center')) ? `content-position="${wop.contentPosition}"` : '',
+
+ appendButtonChild: !!wop.appendButton ? `` : '',
+ }
+}
+
+function buildRadioChildren(widget, formConfig) {
+ let wop = widget.options
+ const childTag = !!wop.buttonStyle ? 'el-radio-button' : 'el-radio'
+ const borderAttr = !!wop.border ? `border` : ''
+ const styleAttr = `style="{display: ${wop.displayStyle}}"`
+ return `<${childTag} v-for="(item, index) in ${wop.name}Options" :key="index" :label="item.value"
+ :disabled="item.disabled" ${borderAttr} ${styleAttr}>{{item.label}}${childTag}>`
+}
+
+function buildCheckboxChildren(widget, formConfig) {
+ let wop = widget.options
+ const childTag = !!wop.buttonStyle ? 'el-checkbox-button' : 'el-checkbox'
+ const borderAttr = !!wop.border ? `border` : ''
+ const styleAttr = `style="{display: ${wop.displayStyle}}"`
+ return `<${childTag} v-for="(item, index) in ${wop.name}Options" :key="index" :label="item.value"
+ :disabled="item.disabled" ${borderAttr} ${styleAttr}>{{item.label}}${childTag}>`
+}
+
+function buildSelectChildren(widget, formConfig) {
+ let wop = widget.options
+ const childTag = 'el-option'
+ return `<${childTag} v-for="(item, index) in ${wop.name}Options" :key="index" :label="item.label"
+ :value="item.value" :disabled="item.disabled">${childTag}>`
+}
+
+const elTemplates = { //字段组件属性
+ 'input': (widget, formConfig) => {
+ const {vModel, readonly, disabled, size, type, showPassword, placeholder, clearable,
+ minlength, maxlength, showWordLimit, prefixIcon, suffixIcon, appendButtonChild} = getElAttrs(widget, formConfig)
+ return `${appendButtonChild}`
+ },
+
+ 'textarea': (widget, formConfig) => {
+ const {vModel, readonly, disabled, size, type, showPassword, placeholder, rows, clearable,
+ minlength, maxlength, showWordLimit} = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'number': (widget, formConfig) => {
+ const {vModel, disabled, size, type, showPassword, placeholder, controlsPosition, min, max, precision, step
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'radio': (widget, formConfig) => {
+ const {vModel, disabled, size} = getElAttrs(widget, formConfig)
+ const radioOptions = buildRadioChildren(widget, formConfig)
+ return `${radioOptions}`
+ },
+
+ 'checkbox': (widget, formConfig) => {
+ const {vModel, disabled, size} = getElAttrs(widget, formConfig)
+ const checkboxOptions = buildCheckboxChildren(widget, formConfig)
+ return `${checkboxOptions}`
+ },
+
+ 'select': (widget, formConfig) => {
+ const {vModel, disabled, size, clearable, filterable, allowCreate, defaultFirstOption, automaticDropdown,
+ multiple, multipleLimit, remote, placeholder} = getElAttrs(widget, formConfig)
+ const selectOptions = buildSelectChildren(widget, formConfig)
+ return `${selectOptions}`
+ },
+
+ 'time': (widget, formConfig) => {
+ const {vModel, readonly, disabled, size, placeholder, clearable, format, editable
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'time-range': (widget, formConfig) => {
+ const {vModel, readonly, disabled, size, startPlaceholder, endPlaceholder, clearable, format, editable
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'date': (widget, formConfig) => {
+ const {vModel, readonly, disabled, size, type, placeholder, clearable, format, valueFormat, editable
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'date-range': (widget, formConfig) => {
+ const {vModel, readonly, disabled, size, type, startPlaceholder, endPlaceholder, clearable, format, valueFormat, editable
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'switch': (widget, formConfig) => {
+ const {vModel, disabled, activeText, inactiveText, activeColor, inactiveColor, switchWidth
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'rate': (widget, formConfig) => {
+ const {vModel, disabled, rateMax, lowThreshold, highThreshold, allowHalf, showText,
+ showScore} = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'color': (widget, formConfig) => {
+ const {vModel, disabled, size
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'slider': (widget, formConfig) => {
+ const {vModel, disabled, sliderMin, sliderMax, sliderStep, sliderRange, sliderVertical
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'picture-upload': (widget, formConfig) => {
+ const {vModel, disabled, uploadAction, withCredentials, multipleSelect, showFileList, limit,
+ uploadTipSlotChild, pictureUploadIconChild} = getElAttrs(widget, formConfig)
+ let wop = widget.options
+ return `${uploadTipSlotChild} ${pictureUploadIconChild}`
+ },
+
+ 'file-upload': (widget, formConfig) => {
+ const {vModel, disabled, uploadAction, withCredentials, multipleSelect, showFileList, limit,
+ uploadTipSlotChild, fileUploadIconChild} = getElAttrs(widget, formConfig)
+ let wop = widget.options
+ return `${uploadTipSlotChild} ${fileUploadIconChild}`
+ },
+
+ 'rich-editor': (widget, formConfig) => {
+ const {vModel, disabled, placeholder
+ } = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+ 'cascader': (widget, formConfig) => {
+ const {vModel, disabled, size, clearable, filterable, placeholder} = getElAttrs(widget, formConfig)
+ let wop = widget.options
+ const optionsAttr = `:options="${wop.name}Options"`
+ return ``
+ },
+
+ 'static-text': (widget, formConfig) => {
+ return `${widget.options.textContent}
`
+ },
+
+ 'html-text': (widget, formConfig) => {
+ return ``
+ },
+
+ 'button': (widget, formConfig) => {
+ const {buttonType, buttonPlain, buttonRound, buttonCircle, buttonIcon, disabled} = getElAttrs(widget, formConfig)
+ return `${widget.options.label}`
+ },
+
+ 'divider': (widget, formConfig) => {
+ const {contentPosition} = getElAttrs(widget, formConfig)
+ return ``
+ },
+
+}
+
+export function buildFieldWidget(widget, formConfig) {
+ let wop = widget.options
+ const label = wop.labelHidden ? '' : wop.label
+ const labelWidthAttr = wop.labelHidden ? `label-width="0"` : (!!wop.labelWidth ? `label-width="${wop.labelWidth}px"` : '')
+ const labelTooltipAttr = wop.labelTooltip ? `title="${wop.labelTooltip}"` : ''
+ const propAttr = `prop="${wop.name}"`
+
+ let classArray = []
+ !!wop.required && classArray.push('required')
+ !!wop.customClass && (wop.customClass.length > 0) && classArray.push(wop.customClass.join(' '))
+ if (!!wop.labelAlign) {
+ wop.labelAlign !== 'label-left-align' && classArray.push(wop.labelAlign)
+ } else if (!!widget.formItemFlag) {
+ //classArray.push(formConfig.labelAlign || 'label-left-align')
+ formConfig.labelAlign !== 'label-left-align' && classArray.push(formConfig.labelAlign)
+ }
+ if (!widget.formItemFlag) {
+ classArray.push('static-content-item')
+ }
+ const classAttr = (classArray.length > 0) ? `class="${classArray.join(' ')}"` : ''
+
+ let customLabelDom =
+`${wop.labelIconPosition === 'front' ?
+ (!!wop.labelTooltip ?
+ `${wop.label}` :
+ `${wop.label}`
+ )
+ :
+ (!!wop.labelTooltip ?
+ `${wop.label}` :
+ `${wop.label}`
+ )
+}
+`
+ !wop.labelIconClass && (customLabelDom = '')
+
+ const fwDom = elTemplates[widget.type] ? elTemplates[widget.type](widget, formConfig) : null
+ const isFormItem = !!widget.formItemFlag
+ const vShowAttr = !!wop.hidden ? `v-show="false"` : ''
+ return isFormItem ?
+`
+ ${customLabelDom}
+ ${fwDom}
+`
+ :
+`${fwDom}
`
+}
+
+function genTemplate(formConfig, widgetList, vue3Flag = false) {
+ const submitAttr = !!vue3Flag ? `@submit.prevent` : `@submit.native.prevent`
+ let childrenList = []
+ widgetList.forEach(wgt => {
+ if (wgt.category === 'container') {
+ childrenList.push( buildContainerWidget(wgt, formConfig) )
+ } else {
+ childrenList.push( buildFieldWidget(wgt, formConfig) )
+ }
+ })
+
+ const formTemplate =
+`
+ ${!!childrenList ? childrenList.join('\n') : ''}
+`
+
+ return formTemplate
+}
+
+const genGlobalCSS = function (formConfig) {
+ const globalCssTemplate =
+` .el-input-number.full-width-input, .el-cascader.full-width-input {
+ width: 100% !important;
+ }
+
+ .el-form-item--medium {
+ .el-radio {
+ line-height: 36px !important;
+ }
+
+ .el-rate{
+ margin-top: 8px;
+ }
+ }
+
+ .el-form-item--small {
+ .el-radio {
+ line-height: 32px !important;
+ }
+
+ .el-rate{
+ margin-top: 6px;
+ }
+ }
+
+ .el-form-item--mini {
+ .el-radio {
+ line-height: 28px !important;
+ }
+
+ .el-rate{
+ margin-top: 4px;
+ }
+ }
+
+ .clear-fix:before, .clear-fix:after {
+ display: table;
+ content: "";
+ }
+
+ .clear-fix:after {
+ clear: both;
+ }
+
+ .float-right {
+ float: right;
+ }
+
+${formConfig.cssCode}`
+
+ return globalCssTemplate
+}
+
+const genScopedCSS = function (formConfig, vue3Flag = false) {
+ //const vDeep = !!vue3Flag ? `::v-deep` : `:deep`
+ const cssTemplate =
+` div.table-container {
+ table.table-layout {
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: collapse;
+
+ td.table-cell {
+ display: table-cell;
+ height: 36px;
+ border: 1px solid #e1e2e3;
+ }
+ }
+ }
+
+ div.tab-container {
+ }
+
+ .label-left-align ${!!vue3Flag ? `:deep(.el-form-item__label)` : `::v-deep .el-form-item__label`} {
+ text-align: left;
+ }
+
+ .label-center-align ${!!vue3Flag ? `:deep(.el-form-item__label)` : `::v-deep .el-form-item__label`} {
+ text-align: center;
+ }
+
+ .label-right-align ${!!vue3Flag ? `:deep(.el-form-item__label)` : `::v-deep .el-form-item__label`} {
+ text-align: right;
+ }
+
+ .custom-label {
+ }
+
+ .static-content-item {
+ min-height: 20px;
+ display: flex;
+ align-items: center;
+
+ ${!!vue3Flag ? `:deep(.el-divider--horizontal)` : `::v-deep .el-divider--horizontal`} {
+ margin: 0;
+ }
+ }`
+
+ return cssTemplate
+}
+
+/**
+ * 注册容器组件的代码生成器
+ * @param containerType 容器类型,必须唯一
+ * @param ctGenerator 代码生成器函数,接收两个参数(containerWidget, formConfig),返回生成的容器组件代码
+ */
+export const registerCWGenerator = function (containerType, ctGenerator) {
+ containerTemplates[containerType] = ctGenerator
+}
+
+/**
+ * 注册字段组件的代码生成器
+ * @param fieldType 字段类型,必须唯一
+ * @param ftGenerator 代码生成器函数,接收两个参数(fieldWidget, formConfig),返回生成的字段组件代码
+ */
+export const registerFWGenerator = function (fieldType, ftGenerator) {
+ elTemplates[fieldType] = ftGenerator
+}
+
+export const genSFC = function (formConfig, widgetList, beautifier, vue3Flag = false) {
+ const html = beautifier.html(genTemplate(formConfig, widgetList, vue3Flag), beautifierOpts.html)
+ const js = beautifier.js(!!vue3Flag ? genVue3JS(formConfig, widgetList): genVue2JS(formConfig, widgetList), beautifierOpts.js)
+ const globalCss = beautifier.css(genGlobalCSS(formConfig), beautifierOpts.css)
+ const scopedCss = beautifier.css(genScopedCSS(formConfig, vue3Flag), beautifierOpts.css)
+
+ return `
+
+
+${html}
+
+
+
+
+
+
+`
+}
diff --git a/src/utils/smart-vue-i18n/index.js b/src/utils/smart-vue-i18n/index.js
new file mode 100644
index 0000000..2072cac
--- /dev/null
+++ b/src/utils/smart-vue-i18n/index.js
@@ -0,0 +1,56 @@
+import {deepAssign, get} from './utils'
+
+const install = (Vue, options) => {
+ const proto = Vue.prototype
+ proto.$si18n = proto.$si18n || {}
+ // 初始化多语言
+ deepAssign(proto.$si18n, options)
+
+ const _vm = new Vue({
+ data: options
+ })
+ Object.defineProperty(Vue.prototype.$si18n, 'lang', {
+ get() {
+ return _vm.lang
+ }
+ })
+
+ proto.$st = (path, ...args) => {
+ let messages = _vm.messages[_vm.lang]
+ if (!proto.$si18n.messages) {
+ if (process.env.NODE_ENV !== 'production') {
+ console.error('[yxI18n] Locale not correctly registered')
+ }
+ return () => path
+ }
+ //const message = get(messages, path) || get(messages, path)
+ const message = get(messages, path)
+ return typeof message === 'function'
+ ? message(...args)
+ : (message !== null ? message : path)
+ }
+
+ proto.$st2 = (path, path2) => {
+ let messages = _vm.messages[_vm.lang]
+ const message = get(messages, path)
+ return (message !== null) ? message : get(messages, path2)
+ }
+
+ proto.$si18n.add = (messages = {}) => {
+ deepAssign(proto.$si18n.messages, messages)
+ }
+
+ proto.$si18n.setLang = lang => {
+ _vm.lang = lang
+ }
+
+ Vue.mixin({
+ beforeCreate() {
+ this.$options.i18n && this.$si18n.add(this.$options.i18n)
+ }
+ })
+}
+
+export default {
+ install
+}
diff --git a/src/utils/smart-vue-i18n/utils.js b/src/utils/smart-vue-i18n/utils.js
new file mode 100644
index 0000000..0ae1609
--- /dev/null
+++ b/src/utils/smart-vue-i18n/utils.js
@@ -0,0 +1,47 @@
+// 值存在
+export function isDef(value) {
+ return value !== undefined && value !== null
+}
+
+// 对象映射 'a.b' {a: {b: 'val'}}
+export function get(object, path) {
+ const keys = path.split('.')
+ let result = object
+
+ keys.forEach(key => {
+ result = isDef(result) && isDef(result[key]) ? result[key] : null
+ })
+
+ return result
+}
+
+// 是否是对象
+export function isObj(x) {
+ const type = typeof x
+ return x !== null && (type === 'object' || type === 'function')
+}
+
+// 深拷贝
+const { hasOwnProperty } = Object.prototype
+
+function assignKey(to, from, key) {
+ const val = from[key]
+
+ if (!isDef(val)) {
+ return
+ }
+
+ if (!hasOwnProperty.call(to, key) || !isObj(val)) {
+ to[key] = val
+ } else {
+ to[key] = deepAssign(Object(to[key]), from[key])
+ }
+}
+
+export function deepAssign(to, from) {
+ Object.keys(from).forEach(key => {
+ assignKey(to, from, key)
+ })
+
+ return to
+}
diff --git a/src/utils/util.js b/src/utils/util.js
new file mode 100644
index 0000000..1f10847
--- /dev/null
+++ b/src/utils/util.js
@@ -0,0 +1,370 @@
+import Clipboard from 'clipboard'
+
+export function isNull(value) {
+ return (value === null) || (value === undefined);
+}
+
+export function isNotNull(value) {
+ return (value !== null) && (value !== undefined);
+}
+
+export function isEmptyStr(str) {
+ //return (str === undefined) || (!str) || (!/[^\s]/.test(str));
+ return (str === undefined) || (!str && (str !== 0) && (str !== '0')) || (!/[^\s]/.test(str));
+}
+
+export const generateId = function() {
+ return Math.floor(Math.random() * 100000 + Math.random() * 20000 + Math.random() * 5000);
+};
+
+export const deepClone = function (origin) {
+ if (origin === undefined) {
+ return undefined
+ }
+
+ return JSON.parse(JSON.stringify(origin))
+}
+
+export const overwriteObj = function(obj1, obj2) { /* 浅拷贝对象属性,obj2覆盖obj1 */
+ // for (let prop in obj2) {
+ // if (obj2.hasOwnProperty(prop)) {
+ // obj1[prop] = obj2[prop]
+ // }
+ // }
+
+ Object.keys(obj2).forEach(prop => {
+ obj1[prop] = obj2[prop]
+ })
+}
+
+export const addWindowResizeHandler = function (handler) {
+ let oldHandler = window.onresize
+ if (typeof window.onresize != 'function') {
+ window.onresize = handler
+ } else {
+ window.onresize = function () {
+ oldHandler()
+ handler()
+ }
+ }
+}
+
+const createStyleSheet = function() {
+ let head = document.head || document.getElementsByTagName('head')[0];
+ let style = document.createElement('style');
+ style.type = 'text/css';
+ head.appendChild(style);
+ return style.sheet;
+}
+
+export const insertCustomCssToHead = function (cssCode, formId = '') {
+ let head = document.getElementsByTagName('head')[0]
+ let oldStyle = document.getElementById('vform-custom-css')
+ if (!!oldStyle) {
+ head.removeChild(oldStyle) //先清除后插入!!
+ }
+ if (!!formId) {
+ oldStyle = document.getElementById('vform-custom-css' + '-' + formId)
+ !!oldStyle && head.removeChild(oldStyle) //先清除后插入!!
+ }
+
+ let newStyle = document.createElement('style')
+ newStyle.type = 'text/css'
+ newStyle.rel = 'stylesheet'
+ newStyle.id = !!formId ? 'vform-custom-css' + '-' + formId : 'vform-custom-css'
+ try {
+ newStyle.appendChild(document.createTextNode(cssCode))
+ } catch(ex) {
+ newStyle.styleSheet.cssText = cssCode
+ }
+
+ head.appendChild(newStyle)
+}
+
+export const insertGlobalFunctionsToHtml = function (functionsCode, formId = '') {
+ let bodyEle = document.getElementsByTagName('body')[0]
+ let oldScriptEle = document.getElementById('v_form_global_functions')
+ !!oldScriptEle && bodyEle.removeChild(oldScriptEle) //先清除后插入!!
+ if (!!formId) {
+ oldScriptEle = document.getElementById('v_form_global_functions' + '-' + formId)
+ !!oldScriptEle && bodyEle.removeChild(oldScriptEle) //先清除后插入!!
+ }
+
+ let newScriptEle = document.createElement('script')
+ newScriptEle.id = !!formId ? 'v_form_global_functions' + '-' + formId : 'v_form_global_functions'
+ newScriptEle.type = 'text/javascript'
+ newScriptEle.innerHTML = functionsCode
+ bodyEle.appendChild(newScriptEle)
+}
+
+export const optionExists = function(optionsObj, optionName) {
+ if (!optionsObj) {
+ return false
+ }
+
+ return Object.keys(optionsObj).indexOf(optionName) > -1
+}
+
+export const loadRemoteScript = function(srcPath, callback) { /*加载远程js,加载成功后执行回调函数*/
+ let sid = encodeURIComponent(srcPath)
+ let oldScriptEle = document.getElementById(sid)
+
+ if (!oldScriptEle) {
+ let s = document.createElement('script')
+ s.src = srcPath
+ s.id = sid
+ document.body.appendChild(s)
+
+ s.onload = s.onreadystatechange = function (_, isAbort) { /* 借鉴自ace.js */
+ if (isAbort || !s.readyState || s.readyState === "loaded" || s.readyState === "complete") {
+ s = s.onload = s.onreadystatechange = null
+ if (!isAbort) {
+ callback()
+ }
+ }
+ }
+ }
+}
+
+export function traverseFieldWidgets(widgetList, handler, parent = null) {
+ if (!widgetList) {
+ return
+ }
+
+ widgetList.forEach(w => {
+ if (w.formItemFlag) {
+ handler(w, parent)
+ } else if (w.type === 'grid') {
+ w.cols.forEach(col => {
+ traverseFieldWidgets(col.widgetList, handler, w)
+ })
+ } else if (w.type === 'table') {
+ w.rows.forEach(row => {
+ row.cols.forEach(cell => {
+ traverseFieldWidgets(cell.widgetList, handler, w)
+ })
+ })
+ } else if (w.type === 'tab') {
+ w.tabs.forEach(tab => {
+ traverseFieldWidgets(tab.widgetList, handler, w)
+ })
+ } else if (w.type === 'sub-form') {
+ traverseFieldWidgets(w.widgetList, handler, w)
+ } else if (w.category === 'container') { //自定义容器
+ traverseFieldWidgets(w.widgetList, handler, w)
+ }
+ })
+}
+
+export function traverseContainerWidgets(widgetList, handler) {
+ if (!widgetList) {
+ return
+ }
+
+ widgetList.forEach(w => {
+ if (w.category === 'container') {
+ handler(w)
+ }
+
+ if (w.type === 'grid') {
+ w.cols.forEach(col => {
+ traverseContainerWidgets(col.widgetList, handler)
+ })
+ } else if (w.type === 'table') {
+ w.rows.forEach(row => {
+ row.cols.forEach(cell => {
+ traverseContainerWidgets(cell.widgetList, handler)
+ })
+ })
+ } else if (w.type === 'tab') {
+ w.tabs.forEach(tab => {
+ traverseContainerWidgets(tab.widgetList, handler)
+ })
+ } else if (w.type === 'sub-form') {
+ traverseContainerWidgets(w.widgetList, handler)
+ } else if (w.category === 'container') { //自定义容器
+ traverseContainerWidgets(w.widgetList, handler)
+ }
+ })
+}
+
+export function traverseAllWidgets(widgetList, handler) {
+ if (!widgetList) {
+ return
+ }
+
+ widgetList.forEach(w => {
+ handler(w)
+
+ if (w.type === 'grid') {
+ w.cols.forEach(col => {
+ handler(col)
+ traverseAllWidgets(col.widgetList, handler)
+ })
+ } else if (w.type === 'table') {
+ w.rows.forEach(row => {
+ row.cols.forEach(cell => {
+ handler(cell)
+ traverseAllWidgets(cell.widgetList, handler)
+ })
+ })
+ } else if (w.type === 'tab') {
+ w.tabs.forEach(tab => {
+ traverseAllWidgets(tab.widgetList, handler)
+ })
+ } else if (w.type === 'sub-form') {
+ traverseAllWidgets(w.widgetList, handler)
+ } else if (w.category === 'container') { //自定义容器
+ traverseAllWidgets(w.widgetList, handler)
+ }
+ })
+}
+
+function handleWidgetForTraverse(widget, handler) {
+ if (!!widget.category) {
+ traverseFieldWidgetsOfContainer(widget, handler)
+ } else if (widget.formItemFlag) {
+ handler(widget)
+ }
+}
+
+/**
+ * 遍历容器内的字段组件
+ * @param con
+ * @param handler
+ */
+export function traverseFieldWidgetsOfContainer(con, handler) {
+ if (con.type === 'grid') {
+ con.cols.forEach(col => {
+ col.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ })
+ } else if (con.type === 'table') {
+ con.rows.forEach(row => {
+ row.cols.forEach(cell => {
+ cell.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ })
+ })
+ } else if (con.type === 'tab') {
+ con.tabs.forEach(tab => {
+ tab.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ })
+ } else if (con.type === 'sub-form') {
+ con.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ } else if (con.category === 'container') { //自定义容器
+ con.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ }
+}
+
+/**
+ * 获取所有字段组件
+ * @param widgetList
+ * @returns {[]}
+ */
+export function getAllFieldWidgets(widgetList) {
+ if (!widgetList) {
+ return []
+ }
+
+ let result = []
+ let handlerFn = (w) => {
+ result.push({
+ type: w.type,
+ name: w.options.name,
+ field: w
+ })
+ }
+ traverseFieldWidgets(widgetList, handlerFn)
+
+ return result
+}
+
+/**
+ * 获取所有容器组件
+ * @param widgetList
+ * @returns {[]}
+ */
+export function getAllContainerWidgets(widgetList) {
+ if (!widgetList) {
+ return []
+ }
+
+ let result = []
+ let handlerFn = (w) => {
+ result.push({
+ type: w.type,
+ name: w.options.name,
+ container: w
+ })
+ }
+ traverseContainerWidgets(widgetList, handlerFn)
+
+ return result
+}
+
+export function copyToClipboard(content, clickEvent, $message, successMsg, errorMsg) {
+ const clipboard = new Clipboard(clickEvent.target, {
+ text: () => content
+ })
+
+ clipboard.on('success', () => {
+ $message.success(successMsg)
+ clipboard.destroy()
+ })
+
+ clipboard.on('error', () => {
+ $message.error(errorMsg)
+ clipboard.destroy()
+ })
+
+ clipboard.onClick(clickEvent)
+}
+
+export function getQueryParam(variable) {
+ let query = window.location.search.substring(1);
+ let vars = query.split("&")
+ for (let i=0; i 0 && resultList.push(`${fop.name}: [${fieldRules.join(',')}],`)
+ }
+}
+
+export function buildFieldOptionsFn(formConfig, widgetList, resultList) {
+ return function(fieldWidget) {
+ const fop = fieldWidget.options
+ const ft = fieldWidget.type
+ if ((ft === 'radio') || (ft === 'checkbox') || (ft === 'select') || (ft === 'cascader')) {
+ resultList.push(`${fop.name}Options: ${JSON.stringify(fop.optionItems)},`)
+ }
+ }
+}
+
+export function buildUploadDataFn(formConfig, widgetList, resultList) {
+ return function(fieldWidget) {
+ const fop = fieldWidget.options
+ const ft = fieldWidget.type
+ if ((ft === 'picture-upload') || (ft === 'file-upload')) {
+ resultList.push(`${fop.name}FileList: [],`)
+ resultList.push(`${fop.name}UploadHeaders: {},`)
+ resultList.push(`${fop.name}UploadData: {},`)
+ }
+ }
+}
+
+export function buildActiveTabs(formConfig, widgetList) {
+ let resultList = []
+ const handlerFn = function (cw) {
+ const cop = cw.options
+ const ct = cw.type
+ if (ct === 'tab') {
+ cw.tabs.length > 0 && resultList.push(`'${cop.name}ActiveTab': '${cw.tabs[0].options.name}',`)
+ }
+ }
+ traverseContainerWidgets(widgetList, handlerFn)
+
+ return resultList
+}
+
+export const genVue2JS = function (formConfig, widgetList) {
+ let defaultValueList = []
+ let rulesList = []
+ let fieldOptions = []
+ let uploadData = []
+ traverseFieldWidgets(widgetList, (widget) => {
+ buildDefaultValueListFn(formConfig, widgetList, defaultValueList)(widget)
+ buildRulesListFn(formConfig, widgetList, rulesList)(widget)
+ buildFieldOptionsFn(formConfig, widgetList, fieldOptions)(widget)
+ buildUploadDataFn(formConfig, widgetList, uploadData)(widget)
+ })
+
+ const activeTabs = buildActiveTabs(formConfig, widgetList)
+
+ const v2JSTemplate =
+` export default {
+ components: {},
+ props: {},
+ data() {
+ return {
+ ${formConfig.modelName}: {
+ ${defaultValueList.join('\n')}
+ },
+
+ ${formConfig.rulesName}: {
+ ${rulesList.join('\n')}
+ },
+
+ ${activeTabs.join('\n')}
+
+ ${fieldOptions.join('\n')}
+
+ ${uploadData.join('\n')}
+ }
+ },
+ computed: {},
+ watch: {},
+ created() {
+ },
+ mounted() {
+ },
+ methods: {
+ submitForm() {
+ this.$refs['vForm'].validate(valid => {
+ if (!valid) return
+
+ //TODO: 提交表单
+ })
+ },
+
+ resetForm() {
+ this.$refs['vForm'].resetFields()
+ }
+ }
+ }`
+
+ return v2JSTemplate
+}
diff --git a/src/utils/vue3js-generator.js b/src/utils/vue3js-generator.js
new file mode 100644
index 0000000..9a2daa6
--- /dev/null
+++ b/src/utils/vue3js-generator.js
@@ -0,0 +1,70 @@
+import {
+ buildActiveTabs,
+ buildDefaultValueListFn,
+ buildFieldOptionsFn,
+ buildRulesListFn, buildUploadDataFn
+} from "@/utils/vue2js-generator";
+import {traverseFieldWidgets} from "@/utils/util";
+
+export const genVue3JS = function (formConfig, widgetList) {
+ let defaultValueList = []
+ let rulesList = []
+ let fieldOptions = []
+ let uploadData = []
+ traverseFieldWidgets(widgetList, (widget) => {
+ buildDefaultValueListFn(formConfig, widgetList, defaultValueList)(widget)
+ buildRulesListFn(formConfig, widgetList, rulesList)(widget)
+ buildFieldOptionsFn(formConfig, widgetList, fieldOptions)(widget)
+ buildUploadDataFn(formConfig, widgetList, uploadData)(widget)
+ })
+
+ const activeTabs = buildActiveTabs(formConfig, widgetList)
+
+ const v3JSTemplate =
+` import { defineComponent, toRefs, reactive, getCurrentInstance } from 'vue'
+
+ export default defineComponent({
+ components: {},
+ props: {},
+ setup() {
+ const state = reactive({
+ ${formConfig.modelName}: {
+ ${defaultValueList.join('\n')}
+ },
+
+ ${formConfig.rulesName}: {
+ ${rulesList.join('\n')}
+ },
+
+ ${activeTabs.join('\n')}
+
+ ${fieldOptions.join('\n')}
+
+ ${uploadData.join('\n')}
+ })
+
+ const instance = getCurrentInstance()
+
+ const submitForm = () => {
+ instance.proxy.$refs['vForm'].validate(valid => {
+ if (!valid) return
+
+ //TODO: 提交表单
+ })
+ }
+
+ const resetForm = () => {
+ instance.proxy.$refs['vForm'].resetFields()
+ }
+
+ return {
+ ...toRefs(state),
+ submitForm,
+ resetForm
+ }
+ }
+ })`
+
+ return v3JSTemplate
+}
+
diff --git a/vue.config.js b/vue.config.js
new file mode 100644
index 0000000..f0cc663
--- /dev/null
+++ b/vue.config.js
@@ -0,0 +1,93 @@
+'use strict'
+
+const path = require('path')
+const IS_PROD = process.env.NODE_ENV === 'production'
+
+function resolve (dir) {
+ return path.join(__dirname, dir)
+}
+
+const npmConfigArgv = (process.env.npm_config_argv === undefined) ? null : JSON.parse(process.env.npm_config_argv)
+/*
+console.log('npm config: ', npmConfigArgv)
+const procArgv = process.argv
+console.log('npm config: ', procArgv)
+*/
+let buildProdFlag = false
+if (!!npmConfigArgv) {
+ npmConfigArgv.original.forEach(cItem => {
+ if (cItem === 'build') {
+ buildProdFlag = true
+ }
+ })
+}
+
+const mvdir = require('mvdir');
+if (IS_PROD && buildProdFlag) {
+ mvdir('index_template/index_prod.html', 'public/index.html', { copy: true });
+} else {
+ mvdir('index_template/index_dev.html', 'public/index.html', { copy: true });
+}
+
+
+module.exports = {
+ publicPath: './',
+ assetsDir: './',
+
+ /* 开启vue运行时模板编译功能!! */
+ runtimeCompiler: true,
+
+ lintOnSave: false,
+
+ productionSourceMap: false,
+
+ /* 指定node_modules目录中需要做babel转译的依赖库 */
+ transpileDependencies: [
+ 'element-ui', 'vuedraggable',
+ ],
+
+ css: {
+ loaderOptions: {
+ scss: {
+ /* 自动引入全局scss文件 */
+ prependData: `
+ @import "./src/styles/global.scss";
+ `
+ }
+ }
+ },
+
+ configureWebpack: (config) => {
+ config.devtool = 'source-map'
+ config.output.libraryExport = 'default' /* 解决import UMD打包文件时, 组件install方法执行报错的问题!! */
+
+ if (IS_PROD && buildProdFlag) { /* 仅生产环境使用 */
+ /* CDN打包,需要修改index.html加入CDN资源 */
+ config.externals = {
+ 'vue': 'Vue',
+ 'element-ui': 'ELEMENT',
+ //'quill': 'Quill',
+ }
+ }
+ },
+
+ chainWebpack: config => {
+ /* 配置svg图标自动加载 begin */
+ config.module
+ .rule('svg')
+ .exclude.add(resolve('src/icons'))
+ .end()
+ config.module
+ .rule('icons')
+ .test(/\.svg$/)
+ .include.add(resolve('src/icons'))
+ .end()
+ .use('svg-sprite-loader')
+ .loader('svg-sprite-loader')
+ .options({
+ symbolId: 'icon-[name]'
+ })
+ /* 配置svg图标自动加载 end */
+ },
+
+}