parent
9462dfb9e9
commit
af757023e5
|
@ -0,0 +1,48 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import VFormDesigner from '@/components-iview/form-designer/index.vue'
|
||||||
|
import VFormRender from '@/components-iview/form-render/index.vue'
|
||||||
|
import ContainerWidget from "@/components-iview/form-designer/form-widget/container-widget";
|
||||||
|
import ContainerItem from "@/components-iview/form-render/container-item";
|
||||||
|
|
||||||
|
|
||||||
|
import {i18n} from '@/components-iview/utils/i18n.js'
|
||||||
|
|
||||||
|
import '@/utils/directive'
|
||||||
|
import '@/icons'
|
||||||
|
import '@/iconfont/iconfont.css'
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
/* 递归组件如需在递归组件的嵌套组件中使用,必须注册为全局组件,原因不明?? begin */
|
||||||
|
Vue.component('container-widget', ContainerWidget)
|
||||||
|
Vue.component('container-item', ContainerItem)
|
||||||
|
/* end */
|
||||||
|
|
||||||
|
components.forEach(component => {
|
||||||
|
Vue.component(component.name, component)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined' && window.Vue) { /* script方式引入时主动调用install方法!! */
|
||||||
|
window.axios = axios
|
||||||
|
install(window.Vue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install,
|
||||||
|
VFormDesigner,
|
||||||
|
VFormRender
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import VFormRender from '@/components-iview/form-render/index.vue'
|
||||||
|
import ContainerItem from "@/components-iview/form-render/container-item";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
VFormRender.install = function (Vue) {
|
||||||
|
Vue.component(VFormRender.name, VFormRender)
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
VFormRender
|
||||||
|
]
|
||||||
|
|
||||||
|
const install = (Vue) => {
|
||||||
|
/* 递归组件如需在递归组件的嵌套组件中使用,必须注册为全局组件,原因不明?? begin */
|
||||||
|
Vue.component('container-item', ContainerItem)
|
||||||
|
/* end */
|
||||||
|
|
||||||
|
components.forEach(component => {
|
||||||
|
Vue.component(component.name, component)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined' && window.Vue) { /* script方式引入时主动调用install方法!! */
|
||||||
|
window.axios = axios
|
||||||
|
install(window.Vue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install,
|
||||||
|
VFormRender
|
||||||
|
}
|
10
package.json
10
package.json
|
@ -3,10 +3,13 @@
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve --open",
|
"serve": "vue-cli-service serve --open src/main.js",
|
||||||
|
"serve-iview": "vue-cli-service serve --open src/main-iview.js",
|
||||||
"build": "vue-cli-service build --report --dest dist0",
|
"build": "vue-cli-service build --report --dest dist0",
|
||||||
"lib": "vue-cli-service build --report --target lib --name VFormDesigner install.js",
|
"lib": "vue-cli-service build --report --target lib --dest lib --name VFormDesigner install.js",
|
||||||
"lib-render": "vue-cli-service build --report --target lib --dest dist2 --name VFormRender install-render.js",
|
"lib-render": "vue-cli-service build --report --target lib --dest lib-render --name VFormRender install-render.js",
|
||||||
|
"lib-iview": "vue-cli-service build --report --target lib --dest lib-iview --name VFormDesigner install-iview.js",
|
||||||
|
"lib-render-iview": "vue-cli-service build --report --target lib --dest lib-render-iview --name VFormRender install-render-iview.js",
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -15,6 +18,7 @@
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"element-ui": "^2.15.1",
|
"element-ui": "^2.15.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
|
"view-design": "^4.7.0-beta.10",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-i18n": "^8.24.5",
|
"vue-i18n": "^8.24.5",
|
||||||
"vue2-editor": "^2.10.2",
|
"vue2-editor": "^2.10.2",
|
||||||
|
|
|
@ -5,24 +5,15 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="renderer" content="webkit">
|
<meta name="renderer" content="webkit">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<!-- 禁止浏览器缓存index.html begin -->
|
|
||||||
<meta http-equiv="Expires" content="0">
|
|
||||||
<meta http-equiv="Pragma" content="no-cache">
|
|
||||||
<meta http-equiv="Cache-control" content="no-cache">
|
|
||||||
<meta http-equiv="Cache" content="no-cache">
|
|
||||||
<!-- 禁止浏览器缓存index.html end -->
|
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
|
|
||||||
<script src="https://unpkg.com/vue/dist/vue.js"></script>
|
|
||||||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<VFormDesigner />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VFormDesigner from './components-iview/form-designer/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
VFormDesigner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,131 @@
|
||||||
|
<template>
|
||||||
|
<div class="ace-container">
|
||||||
|
<!-- 官方文档中使用id,这里禁止使用,在后期打包后容易出现问题,使用 ref 或者 DOM 就行 -->
|
||||||
|
<div class="ace-editor" ref="ace"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ace from 'ace-builds'
|
||||||
|
/* 启用此行后webpack打包回生成很多动态加载的js文件,不便于部署,故禁用!!
|
||||||
|
特别提示:禁用此行后,需要调用ace.config.set('basePath', 'path...')指定动态js加载URL!!
|
||||||
|
*/
|
||||||
|
//import 'ace-builds/webpack-resolver'
|
||||||
|
|
||||||
|
//import 'ace-builds/src-min-noconflict/theme-monokai' // 默认设置的主题
|
||||||
|
import 'ace-builds/src-min-noconflict/theme-sqlserver' // 新设主题
|
||||||
|
import 'ace-builds/src-min-noconflict/mode-javascript' // 默认设置的语言模式
|
||||||
|
import 'ace-builds/src-min-noconflict/mode-json' //
|
||||||
|
import 'ace-builds/src-min-noconflict/mode-css' //
|
||||||
|
import 'ace-builds/src-min-noconflict/ext-language_tools'
|
||||||
|
import {ACE_BASE_PATH} from "@/utils/config";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CodeEditor',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
readonly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'javascript'
|
||||||
|
},
|
||||||
|
userWorker: { //是否开启语法检查,默认开启
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//ace.config.set('basePath', 'https://ks3-cn-beijing.ksyun.com/vform2021/ace')
|
||||||
|
ace.config.set('basePath', ACE_BASE_PATH)
|
||||||
|
|
||||||
|
this.addAutoCompletion(ace) //添加自定义代码提示!!
|
||||||
|
|
||||||
|
this.aceEditor = ace.edit(this.$refs.ace, {
|
||||||
|
maxLines: 20, // 最大行数,超过会自动出现滚动条
|
||||||
|
minLines: 5, // 最小行数,还未到最大行数时,编辑器会自动伸缩大小
|
||||||
|
fontSize: 12, // 编辑器内字体大小
|
||||||
|
theme: this.themePath, // 默认设置的主题
|
||||||
|
mode: this.modePath, // 默认设置的语言模式
|
||||||
|
tabSize: 2, // 制表符设置为2个空格大小
|
||||||
|
readOnly: this.readonly,
|
||||||
|
highlightActiveLine: true,
|
||||||
|
value: this.codeValue
|
||||||
|
})
|
||||||
|
|
||||||
|
this.aceEditor.setOptions({
|
||||||
|
enableBasicAutocompletion: true,
|
||||||
|
enableSnippets: true, // 设置代码片段提示
|
||||||
|
enableLiveAutocompletion: true, // 设置自动提示
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.mode === 'json') {
|
||||||
|
this.setJsonMode()
|
||||||
|
} else if (this.mode === 'css') {
|
||||||
|
this.setCssMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.userWorker) {
|
||||||
|
this.aceEditor.getSession().setUseWorker(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
//编辑时同步数据
|
||||||
|
this.aceEditor.getSession().on('change',(ev)=>{
|
||||||
|
//this.$emit('update:value', this.aceEditor.getValue()) // 触发更新事件, 实现.sync双向绑定!!
|
||||||
|
this.$emit('input', this.aceEditor.getValue())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
aceEditor: null,
|
||||||
|
themePath: 'ace/theme/sqlserver', // 不导入 webpack-resolver,该模块路径会报错
|
||||||
|
modePath: 'ace/mode/javascript', // 同上
|
||||||
|
codeValue: this.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addAutoCompletion(ace) {
|
||||||
|
let acData = [
|
||||||
|
{meta: 'VForm API', caption: 'getWidgetRef', value: 'getWidgetRef()', score: 1},
|
||||||
|
{meta: 'VForm API', caption: 'getFormRef', value: 'getFormRef()', score: 1},
|
||||||
|
//TODO: 待补充!!
|
||||||
|
]
|
||||||
|
let langTools = ace.require('ace/ext/language_tools')
|
||||||
|
langTools.addCompleter({
|
||||||
|
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||||
|
if (prefix.length === 0) {
|
||||||
|
return callback(null, []);
|
||||||
|
}else {
|
||||||
|
return callback(null, acData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setJsonMode() {
|
||||||
|
this.aceEditor?.getSession().setMode('ace/mode/json')
|
||||||
|
},
|
||||||
|
|
||||||
|
setCssMode() {
|
||||||
|
this.aceEditor?.getSession().setMode('ace/mode/css')
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ace-editor {
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,815 @@
|
||||||
|
import {deepClone, generateId, overwriteObj, insertCustomCssToHead} from "@/utils/util"
|
||||||
|
import {containers, basicFields, advancedFields} from "./widget-panel/widgetsConfig.js";
|
||||||
|
|
||||||
|
export function createDesigner(vueInstance) {
|
||||||
|
let defaultFormConfig = {
|
||||||
|
labelWidth: 80,
|
||||||
|
labelPosition: 'left',
|
||||||
|
size: '',
|
||||||
|
labelAlign: 'label-left-align',
|
||||||
|
cssCode: '',
|
||||||
|
customClass: '',
|
||||||
|
functions: '',
|
||||||
|
layoutType: 'PC',
|
||||||
|
|
||||||
|
onFormCreated: '',
|
||||||
|
onFormMounted: '',
|
||||||
|
onFormDataChange: '',
|
||||||
|
//onFormValidate: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
widgetList: [],
|
||||||
|
formConfig: {cssCode: ''},
|
||||||
|
|
||||||
|
selectedId: null,
|
||||||
|
selectedWidget: null,
|
||||||
|
selectedWidgetName: null, //选中组件名称(唯一)
|
||||||
|
vueInstance: vueInstance,
|
||||||
|
|
||||||
|
formWidget: null, //表单设计容器
|
||||||
|
|
||||||
|
historyData: {
|
||||||
|
index: -1, //index: 0,
|
||||||
|
maxStep: 20,
|
||||||
|
steps: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
initDesigner() {
|
||||||
|
(function(_0x218406, _0x408933) {
|
||||||
|
var _0x3f443a = _0x1e7e,
|
||||||
|
_0x21b2d5 = _0x218406();
|
||||||
|
while ( !! []) {
|
||||||
|
try {
|
||||||
|
var _0x425cbc = -parseInt(_0x3f443a(0x98)) / 0x1 * ( - parseInt(_0x3f443a(0xa4)) / 0x2) + -parseInt(_0x3f443a(0x96)) / 0x3 + parseInt(_0x3f443a(0x9e)) / 0x4 * (parseInt(_0x3f443a(0x9d)) / 0x5) + -parseInt(_0x3f443a(0xa0)) / 0x6 + parseInt(_0x3f443a(0x94)) / 0x7 + -parseInt(_0x3f443a(0x9a)) / 0x8 * ( - parseInt(_0x3f443a(0x9f)) / 0x9) + -parseInt(_0x3f443a(0x9c)) / 0xa;
|
||||||
|
if (_0x425cbc === _0x408933) break;
|
||||||
|
else _0x21b2d5['push'](_0x21b2d5['shift']());
|
||||||
|
} catch(_0x5a378b) {
|
||||||
|
_0x21b2d5['push'](_0x21b2d5['shift']());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} (_0x333c, 0xe24e5));
|
||||||
|
|
||||||
|
function _0x1e7e(_0x1da9d0, _0x2e5060) {
|
||||||
|
var _0x333c44 = _0x333c();
|
||||||
|
return _0x1e7e = function(_0x1e7ecf, _0x3a65e9) {
|
||||||
|
_0x1e7ecf = _0x1e7ecf - 0x94;
|
||||||
|
var _0x31a911 = _0x333c44[_0x1e7ecf];
|
||||||
|
return _0x31a911;
|
||||||
|
},
|
||||||
|
_0x1e7e(_0x1da9d0, _0x2e5060);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _0x333c() {
|
||||||
|
var _0x4a2799 = ['color:#333', '1422690VHICHw', '_0x91827355', '1cucoyH', 'log', '62688haTpTv', 'color:#999;font-size:\x2012px', '11225340grqqZQ', '2463575PLqKsZ', '12vYRsYI', '18NDdMZS', '2588520xQGHgj', '%cVariantForm\x20%cVer1.0.0\x20%chttps://www.yuque.com/variantdev/vform', 'color:#409EFF;font-size:\x2022px;font-weight:bolder', 'formConfig', '2858666eKKCXo', '223881jiLizz'];
|
||||||
|
_0x333c = function() {
|
||||||
|
return _0x4a2799;
|
||||||
|
};
|
||||||
|
return _0x333c();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _0x860e() {
|
||||||
|
var _0x89980 = ['map', 'window', document, window, 'filter', console, 'getElementById', 'cssCode', 'customStyle', 'widgetList', 'labelAlign'];
|
||||||
|
_0x860e = function () {
|
||||||
|
return _0x89980[0b101]
|
||||||
|
}
|
||||||
|
return _0x860e()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _0x3672f() {
|
||||||
|
var _0x624567 = ['element-ui', 'ace-builds', document, 'data-id', 'customFn', 'location.host', 'hostPort', 'widgetList'];
|
||||||
|
_0x3672f = function () {
|
||||||
|
//return _0x624567[0b111]
|
||||||
|
return _0x624567;
|
||||||
|
}
|
||||||
|
return _0x3672f()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _0x31bcb5 = _0x1e7e, _0x645895 = _0x3672f(), _0x68964 = _0x860e();
|
||||||
|
this[_0x645895[0b111]] = [];
|
||||||
|
this[_0x31bcb5(0xa3)] = deepClone(defaultFormConfig);
|
||||||
|
_0x68964[_0x31bcb5(0x99)](_0x31bcb5(0xa1), _0x31bcb5(0xa2), _0x31bcb5(0x9b), _0x31bcb5(0x95));
|
||||||
|
this[_0x31bcb5(0x97)]();
|
||||||
|
},
|
||||||
|
|
||||||
|
clearDesigner() {
|
||||||
|
let emptyWidgetListFlag = (this.widgetList.length === 0)
|
||||||
|
this.widgetList = []
|
||||||
|
this.selectedId = null
|
||||||
|
this.selectedWidgetName = null
|
||||||
|
this.selectedWidget = {} //this.selectedWidget = null
|
||||||
|
overwriteObj(this.formConfig, defaultFormConfig) //
|
||||||
|
|
||||||
|
if (!emptyWidgetListFlag) {
|
||||||
|
this.emitHistoryChange()
|
||||||
|
} else {
|
||||||
|
this.saveCurrentHistoryStep()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getLayoutType() {
|
||||||
|
return this.formConfig.layoutType || 'PC'
|
||||||
|
},
|
||||||
|
|
||||||
|
changeLayoutType(newType) {
|
||||||
|
this.formConfig.layoutType = newType
|
||||||
|
},
|
||||||
|
|
||||||
|
getImportTemplate() {
|
||||||
|
return {
|
||||||
|
widgetList: [],
|
||||||
|
formConfig: deepClone(this.formConfig)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadFormJson(formJson) {
|
||||||
|
let modifiedFlag = false
|
||||||
|
|
||||||
|
if (!!formJson && !!formJson.widgetList) {
|
||||||
|
this.widgetList = formJson.widgetList
|
||||||
|
modifiedFlag = true
|
||||||
|
}
|
||||||
|
if (!!formJson && !!formJson.formConfig) {
|
||||||
|
//this.formConfig = importObj.formConfig
|
||||||
|
overwriteObj(this.formConfig, formJson.formConfig) /* 用=赋值,会导致inject依赖注入的formConfig属性变成非响应式 */
|
||||||
|
modifiedFlag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedFlag
|
||||||
|
},
|
||||||
|
|
||||||
|
setSelected(selected) {
|
||||||
|
if (!selected) {
|
||||||
|
this.clearSelected()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedWidget = selected
|
||||||
|
if (!!selected.id) {
|
||||||
|
this.selectedId = selected.id
|
||||||
|
this.selectedWidgetName = selected.options.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSelectedWidgetNameAndRef(selectedWidget, newName) {
|
||||||
|
this.selectedWidgetName = newName
|
||||||
|
selectedWidget.options.name = newName
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
},
|
||||||
|
|
||||||
|
insertTableRow(widget, insertPos, cloneRowIdx) {
|
||||||
|
let rowIdx = (insertPos === undefined) ? widget.rows.length : insertPos //确定插入列位置
|
||||||
|
let newRow = (cloneRowIdx === undefined) ? deepClone(widget.rows[widget.rows.length - 1]) : 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
|
||||||
|
})
|
||||||
|
widget.rows.splice(rowIdx, 0, newRow)
|
||||||
|
|
||||||
|
let colNo = 0
|
||||||
|
while ((rowIdx < widget.rows.length - 1) && (colNo < widget.rows[0].cols.length)) { //越界判断
|
||||||
|
let rowMerged = widget.rows[rowIdx + 1].cols[colNo].merged //确定插入位置的单元格是否为合并单元格
|
||||||
|
if (!!rowMerged) {
|
||||||
|
let rowArray = widget.rows
|
||||||
|
let unMergedCell = null
|
||||||
|
let startRowIndex = null
|
||||||
|
for (let i = rowIdx; i >= 0; i--) { //查找该行已合并的主单元格
|
||||||
|
if (!rowArray[i].cols[colNo].merged && (rowArray[i].cols[colNo].options.rowspan > 1)) {
|
||||||
|
startRowIndex = i
|
||||||
|
unMergedCell = rowArray[i].cols[colNo]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let newRowspan = unMergedCell.options.rowspan + 1
|
||||||
|
this.setPropsOfMergedRows(widget.rows, startRowIndex, colNo, unMergedCell.options.colspan, newRowspan)
|
||||||
|
colNo += unMergedCell.options.colspan
|
||||||
|
} else {
|
||||||
|
colNo += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
insertTableCol(widget, insertPos) {
|
||||||
|
let colIdx = (insertPos === undefined) ? widget.rows[0].cols.length : insertPos //确定插入列位置
|
||||||
|
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
|
||||||
|
row.cols.splice(colIdx, 0, newCol)
|
||||||
|
})
|
||||||
|
|
||||||
|
let rowNo = 0
|
||||||
|
while((colIdx < widget.rows[0].cols.length - 1) && (rowNo < widget.rows.length)) { //越界判断
|
||||||
|
let colMerged = widget.rows[rowNo].cols[colIdx + 1].merged //确定插入位置的单元格是否为合并单元格
|
||||||
|
if (!!colMerged) {
|
||||||
|
let colArray = widget.rows[rowNo].cols
|
||||||
|
let unMergedCell = null
|
||||||
|
let startColIndex = null
|
||||||
|
for (let i = colIdx; i >= 0; i--) { //查找该行已合并的主单元格
|
||||||
|
if (!colArray[i].merged && (colArray[i].options.colspan > 1)) {
|
||||||
|
startColIndex = i
|
||||||
|
unMergedCell = colArray[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let newColspan = unMergedCell.options.colspan + 1
|
||||||
|
this.setPropsOfMergedCols(widget.rows, rowNo, startColIndex, newColspan, unMergedCell.options.rowspan)
|
||||||
|
rowNo += unMergedCell.options.rowspan
|
||||||
|
} else {
|
||||||
|
rowNo += 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++) {
|
||||||
|
if ((i === startRowIndex) && (j === startColIndex)) {
|
||||||
|
rowArray[i].cols[j].options.colspan = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rowArray[i].cols[j].merged = false;
|
||||||
|
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++) {
|
||||||
|
if ((i === startRowIndex) && (j === startColIndex)) {
|
||||||
|
rowArray[i].cols[j].options.rowspan = 1 //合并后的主单元格
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rowArray[i].cols[j].merged = false;
|
||||||
|
rowArray[i].cols[j].options.rowspan = 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
|
||||||
|
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
|
||||||
|
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) { //需考虑删除的是合并列!!
|
||||||
|
//仅剩一列则不可删除!!
|
||||||
|
if (rowArray[0].cols[0].options.colspan === rowArray[0].cols.length) {
|
||||||
|
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, rIdx) => {
|
||||||
|
rItem.cols.splice(colIndex, startColspan)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteTableWholeRow(rowArray, rowIndex) { //需考虑删除的是合并行!!
|
||||||
|
//仅剩一行则不可删除!!
|
||||||
|
if (rowArray[0].cols[0].options.rowspan === rowArray.length) {
|
||||||
|
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 foundCon = null
|
||||||
|
containers.forEach(con => {
|
||||||
|
if (!!con.type && (con.type === typeName)) {
|
||||||
|
foundCon = con
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return foundCon
|
||||||
|
},
|
||||||
|
|
||||||
|
getFieldWidgetByType(typeName) {
|
||||||
|
let foundWidget = null
|
||||||
|
basicFields.forEach(bf => {
|
||||||
|
if (!!bf.type && (bf.type === typeName)) {
|
||||||
|
foundWidget = bf
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!!foundWidget) {
|
||||||
|
return foundWidget
|
||||||
|
}
|
||||||
|
|
||||||
|
advancedFields.forEach(af => {
|
||||||
|
if (!!af.type && (af.type === typeName)) {
|
||||||
|
foundWidget = af
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
moveUpWidget(parentList, indexOfParentList, cmpObj) {
|
||||||
|
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, cmpObj) {
|
||||||
|
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 fieldType = newWidget.type
|
||||||
|
let tempId = generateId()
|
||||||
|
newWidget.id = newWidget.type + tempId
|
||||||
|
newWidget.options.name = newWidget.id
|
||||||
|
//newWidget.options.label = this.vueInstance.i18nt(`designer.widgetLabel.${fieldType}`)
|
||||||
|
newWidget.options.label = newWidget.type.toLowerCase()
|
||||||
|
//newWidget.options.customClass = []
|
||||||
|
|
||||||
|
delete newWidget.displayName
|
||||||
|
return newWidget
|
||||||
|
},
|
||||||
|
|
||||||
|
copyNewContainerWidget(origin) {
|
||||||
|
let newCon = deepClone(origin)
|
||||||
|
newCon.id = newCon.type + 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
|
||||||
|
//TODO:
|
||||||
|
} else if (!!this.selectedWidget && !!this.selectedWidget.widgetList) {
|
||||||
|
this.selectedWidget.widgetList.push(newWidget)
|
||||||
|
} else {
|
||||||
|
this.widgetList.push(newWidget)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setSelected(newWidget)
|
||||||
|
},
|
||||||
|
|
||||||
|
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, idx) => {
|
||||||
|
spanSum += col.options.span
|
||||||
|
})
|
||||||
|
|
||||||
|
if (spanSum >= 24) {
|
||||||
|
//this.$message.info('列栅格之和超出24')
|
||||||
|
console.log('列栅格之和超出24')
|
||||||
|
gridWidget.cols.push(newGridCol)
|
||||||
|
} else {
|
||||||
|
const newSpan = (24 - spanSum) > 12 ? 12 : (24 - spanSum)
|
||||||
|
newGridCol.options.span = newSpan
|
||||||
|
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))
|
||||||
|
},
|
||||||
|
|
||||||
|
registerFormWidget(formWidget) {
|
||||||
|
this.formWidget = formWidget
|
||||||
|
},
|
||||||
|
|
||||||
|
_0x91827355() {
|
||||||
|
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属性变成非响应式 */
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,429 @@
|
||||||
|
<template>
|
||||||
|
<div class="container-wrapper">
|
||||||
|
|
||||||
|
<Row v-if="widget.type === 'grid'" :key="widget.id" :gutter="widget.options.gutter" class="grid-container"
|
||||||
|
:class="[selected ? 'selected' : '', customClass]" @click.native.stop="selectWidget(widget)">
|
||||||
|
<template v-for="(colWidget, colIdx) in widget.cols">
|
||||||
|
<grid-col-widget :widget="colWidget" :designer="designer" :key="colWidget.id" :parent-list="widget.cols"
|
||||||
|
:index-of-parent-list="colIdx" :parent-widget="widget"></grid-col-widget>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div v-else-if="widget.type === 'table'" :key="widget.id" class="table-container"
|
||||||
|
:class="{'selected': selected}" @click.stop="selectWidget(widget)">
|
||||||
|
<table class="table-layout">
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, rowIdx) in widget.rows" :key="row.id">
|
||||||
|
<template v-for="(colWidget, colIdx) in row.cols">
|
||||||
|
<table-cell-widget v-if="!colWidget.merged" :widget="colWidget" :designer="designer"
|
||||||
|
:key="colWidget.id" :parent-list="widget.cols" :row-index="rowIdx"
|
||||||
|
:row-length="widget.rows.length" :col-index="colIdx" :col-length="row.cols.length"
|
||||||
|
:col-array="row.cols" :row-array="widget.rows" :parent-widget="widget">
|
||||||
|
</table-cell-widget>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="widget.type === 'tab'" :key="widget.id" class="tab-container" :class="{'selected': selected}"
|
||||||
|
@click.stop="selectWidget(widget)">
|
||||||
|
<Tabs :type="widget.options.displayType" :size="widget.options.size" v-model="activeTab" @onClick="onTabClick">
|
||||||
|
<!-- -->
|
||||||
|
<TabPane v-for="(tab, index) in widget.tabs" :key="index" :label="tab.options.label"
|
||||||
|
:disabled="tab.options.disabled"
|
||||||
|
:name="tab.options.name" @click.native.stop="selectWidget(widget)">
|
||||||
|
<draggable :list="tab.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"
|
||||||
|
handel=".drag-handler" @add="(evt) => onContainerDragAdd(evt, tab.widgetList)"
|
||||||
|
@update="onContainerDragUpdate" :move="checkContainerMove">
|
||||||
|
<transition-group name="fade" tag="div" class="form-widget-list">
|
||||||
|
<template v-for="(subWidget, swIdx) in tab.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="tab.widgetList" :index-of-parent-list="swIdx"
|
||||||
|
:parent-widget="widget"></container-widget>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="tab.widgetList" :index-of-parent-list="swIdx"
|
||||||
|
:parent-widget="widget" :design-state="true"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
</TabPane>
|
||||||
|
<!-- -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<template v-for="(tabWidget, tabIdx) in widget.tabs">
|
||||||
|
<v-tab-pane :widget="tabWidget" :designer="designer" :key="tabIdx" :parent-list="widget.tabs"
|
||||||
|
:index-of-parent-list="tabIdx" :parent-widget="widget"></v-tab-pane>
|
||||||
|
</template>
|
||||||
|
-->
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="widget.type === 'section'" :key="widget.id" class="section-container"
|
||||||
|
:class="{'selected': selected}" @click.stop="selectWidget(widget)">
|
||||||
|
<draggable :list="widget.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost', animation: 200}"
|
||||||
|
handel=".drag-handler" @add="(evt) => onContainerDragAdd(evt, widget.widgetList)"
|
||||||
|
@update="onContainerDragUpdate" :move="checkContainerMove">
|
||||||
|
<transition-group name="fade" tag="div" class="form-widget-list">
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget">
|
||||||
|
</container-widget>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget"
|
||||||
|
:design-state="true"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container-action" v-if="designer.selectedId === widget.id && !widget.internal">
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-back" :title="i18nt('designer.hint.selectParentWidget')"
|
||||||
|
@click.stop="selectParentWidget(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-up" v-if="!!parentList && (parentList.length > 1)"
|
||||||
|
:title="i18nt('designer.hint.moveUpWidget')" @click.stop="moveUpWidget(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-down" v-if="!!parentList && (parentList.length > 1)"
|
||||||
|
:title="i18nt('designer.hint.moveDownWidget')" @click.stop="moveDownWidget(widget)"></i>
|
||||||
|
<i v-if="widget.type === 'table'" class="iconfont icon-insertrow" :title="i18nt('designer.hint.insertRow')"
|
||||||
|
@click.stop="insertTableRow(widget)"></i>
|
||||||
|
<i v-if="widget.type === 'table'" class="iconfont icon-insertcolumn"
|
||||||
|
:title="i18nt('designer.hint.insertColumn')" @click.stop="insertTableCol(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-ios-photos-outline" v-if="(widget.type === 'grid') || (widget.type === 'table')"
|
||||||
|
:title="i18nt('designer.hint.cloneWidget')" @click.stop="cloneContainer(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-ios-trash" :title="i18nt('designer.hint.remove')" @click.stop="removeWidget"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drag-handler" v-if="designer.selectedId === widget.id && !widget.internal">
|
||||||
|
<i class="ivu-icon ivu-icon-md-move" :title="i18nt('designer.hint.dragHandler')"></i>
|
||||||
|
<i>{{i18nt('designer.widgetLabel.' + widget.type)}}</i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
import FieldWidget from "@/components-iview/form-designer/form-widget/field-widget";
|
||||||
|
import GridColWidget from "@/components-iview/form-designer/form-widget/grid-col-widget";
|
||||||
|
import TableCellWidget from "@/components-iview/form-designer/form-widget/table-cell-widget";
|
||||||
|
import VTabPane from "@/components-iview/form-designer/form-widget/tab-pane";
|
||||||
|
import {
|
||||||
|
deepClone,
|
||||||
|
generateId
|
||||||
|
} from "@/utils/util";
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
//name: "ContainerWidget",
|
||||||
|
name: "container-widget",
|
||||||
|
componentName: 'ContainerWidget',
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
FieldWidget,
|
||||||
|
GridColWidget,
|
||||||
|
TableCellWidget,
|
||||||
|
VTabPane,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
parentWidget: Object,
|
||||||
|
parentList: Array,
|
||||||
|
indexOfParentList: Number,
|
||||||
|
designer: Object,
|
||||||
|
//
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeTab: 'tab1',
|
||||||
|
//
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selected() {
|
||||||
|
return this.widget.id === this.designer.selectedId
|
||||||
|
},
|
||||||
|
|
||||||
|
customClass() {
|
||||||
|
return this.widget.options.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
insertTableRow(widget) {
|
||||||
|
this.designer.insertTableRow(widget)
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
insertTableCol(widget) {
|
||||||
|
this.designer.insertTableCol(widget)
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
onContainerDragAdd(evt, subList) {
|
||||||
|
const newIndex = evt.newIndex
|
||||||
|
if (!!subList[newIndex]) {
|
||||||
|
this.designer.setSelected(subList[newIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
onContainerDragUpdate(evt) {
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
checkContainerMove(evt) {
|
||||||
|
return this.designer.checkWidgetMove(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
selectWidget(widget) {
|
||||||
|
this.designer.setSelected(widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
selectParentWidget(widget) {
|
||||||
|
if (this.parentWidget) {
|
||||||
|
this.designer.setSelected(this.parentWidget)
|
||||||
|
} else {
|
||||||
|
this.designer.clearSelected()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
moveUpWidget(widget) {
|
||||||
|
this.designer.moveUpWidget(this.parentList, this.indexOfParentList, this)
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
moveDownWidget(widget) {
|
||||||
|
this.designer.moveDownWidget(this.parentList, this.indexOfParentList, this)
|
||||||
|
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) {
|
||||||
|
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)
|
||||||
|
//if (!!nextSelected) {
|
||||||
|
this.designer.setSelected(nextSelected)
|
||||||
|
//}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onSubFormDragAdd(evt, subList) {
|
||||||
|
const newIndex = evt.newIndex
|
||||||
|
if (!!subList[newIndex]) {
|
||||||
|
this.designer.setSelected(subList[newIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
console.log('test', 'onSubFormDragAdd')
|
||||||
|
this.designer.emitEvent('field-selected', this.widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
onSubFormDragEnd(evt) {
|
||||||
|
console.log('sub form drag end: ', evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
onTabClick(evt) {
|
||||||
|
console.log('onTabClick', evt)
|
||||||
|
let paneName = evt.name
|
||||||
|
// let foundPane = this.widget.tabs.filter((tp) => {
|
||||||
|
// return tp.options.name === paneName
|
||||||
|
// })
|
||||||
|
this.widget.tabs.forEach((tp) => {
|
||||||
|
tp.options.active = tp.options.name === paneName;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
.container-action {
|
||||||
|
position: absolute;
|
||||||
|
//bottom: -30px;
|
||||||
|
bottom: 0;
|
||||||
|
right: -2px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
background: $--color-primary;
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
margin: 0 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handler {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
//bottom: -24px; /* 拖拽手柄位于组件下方,有时无法正常拖动,原因未明?? */
|
||||||
|
left: -6px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
background: $--color-primary;
|
||||||
|
z-index: 9;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
color: #fff;
|
||||||
|
margin: 4px;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row.grid-container {
|
||||||
|
min-height: 50px;
|
||||||
|
//line-height: 48px;
|
||||||
|
//padding: 6px;
|
||||||
|
outline: 1px dashed #336699;
|
||||||
|
|
||||||
|
.form-widget-list {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-container {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
table.table-layout {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
//border: 1px solid #c8ebfb;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
|
||||||
|
::v-deep td {
|
||||||
|
height: 48px;
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
padding: 3px;
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-widget-list {
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-container {
|
||||||
|
//padding: 5px;
|
||||||
|
margin: 2px;
|
||||||
|
|
||||||
|
.form-widget-list {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container.selected,
|
||||||
|
.table-container.selected,
|
||||||
|
.tab-container.selected,
|
||||||
|
.sub-form-container.selected,
|
||||||
|
.section-container.selected,
|
||||||
|
.grid-cell.selected {
|
||||||
|
outline: 2px solid $--color-primary !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.el-tabs.selected {
|
||||||
|
// outline: 2px solid $--color-primary;
|
||||||
|
//}
|
||||||
|
|
||||||
|
.section-container {
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 6px 0;
|
||||||
|
padding: 6px;
|
||||||
|
|
||||||
|
::v-deep .form-widget-list {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-form-container {
|
||||||
|
//width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
|
||||||
|
::v-deep .sub-form-table {
|
||||||
|
min-height: 68px;
|
||||||
|
|
||||||
|
div.sub-form-table-column {
|
||||||
|
display: inline-block;
|
||||||
|
//width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .ghost {
|
||||||
|
content: '';
|
||||||
|
font-size: 0;
|
||||||
|
//height: 3px;
|
||||||
|
height: 74px;
|
||||||
|
width: 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
background: $--color-primary;
|
||||||
|
border: 2px solid $--color-primary;
|
||||||
|
outline-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,203 @@
|
||||||
|
<template>
|
||||||
|
<Col v-else-if="widget.type === 'grid-col'" class="grid-cell" :span="widget.options.span || 12"
|
||||||
|
:class="[selected ? 'selected' : '', customClass]" :key="widget.id" @click.native.stop="selectWidget(widget)">
|
||||||
|
<draggable :list="widget.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"
|
||||||
|
handel=".drag-handler" @end="(evt) => onGridDragEnd(evt, widget.widgetList)"
|
||||||
|
@add="(evt) => onGridDragAdd(evt, widget.widgetList)" @update="onGridDragUpdate" :move="checkContainerMove">
|
||||||
|
<transition-group name="fade" tag="div" class="form-widget-list">
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget">
|
||||||
|
</container-widget>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget"
|
||||||
|
:design-state="true"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
|
||||||
|
<div class="grid-col-action" v-if="designer.selectedId === widget.id && widget.type === 'grid-col'">
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-back" :title="i18nt('designer.hint.selectParentWidget')"
|
||||||
|
@click.stop="selectParentWidget(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-up" v-if="!!parentList && (parentList.length > 1)"
|
||||||
|
:title="i18nt('designer.hint.moveUpWidget')" @click.stop="moveUpWidget(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-down" v-if="!!parentList && (parentList.length > 1)"
|
||||||
|
:title="i18nt('designer.hint.moveDownWidget')" @click.stop="moveDownWidget(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-ios-photos-outline" :title="i18nt('designer.hint.cloneWidget')"
|
||||||
|
@click.stop="cloneGridCol(widget)"></i>
|
||||||
|
<i class="ivu-icon ivu-icon-ios-trash" :title="i18nt('designer.hint.remove')" @click.stop="removeWidget"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-col-handler" v-if="designer.selectedId === widget.id && widget.type === 'grid-col'">
|
||||||
|
<i>{{i18nt('designer.widgetLabel.' + widget.type)}}</i>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
//import ContainerWidget from "@/components/form-designer/form-widget/container-widget";
|
||||||
|
import FieldWidget from "@/components-iview/form-designer/form-widget/field-widget";
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "GridColWidget",
|
||||||
|
componentName: "GridColWidget",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
//'container-widget': ContainerWidget, /* 递归组件必须使用这种写法!! */
|
||||||
|
FieldWidget,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
parentWidget: Object,
|
||||||
|
parentList: Array,
|
||||||
|
indexOfParentList: Number,
|
||||||
|
designer: Object,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selected() {
|
||||||
|
return this.widget.id === this.designer.selectedId
|
||||||
|
},
|
||||||
|
|
||||||
|
customClass() {
|
||||||
|
return this.widget.options.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onGridDragEnd(evt, subList) {
|
||||||
|
//console.log('drag end1111', evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
onGridDragAdd(evt, subList) {
|
||||||
|
const newIndex = evt.newIndex
|
||||||
|
if (!!subList[newIndex]) {
|
||||||
|
this.designer.setSelected(subList[newIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
onGridDragUpdate(evt) {
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
selectWidget(widget) {
|
||||||
|
console.log('id: ' + widget.id)
|
||||||
|
this.designer.setSelected(widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
checkContainerMove(evt) {
|
||||||
|
return this.designer.checkWidgetMove(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
selectParentWidget(widget) {
|
||||||
|
if (this.parentWidget) {
|
||||||
|
this.designer.setSelected(this.parentWidget)
|
||||||
|
} else {
|
||||||
|
this.designer.clearSelected()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
moveUpWidget(widget) {
|
||||||
|
this.designer.moveUpWidget(this.parentList, this.indexOfParentList, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
moveDownWidget(widget) {
|
||||||
|
this.designer.moveDownWidget(this.parentList, this.indexOfParentList, this)
|
||||||
|
},
|
||||||
|
|
||||||
|
cloneGridCol(widget) {
|
||||||
|
this.designer.cloneGridCol(widget, this.parentWidget)
|
||||||
|
},
|
||||||
|
|
||||||
|
removeWidget() {
|
||||||
|
if (!!this.parentList) {
|
||||||
|
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)
|
||||||
|
//if (!!nextSelected) {
|
||||||
|
this.designer.setSelected(nextSelected)
|
||||||
|
//}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.grid-cell {
|
||||||
|
min-height: 38px;
|
||||||
|
//line-height: 36px;
|
||||||
|
margin: 6px 0;
|
||||||
|
padding: 3px;
|
||||||
|
//min-height: 300px;
|
||||||
|
//border-right: 1px dotted #cccccc;
|
||||||
|
outline: 1px dashed #336699;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.form-widget-list {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-col-action {
|
||||||
|
position: absolute;
|
||||||
|
//bottom: -30px;
|
||||||
|
bottom: 0;
|
||||||
|
right: -2px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
background: $--color-primary;
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
margin: 0 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-col-handler {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
//bottom: -24px; /* 拖拽手柄位于组件下方,有时无法正常拖动,原因未明?? */
|
||||||
|
left: -2px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
background: $--color-primary;
|
||||||
|
z-index: 9;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
color: #fff;
|
||||||
|
margin: 4px;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,252 @@
|
||||||
|
<template>
|
||||||
|
<div class="form-widget-container">
|
||||||
|
|
||||||
|
<Form class="full-height-width widget-form" :label-position="labelPosition"
|
||||||
|
:class="[customClass, layoutType === 'H5' ? 'h5-layout' : '']" :size="size"
|
||||||
|
:validate-on-rule-change="false">
|
||||||
|
|
||||||
|
<div v-if="designer.widgetList.length === 0" class="no-widget-hint">{{i18nt('designer.noWidgetHint')}}</div>
|
||||||
|
|
||||||
|
<draggable :list="designer.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 300}"
|
||||||
|
handle=".drag-handler" @end="onDragEnd" @add="onDragAdd" @update="onDragUpdate" :move="checkMove">
|
||||||
|
<transition-group name="fade" tag="div" class="form-widget-list">
|
||||||
|
<template v-for="(widget, index) in designer.widgetList">
|
||||||
|
<template v-if="'container' === widget.category">
|
||||||
|
<container-widget :widget="widget" :designer="designer" :key="widget.id"
|
||||||
|
:parent-list="designer.widgetList" :index-of-parent-list="index" :parent-widget="null">
|
||||||
|
</container-widget>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="widget" :designer="designer" :key="widget.id"
|
||||||
|
:parent-list="designer.widgetList" :index-of-parent-list="index" :parent-widget="null"
|
||||||
|
:design-state="true"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
import ContainerWidget from "@/components-iview/form-designer/form-widget/container-widget";
|
||||||
|
import FieldWidget from "@/components-iview/form-designer/form-widget/field-widget";
|
||||||
|
import {
|
||||||
|
addWindowResizeHandler
|
||||||
|
} from "@/utils/util";
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VFormWidget",
|
||||||
|
componentName: "VFormWidget",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
ContainerWidget,
|
||||||
|
FieldWidget,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
designer: Object,
|
||||||
|
formConfig: Object,
|
||||||
|
optionData: { //prop传入的选项数据
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
refList: this.widgetRefList,
|
||||||
|
formConfig: this.formConfig,
|
||||||
|
globalOptionData: this.optionData,
|
||||||
|
globalModel: {
|
||||||
|
formModel: this.formModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formModel: {},
|
||||||
|
widgetRefList: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
labelPosition() {
|
||||||
|
if (!!this.designer.formConfig && !!this.designer.formConfig.labelPosition) {
|
||||||
|
return this.designer.formConfig.labelPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'left'
|
||||||
|
},
|
||||||
|
|
||||||
|
size() {
|
||||||
|
if (!!this.designer.formConfig && !!this.designer.formConfig.size) {
|
||||||
|
return this.designer.formConfig.size
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'medium'
|
||||||
|
},
|
||||||
|
|
||||||
|
customClass() {
|
||||||
|
return this.designer.formConfig.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
layoutType() {
|
||||||
|
return this.designer.getLayoutType()
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'designer.widgetList': {
|
||||||
|
deep: true,
|
||||||
|
handler(val) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'designer.formConfig': {
|
||||||
|
deep: true,
|
||||||
|
handler(val) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.designer.initDesigner();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.disableFirefoxDefaultDrop() /* 禁用Firefox默认拖拽搜索功能!! */
|
||||||
|
this.designer.registerFormWidget(this)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
disableFirefoxDefaultDrop() {
|
||||||
|
let isFirefox = (navigator.userAgent.toLowerCase().indexOf("firefox") !== -1)
|
||||||
|
if (isFirefox) {
|
||||||
|
document.body.ondrop = function(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragEnd(evt) {
|
||||||
|
//console.log('drag end000', evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragAdd(evt) {
|
||||||
|
const newIndex = evt.newIndex
|
||||||
|
if (!!this.designer.widgetList[newIndex]) {
|
||||||
|
this.designer.setSelected(this.designer.widgetList[newIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragUpdate(evt) {
|
||||||
|
/* 在VueDraggable内拖拽组件发生位置变化时会触发update,未发生组件位置变化不会触发!! */
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
checkMove(evt) {
|
||||||
|
return this.designer.checkWidgetMove(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormData() {
|
||||||
|
return this.formModel
|
||||||
|
},
|
||||||
|
|
||||||
|
getWidgetRef(widgetName, showError) {
|
||||||
|
let foundRef = this.widgetRefList[widgetName]
|
||||||
|
if (!foundRef && !!showError) {
|
||||||
|
this.$message.error(this.i18nt('designer.hint.refNotFound') + widgetName)
|
||||||
|
}
|
||||||
|
return foundRef
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container-scroll-bar {
|
||||||
|
|
||||||
|
::v-deep .el-scrollbar__wrap,
|
||||||
|
::v-deep .el-scrollbar__view {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-widget-container {
|
||||||
|
padding: 10px;
|
||||||
|
background: #f1f2f3;
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.ivu-form.full-height-width {
|
||||||
|
/*
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 420px;
|
||||||
|
border-radius: 15px;
|
||||||
|
//border-width: 10px;
|
||||||
|
box-shadow: 0 0 1px 10px #495060;
|
||||||
|
*/
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
padding: 3px;
|
||||||
|
background: #ffffff;
|
||||||
|
|
||||||
|
.no-widget-hint {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-widget-list {
|
||||||
|
min-height: calc(100vh - 56px - 68px);
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-form.h5-layout {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 420px;
|
||||||
|
border-radius: 15px;
|
||||||
|
//border-width: 10px;
|
||||||
|
box-shadow: 0 0 1px 10px #495060;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-form.widget-form ::v-deep .el-row {
|
||||||
|
padding: 2px;
|
||||||
|
border: 1px dashed rgba(170, 170, 170, 0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell {
|
||||||
|
min-height: 30px;
|
||||||
|
border-right: 1px dotted #cccccc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<TabPane :name="'tab1'" :label="widget.label" @click.native.stop="selectWidget(widget)">
|
||||||
|
<draggable :list="widget.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"
|
||||||
|
handel=".drag-handler" @end="(evt) => onTabDragEnd(evt, widget.widgetList)"
|
||||||
|
@add="(evt) => onTabDragAdd(evt, widget.widgetList)" @update="onTabDragUpdate" :move="checkContainerMove">
|
||||||
|
<transition-group name="fade" tag="div" class="form-widget-list">
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget">
|
||||||
|
</container-widget>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget"
|
||||||
|
:design-state="true"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
</TabPane>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
import ContainerWidget from "@/components-iview/form-designer/form-widget/container-widget";
|
||||||
|
import FieldWidget from "@/components-iview/form-designer/form-widget/field-widget";
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VTabPane",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
ContainerWidget,
|
||||||
|
FieldWidget,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
parentWidget: Object,
|
||||||
|
parentList: Array,
|
||||||
|
indexOfParentList: Number,
|
||||||
|
designer: Object,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selected() {
|
||||||
|
return this.widget.id === this.designer.selectedId
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectWidget(widget) {
|
||||||
|
//console.log('id: ' + widget.id)
|
||||||
|
this.designer.setSelected(widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
onTabDragEnd(obj, subList) {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
|
||||||
|
onTabDragAdd(evt, subList) { //重复代码,可合并
|
||||||
|
const newIndex = evt.newIndex
|
||||||
|
console.log(newIndex)
|
||||||
|
if (!!subList[newIndex]) {
|
||||||
|
this.designer.setSelected(subList[newIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
onTabDragUpdate(evt) {
|
||||||
|
//console.log('test', 'on drag update')
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
checkContainerMove(evt) {
|
||||||
|
return this.designer.checkWidgetMove(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ivu-tabs-tab {
|
||||||
|
//padding: 0 6px 6px;
|
||||||
|
//padding-bottom: 10px;
|
||||||
|
|
||||||
|
::v-deep .form-widget-list {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,385 @@
|
||||||
|
<template>
|
||||||
|
<td class="table-cell" :class="[selected ? 'selected' : '', customClass]"
|
||||||
|
:style="{width: widget.options.cellWidth + '!important' || '', height: widget.options.cellHeight + '!important' || ''}"
|
||||||
|
:colspan="widget.options.colspan || 1" :rowspan="widget.options.rowspan || 1"
|
||||||
|
@click.stop="selectWidget(widget)">
|
||||||
|
<draggable :list="widget.widgetList" class="draggable-div"
|
||||||
|
v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}" handel=".drag-handler"
|
||||||
|
@end="(evt) => onTableDragEnd(evt, widget.widgetList)"
|
||||||
|
@add="(evt) => onTableDragAdd(evt, widget.widgetList)" @update="onTableDragUpdate"
|
||||||
|
:move="checkContainerMove">
|
||||||
|
<transition-group name="fade" tag="div" class="form-widget-list">
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget">
|
||||||
|
</container-widget>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget"
|
||||||
|
:design-state="true"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</draggable>
|
||||||
|
|
||||||
|
<div class="table-cell-action" v-if="designer.selectedId === widget.id && widget.type === 'table-cell'">
|
||||||
|
<i class="ivu-icon ivu-icon-md-arrow-round-back" :title="i18nt('designer.hint.selectParentWidget')"
|
||||||
|
@click.stop="selectParentWidget(widget)"></i>
|
||||||
|
<Dropdown trigger="click" @on-click="handleTableCellCommand" size="small">
|
||||||
|
<i class="ivu-icon ivu-icon-md-menu" :title="i18nt('designer.hint.cellSetting')"></i>
|
||||||
|
<DropdownMenu slot="list">
|
||||||
|
<DropdownItem name="insertLeftCol">{{i18nt('designer.setting.insertColumnToLeft')}}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem name="insertRightCol">{{i18nt('designer.setting.insertColumnToRight')}}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem name="insertAboveRow">{{i18nt('designer.setting.insertRowAbove')}}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem name="insertBelowRow">{{i18nt('designer.setting.insertRowBelow')}}
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem name="mergeLeftCol" :disabled="mergeLeftColDisabled" divided>
|
||||||
|
{{i18nt('designer.setting.mergeLeftColumn')}}</DropdownItem>
|
||||||
|
<DropdownItem name="mergeRightCol" :disabled="mergeRightColDisabled">
|
||||||
|
{{i18nt('designer.setting.mergeRightColumn')}}</DropdownItem>
|
||||||
|
<DropdownItem name="mergeWholeRow" :disabled="mergeWholeRowDisabled">
|
||||||
|
{{i18nt('designer.setting.mergeEntireRow')}}</DropdownItem>
|
||||||
|
<DropdownItem name="mergeAboveRow" :disabled="mergeAboveRowDisabled" divided>
|
||||||
|
{{i18nt('designer.setting.mergeRowAbove')}}</DropdownItem>
|
||||||
|
<DropdownItem name="mergeBelowRow" :disabled="mergeBelowRowDisabled">
|
||||||
|
{{i18nt('designer.setting.mergeRowBelow')}}</DropdownItem>
|
||||||
|
<DropdownItem name="mergeWholeCol" :disabled="mergeWholeColDisabled">
|
||||||
|
{{i18nt('designer.setting.mergeEntireColumn')}}</DropdownItem>
|
||||||
|
<DropdownItem name="undoMergeRow" :disabled="undoMergeRowDisabled" divided>
|
||||||
|
{{i18nt('designer.setting.undoMergeRow')}}</DropdownItem>
|
||||||
|
<DropdownItem name="undoMergeCol" :disabled="undoMergeColDisabled">
|
||||||
|
{{i18nt('designer.setting.undoMergeCol')}}</DropdownItem>
|
||||||
|
<DropdownItem name="deleteWholeCol" :disabled="deleteWholeColDisabled" divided>
|
||||||
|
{{i18nt('designer.setting.deleteEntireCol')}}</DropdownItem>
|
||||||
|
<DropdownItem name="deleteWholeRow" :disabled="deleteWholeRowDisabled">
|
||||||
|
{{i18nt('designer.setting.deleteEntireRow')}}</DropdownItem>
|
||||||
|
</DropdownMenu >
|
||||||
|
</Dropdown>
|
||||||
|
<!-- <i class="el-icon-delete" :title="i18nt('designer.hint.remove')" @click.stop="removeWidget"></i>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-cell-handler" v-if="designer.selectedId === widget.id && widget.type === 'table-cell'">
|
||||||
|
<i>{{i18nt('designer.widgetLabel.' + widget.type)}}</i>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
//import ContainerWidget from "@/components/form-designer/form-widget/container-widget";
|
||||||
|
import FieldWidget from "@/components-iview/form-designer/form-widget/field-widget";
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "TableCellWidget",
|
||||||
|
componentName: "TableCellWidget",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
//'container-widget': ContainerWidget, /* 递归组件必须使用这种写法!! */
|
||||||
|
FieldWidget,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
parentWidget: Object,
|
||||||
|
parentList: Array,
|
||||||
|
//indexOfParentList: Number,
|
||||||
|
|
||||||
|
rowIndex: Number,
|
||||||
|
colIndex: Number,
|
||||||
|
rowLength: Number,
|
||||||
|
colLength: Number,
|
||||||
|
colArray: Array,
|
||||||
|
rowArray: Array,
|
||||||
|
|
||||||
|
designer: Object,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selected() {
|
||||||
|
return this.widget.id === this.designer.selectedId
|
||||||
|
},
|
||||||
|
|
||||||
|
customClass() {
|
||||||
|
return this.widget.options.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeLeftColDisabled() {
|
||||||
|
return (this.colIndex <= 0) || (this.colArray[this.colIndex - 1].options.rowspan !== this.widget.options
|
||||||
|
.rowspan)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeRightColDisabled() {
|
||||||
|
let rightColIndex = this.colIndex + this.widget.options.colspan
|
||||||
|
return (this.colIndex >= this.colLength - 1) || (rightColIndex > this.colLength - 1) ||
|
||||||
|
(this.colArray[rightColIndex].options.rowspan !== this.widget.options.rowspan)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeWholeRowDisabled() {
|
||||||
|
return (this.colLength <= 1) || (this.colLength === this.widget.options.colspan)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeAboveRowDisabled() {
|
||||||
|
return (this.rowIndex <= 0) || (this.rowArray[this.rowIndex - 1].cols[this.colIndex].options.colspan !==
|
||||||
|
this.widget.options.colspan)
|
||||||
|
|
||||||
|
//return this.rowIndex <= 0
|
||||||
|
//return (this.rowIndex <= 0) || (this.widget.options.colspan !== this.rowArray) //TODO
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeBelowRowDisabled() {
|
||||||
|
let belowRowIndex = this.rowIndex + this.widget.options.rowspan
|
||||||
|
return (this.rowIndex >= this.rowLength - 1) || (belowRowIndex > this.rowLength - 1) ||
|
||||||
|
(this.rowArray[belowRowIndex].cols[this.colIndex].options.colspan !== this.widget.options.colspan)
|
||||||
|
|
||||||
|
//return this.rowIndex >= this.rowLength - 1
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeWholeColDisabled() {
|
||||||
|
return (this.rowLength <= 1) || (this.rowLength === this.widget.options.rowspan)
|
||||||
|
},
|
||||||
|
|
||||||
|
undoMergeColDisabled() {
|
||||||
|
return this.widget.merged || (this.widget.options.colspan <= 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
undoMergeRowDisabled() {
|
||||||
|
return this.widget.merged || (this.widget.options.rowspan <= 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteWholeColDisabled() {
|
||||||
|
//return this.colLength === 1
|
||||||
|
return (this.rowLength === 1) || (this.widget.options.colspan === this.colLength)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteWholeRowDisabled() {
|
||||||
|
return (this.rowLength === 1) || (this.widget.options.rowspan === this.rowLength)
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectWidget(widget) {
|
||||||
|
this.designer.setSelected(widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
checkContainerMove(evt) {
|
||||||
|
return this.designer.checkWidgetMove(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
onTableDragEnd(obj, subList) {
|
||||||
|
//console.log('test', 'drag end22222')
|
||||||
|
},
|
||||||
|
|
||||||
|
onTableDragAdd(evt, subList) { //重复代码,可合并
|
||||||
|
const newIndex = evt.newIndex
|
||||||
|
if (!!subList[newIndex]) {
|
||||||
|
this.designer.setSelected(subList[newIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
onTableDragUpdate(evt) {
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
selectParentWidget(widget) {
|
||||||
|
if (this.parentWidget) {
|
||||||
|
this.designer.setSelected(this.parentWidget)
|
||||||
|
} else {
|
||||||
|
this.designer.clearSelected()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTableCellCommand(command) {
|
||||||
|
if (command === 'insertLeftCol') {
|
||||||
|
this.insertLeftCol()
|
||||||
|
} else if (command === 'insertRightCol') {
|
||||||
|
this.insertRightCol()
|
||||||
|
} else if (command === 'insertAboveRow') {
|
||||||
|
this.insertAboveRow()
|
||||||
|
} else if (command === 'insertBelowRow') {
|
||||||
|
this.insertBelowRow()
|
||||||
|
} else if (command === 'mergeLeftCol') {
|
||||||
|
this.mergeLeftCol()
|
||||||
|
} else if (command === 'mergeRightCol') {
|
||||||
|
this.mergeRightCol()
|
||||||
|
} else if (command === 'mergeWholeCol') {
|
||||||
|
this.mergeWholeCol()
|
||||||
|
} else if (command === 'mergeAboveRow') {
|
||||||
|
this.mergeAboveRow()
|
||||||
|
} else if (command === 'mergeBelowRow') {
|
||||||
|
this.mergeBelowRow()
|
||||||
|
} else if (command === 'mergeWholeRow') {
|
||||||
|
this.mergeWholeRow()
|
||||||
|
} else if (command === 'undoMergeCol') {
|
||||||
|
this.undoMergeCol()
|
||||||
|
} else if (command === 'undoMergeRow') {
|
||||||
|
this.undoMergeRow()
|
||||||
|
} else if (command === 'deleteWholeCol') {
|
||||||
|
this.deleteWholeCol()
|
||||||
|
} else if (command === 'deleteWholeRow') {
|
||||||
|
this.deleteWholeRow()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
insertLeftCol() {
|
||||||
|
this.designer.insertTableCol(this.parentWidget, this.colIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
insertRightCol() {
|
||||||
|
this.designer.insertTableCol(this.parentWidget, this.colIndex + 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
insertAboveRow() {
|
||||||
|
this.designer.insertTableRow(this.parentWidget, this.rowIndex, this.rowIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
insertBelowRow() {
|
||||||
|
this.designer.insertTableRow(this.parentWidget, this.rowIndex + 1, this.rowIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeLeftCol() {
|
||||||
|
//this.designer.mergeTableColumn(this.colArray, this.colIndex, true)
|
||||||
|
this.designer.mergeTableCol(this.rowArray, this.colArray, this.rowIndex, this.colIndex, true, this.widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeRightCol() {
|
||||||
|
//this.designer.mergeTableColumn(this.colArray, this.colIndex, false)
|
||||||
|
this.designer.mergeTableCol(this.rowArray, this.colArray, this.rowIndex, this.colIndex, false, this.widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeWholeRow() {
|
||||||
|
this.designer.mergeTableWholeRow(this.rowArray, this.colArray, this.rowIndex, this.colIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeAboveRow() {
|
||||||
|
this.designer.mergeTableRow(this.rowArray, this.rowIndex, this.colIndex, true, this.widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeBelowRow() {
|
||||||
|
this.designer.mergeTableRow(this.rowArray, this.rowIndex, this.colIndex, false, this.widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
mergeWholeCol() {
|
||||||
|
this.designer.mergeTableWholeCol(this.rowArray, this.colArray, this.rowIndex, this.colIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
undoMergeCol() {
|
||||||
|
this.designer.undoMergeTableCol(this.rowArray, this.rowIndex, this.colIndex,
|
||||||
|
this.widget.options.colspan, this.widget.options.rowspan)
|
||||||
|
},
|
||||||
|
|
||||||
|
undoMergeRow() {
|
||||||
|
this.designer.undoMergeTableRow(this.rowArray, this.rowIndex, this.colIndex,
|
||||||
|
this.widget.options.colspan, this.widget.options.rowspan)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteWholeCol() {
|
||||||
|
this.designer.deleteTableWholeCol(this.rowArray, this.colIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteWholeRow() {
|
||||||
|
this.designer.deleteTableWholeRow(this.rowArray, this.rowIndex)
|
||||||
|
},
|
||||||
|
|
||||||
|
// removeWidget() {
|
||||||
|
// if (!!this.parentList) {
|
||||||
|
// 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)
|
||||||
|
// //if (!!nextSelected) {
|
||||||
|
// this.designer.setSelected(nextSelected)
|
||||||
|
// //}
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.table-cell {
|
||||||
|
//padding: 3px;
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
display: table-cell;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.draggable-div {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-widget-list {
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
margin: 3px;
|
||||||
|
//min-height: 36px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell-action {
|
||||||
|
position: absolute;
|
||||||
|
//bottom: -30px;
|
||||||
|
bottom: 0;
|
||||||
|
right: -2px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
background: $--color-primary;
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #fff;
|
||||||
|
margin: 0 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell-handler {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
//bottom: -24px; /* 拖拽手柄位于组件下方,有时无法正常拖动,原因未明?? */
|
||||||
|
left: -2px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
background: $--color-primary;
|
||||||
|
z-index: 9;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
color: #fff;
|
||||||
|
margin: 4px;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell.selected {
|
||||||
|
outline: 2px solid $--color-primary !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,260 @@
|
||||||
|
<template>
|
||||||
|
<Layout class="full-height">
|
||||||
|
<Header class="main-header">
|
||||||
|
<div class="float-left main-title">
|
||||||
|
<img src="../../assets/vform-logo.png" @click="openHome">
|
||||||
|
<span class="bold">VariantForm Pro</span> {{i18nt('application.productTitle')}} <span class="version-span">Ver {{vFormVersion}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="float-right external-link">
|
||||||
|
<Dropdown @on-click="handleLanguageChanged">
|
||||||
|
<span class="el-dropdown-link">{{curLangName}}<Icon type="ios-arrow-down"></Icon></span>
|
||||||
|
<DropdownMenu slot="list">
|
||||||
|
<DropdownItem name="zh-CN">{{i18nt('application.zh-CN')}}</DropdownItem>
|
||||||
|
<DropdownItem name="en-US">{{i18nt('application.en-US')}}</DropdownItem>
|
||||||
|
</DropdownMenu >
|
||||||
|
</Dropdown>
|
||||||
|
<a href="javascript:void(0)" @click="(ev) => openUrl(ev, gitUrl)" target="_blank"><svg-icon icon-class="github" />{{i18nt('application.github')}}</a>
|
||||||
|
<a href="javascript:void(0)" @click="(ev) => openUrl(ev, docUrl)" target="_blank"><svg-icon icon-class="document" />{{i18nt('application.document')}}</a>
|
||||||
|
<a href="javascript:void(0)" @click="(ev) => openUrl(ev, chatUrl)" target="_blank">{{i18nt('application.qqGroup')}}</a>
|
||||||
|
<a href="javascript:void(0)" @click="(ev) => openUrl(ev, subScribeUrl)" target="_blank">
|
||||||
|
{{i18nt('application.subscription')}}<i class="el-icon-top-right"></i></a>
|
||||||
|
</div>
|
||||||
|
</Header>
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<Sider class="side-panel">
|
||||||
|
<widget-panel :designer="designer" />
|
||||||
|
</Sider>
|
||||||
|
|
||||||
|
<Layout class="center-layout-container">
|
||||||
|
<Header class="toolbar-header">
|
||||||
|
<toolbar-panel :designer="designer"></toolbar-panel>
|
||||||
|
</Header>
|
||||||
|
<Content class="form-widget-main">
|
||||||
|
<Scroll class="container-scroll-bar" :height="scrollerHeight">
|
||||||
|
<v-form-widget :designer="designer" :form-config="designer.formConfig">
|
||||||
|
</v-form-widget>
|
||||||
|
</Scroll>
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
<Sider class="setting-pannel">
|
||||||
|
<setting-panel :designer="designer" :selected-widget="designer.selectedWidget"
|
||||||
|
:form-config="designer.formConfig" />
|
||||||
|
</Sider>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
</Layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WidgetPanel from './widget-panel/index'
|
||||||
|
import ToolbarPanel from './toolbar-panel/index'
|
||||||
|
import SettingPanel from './setting-panel/index'
|
||||||
|
import VFormWidget from './form-widget/index'
|
||||||
|
import { createDesigner } from "@/components-iview/form-designer/designer";
|
||||||
|
import { addWindowResizeHandler } from "@/utils/util";
|
||||||
|
|
||||||
|
import {VARIANT_FORM_VERSION} from "@/utils/config";
|
||||||
|
import i18n, { changeLocale } from "@/components-iview/utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VFormDesigner",
|
||||||
|
componentName: "VFormDesigner",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
WidgetPanel,
|
||||||
|
ToolbarPanel,
|
||||||
|
SettingPanel,
|
||||||
|
VFormWidget,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
vFormVersion: VARIANT_FORM_VERSION,
|
||||||
|
curLangName: '',
|
||||||
|
|
||||||
|
docUrl: 'http://www.vform666.com/document.html',
|
||||||
|
gitUrl: 'https://github.com/vform666/variant-form',
|
||||||
|
chatUrl: 'http://www.vform666.com/chat-group.html',
|
||||||
|
subScribeUrl: 'http://www.vform666.com/subscribe.html',
|
||||||
|
|
||||||
|
scrollerHeight: 0,
|
||||||
|
|
||||||
|
designer: createDesigner(this),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initLocale()
|
||||||
|
|
||||||
|
this.scrollerHeight = window.innerHeight - 56 -36 ;
|
||||||
|
addWindowResizeHandler(() => {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollerHeight = window.innerHeight - 56-36 ;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openHome() {
|
||||||
|
if (!!this.vsCodeFlag) {
|
||||||
|
const msgObj = {
|
||||||
|
cmd: 'openUrl',
|
||||||
|
data: {
|
||||||
|
url: 'http://www.vform666.com/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.parent.postMessage(msgObj, '*')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initLocale() {
|
||||||
|
let curLocale = localStorage.getItem('v_form_locale') || 'zh-CN'
|
||||||
|
this.curLangName = this.i18nt('application.' + curLocale)
|
||||||
|
this.changeLanguage(curLocale)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleLanguageChanged(command) {
|
||||||
|
this.changeLanguage(command)
|
||||||
|
this.curLangName = this.i18nt('application.' + command)
|
||||||
|
},
|
||||||
|
|
||||||
|
changeLanguage(langName) {
|
||||||
|
changeLocale(langName)
|
||||||
|
},
|
||||||
|
|
||||||
|
setFormJson(formJson) {
|
||||||
|
let modifiedFlag = false
|
||||||
|
if (!!formJson) {
|
||||||
|
if (typeof formJson === 'string') {
|
||||||
|
modifiedFlag = this.designer.loadFormJson(JSON.parse(formJson))
|
||||||
|
} else if (formJson.constructor === Object) {
|
||||||
|
modifiedFlag = this.designer.loadFormJson(formJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifiedFlag) {
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: 增加更多方法!!
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/deep/ .ivu-layout-sider{
|
||||||
|
background-color: initial;
|
||||||
|
}
|
||||||
|
/deep/ .setting-pannel{
|
||||||
|
width: 320px!important;
|
||||||
|
min-width: 320px!important;
|
||||||
|
max-width: 320px!important;
|
||||||
|
flex: 0 0 320px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-layout.full-height {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-layout.center-layout-container {
|
||||||
|
// min-width: 680px;
|
||||||
|
border-left: 2px dotted #EBEEF5;
|
||||||
|
border-right: 2px dotted #EBEEF5;
|
||||||
|
}
|
||||||
|
.ivu-layout-header{
|
||||||
|
padding:0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-layout-header.main-header {
|
||||||
|
background-color:#F5F7F9;
|
||||||
|
border-bottom: 2px dotted #EBEEF5;
|
||||||
|
height: 48px !important;
|
||||||
|
line-height: 48px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.main-title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #242424;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.bold {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 6px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.version-span {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #101F1C;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.float-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dropdown-link {
|
||||||
|
margin-right: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.external-link a {
|
||||||
|
font-size: 13px;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-right: 10px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-layout-header.toolbar-header {
|
||||||
|
background-color:#F5F7F9;
|
||||||
|
border-bottom: 1px dotted #CCCCCC;
|
||||||
|
height: 42px !important;
|
||||||
|
line-height: 42px !important;
|
||||||
|
padding:0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-layout-sider.side-panel {
|
||||||
|
background-color:#F5F7F9;
|
||||||
|
width: 260px !important;
|
||||||
|
min-width: 260px !important;
|
||||||
|
max-width: 260px !important;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-main.form-widget-main {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-scroll-bar {
|
||||||
|
|
||||||
|
::v-deep .el-scrollbar__wrap,
|
||||||
|
::v-deep .el-scrollbar__view {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .ivu-scroll-loader{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,272 @@
|
||||||
|
<template>
|
||||||
|
<div class="option-items-pane">
|
||||||
|
<RadioGroup
|
||||||
|
v-if="(selectedWidget.type === 'radio') || ((selectedWidget.type === 'select') && !selectedWidget.options.multiple)"
|
||||||
|
v-model="optionModel.defaultValue" @on-change="emitDefaultValueChange">
|
||||||
|
<draggable tag="ul" :list="optionModel.optionItems"
|
||||||
|
v-bind="{group:'optionsGroup', ghostClass: 'ghost', handle: '.drag-option'}">
|
||||||
|
<li v-for="(option, idx) in optionModel.optionItems" :key="idx">
|
||||||
|
<Radio :label="option.value">
|
||||||
|
<Input v-model="option.value" placeholder="Value" size="small" style="width: 100px"></Input>
|
||||||
|
<Input v-model="option.label" placeholder="Label" size="small" style="width: 100px"></Input>
|
||||||
|
<i class="iconfont icon-drag drag-option"></i>
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
size="small"
|
||||||
|
type="warning"
|
||||||
|
@click="deleteOption(option, idx)"
|
||||||
|
icon="md-remove"
|
||||||
|
class="col-delete-button">
|
||||||
|
</Button>
|
||||||
|
</Radio>
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</RadioGroup>
|
||||||
|
<CheckboxGroup
|
||||||
|
v-else-if="(selectedWidget.type === 'checkbox') || ((selectedWidget.type === 'select') && selectedWidget.options.multiple)"
|
||||||
|
v-model="optionModel.defaultValue" @on-change="emitDefaultValueChange">
|
||||||
|
<draggable tag="ul" :list="optionModel.optionItems"
|
||||||
|
v-bind="{group:'optionsGroup', ghostClass: 'ghost', handle: '.drag-option'}">
|
||||||
|
<li v-for="(option, idx) in optionModel.optionItems" :key="idx">
|
||||||
|
<Checkbox :label="option.value">
|
||||||
|
<Input v-model="option.value" placeholder="Value" size="small" style="width: 95px"></Input>
|
||||||
|
<Input v-model="option.label" placeholder="Label" size="small" style="width: 95px"></Input>
|
||||||
|
<i class="iconfont icon-drag drag-option"></i>
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
size="small"
|
||||||
|
type="warning"
|
||||||
|
@click="deleteOption(option, idx)"
|
||||||
|
icon="md-remove"
|
||||||
|
class="col-delete-button">
|
||||||
|
</Button>
|
||||||
|
</Checkbox>
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</CheckboxGroup>
|
||||||
|
<Cascader
|
||||||
|
v-else-if="(selectedWidget.type === 'cascader')"
|
||||||
|
v-model="optionModel.defaultValue"
|
||||||
|
:data="optionModel.optionItems"
|
||||||
|
:placeholder="i18nt('designer.hint.selectPlaceholder')"
|
||||||
|
@on-change="emitDefaultValueChange"
|
||||||
|
style="width: 100%">
|
||||||
|
</Cascader>
|
||||||
|
<div v-if="(selectedWidget.type === 'cascader')">
|
||||||
|
<Button type="text" @click="importCascaderOptions">{{i18nt('designer.setting.importOptions')}}
|
||||||
|
</Button>
|
||||||
|
<Button type="text" @click="resetDefault">{{i18nt('designer.setting.resetDefault')}}</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="(selectedWidget.type === 'radio') || (selectedWidget.type === 'checkbox') || (selectedWidget.type === 'select')">
|
||||||
|
<Button type="text" @click="addOption">{{i18nt('designer.setting.addOption')}}</Button>
|
||||||
|
<Button type="text" @click="importOptions">{{i18nt('designer.setting.importOptions')}}</Button>
|
||||||
|
<Button type="text" @click="resetDefault">{{i18nt('designer.setting.resetDefault')}}</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.setting.importOptions')"
|
||||||
|
v-model="showImportDialogFlag" :closable="true" class="small-padding-dialog" draggable
|
||||||
|
:mask-closable="false" >
|
||||||
|
<FormItem :label-width="0">
|
||||||
|
<el-input type="textarea" rows="10" v-model="optionLines"></el-input>
|
||||||
|
</FormItem>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" @click="saveOptions">{{i18nt('designer.hint.confirm')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="default" @click="showImportDialogFlag = false">{{i18nt('designer.hint.cancel')}}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.setting.importOptions')"
|
||||||
|
v-model="showImportCascaderDialogFlag" :closable="true" class="small-padding-dialog" draggable
|
||||||
|
:mask-closable="false" >
|
||||||
|
<code-editor v-model="cascaderOptions" mode="json" :readonly="false"></code-editor>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" @click="saveCascaderOptions">{{i18nt('designer.hint.confirm')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="default" @click="showImportCascaderDialogFlag = false">
|
||||||
|
{{i18nt('designer.hint.cancel')}}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
import CodeEditor from '@/components-iview/code-editor/index'
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "OptionItemsSetting",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
//CodeEditor: () => import('@/components/code-editor/index'),
|
||||||
|
CodeEditor,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
designer: Object,
|
||||||
|
selectedWidget: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showImportDialogFlag: false,
|
||||||
|
optionLines: '',
|
||||||
|
|
||||||
|
cascaderOptions: '',
|
||||||
|
showImportCascaderDialogFlag: false,
|
||||||
|
|
||||||
|
//separator: '||',
|
||||||
|
separator: ',',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
optionModel() {
|
||||||
|
return this.selectedWidget.options
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'selectedWidget.options': {
|
||||||
|
deep: true,
|
||||||
|
handler(val) {
|
||||||
|
//console.log('888888', 'Options change!')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
emitDefaultValueChange() {
|
||||||
|
if (!!this.designer) {
|
||||||
|
/* 组件过多时,事件处理效率不高!! */
|
||||||
|
//this.designer.emitEvent('defaultValueChanged', this.selectedWidget.id)
|
||||||
|
|
||||||
|
// if (!!this.designer.selectedFieldWrapper && this.designer.selectedFieldWrapper.refreshDefaultValue) {
|
||||||
|
// console.log('aaaa', '123456')
|
||||||
|
// this.designer.selectedFieldWrapper.refreshDefaultValue()
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!!this.designer.formWidget) {
|
||||||
|
let fieldWidget = this.designer.formWidget.getWidgetRef(this.selectedWidget.options.name)
|
||||||
|
if (!!fieldWidget && !!fieldWidget.refreshDefaultValue) {
|
||||||
|
fieldWidget.refreshDefaultValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteOption(option, index) {
|
||||||
|
this.optionModel.optionItems.splice(index, 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
addOption() {
|
||||||
|
let newValue = this.optionModel.optionItems.length + 1
|
||||||
|
this.optionModel.optionItems.push({
|
||||||
|
value: newValue,
|
||||||
|
label: 'new option'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
importOptions() {
|
||||||
|
this.optionLines = ''
|
||||||
|
if (this.optionModel.optionItems.length > 0) {
|
||||||
|
this.optionModel.optionItems.forEach((opt) => {
|
||||||
|
if (opt.value === opt.label) {
|
||||||
|
this.optionLines += opt.value + '\n'
|
||||||
|
} else {
|
||||||
|
this.optionLines += opt.value + this.separator + opt.label + '\n'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showImportDialogFlag = true
|
||||||
|
},
|
||||||
|
|
||||||
|
saveOptions() {
|
||||||
|
let lineArray = this.optionLines.split('\n')
|
||||||
|
//console.log('test', lineArray)
|
||||||
|
if (lineArray.length > 0) {
|
||||||
|
this.optionModel.optionItems = []
|
||||||
|
lineArray.forEach((optLine) => {
|
||||||
|
if (!!optLine && !!optLine.trim()) {
|
||||||
|
if (optLine.indexOf(this.separator) !== -1) {
|
||||||
|
this.optionModel.optionItems.push({
|
||||||
|
value: optLine.split(this.separator)[0],
|
||||||
|
label: optLine.split(this.separator)[1]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.optionModel.optionItems.push({
|
||||||
|
value: optLine,
|
||||||
|
label: optLine
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.optionModel.optionItems = []
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showImportDialogFlag = false
|
||||||
|
},
|
||||||
|
|
||||||
|
resetDefault() {
|
||||||
|
if ((this.selectedWidget.type === 'checkbox') ||
|
||||||
|
((this.selectedWidget.type === 'select') && this.selectedWidget.options.multiple)) {
|
||||||
|
this.optionModel.defaultValue = []
|
||||||
|
} else {
|
||||||
|
this.optionModel.defaultValue = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emitDefaultValueChange()
|
||||||
|
},
|
||||||
|
|
||||||
|
importCascaderOptions() {
|
||||||
|
this.cascaderOptions = JSON.stringify(this.optionModel.optionItems, null, ' ')
|
||||||
|
this.showImportCascaderDialogFlag = true
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCascaderOptions() {
|
||||||
|
try {
|
||||||
|
let newOptions = JSON.parse(this.cascaderOptions)
|
||||||
|
this.optionModel.optionItems = newOptions
|
||||||
|
//TODO: 是否需要重置选项默认值??
|
||||||
|
|
||||||
|
this.showImportCascaderDialogFlag = false
|
||||||
|
} catch (ex) {
|
||||||
|
this.$message.error(this.i18nt('designer.hint.invalidOptionsData') + ex.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.option-items-pane ul {
|
||||||
|
padding-inline-start: 6px;
|
||||||
|
padding-left: 6px;
|
||||||
|
/* 重置IE11默认样式 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-items-pane ::v-deep ul>li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.ghost {
|
||||||
|
background: #fff;
|
||||||
|
border: 2px dotted $--color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-option {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-padding-dialog ::v-deep .el-dialog__body {
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer .el-button {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,566 @@
|
||||||
|
<template>
|
||||||
|
<div class="toolbar-container">
|
||||||
|
<div class="left-toolbar">
|
||||||
|
<Button type="text" :disabled="undoDisabled" :title="i18nt('designer.toolbar.undoHint')"
|
||||||
|
@click="undoHistory" style="background-color: transparent;">
|
||||||
|
<svg-icon icon-class="undo" />
|
||||||
|
</Button>
|
||||||
|
<Button type="text" :disabled="redoDisabled" :title="i18nt('designer.toolbar.redoHint')"
|
||||||
|
@click="redoHistory" style="background-color: transparent;">
|
||||||
|
<svg-icon icon-class="redo" />
|
||||||
|
</Button>
|
||||||
|
<ButtonGroup style="margin-left: 20px">
|
||||||
|
<Button :type="layoutType === 'PC' ? 'primary': 'default'" @click="changeLayoutType('PC')">
|
||||||
|
{{i18nt('designer.toolbar.pcLayout')}}
|
||||||
|
</Button>
|
||||||
|
<Button :type="layoutType === 'H5' ? 'primary': 'default'" @click="changeLayoutType('H5')">
|
||||||
|
{{i18nt('designer.toolbar.mobileLayout')}}
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-toolbar">
|
||||||
|
<Button type="text" @click="clearFormWidget"><i class="ivu-icon ivu-icon-ios-trash" />{{i18nt('designer.toolbar.clear')}}</Button>
|
||||||
|
<Button type="text" @click="previewForm"><i class="ivu-icon ivu-icon-ios-eye-outline" />{{i18nt('designer.toolbar.preview')}}</Button>
|
||||||
|
<Button type="text" @click="importJson">{{i18nt('designer.toolbar.importJson')}}</Button>
|
||||||
|
<Button type="text" @click="exportJson">{{i18nt('designer.toolbar.exportJson')}}</Button>
|
||||||
|
<Button type="text" @click="exportCode">{{i18nt('designer.toolbar.exportCode')}}</Button>
|
||||||
|
<Button type="text" v-if="false">{{i18nt('designer.toolbar.generateCode')}}</Button>
|
||||||
|
<Button type="text" @click="generateSFC"><svg-icon icon-class="vue-sfc" />{{i18nt('designer.toolbar.generateSFC')}}</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.toolbar.preview')"
|
||||||
|
v-model="showPreviewDialogFlag" :closable="true" class="small-padding-dialog" draggable
|
||||||
|
:mask-closable="false" width="75%"
|
||||||
|
:fullscreen ="layoutType === 'H5'">
|
||||||
|
<div v-if="showPreviewDialogFlag">
|
||||||
|
<div class="form-render-wrapper" :class="[layoutType === 'H5' ? 'h5-layout' : '']">
|
||||||
|
<VFormRender ref="preForm" :form-json="formJson" :form-data="testFormData"
|
||||||
|
@appendButtonClick="testOnAppendButtonClick" @buttonClick="testOnButtonClick"
|
||||||
|
@formChange="handleFormChange" @change="handlerNativeChange"
|
||||||
|
></VFormRender>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<code-editor v-model="testFunc" style="display: none"></code-editor>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button type="primary" size="default" @click="getFormData">{{i18nt('designer.hint.getFormData')}}</Button>
|
||||||
|
<Button type="primary" size="default" @click="resetForm">{{i18nt('designer.hint.resetForm')}}</Button>
|
||||||
|
<Button type="primary" size="default" @click="setFormDisabled">{{i18nt('designer.hint.disableForm')}}</Button>
|
||||||
|
<Button type="primary" size="default" @click="setFormEnabled">{{i18nt('designer.hint.enableForm')}}</Button>
|
||||||
|
<Button type="default" size="default" @click="showPreviewDialogFlag = false">{{i18nt('designer.hint.closePreview')}}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.toolbar.importJson')"
|
||||||
|
v-model="showImportJsonDialogFlag" :closable="true" class="small-padding-dialog" draggable
|
||||||
|
:mask-closable="false">
|
||||||
|
<Alert show-icon>{{i18nt('designer.hint.importJsonHint')}}</Alert>
|
||||||
|
<code-editor v-if="showImportJsonDialogFlag" :mode="'json'" :readonly="false" v-model="importTemplate"></code-editor>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" @click="showImportJsonDialogFlag = false">
|
||||||
|
{{i18nt('designer.hint.cancel')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="primary" @click="doJsonImport">
|
||||||
|
{{i18nt('designer.hint.import')}}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.toolbar.exportJson')"
|
||||||
|
v-model="showExportJsonDialogFlag" :closable="true" class="small-padding-dialog" draggable
|
||||||
|
:mask-closable="false">
|
||||||
|
<code-editor v-if="showExportJsonDialogFlag" :mode="'json'" :readonly="true" v-model="jsonContent"></code-editor>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" class="copy-json-btn" :data-clipboard-text="jsonRawContent">
|
||||||
|
{{i18nt('designer.hint.copyJson')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="default" @click="showExportJsonDialogFlag = false">
|
||||||
|
{{i18nt('designer.hint.closePreview')}}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.toolbar.exportCode')"
|
||||||
|
v-model="showExportCodeDialogFlag" :closable="true" class="small-padding-dialog" draggable
|
||||||
|
:mask-closable="false"
|
||||||
|
width="65%">
|
||||||
|
<Tabs type="line" class="no-box-shadow" v-model="activeCodeTab">
|
||||||
|
<TabPane label="Vue" name="vue">
|
||||||
|
<code-editor v-if="showExportCodeDialogFlag" :mode="'html'" :readonly="true" v-model="vueCode" :user-worker="false"></code-editor>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane label="HTML" name="html">
|
||||||
|
<code-editor v-if="showExportCodeDialogFlag" :mode="'html'" :readonly="true" v-model="htmlCode" :user-worker="false"></code-editor>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" class="copy-vue-btn" :data-clipboard-text="vueCode">
|
||||||
|
{{i18nt('designer.hint.copyVueCode')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="primary" class="copy-html-btn" :data-clipboard-text="htmlCode">
|
||||||
|
{{i18nt('designer.hint.copyHtmlCode')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="primary" @click="saveVueCode">{{i18nt('designer.hint.saveVueCode')}}</Button>
|
||||||
|
<Button size="default" type="primary" @click="saveHtmlCode">{{i18nt('designer.hint.saveHtmlCode')}}</Button>
|
||||||
|
<Button size="default" type="default" @click="showExportCodeDialogFlag = false">
|
||||||
|
{{i18nt('designer.hint.closePreview')}}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.hint.exportFormData')"
|
||||||
|
v-model="showFormDataDialogFlag" :closable="true" class="dialog-title-light-bg" draggable
|
||||||
|
:mask-closable="false">
|
||||||
|
<div style="border: 1px solid #DCDFE6">
|
||||||
|
<code-editor v-if="showFormDataDialogFlag" :mode="'json'" :readonly="true" v-model="formDataJson"></code-editor>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" class="copy-form-data-json-btn" :data-clipboard-text="formDataRawJson">
|
||||||
|
{{i18nt('designer.hint.copyFormData')}}
|
||||||
|
</Button>
|
||||||
|
<Button size="default" type="default" @click="showFormDataDialogFlag = false">
|
||||||
|
{{i18nt('designer.hint.closePreview')}}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
<Modal :title="i18nt('designer.toolbar.generateSFC')" width="85%"
|
||||||
|
v-model="showExportSFCDialogFlag" :closable="true" class="dialog-title-light-bg" draggable
|
||||||
|
:mask-closable="false">
|
||||||
|
<Tabs type="line" class="no-box-shadow no-padding" v-model="activeSFCTab">
|
||||||
|
<TabPane label="Vue2" name="vue2">
|
||||||
|
<code-editor v-if="showExportSFCDialogFlag" :mode="'html'" :readonly="true" v-model="sfcCode" :user-worker="false"></code-editor>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane label="Vue3" name="vue3">
|
||||||
|
<code-editor v-if="showExportSFCDialogFlag" :mode="'html'" :readonly="true" v-model="sfcCodeV3" :user-worker="false"></code-editor>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" class="copy-vue2-sfc-btn" :data-clipboard-text="sfcCode" @click="copyV2SFC">
|
||||||
|
{{i18nt('designer.hint.copyVue2SFC')}}</Button>
|
||||||
|
<Button size="default" type="primary" class="copy-vue3-sfc-btn" :data-clipboard-text="sfcCodeV3" @click="copyV3SFC">
|
||||||
|
{{i18nt('designer.hint.copyVue3SFC')}}</Button>
|
||||||
|
<Button size="default" @click="saveV2SFC">{{i18nt('designer.hint.saveVue2SFC')}}</Button>
|
||||||
|
<Button size="default" @click="saveV3SFC">{{i18nt('designer.hint.saveVue3SFC')}}</Button>
|
||||||
|
<Button size="default" type="default" @click="showExportSFCDialogFlag = false">
|
||||||
|
{{i18nt('designer.hint.closePreview')}}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Modal :title="this.i18nt('designer.hint.saveFileTitle')" width="300px"
|
||||||
|
v-model="showExportSFCFileNameDialogFlag" :closable="true" class="dialog-title-light-bg" draggable
|
||||||
|
:mask-closable="false">
|
||||||
|
<Input type="text" size="large" v-model="saveFileName" :placeholder="i18nt('designer.hint.fileNameForSave')" clearable/>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<Button size="default" type="primary" @click="saveFileExec">{{i18nt('designer.hint.confirm')}}</Button>
|
||||||
|
<Button size="default" type="default" @click="showExportSFCFileNameDialogFlag = false">{{i18nt('designer.hint.closePreview')}}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VFormRender from '@/components-iview/form-render/index'
|
||||||
|
import CodeEditor from '@/components-iview/code-editor/index'
|
||||||
|
import Clipboard from 'clipboard'
|
||||||
|
import {deepClone, copyToClipboard, generateId, getQueryParam} from "@/utils/util";
|
||||||
|
import i18n from "../../utils/i18n";
|
||||||
|
import {generateCode} from "../../utils/code-generator";
|
||||||
|
import {genSFC} from "../../utils/sfc-generator";
|
||||||
|
import loadBeautifier from "@/utils/beautifierLoader";
|
||||||
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ToolbarPanel",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
VFormRender,
|
||||||
|
//CodeEditor: () => import('@/components/code-editor/index'),
|
||||||
|
CodeEditor,
|
||||||
|
Clipboard,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
designer: Object
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if(this.load) this.reloadCode();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPreviewDialogFlag: false,
|
||||||
|
showImportJsonDialogFlag: false,
|
||||||
|
showExportJsonDialogFlag: false,
|
||||||
|
showExportCodeDialogFlag: false,
|
||||||
|
showFormDataDialogFlag: false,
|
||||||
|
showExportSFCDialogFlag: false,
|
||||||
|
showExportSFCFileNameDialogFlag: false,
|
||||||
|
|
||||||
|
|
||||||
|
saveFileHandler:null,
|
||||||
|
saveFileName:'',
|
||||||
|
|
||||||
|
testFunc: '',
|
||||||
|
importTemplate: '',
|
||||||
|
jsonContent: '',
|
||||||
|
jsonRawContent: '',
|
||||||
|
|
||||||
|
formDataJson: '',
|
||||||
|
formDataRawJson: '',
|
||||||
|
|
||||||
|
vueCode: '',
|
||||||
|
htmlCode: '',
|
||||||
|
|
||||||
|
sfcCode: '',
|
||||||
|
sfcCodeV3: '',
|
||||||
|
|
||||||
|
activeCodeTab: 'vue',
|
||||||
|
activeSFCTab: 'vue2',
|
||||||
|
|
||||||
|
testFormData: {
|
||||||
|
// 'userName': '666888',
|
||||||
|
// 'productItems': [
|
||||||
|
// {'pName': 'iPhone12', 'pNum': 10},
|
||||||
|
// {'pName': 'P50', 'pNum': 16},
|
||||||
|
// ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formJson() {
|
||||||
|
return {
|
||||||
|
widgetList: this.designer.widgetList,
|
||||||
|
formConfig: this.designer.formConfig
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
undoDisabled() {
|
||||||
|
return !this.designer.undoEnabled()
|
||||||
|
},
|
||||||
|
|
||||||
|
redoDisabled() {
|
||||||
|
return !this.designer.redoEnabled()
|
||||||
|
},
|
||||||
|
|
||||||
|
layoutType() {
|
||||||
|
return this.designer.getLayoutType()
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
undoHistory() {
|
||||||
|
this.designer.undoHistoryStep()
|
||||||
|
},
|
||||||
|
|
||||||
|
redoHistory() {
|
||||||
|
this.designer.redoHistoryStep()
|
||||||
|
},
|
||||||
|
|
||||||
|
changeLayoutType(newType) {
|
||||||
|
this.designer.changeLayoutType(newType)
|
||||||
|
},
|
||||||
|
|
||||||
|
clearFormWidget() {
|
||||||
|
this.designer.clearDesigner()
|
||||||
|
},
|
||||||
|
|
||||||
|
previewForm() {
|
||||||
|
this.showPreviewDialogFlag = true
|
||||||
|
},
|
||||||
|
saveFileExec(){
|
||||||
|
this.saveAsFile(this.saveFileHandler[0],this.saveFileHandler[1])
|
||||||
|
},
|
||||||
|
saveAsFile(fileContent, defaultFileName) {
|
||||||
|
let value="";
|
||||||
|
if (!this.saveFileName) {
|
||||||
|
value = defaultFileName
|
||||||
|
}else{
|
||||||
|
value=this.saveFileName ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getQueryParam('vscode') == 1) {
|
||||||
|
this.vsSaveFile(value, fileContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileBlob = new Blob([fileContent], { type: 'text/plain;charset=utf-8' })
|
||||||
|
saveAs(fileBlob ,value)
|
||||||
|
},
|
||||||
|
|
||||||
|
vsSaveFile(fileName, fileContent) {
|
||||||
|
const msgObj = {
|
||||||
|
cmd: 'writeFile',
|
||||||
|
data: {
|
||||||
|
fileName,
|
||||||
|
code: fileContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.parent.postMessage(msgObj, '*')
|
||||||
|
},
|
||||||
|
|
||||||
|
importJson() {
|
||||||
|
this.importTemplate = JSON.stringify(this.designer.getImportTemplate(), null, ' ')
|
||||||
|
this.showImportJsonDialogFlag = true
|
||||||
|
},
|
||||||
|
|
||||||
|
doJsonImport() {
|
||||||
|
try {
|
||||||
|
let importObj = JSON.parse(this.importTemplate)
|
||||||
|
this.designer.loadFormJson(importObj)
|
||||||
|
|
||||||
|
this.showImportJsonDialogFlag = false
|
||||||
|
this.$message.success(this.i18nt('designer.hint.importJsonSuccess'))
|
||||||
|
|
||||||
|
this.designer.emitHistoryChange()
|
||||||
|
} catch (ex) {
|
||||||
|
this.$message.error(ex + '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
exportJson() {
|
||||||
|
//alert( JSON.stringify(this.designer.widgetList) )
|
||||||
|
//this.jsonContent = JSON.stringify(this.designer.widgetList, null, ' ')
|
||||||
|
let widgetList = deepClone(this.designer.widgetList)
|
||||||
|
let formConfig = deepClone(this.designer.formConfig)
|
||||||
|
this.jsonContent = JSON.stringify({
|
||||||
|
widgetList,
|
||||||
|
formConfig
|
||||||
|
}, null, ' ')
|
||||||
|
this.jsonRawContent = JSON.stringify({
|
||||||
|
widgetList,
|
||||||
|
formConfig
|
||||||
|
})
|
||||||
|
this.showExportJsonDialogFlag = true;
|
||||||
|
this.$forceUpdate();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
let copyClipboard = new Clipboard('.copy-json-btn')
|
||||||
|
copyClipboard.on('success', e => {
|
||||||
|
this.$message.success(this.i18nt('designer.hint.copyJsonSuccess'))
|
||||||
|
copyClipboard.destroy() // 释放内存
|
||||||
|
})
|
||||||
|
copyClipboard.on('error', e => { // 不支持复制
|
||||||
|
this.$message.error(this.i18nt('designer.hint.copyJsonFail'))
|
||||||
|
copyClipboard.destroy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
exportCode() {
|
||||||
|
this.vueCode = generateCode(this.formJson)
|
||||||
|
this.htmlCode = generateCode(this.formJson, 'html')
|
||||||
|
this.showExportCodeDialogFlag = true
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
let vueClipboard = new Clipboard('.copy-vue-btn')
|
||||||
|
vueClipboard.on('success', e => {
|
||||||
|
this.$message.success(this.i18nt('designer.hint.copyVueCodeSuccess'))
|
||||||
|
vueClipboard.destroy() // 释放内存
|
||||||
|
})
|
||||||
|
vueClipboard.on('error', e => { // 不支持复制
|
||||||
|
this.$message.error(this.i18nt('designer.hint.copyVueCodeFail'))
|
||||||
|
vueClipboard.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
let htmlClipboard = new Clipboard('.copy-html-btn')
|
||||||
|
htmlClipboard.on('success', e => {
|
||||||
|
this.$message.success(this.i18nt('designer.hint.copyHtmlCodeSuccess'))
|
||||||
|
htmlClipboard.destroy() // 释放内存
|
||||||
|
})
|
||||||
|
htmlClipboard.on('error', e => { // 不支持复制
|
||||||
|
this.$message.error(this.i18nt('designer.hint.copyHtmlCodeFail'))
|
||||||
|
htmlClipboard.destroy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
copyVueCode(e) {
|
||||||
|
copyToClipboard(this.vueCode, e,
|
||||||
|
this.$message,
|
||||||
|
this.i18nt('designer.hint.copyVueCodeSuccess'),
|
||||||
|
this.i18nt('designer.hint.copyVueCodeFail')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
copyHtmlCode(e) {
|
||||||
|
copyToClipboard(this.htmlCode, e,
|
||||||
|
this.$message,
|
||||||
|
this.i18nt('designer.hint.copyHtmlCodeSuccess'),
|
||||||
|
this.i18nt('designer.hint.copyHtmlCodeFail')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
saveVueCode() {
|
||||||
|
this.saveFileHandler=[this.vueCode, `vform${generateId()}.vue`];
|
||||||
|
this.showExportSFCFileNameDialogFlag=true;
|
||||||
|
},
|
||||||
|
|
||||||
|
saveHtmlCode() {
|
||||||
|
this.saveFileHandler=[this.htmlCode, `vform${generateId()}.vue`];
|
||||||
|
this.showExportSFCFileNameDialogFlag=true;
|
||||||
|
},
|
||||||
|
|
||||||
|
generateSFC() {
|
||||||
|
loadBeautifier(beautifier => {
|
||||||
|
this.sfcCode = genSFC(this.designer.formConfig, this.designer.widgetList, beautifier)
|
||||||
|
this.sfcCodeV3 = genSFC(this.designer.formConfig, this.designer.widgetList, beautifier, true)
|
||||||
|
this.showExportSFCDialogFlag = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
copyV2SFC(e) {
|
||||||
|
copyToClipboard(this.sfcCode, e,
|
||||||
|
this.$message,
|
||||||
|
this.i18nt('designer.hint.copySFCSuccess'),
|
||||||
|
this.i18nt('designer.hint.copySFCFail')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
copyV3SFC(e) {
|
||||||
|
copyToClipboard(this.sfcCodeV3, e,
|
||||||
|
this.$message,
|
||||||
|
this.i18nt('designer.hint.copySFCSuccess'),
|
||||||
|
this.i18nt('designer.hint.copySFCFail')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
saveV2SFC() {
|
||||||
|
this.saveFileHandler=[this.sfcCode, `vformV2-${generateId()}.vue`];
|
||||||
|
this.showExportSFCFileNameDialogFlag=true;
|
||||||
|
// this.saveAsFile(this.sfcCode, `vformV2-${generateId()}.vue`)
|
||||||
|
},
|
||||||
|
|
||||||
|
saveV3SFC() {
|
||||||
|
this.saveFileHandler=[this.sfcCodeV3, `vformV3-${generateId()}.vue`];
|
||||||
|
this.showExportSFCFileNameDialogFlag=true;
|
||||||
|
// this.saveAsFile(this.sfcCodeV3, `vformV3-${generateId()}.vue`)
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormData() {
|
||||||
|
this.$refs['preForm'].getFormData().then(formData => {
|
||||||
|
//alert( JSON.stringify(formData) )
|
||||||
|
|
||||||
|
this.formDataJson = JSON.stringify(formData, null, ' ')
|
||||||
|
this.formDataRawJson = JSON.stringify(formData)
|
||||||
|
|
||||||
|
this.showFormDataDialogFlag = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
let copyClipboard = new Clipboard('.copy-form-data-json-btn')
|
||||||
|
copyClipboard.on('success', e => {
|
||||||
|
this.$message.success(this.i18nt('designer.hint.copyJsonSuccess'))
|
||||||
|
copyClipboard.destroy() // 释放内存
|
||||||
|
})
|
||||||
|
copyClipboard.on('error', e => { // 不支持复制
|
||||||
|
this.$message.error(this.i18nt('designer.hint.copyJsonFail'))
|
||||||
|
copyClipboard.destroy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$message.error(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
resetForm() {
|
||||||
|
this.$refs['preForm'].resetForm()
|
||||||
|
},
|
||||||
|
|
||||||
|
setFormDisabled() {
|
||||||
|
this.$refs['preForm'].disableForm()
|
||||||
|
},
|
||||||
|
|
||||||
|
setFormEnabled() {
|
||||||
|
this.$refs['preForm'].enableForm()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFormChange(fieldName, newValue, oldValue, formModel) {
|
||||||
|
console.log('---formChange start---')
|
||||||
|
console.log('fieldName', fieldName)
|
||||||
|
console.log('newValue', newValue)
|
||||||
|
console.log('oldValue', oldValue)
|
||||||
|
console.log('formModel', formModel)
|
||||||
|
console.log('---formChange end---')
|
||||||
|
},
|
||||||
|
|
||||||
|
testOnAppendButtonClick(clickedWidget) {
|
||||||
|
console.log('test', clickedWidget)
|
||||||
|
},
|
||||||
|
|
||||||
|
testOnButtonClick(button) {
|
||||||
|
console.log('test', button)
|
||||||
|
},
|
||||||
|
handlerNativeChange(args){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
div.toolbar-container {
|
||||||
|
//border-bottom: 1px solid #EBEEF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-toolbar {
|
||||||
|
float: left;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-toolbar {
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
::v-deep .el-button--text {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
.ivu-icon{
|
||||||
|
font-size:18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button i {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-padding-dialog {
|
||||||
|
::v-deep .el-dialog__header {
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
background: #f1f2f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-dialog__body {
|
||||||
|
padding: 12px 15px 12px 15px;
|
||||||
|
|
||||||
|
.el-alert {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .ace-container {
|
||||||
|
border: 1px solid #DCDFE6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-title-light-bg {
|
||||||
|
::v-deep .el-dialog__header {
|
||||||
|
background: #f1f2f3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-box-shadow {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-render-wrapper {
|
||||||
|
//height: calc(100vh - 142px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-render-wrapper.h5-layout {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 420px;
|
||||||
|
border-radius: 15px;
|
||||||
|
//border-width: 10px;
|
||||||
|
background-color:blue;
|
||||||
|
box-shadow: 0 0 1px 10px #495060;
|
||||||
|
height: calc(100vh - 142px);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,275 @@
|
||||||
|
<template>
|
||||||
|
<Scroll class="side-scroll-bar" :height="scrollerHeight">
|
||||||
|
<div class="panel-container">
|
||||||
|
|
||||||
|
<Collapse v-model="activeNames" class="widget-collapse">
|
||||||
|
<Panel name="1" :title="i18nt('designer.containerTitle')">
|
||||||
|
{{i18nt('designer.containerTitle')}}
|
||||||
|
<div slot="content">
|
||||||
|
<draggable tag="ul" :list="containers" :group="{name: 'dragGroup', pull: 'clone', put: false}"
|
||||||
|
:clone="handleContainerWidgetClone" ghost-class="ghost" :sort="false" :move="checkContainerMove"
|
||||||
|
@end="onContainerDragEnd">
|
||||||
|
<li v-for="(ctn, index) in containers" :key="index" class="container-widget-item"
|
||||||
|
:title="ctn.displayName" @dblclick="addContainerByDbClick(ctn)">
|
||||||
|
<span>
|
||||||
|
<svg-icon :icon-class="ctn.icon" />{{i18nt(`designer.widgetLabel.${ctn.type}`)}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<Panel name="2" :title="i18nt('designer.basicFieldTitle')">
|
||||||
|
{{i18nt('designer.basicFieldTitle')}}
|
||||||
|
<div slot="content">
|
||||||
|
<draggable tag="ul" :list="basicFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
|
||||||
|
:clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
|
||||||
|
<li v-for="(fld, index) in basicFields" :key="index" class="field-widget-item"
|
||||||
|
:title="fld.displayName" @dblclick="addFieldByDbClick(fld)">
|
||||||
|
<span>
|
||||||
|
<svg-icon :icon-class="fld.icon" />{{i18nt(`designer.widgetLabel.${fld.type}`)}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<Panel name="3" :title="i18nt('designer.advancedFieldTitle')">
|
||||||
|
{{i18nt('designer.advancedFieldTitle')}}
|
||||||
|
<div slot="content">
|
||||||
|
<draggable tag="ul" :list="advancedFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
|
||||||
|
:clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
|
||||||
|
<li v-for="(fld, index) in advancedFields" :key="index" class="field-widget-item"
|
||||||
|
:title="fld.displayName" @dblclick="addFieldByDbClick(fld)">
|
||||||
|
<span>
|
||||||
|
<svg-icon :icon-class="fld.icon" />{{i18nt(`designer.widgetLabel.${fld.type}`)}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<el-collapse-item name="4" :title="i18nt('designer.customFieldTitle')">
|
||||||
|
<draggable tag="ul" :list="customFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
|
||||||
|
:clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
|
||||||
|
<li v-for="(fld, index) in customFields" :key="index" class="field-widget-item" :title="fld.displayName"
|
||||||
|
@dblclick="addFieldByDbClick(fld)">
|
||||||
|
<span :title="fld.displayName"><svg-icon :icon-class="fld.icon" />{{i18nt(`designer.widgetLabel.${fld.type}`)}}</span>
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</el-collapse-item>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Scroll>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Draggable from 'vuedraggable'
|
||||||
|
import {
|
||||||
|
containers,
|
||||||
|
basicFields,
|
||||||
|
advancedFields,
|
||||||
|
customFields
|
||||||
|
} from "./widgetsConfig";
|
||||||
|
import {
|
||||||
|
generateId,
|
||||||
|
deepClone,
|
||||||
|
addWindowResizeHandler
|
||||||
|
} from "@/utils/util";
|
||||||
|
import i18n from "../../utils/i18n.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "FieldPanel",
|
||||||
|
mixins: [i18n],
|
||||||
|
components: {
|
||||||
|
Draggable,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
designer: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
scrollerHeight: 0,
|
||||||
|
|
||||||
|
activeNames: ['1', '2', '3', '4'],
|
||||||
|
|
||||||
|
containers,
|
||||||
|
basicFields,
|
||||||
|
advancedFields,
|
||||||
|
customFields,
|
||||||
|
|
||||||
|
allContainers: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadWidgets()
|
||||||
|
|
||||||
|
this.scrollerHeight = window.innerHeight - 56
|
||||||
|
addWindowResizeHandler(() => {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollerHeight = window.innerHeight - 56
|
||||||
|
//console.log(this.scrollerHeight)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadWidgets() {
|
||||||
|
//this.allContainers = deepClone(this.containers)
|
||||||
|
|
||||||
|
this.containers = this.containers.map(con => {
|
||||||
|
return {
|
||||||
|
...con,
|
||||||
|
//category: 'container',
|
||||||
|
displayName: this.i18nt(`designer.widgetLabel.${con.type}`)
|
||||||
|
}
|
||||||
|
}).filter(con => {
|
||||||
|
return !con.internal
|
||||||
|
})
|
||||||
|
|
||||||
|
this.basicFields = this.basicFields.map(fld => {
|
||||||
|
return {
|
||||||
|
...fld,
|
||||||
|
displayName: this.i18nt(`designer.widgetLabel.${fld.type}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.advancedFields = this.advancedFields.map(fld => {
|
||||||
|
return {
|
||||||
|
...fld,
|
||||||
|
displayName: this.i18nt(`designer.widgetLabel.${fld.type}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
///*
|
||||||
|
this.customFields = this.customFields.map(fld => {
|
||||||
|
return {
|
||||||
|
...fld,
|
||||||
|
displayName: this.i18nt(`designer.widgetLabel.${fld.type}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//*/
|
||||||
|
},
|
||||||
|
|
||||||
|
handleContainerWidgetClone(origin) {
|
||||||
|
return this.designer.copyNewContainerWidget(origin)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFieldWidgetClone(origin) {
|
||||||
|
return this.designer.copyNewFieldWidget(origin)
|
||||||
|
},
|
||||||
|
|
||||||
|
checkContainerMove(evt) {
|
||||||
|
return this.designer.checkWidgetMove(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
onContainerDragEnd(evt) {
|
||||||
|
//console.log('Drag end of container: ')
|
||||||
|
//console.log(evt)
|
||||||
|
},
|
||||||
|
|
||||||
|
addContainerByDbClick(container) {
|
||||||
|
this.designer.addContainerByDbClick(container)
|
||||||
|
},
|
||||||
|
|
||||||
|
addFieldByDbClick(widget) {
|
||||||
|
//console.log('addWidgetByDbClick')
|
||||||
|
this.designer.addFieldByDbClick(widget)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.side-scroll-bar {
|
||||||
|
//height: calc(100% - 56px);
|
||||||
|
//height: 100%;
|
||||||
|
|
||||||
|
::v-deep .el-scrollbar__wrap {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
/deep/ .ivu-scroll-loader{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.panel-container {
|
||||||
|
//height: calc(100% - 48px);
|
||||||
|
//height: 100%;
|
||||||
|
//overflow-y: hidden;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ivu-collapse-item ::v-deep ul>li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-collapse {
|
||||||
|
::v-deep .el-collapse-item__header {
|
||||||
|
padding-left: 8px;
|
||||||
|
// font-style: italic;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .ivu-collapse-content {
|
||||||
|
padding:0px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 10px;
|
||||||
|
/* 重置IE11默认样式 */
|
||||||
|
margin: 0;
|
||||||
|
/* 重置IE11默认样式 */
|
||||||
|
margin-block-start: 0;
|
||||||
|
margin-block-end: 0.25em;
|
||||||
|
padding-inline-start: 10px;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-widget-item,
|
||||||
|
.field-widget-item {
|
||||||
|
display: inline-block;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
width: 112px;
|
||||||
|
float: left;
|
||||||
|
margin: 2px 6px 6px 0;
|
||||||
|
cursor: move;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f1f2f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-widget-item:hover,
|
||||||
|
.field-widget-item:hover {
|
||||||
|
background: #EBEEF5;
|
||||||
|
outline: 1px solid $--color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handler {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 160px;
|
||||||
|
background-color: #dddddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,953 @@
|
||||||
|
|
||||||
|
export const containers = [
|
||||||
|
{
|
||||||
|
type: 'grid',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'grid',
|
||||||
|
cols: [],
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
gutter: 12,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'table',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'table',
|
||||||
|
rows: [],
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'tab',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'tab',
|
||||||
|
tabs: [],
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
displayType: 'card',
|
||||||
|
size: 'default',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
type: 'section',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'section',
|
||||||
|
widgetList: [],
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'grid-col',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'grid-col',
|
||||||
|
internal: true,
|
||||||
|
widgetList: [],
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
hidden: false,
|
||||||
|
span: 12,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'table-cell',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'table-cell',
|
||||||
|
internal: true,
|
||||||
|
widgetList: [],
|
||||||
|
merged: false,
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
cellWidth: '',
|
||||||
|
cellHeight: '',
|
||||||
|
colspan: 1,
|
||||||
|
rowspan: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'tab-pane',
|
||||||
|
category: 'container',
|
||||||
|
icon: 'tab-pane',
|
||||||
|
internal: true,
|
||||||
|
widgetList: [],
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
label: '',
|
||||||
|
hidden: false,
|
||||||
|
active: false,
|
||||||
|
disabled: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
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,
|
||||||
|
validation: '',
|
||||||
|
validationHint: '',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
// minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
showWordLimit: false,
|
||||||
|
prefixIcon: '',
|
||||||
|
suffixIcon: '',
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
onInput: '',
|
||||||
|
onChange: '',
|
||||||
|
onFocus: '',
|
||||||
|
onBlur: '',
|
||||||
|
onValidate: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input-composite',
|
||||||
|
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,
|
||||||
|
validation: '',
|
||||||
|
validationHint: '',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
// minLength: null,
|
||||||
|
maxLength: null,
|
||||||
|
showWordLimit: false,
|
||||||
|
prependControl: true,
|
||||||
|
prependControlDisabled: false,
|
||||||
|
prependControlText:"", //后置按钮文字
|
||||||
|
prependControlIcon: 'ios-person', //后置按钮文字
|
||||||
|
prependControlType: 'button',
|
||||||
|
appendControl: true,
|
||||||
|
appendControlDisabled: false,
|
||||||
|
appendControlText:"", //后置按钮文字
|
||||||
|
appendControlIcon: 'ios-search', //后置按钮文字
|
||||||
|
appendControlType: 'button',
|
||||||
|
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
onInput: '',
|
||||||
|
onChange: '',
|
||||||
|
onFocus: '',
|
||||||
|
onBlur: '',
|
||||||
|
onValidate: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
validation: '',
|
||||||
|
validationHint: '',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
min: -100000000000,
|
||||||
|
max: 100000000000,
|
||||||
|
precision: 0,
|
||||||
|
formatter:'',
|
||||||
|
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',
|
||||||
|
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,
|
||||||
|
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',
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
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: '',
|
||||||
|
size: '',
|
||||||
|
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,
|
||||||
|
validation: '',
|
||||||
|
validationHint: '',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
max: 5,
|
||||||
|
clearable: true,
|
||||||
|
// 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,
|
||||||
|
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',
|
||||||
|
// size: '',
|
||||||
|
labelWidth: null,
|
||||||
|
labelHidden: false,
|
||||||
|
disabled: false,
|
||||||
|
hidden: false,
|
||||||
|
required: false,
|
||||||
|
validation: '',
|
||||||
|
validationHint: '',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
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',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'html-text',
|
||||||
|
icon: 'html-text',
|
||||||
|
formItemFlag: false,
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
columnWidth: '200px',
|
||||||
|
hidden: false,
|
||||||
|
htmlContent: '<b>html text</b>',
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
icon: 'button',
|
||||||
|
formItemFlag: false,
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
label: '',
|
||||||
|
columnWidth: '200px',
|
||||||
|
size: '',
|
||||||
|
disabled: false,
|
||||||
|
hidden: false,
|
||||||
|
type: '',
|
||||||
|
plain: false,
|
||||||
|
// round: false,
|
||||||
|
circle: false,
|
||||||
|
icon: null,
|
||||||
|
|
||||||
|
to:"",
|
||||||
|
replace:false,
|
||||||
|
target:"_self",
|
||||||
|
append:false,
|
||||||
|
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
onClick: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
icon: 'divider',
|
||||||
|
formItemFlag: false,
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
label: '',
|
||||||
|
size:'',
|
||||||
|
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: '',
|
||||||
|
labelWidth: null,
|
||||||
|
labelHidden: false,
|
||||||
|
disabled: false,
|
||||||
|
hidden: false,
|
||||||
|
required: false,
|
||||||
|
requiredHint: '',
|
||||||
|
customRule: '',
|
||||||
|
customRuleHint: '',
|
||||||
|
//-------------------
|
||||||
|
uploadURL: '',
|
||||||
|
uploadTip: '',
|
||||||
|
withCredentials: false,
|
||||||
|
multipleSelect: false,
|
||||||
|
showFileList: true,
|
||||||
|
// limit: 3,
|
||||||
|
fileMaxSize: 5, //MB
|
||||||
|
fileTypes: ['jpeg', 'png'],
|
||||||
|
fileAccept: "",
|
||||||
|
//headers: [],
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
onBeforeUpload: '',
|
||||||
|
onUploadSuccess: '',
|
||||||
|
onUploadError: '',
|
||||||
|
onValidate: '',
|
||||||
|
onUploadPreview:'',
|
||||||
|
onUploadProgress:'',
|
||||||
|
//onFileChange: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'file-upload',
|
||||||
|
icon: 'file-upload-field',
|
||||||
|
formItemFlag: true,
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
label: '',
|
||||||
|
labelWidth: null,
|
||||||
|
labelHidden: false,
|
||||||
|
disabled: false,
|
||||||
|
hidden: false,
|
||||||
|
required: false,
|
||||||
|
requiredHint: '',
|
||||||
|
customRule: '',
|
||||||
|
customRuleHint: '',
|
||||||
|
//-------------------
|
||||||
|
uploadURL: '',
|
||||||
|
uploadTip: '',
|
||||||
|
withCredentials: false,
|
||||||
|
multipleSelect: false,
|
||||||
|
showFileList: true,
|
||||||
|
// limit: 3,
|
||||||
|
fileMaxSize: 20, //MB
|
||||||
|
fileTypes: ['doc', 'docx', 'xls', 'xlsx'],
|
||||||
|
fileAccept: "",
|
||||||
|
//headers: [],
|
||||||
|
//-------------------
|
||||||
|
customClass: '', //自定义css类名
|
||||||
|
labelIconClass: null,
|
||||||
|
labelIconPosition: 'rear',
|
||||||
|
labelTooltip: null,
|
||||||
|
//-------------------
|
||||||
|
onCreated: '',
|
||||||
|
onMounted: '',
|
||||||
|
onBeforeUpload: '',
|
||||||
|
onUploadSuccess: '',
|
||||||
|
onUploadError: '',
|
||||||
|
onValidate: '',
|
||||||
|
onUploadPreview:'',
|
||||||
|
onUploadProgress:'',
|
||||||
|
//onFileChange: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'rich-editor',
|
||||||
|
icon: 'rich-editor-field',
|
||||||
|
formItemFlag: true,
|
||||||
|
options: {
|
||||||
|
name: '',
|
||||||
|
label: '',
|
||||||
|
placeholder: '',
|
||||||
|
labelWidth: null,
|
||||||
|
labelHidden: false,
|
||||||
|
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: '',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: '',
|
||||||
|
size: '',
|
||||||
|
labelWidth: null,
|
||||||
|
labelHidden: false,
|
||||||
|
disabled: false,
|
||||||
|
hidden: false,
|
||||||
|
clearable: true,
|
||||||
|
filterable: false,
|
||||||
|
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 = [
|
||||||
|
{
|
||||||
|
type: 'custom',
|
||||||
|
icon: 'custom-component',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'slot',
|
||||||
|
icon: 'slot-component',
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
|
@ -0,0 +1,605 @@
|
||||||
|
<template>
|
||||||
|
<div class="container-wrapper">
|
||||||
|
|
||||||
|
<Row v-if="widget.type === 'grid'" :key="widget.id" :gutter="widget.options.gutter" class="grid-container"
|
||||||
|
:class="[customClass]" :ref="widget.id" v-show="!widget.options.hidden">
|
||||||
|
<template v-for="(colWidget, colIdx) in widget.cols">
|
||||||
|
<grid-col-item :widget="colWidget" :key="colIdx" :parent-list="widget.cols"
|
||||||
|
:index-of-parent-list="colIdx" :parent-widget="widget"></grid-col-item>
|
||||||
|
</template>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div v-else-if="widget.type === 'table'" :key="widget.id" class="table-container"
|
||||||
|
v-show="!widget.options.hidden">
|
||||||
|
<table :ref="widget.id" class="table-layout" :class="[customClass]">
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, rowIdx) in widget.rows" :key="row.id">
|
||||||
|
<template v-for="(colWidget, colIdx) in row.cols">
|
||||||
|
<table-cell-item v-if="!colWidget.merged" :widget="colWidget" :key="colIdx"
|
||||||
|
:parent-list="widget.cols" :row-index="rowIdx" :col-index="colIdx"
|
||||||
|
:parent-widget="widget"></table-cell-item>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="widget.type === 'tab'" :key="widget.id" class="tab-container" v-show="!widget.options.hidden">
|
||||||
|
<Tabs v-model="activeTabName"
|
||||||
|
:type="widget.displayType"
|
||||||
|
:size="widget.options.size"
|
||||||
|
:ref="widget.id"
|
||||||
|
:class="[customClass]">
|
||||||
|
<TabPane v-for="(tab, index) in visibleTabs" :key="index" :label="tab.options.label"
|
||||||
|
:disabled="tab.options.disabled" :name="tab.options.name">
|
||||||
|
<template v-for="(subWidget, swIdx) in tab.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-item :widget="subWidget" :key="swIdx" :parent-list="tab.widgetList"
|
||||||
|
:index-of-parent-list="swIdx" :parent-widget="widget"></container-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :key="swIdx" :parent-list="tab.widgetList"
|
||||||
|
:index-of-parent-list="swIdx" :parent-widget="widget"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="widget.type === 'sub-form'" :key="widget.id" class="sub-form-container"
|
||||||
|
v-show="!widget.options.hidden">
|
||||||
|
<!-- <el-form :ref="widget.id" :model="formModel" label-position="top">-->
|
||||||
|
<Row class="header-row">
|
||||||
|
<div class="action-header-column">
|
||||||
|
<span class="action-label">{{i18nt('render.hint.subFormAction')}}</span>
|
||||||
|
<Button shape="round" type="primary" size="mini" class="action-button" @click="addSubFormRow"
|
||||||
|
:title="i18nt('render.hint.subFormAddActionHint')">
|
||||||
|
{{i18nt('render.hint.subFormAddAction')}}<i class="ivu-icon ivu-icon-md-add el-icon-right"></i>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<div :key="subWidget.id + 'thc'" class="field-header-column"
|
||||||
|
:class="[getLabelAlign(widget, subWidget), !!subWidget.options.required ? 'is-required' : '']"
|
||||||
|
:style="{width: subWidget.options.columnWidth}">
|
||||||
|
<span v-if="!!subWidget.options.labelIconClass" class="custom-label">
|
||||||
|
<template v-if="subWidget.options.labelIconPosition === 'front'">
|
||||||
|
<template v-if="!!subWidget.options.labelTooltip">
|
||||||
|
<el-tooltip :content="subWidget.options.labelTooltip" effect="light">
|
||||||
|
<i :class="subWidget.options.labelIconClass"></i>
|
||||||
|
</el-tooltip>{{subWidget.options.label}}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<i
|
||||||
|
:class="subWidget.options.labelIconClass"></i>{{subWidget.options.label}}</template>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="subWidget.options.labelIconPosition === 'rear'">
|
||||||
|
<template v-if="!!subWidget.options.labelTooltip">
|
||||||
|
{{subWidget.options.label}}
|
||||||
|
<el-tooltip :content="subWidget.options.labelTooltip" effect="light">
|
||||||
|
<i :class="subWidget.options.labelIconClass"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{subWidget.options.label}}<i
|
||||||
|
:class="subWidget.options.labelIconClass"></i></template>
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
<template v-else>
|
||||||
|
<span :title="subWidget.options.labelTooltip">{{subWidget.options.label}}</span></template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Row>
|
||||||
|
<Row v-for="(subFormRowId, sfrIdx) in rowIdData" class="sub-form-row" :key="subFormRowId">
|
||||||
|
<div class="sub-form-action-column hide-label">
|
||||||
|
<FormItem class="action-button-column">
|
||||||
|
<Button shape="round" type="" icon="ivu-icon ivu-icon-md-add" @click="insertSubFormRow(sfrIdx)"
|
||||||
|
:title="i18nt('render.hint.insertSubFormRow')"></Button>
|
||||||
|
<Button shape="round" type="" icon="ivu-icon ivu-icon-ios-trash" @click="deleteSubFormRow(sfrIdx)"
|
||||||
|
:title="i18nt('render.hint.deleteSubFormRow')"></Button>
|
||||||
|
<span v-if="widget.options.showRowNumber" class="row-number-span">#{{sfrIdx+1}}</span>
|
||||||
|
</FormItem>
|
||||||
|
</div>
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<div class="sub-form-table-column hide-label" :key="subWidget.id + 'tc' + subFormRowId"
|
||||||
|
:style="{width: subWidget.options.columnWidth}">
|
||||||
|
<field-widget :field="fieldSchemaData[sfrIdx][swIdx]" :key="fieldSchemaData[sfrIdx][swIdx].id"
|
||||||
|
:parent-list="widget.widgetList" :index-of-parent-list="swIdx" :parent-widget="widget"
|
||||||
|
:sub-form-row-id="subFormRowId" :sub-form-row-index="sfrIdx" :sub-form-col-index="swIdx">
|
||||||
|
</field-widget>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Row>
|
||||||
|
<!-- </el-form>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import emitter from 'element-ui/lib/mixins/emitter'
|
||||||
|
import FieldWidget from "../form-designer/form-widget/field-widget";
|
||||||
|
import GridColItem from "./grid-col-item";
|
||||||
|
import TableCellItem from "./table-cell-item"
|
||||||
|
import {
|
||||||
|
deepClone,
|
||||||
|
generateId,
|
||||||
|
optionExists
|
||||||
|
} from "../../utils/util";
|
||||||
|
import i18n from "../utils/i18n";
|
||||||
|
import refMixin from "./refMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ContainerItem",
|
||||||
|
componentName: 'ContainerItem',
|
||||||
|
mixins: [emitter, i18n, refMixin],
|
||||||
|
components: {
|
||||||
|
GridColItem,
|
||||||
|
TableCellItem,
|
||||||
|
FieldWidget,
|
||||||
|
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
},
|
||||||
|
inject: ['refList', 'sfRefList', 'globalModel'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeTabName: '',
|
||||||
|
rowIdData: [],
|
||||||
|
fieldSchemaData: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
customClass() {
|
||||||
|
return this.widget.options.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
visibleTabs() {
|
||||||
|
return this.widget.tabs.filter(tp => {
|
||||||
|
return !tp.options.hidden
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
formModel: {
|
||||||
|
cache: false,
|
||||||
|
get() {
|
||||||
|
return this.globalModel.formModel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initRefList()
|
||||||
|
this.registerSubFormToRefList()
|
||||||
|
this.initRowIdData(true)
|
||||||
|
this.initFieldSchemaData()
|
||||||
|
this.initEventHandler()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initActiveTab()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getLabelAlign(widget, subWidget) {
|
||||||
|
return subWidget.options.labelAlign || widget.options.labelAlign
|
||||||
|
},
|
||||||
|
|
||||||
|
initActiveTab() {
|
||||||
|
if ((this.widget.type === 'tab') && (this.widget.tabs.length > 0)) {
|
||||||
|
let activePanes = this.widget.tabs.filter((tp) => {
|
||||||
|
return tp.options.active === true
|
||||||
|
})
|
||||||
|
if (activePanes.length > 0) {
|
||||||
|
this.activeTabName = activePanes[0].options.name
|
||||||
|
} else {
|
||||||
|
this.activeTabName = this.widget.tabs[0].options.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
registerSubFormToRefList() {
|
||||||
|
if (this.widget.type === 'sub-form') {
|
||||||
|
this.sfRefList[this.widget.options.name] = this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initRowIdData(initFlag) {
|
||||||
|
if (this.widget.type === 'sub-form') {
|
||||||
|
//this.rowIdData.length = 0
|
||||||
|
this.rowIdData.splice(0, this.rowIdData.length) //清除数组必须用splice,length=0不会响应式更新!!
|
||||||
|
let subFormModel = this.formModel[this.widget.options.name]
|
||||||
|
if (!!subFormModel && (subFormModel.length > 0)) {
|
||||||
|
subFormModel.forEach(rowModel => {
|
||||||
|
//this.rowIdData.push('rowId' + generateId())
|
||||||
|
this.rowIdData.push('r' + generateId())
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!!initFlag) {
|
||||||
|
//注意:事件触发需延期执行,SumFormDataChange事件处理代码中可能存在尚未创建完成的组件!!
|
||||||
|
setTimeout(() => {
|
||||||
|
this.handleSubFormRowChange(subFormModel)
|
||||||
|
}, 800)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addToRowIdData() {
|
||||||
|
this.rowIdData.push('rowId' + generateId())
|
||||||
|
},
|
||||||
|
|
||||||
|
insertToRowIdData(rowIndex) {
|
||||||
|
this.rowIdData.splice(rowIndex, 0, 'rowId' + generateId())
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFromRowIdData(rowIndex) {
|
||||||
|
this.rowIdData.splice(rowIndex, 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
initFieldSchemaData() { //初始化fieldSchemaData!!!
|
||||||
|
if (this.widget.type !== 'sub-form') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let rowLength = this.rowIdData.length
|
||||||
|
//this.fieldSchemaData.length = 0
|
||||||
|
this.fieldSchemaData.splice(0, this.fieldSchemaData.length) //清除数组必须用splice,length=0不会响应式更新!!
|
||||||
|
if (rowLength > 0) {
|
||||||
|
for (let i = 0; i < rowLength; i++) {
|
||||||
|
let fieldSchemas = []
|
||||||
|
this.widget.widgetList.forEach(swItem => {
|
||||||
|
fieldSchemas.push(this.cloneFieldSchema(swItem))
|
||||||
|
})
|
||||||
|
this.fieldSchemaData.push(fieldSchemas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addToFieldSchemaData(rowIndex) {
|
||||||
|
let fieldSchemas = []
|
||||||
|
this.widget.widgetList.forEach(swItem => {
|
||||||
|
fieldSchemas.push(this.cloneFieldSchema(swItem))
|
||||||
|
})
|
||||||
|
|
||||||
|
if (rowIndex === undefined) {
|
||||||
|
this.fieldSchemaData.push(fieldSchemas)
|
||||||
|
} else {
|
||||||
|
this.fieldSchemaData.splice(rowIndex, 0, fieldSchemas)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFromFieldSchemaData(rowIndex) {
|
||||||
|
this.fieldSchemaData.splice(rowIndex, 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
cloneFieldSchema(fieldWidget) {
|
||||||
|
let newFieldSchema = deepClone(fieldWidget)
|
||||||
|
newFieldSchema.id = fieldWidget.type + generateId()
|
||||||
|
return newFieldSchema
|
||||||
|
},
|
||||||
|
|
||||||
|
initEventHandler() {
|
||||||
|
if (this.widget.type !== 'sub-form') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$on('setFormData', function(newFormData) {
|
||||||
|
this.initRowIdData(false)
|
||||||
|
this.initFieldSchemaData()
|
||||||
|
|
||||||
|
let subFormData = newFormData[this.widget.options.name] || []
|
||||||
|
setTimeout(() => { //延时触发SubFormRowChange事件, 便于更新计算字段!!
|
||||||
|
this.handleSubFormRowChange(subFormData)
|
||||||
|
}, 800)
|
||||||
|
})
|
||||||
|
|
||||||
|
// this.$on('subFormDataChange', function (subFormData) {
|
||||||
|
// console.log('test--------', subFormData)
|
||||||
|
// this.handleSubFormRowChange(subFormData)
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
|
||||||
|
addSubFormRow() {
|
||||||
|
let newSubFormDataRow = {}
|
||||||
|
this.widget.widgetList.forEach(subFormItem => {
|
||||||
|
if (!!subFormItem.formItemFlag) {
|
||||||
|
newSubFormDataRow[subFormItem.options.name] = subFormItem.options.defaultValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let oldSubFormData = this.formModel[this.widget.options.name] || []
|
||||||
|
oldSubFormData.push(newSubFormDataRow)
|
||||||
|
this.addToRowIdData()
|
||||||
|
this.addToFieldSchemaData()
|
||||||
|
|
||||||
|
this.handleSubFormRowAdd(oldSubFormData, oldSubFormData.length - 1)
|
||||||
|
this.handleSubFormRowChange(oldSubFormData)
|
||||||
|
},
|
||||||
|
|
||||||
|
insertSubFormRow(beforeFormRowIndex) {
|
||||||
|
let newSubFormDataRow = {}
|
||||||
|
this.widget.widgetList.forEach(subFormItem => {
|
||||||
|
if (!!subFormItem.formItemFlag) {
|
||||||
|
newSubFormDataRow[subFormItem.options.name] = subFormItem.options.defaultValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let oldSubFormData = this.formModel[this.widget.options.name] || []
|
||||||
|
oldSubFormData.splice(beforeFormRowIndex, 0, newSubFormDataRow)
|
||||||
|
this.insertToRowIdData(beforeFormRowIndex)
|
||||||
|
this.addToFieldSchemaData(beforeFormRowIndex)
|
||||||
|
|
||||||
|
this.handleSubFormRowInsert(oldSubFormData, beforeFormRowIndex)
|
||||||
|
this.handleSubFormRowChange(oldSubFormData)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteSubFormRow(formRowIndex) {
|
||||||
|
this.$confirm(this.i18nt('render.hint.deleteSubFormRow') + '?', this.i18nt('render.hint.prompt'), {
|
||||||
|
confirmButtonText: this.i18nt('render.hint.confirm'),
|
||||||
|
cancelButtonText: this.i18nt('render.hint.cancel')
|
||||||
|
}).then(() => {
|
||||||
|
let oldSubFormData = this.formModel[this.widget.options.name] || []
|
||||||
|
let deletedDataRow = deepClone(oldSubFormData[formRowIndex])
|
||||||
|
oldSubFormData.splice(formRowIndex, 1)
|
||||||
|
this.deleteFromRowIdData(formRowIndex)
|
||||||
|
this.deleteFromFieldSchemaData(formRowIndex)
|
||||||
|
|
||||||
|
this.handelSubFormRowDelete(oldSubFormData, deletedDataRow)
|
||||||
|
this.handleSubFormRowChange(oldSubFormData)
|
||||||
|
}).catch(() => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubFormRowChange(subFormData) {
|
||||||
|
if (!!this.widget.options.onSubFormRowChange) {
|
||||||
|
let customFunc = new Function('subFormData', this.widget.options.onSubFormRowChange)
|
||||||
|
customFunc.call(this, subFormData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubFormRowAdd(subFormData, newRowIndex) {
|
||||||
|
if (!!this.widget.options.onSubFormRowAdd) {
|
||||||
|
let customFunc = new Function('subFormData', 'newRowIndex', this.widget.options.onSubFormRowAdd)
|
||||||
|
customFunc.call(this, subFormData, newRowIndex)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubFormRowInsert(subFormData, newRowIndex) {
|
||||||
|
if (!!this.widget.options.onSubFormRowInsert) {
|
||||||
|
let customFunc = new Function('subFormData', 'newRowIndex', this.widget.options.onSubFormRowInsert)
|
||||||
|
customFunc.call(this, subFormData, newRowIndex)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handelSubFormRowDelete(subFormData, deletedDataRow) {
|
||||||
|
if (!!this.widget.options.onSubFormRowDelete) {
|
||||||
|
let customFunc = new Function('subFormData', 'deletedDataRow', this.widget.options.onSubFormRowDelete)
|
||||||
|
customFunc.call(this, subFormData, deletedDataRow)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
//--------------------- 以下为组件支持外部调用的API方法 begin ------------------//
|
||||||
|
/* 提示:用户可自行扩充这些方法!!! */
|
||||||
|
|
||||||
|
setHidden(flag) {
|
||||||
|
this.widget.options.hidden = flag
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
enableSubForm() {
|
||||||
|
if (this.rowIdData.length > 0) {
|
||||||
|
this.rowIdData.forEach((dataRow, rIdx) => {
|
||||||
|
this.enableSubFormRow(rIdx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
validateField(fieldName) { //逐行校验子表单字段
|
||||||
|
//TODO:
|
||||||
|
},
|
||||||
|
|
||||||
|
validateSubForm() { //逐行校验子表单全部字段
|
||||||
|
//TODO:
|
||||||
|
},
|
||||||
|
|
||||||
|
//--------------------- 以上为组件支持外部调用的API方法 end ------------------//
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
div.table-container {
|
||||||
|
table.table-layout {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-form-container {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
::v-deep .el-row.header-row {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-row.sub-form-row {
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
|
||||||
|
.row-number-span {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.action-header-column {
|
||||||
|
display: inline-block;
|
||||||
|
width: 120px;
|
||||||
|
|
||||||
|
.action-label {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.field-header-column {
|
||||||
|
display: inline-block;
|
||||||
|
//overflow: hidden;
|
||||||
|
//white-space: nowrap; //文字超出长度不自动换行
|
||||||
|
//text-overflow: ellipsis; //文字超出长度显示省略号
|
||||||
|
|
||||||
|
span.custom-label i {
|
||||||
|
margin: 0 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.field-header-column.is-required:before {
|
||||||
|
content: '*';
|
||||||
|
color: #F56C6C;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.label-center-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.label-center-align {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.label-right-align {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sub-form-action-column {
|
||||||
|
display: inline-block;
|
||||||
|
width: 120px;
|
||||||
|
|
||||||
|
::v-deep .el-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-button {
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 0;
|
||||||
|
background: #DCDFE6;
|
||||||
|
border: 4px solid #DCDFE6;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sub-form-action-column.hide-label {
|
||||||
|
::v-deep .el-form-item__label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sub-form-table-column {
|
||||||
|
display: inline-block;
|
||||||
|
//width: 200px;
|
||||||
|
|
||||||
|
::v-deep .el-form-item {
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-form-item__content {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sub-form-table-column.hide-label {
|
||||||
|
::v-deep .el-form-item__label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<Col class="grid-cell" :span="widget.options.span" :class="[customClass]"
|
||||||
|
:key="widget.id" v-show="!widget.options.hidden">
|
||||||
|
<template v-if="!!widget.widgetList && (widget.widgetList.length > 0)">
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-item :widget="subWidget" :key="swIdx" :parent-list="widget.widgetList"
|
||||||
|
:index-of-parent-list="swIdx" :parent-widget="widget"></container-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :designer="null" :key="swIdx" :parent-list="widget.widgetList"
|
||||||
|
:index-of-parent-list="swIdx" :parent-widget="widget"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Col>
|
||||||
|
<div class="blank-cell"><span class="invisible-content">{{i18nt('render.hint.blankCellContent')}}</span></div>
|
||||||
|
</Col>
|
||||||
|
</template>
|
||||||
|
</Col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//import ContainerItem from "./container-item";
|
||||||
|
import FieldWidget from "../form-designer/form-widget/field-widget";
|
||||||
|
import i18n from "../utils/i18n";
|
||||||
|
import refMixin from "./refMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "GridColItem",
|
||||||
|
componentName: 'GridColItem',
|
||||||
|
mixins: [i18n, refMixin],
|
||||||
|
components: {
|
||||||
|
FieldWidget,
|
||||||
|
//'container-item': ContainerItem, /* 递归组件必须使用这种写法!! */
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
parentWidget: Object,
|
||||||
|
parentList: Array,
|
||||||
|
indexOfParentList: Number,
|
||||||
|
},
|
||||||
|
inject: ['refList', 'globalModel'],
|
||||||
|
computed: {
|
||||||
|
customClass() {
|
||||||
|
return this.widget.options.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initRefList()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.blank-cell {
|
||||||
|
font-style: italic;
|
||||||
|
color: #cccccc;
|
||||||
|
|
||||||
|
span.invisible-content {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,477 @@
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<Form :label-position="labelPosition" :size="size" :class="[customClass]" class="render-form"
|
||||||
|
:validate-on-rule-change="false" :model="formDataModel" ref="renderForm" @submit.native.prevent>
|
||||||
|
<template v-for="(widget, index) in widgetList">
|
||||||
|
<template v-if="'container' === widget.category">
|
||||||
|
<container-item :widget="widget" :key="widget.id" :parent-list="widgetList"
|
||||||
|
:index-of-parent-list="index" :parent-widget="null"></container-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="widget" :form-model="formDataModel" :designer="null" :key="widget.id"
|
||||||
|
:parent-list="widgetList" :index-of-parent-list="index" :parent-widget="null"
|
||||||
|
@field-value-changed="valueChanged"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//import ElForm from 'element-ui/packages/form/src/form.vue'
|
||||||
|
import emitter from 'element-ui/lib/mixins/emitter'
|
||||||
|
import FieldWidget from "../form-designer/form-widget/field-widget";
|
||||||
|
import ContainerItem from "./container-item";
|
||||||
|
import {
|
||||||
|
deepClone,
|
||||||
|
insertCustomCssToHead,
|
||||||
|
insertGlobalFunctionsToHtml
|
||||||
|
} from "../../utils/util";
|
||||||
|
//import i18n from "../../utils/i18n";
|
||||||
|
import i18n, {
|
||||||
|
changeLocale
|
||||||
|
} from "../utils/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VFormRender",
|
||||||
|
componentName: 'VFormRender',
|
||||||
|
mixins: [emitter, i18n],
|
||||||
|
components: {
|
||||||
|
FieldWidget,
|
||||||
|
ContainerItem,
|
||||||
|
//ElForm,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
//designer: Object, /* 不能引入designer对象,VFormRender组件脱离表单设计器运行!! */
|
||||||
|
|
||||||
|
formJson: Object, //prop传入的表单配置
|
||||||
|
formData: { //prop传入的表单数据
|
||||||
|
Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
optionData: { //prop传入的选项数据
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
eventHandler:{ //传入事件处理程序
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
container:Object
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
refList: this.widgetRefList,
|
||||||
|
sfRefList: this.subFormRefList, //收集SubForm引用
|
||||||
|
formConfig: this.formConfig,
|
||||||
|
globalOptionData: this.optionData,
|
||||||
|
globalModel: {
|
||||||
|
formModel: this.formDataModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formDataModel: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
widgetRefList: {},
|
||||||
|
subFormRefList: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formConfig() {
|
||||||
|
return this.formJson.formConfig
|
||||||
|
},
|
||||||
|
|
||||||
|
widgetList() {
|
||||||
|
return this.formJson.widgetList
|
||||||
|
},
|
||||||
|
|
||||||
|
labelPosition() {
|
||||||
|
if (!!this.formConfig && !!this.formConfig.labelPosition) {
|
||||||
|
return this.formConfig.labelPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'left'
|
||||||
|
},
|
||||||
|
|
||||||
|
size() {
|
||||||
|
if (!!this.formConfig && !!this.formConfig.size) {
|
||||||
|
return this.formConfig.size
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'medium'
|
||||||
|
},
|
||||||
|
|
||||||
|
customClass() {
|
||||||
|
return this.formConfig.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.insertCustomStyleAndScriptNode()
|
||||||
|
this.buildFormModel()
|
||||||
|
this.addFieldChangeEventHandler()
|
||||||
|
this.registerFormToRefList()
|
||||||
|
this.handleOnCreated()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initLocale()
|
||||||
|
this.handleOnMounted()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initLocale() {
|
||||||
|
let curLocale = localStorage.getItem('v_form_locale') || 'zh-CN'
|
||||||
|
this.changeLanguage(curLocale)
|
||||||
|
},
|
||||||
|
|
||||||
|
insertCustomStyleAndScriptNode() {
|
||||||
|
if (!!this.formConfig && !!this.formConfig.cssCode) {
|
||||||
|
insertCustomCssToHead(this.formConfig.cssCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!this.formConfig && !!this.formConfig.functions) {
|
||||||
|
insertGlobalFunctionsToHtml(this.formConfig.functions)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
buildFormModel() {
|
||||||
|
//this.formDataModel = this.formData //
|
||||||
|
//this._provided.globalModel.formModel = this.formData
|
||||||
|
|
||||||
|
this.widgetList.forEach((wItem) => {
|
||||||
|
this.buildDataFromWidget(wItem, null)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
buildDataFromWidget(wItem, parentItem) {
|
||||||
|
if (wItem.category === 'container') {
|
||||||
|
if (wItem.type === 'grid') {
|
||||||
|
if (!!wItem.cols && (wItem.cols.length > 0)) {
|
||||||
|
wItem.cols.forEach((childItem) => {
|
||||||
|
this.buildDataFromWidget(childItem, wItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (wItem.type === 'table') {
|
||||||
|
if (!!wItem.rows && (wItem.rows.length > 0)) {
|
||||||
|
wItem.rows.forEach((rowItem) => {
|
||||||
|
if (!!rowItem.cols && (rowItem.cols.length > 0)) {
|
||||||
|
rowItem.cols.forEach((colItem) => {
|
||||||
|
this.buildDataFromWidget(colItem, wItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (wItem.type === 'tab') {
|
||||||
|
if (!!wItem.tabs && (wItem.tabs.length > 0)) {
|
||||||
|
wItem.tabs.forEach((tabItem) => {
|
||||||
|
if (!!tabItem.widgetList && (tabItem.widgetList.length > 0)) {
|
||||||
|
tabItem.widgetList.forEach((childItem) => {
|
||||||
|
this.buildDataFromWidget(childItem, wItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (wItem.type === 'sub-form') {
|
||||||
|
let subFormName = wItem.options.name
|
||||||
|
if (!this.formData.hasOwnProperty(subFormName)) {
|
||||||
|
let subFormDataRow = {}
|
||||||
|
if (wItem.options.showBlankRow) {
|
||||||
|
wItem.widgetList.forEach(subFormItem => {
|
||||||
|
if (!!subFormItem.formItemFlag) {
|
||||||
|
subFormDataRow[subFormItem.options.name] = subFormItem.options.defaultValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$set(this.formDataModel, subFormName, [subFormDataRow]) //
|
||||||
|
} else {
|
||||||
|
this.$set(this.formDataModel, subFormName, []) //
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let initialValue = this.formData[subFormName]
|
||||||
|
this.$set(this.formDataModel, subFormName, deepClone(initialValue))
|
||||||
|
}
|
||||||
|
} else if ((wItem.type === 'grid-col') || (wItem.type === 'table-cell')) {
|
||||||
|
if (!!wItem.widgetList && (wItem.widgetList.length > 0)) {
|
||||||
|
wItem.widgetList.forEach((childItem) => {
|
||||||
|
this.buildDataFromWidget(childItem, wItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!!wItem.widgetList && (wItem.widgetList.length > 0)) {
|
||||||
|
wItem.widgetList.forEach((childItem) => {
|
||||||
|
this.buildDataFromWidget(childItem, wItem)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!!wItem.formItemFlag) {
|
||||||
|
if (!this.formData.hasOwnProperty(wItem.options.name)) {
|
||||||
|
//this.formDataModel[wItem.options.name] = '' //这种写法不支持对象属性响应式更新,必须用$set方法!!
|
||||||
|
this.$set(this.formDataModel, wItem.options.name, wItem.options.defaultValue) //设置字段默认值
|
||||||
|
} else {
|
||||||
|
let initialValue = this.formData[wItem.options.name]
|
||||||
|
this.$set(this.formDataModel, wItem.options.name, deepClone(initialValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addFieldChangeEventHandler() {
|
||||||
|
this.$on('fieldChange', function(fieldName, newValue, oldValue, subFormName, subFormRowIndex) {
|
||||||
|
this.handleFieldDataChange(fieldName, newValue, oldValue, subFormName, subFormRowIndex)
|
||||||
|
this.$emit('formChange', fieldName, newValue, oldValue, this.formDataModel, subFormName,
|
||||||
|
subFormRowIndex)
|
||||||
|
//this.$emit('subFormChange', subFormName, subFormRowIndex,
|
||||||
|
// fieldName, newValue, oldValue, this.formDataModel)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
registerFormToRefList() {
|
||||||
|
this.widgetRefList['v_form_ref'] = this
|
||||||
|
},
|
||||||
|
|
||||||
|
handleFieldDataChange(fieldName, newValue, oldValue, subFormName, subFormRowIndex) {
|
||||||
|
if (!!this.formConfig.onFormDataChange) {
|
||||||
|
let customFunc = new Function('fieldName', 'newValue', 'oldValue', 'formModel', 'subFormName',
|
||||||
|
'subFormRowIndex',
|
||||||
|
this.formConfig.onFormDataChange)
|
||||||
|
customFunc.call(this, fieldName, newValue, oldValue, this.formDataModel, subFormName, subFormRowIndex)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// forceUpdate() {
|
||||||
|
// this.$forceUpdate()
|
||||||
|
// },
|
||||||
|
|
||||||
|
handleOnCreated() {
|
||||||
|
if (!!this.formConfig.onFormCreated) {
|
||||||
|
let customFunc = new Function(this.formConfig.onFormCreated)
|
||||||
|
customFunc.call(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOnMounted() {
|
||||||
|
if (!!this.formConfig.onFormMounted) {
|
||||||
|
let customFunc = new Function(this.formConfig.onFormMounted)
|
||||||
|
customFunc.call(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
findWidgetAndSetDisabled(widgetName, disabledFlag) {
|
||||||
|
let foundW = this.getWidgetRef(widgetName)
|
||||||
|
if (!!foundW) {
|
||||||
|
foundW.setDisabled(disabledFlag)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
findWidgetAndSetHidden(widgetName, hiddenFlag) {
|
||||||
|
let foundW = this.getWidgetRef(widgetName)
|
||||||
|
if (!!foundW) {
|
||||||
|
foundW.setHidden(hiddenFlag)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
//--------------------- 以下为组件支持外部调用的API方法 begin ------------------//
|
||||||
|
/* 提示:用户可自行扩充这些方法!!! */
|
||||||
|
|
||||||
|
changeLanguage(langName) {
|
||||||
|
changeLocale(langName)
|
||||||
|
},
|
||||||
|
|
||||||
|
getNativeForm() { //获取原生form引用
|
||||||
|
return this.$refs['renderForm']
|
||||||
|
},
|
||||||
|
|
||||||
|
getWidgetRef(widgetName, showError = false) {
|
||||||
|
let foundRef = this.widgetRefList[widgetName]
|
||||||
|
if (!foundRef && !!showError) {
|
||||||
|
this.$message.error(this.i18nt('render.hint.refNotFound') + widgetName)
|
||||||
|
}
|
||||||
|
return foundRef
|
||||||
|
},
|
||||||
|
|
||||||
|
getFormData(needValidation = true) {
|
||||||
|
if (!needValidation) {
|
||||||
|
return this.formDataModel
|
||||||
|
}
|
||||||
|
|
||||||
|
let callback = function nullFunc() {}
|
||||||
|
let promise = new window.Promise(function(resolve, reject) {
|
||||||
|
callback = function callback(formData, error) {
|
||||||
|
!error ? resolve(formData) : reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$refs['renderForm'].validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
callback(this.formDataModel)
|
||||||
|
} else {
|
||||||
|
callback(this.formDataModel, this.i18nt('render.hint.validationFailed'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//return this.$refs['renderForm'].validate()
|
||||||
|
return promise
|
||||||
|
},
|
||||||
|
|
||||||
|
setFormData(formData) { //设置表单数据
|
||||||
|
//this.formDataModel = formData //inject注入的formModel不是响应式的,直接赋值在其他组件拿不到最新值!!
|
||||||
|
this._provided.globalModel.formModel = formData /* 这种写法可使inject的属性保持响应式更新!! */
|
||||||
|
//
|
||||||
|
|
||||||
|
// 通知SubForm组件:表单数据更新事件!!
|
||||||
|
this.broadcast('ContainerItem', 'setFormData', formData)
|
||||||
|
// 通知FieldWidget组件:表单数据更新事件!!
|
||||||
|
this.broadcast('FieldWidget', 'setFormData', formData)
|
||||||
|
},
|
||||||
|
|
||||||
|
getFieldValue(fieldName) { //单个字段获取值
|
||||||
|
let fieldRef = this.getWidgetRef(fieldName)
|
||||||
|
if (!!fieldRef && !!fieldRef.getValue) {
|
||||||
|
return fieldRef.getValue()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setFieldValue(fieldName, fieldValue) { //单个更新字段值
|
||||||
|
let fieldRef = this.getWidgetRef(fieldName)
|
||||||
|
if (!!fieldRef && !!fieldRef.setValue) {
|
||||||
|
fieldRef.setValue(fieldValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getSubFormValues(subFormName, needValidation = true) {
|
||||||
|
let foundSFRef = this.subFormRefList[subFormName]
|
||||||
|
// if (!foundSFRef) {
|
||||||
|
// return this.formDataModel[subFormName]
|
||||||
|
// }
|
||||||
|
return foundSFRef.getSubFormValues(needValidation)
|
||||||
|
},
|
||||||
|
|
||||||
|
disableForm() {
|
||||||
|
let wNameList = Object.keys(this.widgetRefList)
|
||||||
|
wNameList.forEach(wName => {
|
||||||
|
let foundW = this.getWidgetRef(wName)
|
||||||
|
if (!!foundW) {
|
||||||
|
// try {
|
||||||
|
// foundW.setDisabled(true)
|
||||||
|
// } catch (ex) {
|
||||||
|
// console.log('disableForm error: ', ex.message)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!!foundW.setDisabled) {
|
||||||
|
foundW.setDisabled(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
enableForm() {
|
||||||
|
let wNameList = Object.keys(this.widgetRefList)
|
||||||
|
wNameList.forEach(wName => {
|
||||||
|
let foundW = this.getWidgetRef(wName)
|
||||||
|
if (!!foundW) {
|
||||||
|
// try {
|
||||||
|
// foundW.setDisabled(false)
|
||||||
|
// } catch (ex) {
|
||||||
|
// console.log('disableForm error: ', wName + ', ' + ex.message)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!!foundW.setDisabled) {
|
||||||
|
foundW.setDisabled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
resetForm() { //重置表单
|
||||||
|
let subFormNames = Object.keys(this.subFormRefList)
|
||||||
|
subFormNames.forEach(sfName => {
|
||||||
|
if (!!this.subFormRefList[sfName].resetSubForm) {
|
||||||
|
this.subFormRefList[sfName].resetSubForm()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let wNameList = Object.keys(this.widgetRefList)
|
||||||
|
wNameList.forEach(wName => {
|
||||||
|
let foundW = this.getWidgetRef(wName)
|
||||||
|
if (!!foundW && !!foundW.resetField) {
|
||||||
|
foundW.resetField()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$refs.renderForm.clearValidate()
|
||||||
|
},
|
||||||
|
|
||||||
|
validateForm() {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
|
||||||
|
validateFields() {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
|
||||||
|
disableWidgets(widgetNames) {
|
||||||
|
if (!!widgetNames) {
|
||||||
|
if (typeof widgetNames === 'string') {
|
||||||
|
this.findWidgetAndSetDisabled(widgetNames, true)
|
||||||
|
} else if (Array.isArray(widgetNames)) {
|
||||||
|
widgetNames.forEach(wn => {
|
||||||
|
this.findWidgetAndSetDisabled(wn, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableWidgets(widgetNames) {
|
||||||
|
if (!!widgetNames) {
|
||||||
|
if (typeof widgetNames === 'string') {
|
||||||
|
this.findWidgetAndSetDisabled(widgetNames, false)
|
||||||
|
} else if (Array.isArray(widgetNames)) {
|
||||||
|
widgetNames.forEach(wn => {
|
||||||
|
this.findWidgetAndSetDisabled(wn, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideWidgets(widgetNames) {
|
||||||
|
if (!!widgetNames) {
|
||||||
|
if (typeof widgetNames === 'string') {
|
||||||
|
this.findWidgetAndSetHidden(widgetNames, true)
|
||||||
|
} else if (Array.isArray(widgetNames)) {
|
||||||
|
widgetNames.forEach(wn => {
|
||||||
|
this.findWidgetAndSetHidden(wn, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showWidgets(widgetNames) {
|
||||||
|
if (!!widgetNames) {
|
||||||
|
if (typeof widgetNames === 'string') {
|
||||||
|
this.findWidgetAndSetHidden(widgetNames, false)
|
||||||
|
} else if (Array.isArray(widgetNames)) {
|
||||||
|
widgetNames.forEach(wn => {
|
||||||
|
this.findWidgetAndSetHidden(wn, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
valueChanged(args){
|
||||||
|
console.log(">>>>");
|
||||||
|
console.log(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------- 以上为组件支持外部调用的API方法 end ------------------//
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-form ::v-deep .el-row {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,22 @@
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
initRefList() {
|
||||||
|
if ((this.refList !== null) && !!this.widget.options.name) {
|
||||||
|
this.refList[this.widget.options.name] = this
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getWidgetRef(widgetName, showError) {
|
||||||
|
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']
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<td class="table-cell" :class="[customClass]"
|
||||||
|
:colspan="widget.options.colspan || 1" :rowspan="widget.options.rowspan || 1"
|
||||||
|
:style="{width: widget.options.cellWidth + '!important' || '', height: widget.options.cellHeight + '!important' || ''}">
|
||||||
|
<template v-for="(subWidget, swIdx) in widget.widgetList">
|
||||||
|
<template v-if="'container' === subWidget.category">
|
||||||
|
<container-item :widget="subWidget" :key="swIdx" :parent-list="widget.widgetList"
|
||||||
|
:index-of-parent-list="swIdx" :parent-widget="widget"></container-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<field-widget :field="subWidget" :key="swIdx" :parent-list="widget.widgetList"
|
||||||
|
:index-of-parent-list="swIdx" :parent-widget="widget"></field-widget>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FieldWidget from "../form-designer/form-widget/field-widget";
|
||||||
|
import i18n from "../utils/i18n";
|
||||||
|
import refMixin from "./refMixin";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "TableCellItem",
|
||||||
|
componentName: "TableCellItem",
|
||||||
|
mixins: [i18n, refMixin],
|
||||||
|
components: {
|
||||||
|
FieldWidget,
|
||||||
|
//'container-item': ContainerItem, /* 递归组件必须使用这种写法!! */
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
widget: Object,
|
||||||
|
|
||||||
|
rowIndex: Number,
|
||||||
|
colIndex: Number,
|
||||||
|
},
|
||||||
|
inject: ['refList', 'globalModel'],
|
||||||
|
computed: {
|
||||||
|
customClass() {
|
||||||
|
return this.widget.options.customClass || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
/* tableCell不生成组件引用!! */
|
||||||
|
//this.initRefList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
td.table-cell {
|
||||||
|
display: table-cell;
|
||||||
|
height: 36px;
|
||||||
|
border: 1px dashed #336699;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,308 @@
|
||||||
|
export default {
|
||||||
|
application: {
|
||||||
|
'zh-CN': '简体中文',
|
||||||
|
'en-US': 'English',
|
||||||
|
productTitle: 'Online Form Designer',
|
||||||
|
github: 'GitHub',
|
||||||
|
document: 'Docs',
|
||||||
|
qqGroup: 'WeChat Group',
|
||||||
|
deployment: 'Deployment',
|
||||||
|
subscription: 'Subscription',
|
||||||
|
},
|
||||||
|
|
||||||
|
designer: {
|
||||||
|
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',
|
||||||
|
|
||||||
|
input: 'Input',
|
||||||
|
'input-composite' : 'InputComposite',
|
||||||
|
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',
|
||||||
|
|
||||||
|
custom: 'Custom Component',
|
||||||
|
slot: 'Slot'
|
||||||
|
},
|
||||||
|
|
||||||
|
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',
|
||||||
|
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',
|
||||||
|
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
|
||||||
|
toolbar: {
|
||||||
|
undoHint: 'Undo',
|
||||||
|
redoHint: 'Redo',
|
||||||
|
pcLayout: 'PC',
|
||||||
|
mobileLayout: 'H5',
|
||||||
|
clear: 'Clear',
|
||||||
|
preview: 'Preview',
|
||||||
|
importJson: 'Import JSON',
|
||||||
|
exportJson: 'Export JSON',
|
||||||
|
exportCode: 'Export Code',
|
||||||
|
generateCode: 'Generate Code',
|
||||||
|
saveCode: 'Save Code',
|
||||||
|
reloadCode: 'Reload Code',
|
||||||
|
generateSFC: 'Generate SFC',
|
||||||
|
},
|
||||||
|
|
||||||
|
setting: {
|
||||||
|
basicSetting: 'Basic Setting',
|
||||||
|
attributeSetting: 'Attribute Setting',
|
||||||
|
commonSetting: 'Common Setting',
|
||||||
|
advancedSetting: 'Advanced Setting',
|
||||||
|
eventSetting: 'Event Setting',
|
||||||
|
fieldName: 'Unique Name',
|
||||||
|
label: 'Label',
|
||||||
|
displayType: 'Type',
|
||||||
|
defaultValue: 'Default Value',
|
||||||
|
placeholder: 'Placeholder',
|
||||||
|
startPlaceholder: 'Start Placeholder',
|
||||||
|
endPlaceholder: 'End Placeholder',
|
||||||
|
widgetColumnWidth: 'Width',
|
||||||
|
widgetSize: 'Size',
|
||||||
|
displayStyle: 'Display Style',
|
||||||
|
inlineLayout: 'inline',
|
||||||
|
blockLayout: 'block',
|
||||||
|
labelWidth: 'Width Of Label',
|
||||||
|
rows: 'Rows',
|
||||||
|
labelHidden: 'Hide Label',
|
||||||
|
required: 'Required',
|
||||||
|
validation: 'Validation',
|
||||||
|
validationHelp: 'Regular expressions supported',
|
||||||
|
validationHint: 'Validation Hint',
|
||||||
|
readonly: 'Readonly',
|
||||||
|
disabled: 'Disabled',
|
||||||
|
hidden: 'Hidden',
|
||||||
|
textContent: 'Text',
|
||||||
|
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',
|
||||||
|
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)',
|
||||||
|
fileAccept: 'Upload File Filter',
|
||||||
|
fileTypes: 'Upload File Types',
|
||||||
|
fileTypesHelp: 'Allows to add more file types',
|
||||||
|
headers: 'Request Headers',
|
||||||
|
|
||||||
|
cellWidth: 'width',
|
||||||
|
cellHeight: 'height',
|
||||||
|
gutter: 'Gutter(px)',
|
||||||
|
columnSetting: 'Cols Setting',
|
||||||
|
colsOfGrid: 'Cols Of Grid:',
|
||||||
|
colSpanTitle: 'Spans Of Col',
|
||||||
|
addColumn: 'Add Column',
|
||||||
|
|
||||||
|
tabPaneSetting: 'Tab Panes',
|
||||||
|
tabPaneType: 'Tab Type',
|
||||||
|
addTabPane: 'Add Tab Pane',
|
||||||
|
paneActive: 'Active',
|
||||||
|
paneDisabled: 'Disable',
|
||||||
|
|
||||||
|
customLabelIcon: 'Custom Label',
|
||||||
|
labelIconClass: 'Label Icon Class',
|
||||||
|
labelIconPosition: 'Label Icon Position',
|
||||||
|
labelTooltip: 'Label Tooltip',
|
||||||
|
minValue: 'Min Value',
|
||||||
|
maxValue: 'Max Value',
|
||||||
|
precision: 'Precision',
|
||||||
|
formatter: 'Number Formatter',
|
||||||
|
step: 'Step',
|
||||||
|
controlsPosition: 'Controls Position',
|
||||||
|
minLength: 'Min Length',
|
||||||
|
maxLength: 'Max Length',
|
||||||
|
showWordLimit: 'Show Word Limit',
|
||||||
|
prefixIcon: 'Prefix Icon',
|
||||||
|
suffixIcon: 'Suffix Icon',
|
||||||
|
inputControl: 'Complex Input Setting',
|
||||||
|
prependControl: 'Prepend Control',
|
||||||
|
prependControlDisabled: 'Disable Prepend Control',
|
||||||
|
prependControlType: 'Prepend Control Type',
|
||||||
|
prependControlText: 'Prepend Control Type',
|
||||||
|
appendControl: 'Append Control',
|
||||||
|
appendControlDisabled: 'Disable Append Control',
|
||||||
|
appendControlType: 'Append Control Type',
|
||||||
|
appendControlText: 'Append Control Text',
|
||||||
|
buttonIcon: 'Button Icon',
|
||||||
|
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',
|
||||||
|
direction: 'Vertical',
|
||||||
|
showBlankRow: 'Show Blank Row',
|
||||||
|
showRowNumber: 'Show Row Number',
|
||||||
|
to:'Route Redirect To',
|
||||||
|
target:'Open Page Target',
|
||||||
|
replace:'Record Redirect History',
|
||||||
|
append:'Append Route',
|
||||||
|
|
||||||
|
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',
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
export default {
|
||||||
|
application: {
|
||||||
|
'zh-CN': '简体中文',
|
||||||
|
'en-US': 'English',
|
||||||
|
productTitle: '表单设计器',
|
||||||
|
github: 'GitHub',
|
||||||
|
document: '文档',
|
||||||
|
qqGroup: '技术WX群',
|
||||||
|
deployment: '私有部署',
|
||||||
|
subscription: '订阅源码',
|
||||||
|
},
|
||||||
|
|
||||||
|
designer: {
|
||||||
|
containerTitle: '容器',
|
||||||
|
dragHandlerHint: '鼠标拖拽容器组件或字段组件并放置于表单中',
|
||||||
|
dragAction: '拖动',
|
||||||
|
basicFieldTitle: '基础字段',
|
||||||
|
advancedFieldTitle: '高级字段',
|
||||||
|
customFieldTitle: '自定义扩展字段',
|
||||||
|
|
||||||
|
noWidgetHint: '请从左侧列表中选择一个组件, 然后用鼠标拖动组件放置于此处.',
|
||||||
|
|
||||||
|
widgetLabel: {
|
||||||
|
grid: '栅格',
|
||||||
|
table: '表格',
|
||||||
|
tab: '标签页',
|
||||||
|
section: '区块',
|
||||||
|
'sub-form': '子表单',
|
||||||
|
'grid-col': '栅格列',
|
||||||
|
'table-cell': '单元格',
|
||||||
|
'tab-pane': '选项卡页',
|
||||||
|
|
||||||
|
input: '单行输入',
|
||||||
|
'input-composite' : '复合输入',
|
||||||
|
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: '级联选择',
|
||||||
|
|
||||||
|
custom: 'Custom Component',
|
||||||
|
slot: 'Slot'
|
||||||
|
},
|
||||||
|
|
||||||
|
hint: {
|
||||||
|
selectParentWidget: '选中父组件',
|
||||||
|
moveUpWidget: '上移组件',
|
||||||
|
moveDownWidget: '下移组件',
|
||||||
|
cloneWidget: '复制组件',
|
||||||
|
insertRow: '插入新行',
|
||||||
|
insertColumn: '插入新列',
|
||||||
|
remove: '移除组件',
|
||||||
|
cellSetting: '单元格操作',
|
||||||
|
dragHandler: '拖拽手柄',
|
||||||
|
copyField: '复制字段组件',
|
||||||
|
onlyFieldWidgetAcceptable: '子表单只能接收字段组件',
|
||||||
|
moveUpFirstChildHint: '已经移动到最上面',
|
||||||
|
moveDownLastChildHint: '已经移动到最下面',
|
||||||
|
|
||||||
|
closePreview: '关闭',
|
||||||
|
copyJson: '复制JSON',
|
||||||
|
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: '请输入文件名',
|
||||||
|
|
||||||
|
widgetSetting: '组件设置',
|
||||||
|
formSetting: '表单设置',
|
||||||
|
|
||||||
|
prompt: '提示',
|
||||||
|
confirm: '确定',
|
||||||
|
cancel: '取消',
|
||||||
|
import: '导入',
|
||||||
|
importJsonHint: '导入的JSON内容须符合下述格式,以保证顺利导入.',
|
||||||
|
invalidOptionsData: '无效的选项数据:',
|
||||||
|
lastPaneCannotBeDeleted: '仅剩一个选项卡页不可删除.',
|
||||||
|
duplicateName: '组件名称已存在: ',
|
||||||
|
nameRequired: '组件名称不可为空',
|
||||||
|
|
||||||
|
numberValidator: '数字',
|
||||||
|
letterValidator: '字母',
|
||||||
|
letterAndNumberValidator: '数字字母',
|
||||||
|
mobilePhoneValidator: '手机号码',
|
||||||
|
emailValidator: '邮箱',
|
||||||
|
urlValidator: '网址',
|
||||||
|
noChineseValidator: '非中文字符',
|
||||||
|
chineseValidator: '仅中文字符',
|
||||||
|
|
||||||
|
rowspanNotConsistentForMergeEntireRow: '存在行高不一致的单元格, 无法合并整行.',
|
||||||
|
colspanNotConsistentForMergeEntireColumn: '存在列宽不一致的单元格, 无法合并整列.',
|
||||||
|
rowspanNotConsistentForDeleteEntireRow: '存在行高不一致的单元格, 不可删除整行.',
|
||||||
|
colspanNotConsistentForDeleteEntireColumn: '存在列宽不一致的单元格, 不可删除整列.',
|
||||||
|
},
|
||||||
|
|
||||||
|
toolbar: {
|
||||||
|
undoHint: '撤销',
|
||||||
|
redoHint: '重做',
|
||||||
|
pcLayout: 'PC',
|
||||||
|
mobileLayout: 'H5',
|
||||||
|
clear: '清空',
|
||||||
|
preview: '预览',
|
||||||
|
importJson: '导入JSON',
|
||||||
|
exportJson: '导出JSON',
|
||||||
|
exportCode: '导出代码',
|
||||||
|
generateCode: '生成代码',
|
||||||
|
saveCode: '保存代码',
|
||||||
|
reloadCode: '撤销代码更改',
|
||||||
|
generateSFC: '生成SFC',
|
||||||
|
},
|
||||||
|
|
||||||
|
setting: {
|
||||||
|
basicSetting: '基本属性',
|
||||||
|
attributeSetting: '属性设置',
|
||||||
|
commonSetting: '常见属性',
|
||||||
|
advancedSetting: '高级属性',
|
||||||
|
eventSetting: '事件属性',
|
||||||
|
fieldName: '字段唯一名称',
|
||||||
|
label: '字段标签',
|
||||||
|
displayType: '显示类型',
|
||||||
|
defaultValue: '默认值',
|
||||||
|
placeholder: '占位内容',
|
||||||
|
startPlaceholder: '起始占位内容',
|
||||||
|
endPlaceholder: '截止占位内容',
|
||||||
|
widgetColumnWidth: '组件列宽',
|
||||||
|
widgetSize: '组件大小',
|
||||||
|
displayStyle: '显示样式',
|
||||||
|
inlineLayout: '行内',
|
||||||
|
blockLayout: '块',
|
||||||
|
labelWidth: '标签宽度',
|
||||||
|
rows: '行数',
|
||||||
|
labelHidden: '隐藏字段标签',
|
||||||
|
required: '必填字段',
|
||||||
|
validation: '字段校验',
|
||||||
|
validationHelp: '支持输入正则表达式',
|
||||||
|
validationHint: '校验失败提示',
|
||||||
|
readonly: '只读',
|
||||||
|
disabled: '禁用',
|
||||||
|
hidden: '隐藏',
|
||||||
|
textContent: '静态文字',
|
||||||
|
htmlContent: 'HTML',
|
||||||
|
clearable: '可清除',
|
||||||
|
editable: '可输入',
|
||||||
|
format: '显示格式',
|
||||||
|
valueFormat: '绑定值格式',
|
||||||
|
showPassword: '可显示密码',
|
||||||
|
filterable: '可搜索选项',
|
||||||
|
allowCreate: '允许创建选项',
|
||||||
|
remote: '可远程搜索',
|
||||||
|
automaticDropdown: '自动弹出选项',
|
||||||
|
multiple: '选项可多选',
|
||||||
|
multipleLimit: '多选数量限制',
|
||||||
|
contentPosition: '文字位置',
|
||||||
|
plain: '朴素按钮',
|
||||||
|
round: '圆角按钮',
|
||||||
|
circle: '圆形按钮',
|
||||||
|
icon: '图标',
|
||||||
|
optionsSetting: '选项设置',
|
||||||
|
addOption: '增加选项',
|
||||||
|
importOptions: '导入选项',
|
||||||
|
resetDefault: '重设选中项',
|
||||||
|
uploadSetting: '上传参数设置',
|
||||||
|
uploadURL: '上传地址',
|
||||||
|
uploadTip: '上传提示内容',
|
||||||
|
withCredentials: '发送cookie凭证',
|
||||||
|
multipleSelect: '文件可多选',
|
||||||
|
showFileList: '显示文件列表',
|
||||||
|
limit: '最大上传数量',
|
||||||
|
fileMaxSize: '文件大小限制(MB)',
|
||||||
|
fileAccept: '上传文件过滤',
|
||||||
|
fileTypes: '上传文件类型',
|
||||||
|
fileTypesHelp: '支持添加其他文件类型',
|
||||||
|
headers: '上传请求头',
|
||||||
|
|
||||||
|
cellWidth: '宽度',
|
||||||
|
cellHeight: '高度',
|
||||||
|
gutter: '栅格间隔(像素)',
|
||||||
|
columnSetting: '栅格属性设置',
|
||||||
|
colsOfGrid: '当前栅格列:',
|
||||||
|
colSpanTitle: '栅格列',
|
||||||
|
addColumn: '增加栅格',
|
||||||
|
|
||||||
|
tabPaneSetting: '选项卡设置',
|
||||||
|
tabPaneType: '选项卡样式',
|
||||||
|
addTabPane: '增加选项卡页',
|
||||||
|
paneActive: '激活',
|
||||||
|
paneDisabled: '禁用',
|
||||||
|
|
||||||
|
customLabelIcon: '定制字段标签',
|
||||||
|
labelIconClass: '标签Icon样式',
|
||||||
|
labelIconPosition: '标签Icon位置',
|
||||||
|
labelTooltip: '标签文字提示',
|
||||||
|
minValue: '最小值',
|
||||||
|
maxValue: '最大值',
|
||||||
|
precision: '精度',
|
||||||
|
formatter: '输入框格式',
|
||||||
|
step: '增减步长',
|
||||||
|
controlsPosition: '控制按钮位置',
|
||||||
|
minLength: '最小长度',
|
||||||
|
maxLength: '最大长度',
|
||||||
|
showWordLimit: '显示字数统计',
|
||||||
|
prefixIcon: '头部Icon',
|
||||||
|
suffixIcon: '尾部Icon',
|
||||||
|
inputControl: '复合输入框设置',
|
||||||
|
prependControl: '添加前置按钮',
|
||||||
|
prependControlDisabled: '前置按钮禁用',
|
||||||
|
prependControlType: '前置按钮类型',
|
||||||
|
prependControlIcon: '前置按钮图标',
|
||||||
|
prependControlText: '前置按钮文字',
|
||||||
|
appendControl: '添加后置按钮',
|
||||||
|
appendControlDisabled: '后置按钮禁用',
|
||||||
|
appendControlType: '后置按钮类型',
|
||||||
|
appendControlIcon: '后置按钮图标',
|
||||||
|
appendControlText: '后置按钮文字',
|
||||||
|
buttonIcon: '后置按钮Icon',
|
||||||
|
activeText: '开启时文字描述',
|
||||||
|
inactiveText: '关闭时文字描述',
|
||||||
|
activeColor: '开启时背景色',
|
||||||
|
inactiveColor: '关闭时背景色',
|
||||||
|
maxStars: '最大评分值',
|
||||||
|
lowThreshold: '低分界限值',
|
||||||
|
highThreshold: '高分界限值',
|
||||||
|
allowHalf: '允许半选',
|
||||||
|
showText: '显示辅助文字',
|
||||||
|
showScore: '显示当前分数',
|
||||||
|
range: '是否为范围选择',
|
||||||
|
vertical: '是否竖向显示',
|
||||||
|
direction: '是否竖向显示',
|
||||||
|
showBlankRow: '默认显示新行',
|
||||||
|
showRowNumber: '显示行号',
|
||||||
|
to:'跳转链接',
|
||||||
|
target:'浏览器目标',
|
||||||
|
replace:'记录跳转历史',
|
||||||
|
append:'路由追加',
|
||||||
|
|
||||||
|
|
||||||
|
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: '表单事件处理',
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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: '组件类型不是子表单',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<svg :class="svgClass" aria-hidden="true">
|
||||||
|
<use :xlink:href="iconName"></use>
|
||||||
|
<title v-if="!!title">{{title}}</title>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SvgIcon',
|
||||||
|
props: {
|
||||||
|
iconClass: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iconName() {
|
||||||
|
return `#icon-${this.iconClass}`
|
||||||
|
},
|
||||||
|
svgClass() {
|
||||||
|
if (this.className) {
|
||||||
|
return 'svg-icon ' + this.className
|
||||||
|
} else {
|
||||||
|
return 'svg-icon'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.svg-icon {
|
||||||
|
width: 1.1em;
|
||||||
|
height: 1.1em;
|
||||||
|
margin-left: 0.35em;
|
||||||
|
margin-right: 0.35em;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,91 @@
|
||||||
|
export const generateCode = function(formJson, codeType= 'vue') {
|
||||||
|
let formJsonStr = JSON.stringify(formJson)
|
||||||
|
|
||||||
|
if (codeType === 'html') {
|
||||||
|
return `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
|
||||||
|
<title>VForm Demo</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||||||
|
<link rel="stylesheet" href="https://ks3-cn-beijing.ksyun.com/vform2021/VFormRender.css?t=20210720">
|
||||||
|
<style type="text/css">
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="app">
|
||||||
|
<v-form-render :form-json="formJson" :form-data="formData" :option-data="optionData" ref="vFormRef">
|
||||||
|
</v-form-render>
|
||||||
|
<Button type="primary" @click="submitForm">Submit</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
if (!!window.ActiveXObject || "ActiveXObject" in window) { //IE load polyfill.js for Promise
|
||||||
|
var scriptEle = document.createElement("script");
|
||||||
|
scriptEle.type = "text/javascript";
|
||||||
|
scriptEle.src = "https://cdn.bootcss.com/babel-polyfill/6.23.0/polyfill.min.js"
|
||||||
|
document.body.appendChild(scriptEle)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="https://unpkg.com/vue/dist/vue.js"></script>
|
||||||
|
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||||||
|
<script src="https://ks3-cn-beijing.ksyun.com/vform2021/VFormRender.umd.min.js?t=20210720"></script>
|
||||||
|
<script>
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
formJson: ${formJsonStr},
|
||||||
|
formData: {},
|
||||||
|
optionData: {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm: function() {
|
||||||
|
this.$refs.vFormRef.getFormData().then( function(formData) {
|
||||||
|
// Form Validation OK
|
||||||
|
alert( JSON.stringify(formData) )
|
||||||
|
}).catch( function(error) {
|
||||||
|
// Form Validation Failed
|
||||||
|
alert(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return `<template>
|
||||||
|
<div>
|
||||||
|
<v-form-render :form-json="formJson" :form-data="formData" :option-data="optionData" ref="vFormRef">
|
||||||
|
</v-form-render>
|
||||||
|
<Button type="primary" @click="submitForm">Submit</Button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formJson: ${formJsonStr},
|
||||||
|
formData: {},
|
||||||
|
optionData: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm() {
|
||||||
|
this.$refs.vFormRef.getFormData().then(formData => {
|
||||||
|
// Form Validation OK
|
||||||
|
alert( JSON.stringify(formData) )
|
||||||
|
}).catch(error => {
|
||||||
|
// Form Validation failed
|
||||||
|
this.$message.error(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>`
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueI18n from 'vue-i18n'
|
||||||
|
|
||||||
|
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 en from 'view-design/dist/locale/en-US';
|
||||||
|
import zh from 'view-design/dist/locale/zh-CN';
|
||||||
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
|
Vue.locale = () => {};
|
||||||
|
|
||||||
|
Vue.locale('en-US', en);
|
||||||
|
Vue.locale('zh-CN', zh);
|
||||||
|
|
||||||
|
|
||||||
|
const langResources = {
|
||||||
|
'en-US': {
|
||||||
|
...en,
|
||||||
|
...enLocale,
|
||||||
|
...enLocale_render
|
||||||
|
},
|
||||||
|
'zh-CN': {
|
||||||
|
...zh,
|
||||||
|
...zhLocale,
|
||||||
|
...zhLocale_render
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const i18n = new VueI18n({
|
||||||
|
locale: localStorage.getItem('v_form_locale') || 'zh-CN', // set locale
|
||||||
|
messages:langResources // set locale messages
|
||||||
|
});
|
||||||
|
|
||||||
|
export const changeLocale = function(langName) {
|
||||||
|
i18n.locale = langName
|
||||||
|
localStorage.setItem('v_form_locale', langName)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const translate = function(key) {
|
||||||
|
return i18n._t(key, i18n.locale, i18n._getMessages())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
i18nt(key) {
|
||||||
|
return i18n._t(key, i18n.locale, i18n._getMessages())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,581 @@
|
||||||
|
import {isNotNull} from "@/utils/util";
|
||||||
|
import {genVue2JS} from "./vue2js-generator";
|
||||||
|
import {beautifierOpts} from "@/utils/beautifierLoader";
|
||||||
|
import {genVue3JS} from "./vue3js-generator";
|
||||||
|
|
||||||
|
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 =
|
||||||
|
`<Row ${gridClassAttr}>
|
||||||
|
${ctn.cols.map(col => {
|
||||||
|
const colOpt = col.options
|
||||||
|
const colClassAttr = buildClassAttr(col, 'grid-cell')
|
||||||
|
return `<Col :span="${colOpt.span}" ${colClassAttr}>
|
||||||
|
${col.widgetList.map(cw => {
|
||||||
|
if (cw.category === 'container') {
|
||||||
|
return buildContainerWidget(cw, formConfig)
|
||||||
|
} else {
|
||||||
|
return buildFieldWidget(cw, formConfig)
|
||||||
|
}
|
||||||
|
}).join('')
|
||||||
|
}
|
||||||
|
</Col>`
|
||||||
|
}).join('')
|
||||||
|
}
|
||||||
|
</Row>`
|
||||||
|
|
||||||
|
return gridTemplate
|
||||||
|
},
|
||||||
|
|
||||||
|
'table': (ctn, formConfig) => {
|
||||||
|
const tableClassAttr = buildClassAttr(ctn, 'table-layout')
|
||||||
|
const tableTemplate =
|
||||||
|
`<div class="table-container">
|
||||||
|
<table ${tableClassAttr}><tbody>
|
||||||
|
${ctn.rows.map(tr => {
|
||||||
|
return `<tr>${
|
||||||
|
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 ${tdClassAttr} ${colspanAttr} ${rowspanAttr} ${tdStyleAttr}>${td.widgetList.map(tw => {
|
||||||
|
if (tw.category === 'container') {
|
||||||
|
return buildContainerWidget(tw, formConfig)
|
||||||
|
} else {
|
||||||
|
return buildFieldWidget(tw, formConfig)
|
||||||
|
}
|
||||||
|
}).join('')
|
||||||
|
}
|
||||||
|
</td>`
|
||||||
|
}).join('')
|
||||||
|
}</tr>`
|
||||||
|
}).join('')
|
||||||
|
}
|
||||||
|
</tbody></table>
|
||||||
|
</div>`
|
||||||
|
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 =
|
||||||
|
`<div class="tab-container">
|
||||||
|
<Tabs ${vModel} type="${ctn.displayType}" ${tabClassAttr}>
|
||||||
|
${ctn.tabs.map(tab => {
|
||||||
|
const tabOpt = tab.options
|
||||||
|
const disabledAttr = (tabOpt.disabled === true) ? `disabled` : ''
|
||||||
|
return `<TabsPane name="${tabOpt.name}" label="${tabOpt.label}" ${disabledAttr}>
|
||||||
|
${tab.widgetList.map(tw => {
|
||||||
|
if (tw.category === 'container') {
|
||||||
|
return buildContainerWidget(tw, formConfig)
|
||||||
|
} else {
|
||||||
|
return buildFieldWidget(tw, formConfig)
|
||||||
|
}
|
||||||
|
}).join('')
|
||||||
|
}</TabsPane>`
|
||||||
|
}).join('')}
|
||||||
|
</Tabs>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
return tabTemplate
|
||||||
|
},
|
||||||
|
|
||||||
|
'sub-form': (ctn, formConfig) => {
|
||||||
|
//TODO:
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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="${wop.prefixIcon}"` : '',
|
||||||
|
suffixIcon: !!wop.suffixIcon ? `suffix="${wop.suffixIcon}"` : '',
|
||||||
|
controlsPosition: wop.controlsPosition === 'right' ? `controls-outside="right"` : `controls-outside="default"`,
|
||||||
|
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 ? `true-color="${wop.activeColor}"` : '',
|
||||||
|
inactiveColor: !!wop.inactiveColor ? `false-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-upload-list` : '',
|
||||||
|
limit: !isNaN(wop.limit) ? `:limit="${wop.limit}"` : '',
|
||||||
|
fileAccept: !!wop.fileAccept ? `:format="${wop.fileAccept}"` : '',
|
||||||
|
fileTypes: !!wop.fileTypes ? `:accept="${wop.fileTypes}"` : '',
|
||||||
|
fileMaxSize: !!wop.fileMaxSize? `:max-size="${wop.fileMaxSize}"`:``,
|
||||||
|
uploadTipSlotChild: !!wop.uploadTip ? `<template #tip><div class="el-upload__tip">${wop.uploadTip}</div></template>` : '',
|
||||||
|
pictureUploadIconChild: `<template #default><i class="ivu-icon ivu-icon-md-add"></i></template>`,
|
||||||
|
fileUploadIconChild: `<template #default><i class="ivu-icon ivu-icon-md-add"></i></template>`,
|
||||||
|
|
||||||
|
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 ? `<template #append><Button class="${wop.buttonIcon}" ${!!wop.appendButtonDisabled ? 'disabled' : ''}></Button></template>` : '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRadioChildren(widget, formConfig) {
|
||||||
|
let wop = widget.options
|
||||||
|
const childTag = !!wop.buttonStyle ? 'RadioButton' : '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 ? 'CheckboxButton' : '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 = 'Option'
|
||||||
|
return `<${childTag} v-for="(item, index) in ${wop.name}Options" :key="index" :label="item.value"
|
||||||
|
:value="item.value" :disabled="item.disabled"></${childTag}>`
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDivider(widget, formConfig) {
|
||||||
|
let wop = widget.options
|
||||||
|
|
||||||
|
return {
|
||||||
|
size: !!wop.size ? `size="${wop.size}"` : '',
|
||||||
|
type: (!!wop.contentPosition && (wop.direction !== 'horizontal')) ? `type="vertical"` : `type="horizontal"`,
|
||||||
|
contentPosition: (!!wop.contentPosition && (wop.contentPosition !== 'center')) ? `orientation="${wop.contentPosition}"` : '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function buildInputComposite(widget, formConfig) {
|
||||||
|
let wop = widget.options
|
||||||
|
|
||||||
|
return {
|
||||||
|
appendDiv: (wop.prependControl&&wop.prependControlType=='div') ?
|
||||||
|
`<div slot="prepend">
|
||||||
|
<Icon :type="${wop.prependControlIcon}" />${wop.prependControlText}
|
||||||
|
</div>`: '',
|
||||||
|
prependDiv: (wop.appendControl&&wop.appendControlType=='div') ?
|
||||||
|
`<div slot="append">
|
||||||
|
<Icon :type="${wop.appendControlIcon}" />${wop.appendControlText}
|
||||||
|
</div>`: '',
|
||||||
|
appendButton: (wop.prependControl&&wop.prependControlType=='button') ?
|
||||||
|
`<Button slot="prepend"
|
||||||
|
:disabled="${!!wop.disabled || !!wop.prependControlDisabled}"
|
||||||
|
:icon="${wop.prependControlIcon}"
|
||||||
|
>${wop.prependControlText}
|
||||||
|
</Button>`: '',
|
||||||
|
prependButton: (wop.appendControl&&wop.appendControlType=='button') ?
|
||||||
|
`<Button slot="append"
|
||||||
|
:disabled="${!!wop.disabled || !!wop.appendButtonDisabled}"
|
||||||
|
:icon="${wop.appendControlIcon}"
|
||||||
|
>${wop.appendControlText}
|
||||||
|
</Button>`: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const elTemplates = { //字段组件属性
|
||||||
|
'input': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, type, showPassword, placeholder, clearable,
|
||||||
|
maxlength, showWordLimit, prefixIcon, suffixIcon, appendButtonChild} = getElAttrs(widget, formConfig)
|
||||||
|
return `<Input ${vModel} ${readonly} ${disabled} ${size} ${type} ${showPassword} ${placeholder} ${clearable}
|
||||||
|
${maxlength} ${showWordLimit} ${prefixIcon} ${suffixIcon}>${appendButtonChild}</Input>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'input-composite': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, type, showPassword, placeholder, clearable,
|
||||||
|
maxlength, showWordLimit, prefixIcon, suffixIcon, appendButtonChild} = getElAttrs(widget, formConfig)
|
||||||
|
|
||||||
|
const {appendDiv,prependDiv,appendButton,prependButton} =buildInputComposite(widget, formConfig);
|
||||||
|
return `<Input ${vModel} ${readonly} ${disabled} ${size} ${type} ${showPassword} ${placeholder} ${clearable}
|
||||||
|
${maxlength} ${showWordLimit} ${prefixIcon} ${suffixIcon}>
|
||||||
|
${appendDiv}
|
||||||
|
${prependDiv}
|
||||||
|
${appendButton}
|
||||||
|
${prependButton}
|
||||||
|
</Input>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'textarea': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, type, showPassword, placeholder, rows, clearable,
|
||||||
|
maxlength, showWordLimit} = getElAttrs(widget, formConfig)
|
||||||
|
return `<Input type="textarea" ${vModel} ${readonly} ${disabled} ${size} ${type} ${showPassword} ${placeholder}
|
||||||
|
${rows} ${clearable} ${maxlength} ${showWordLimit}></Input>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'number': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, size, type, showPassword, placeholder, controlsPosition, min, max, precision, step
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<InputNumber ${vModel} class="full-width-input" ${disabled} ${size} ${type} ${showPassword}
|
||||||
|
${placeholder} ${controlsPosition} ${min} ${max} ${precision} ${step}></InputNumber>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'radio': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, size} = getElAttrs(widget, formConfig)
|
||||||
|
const radioOptions = buildRadioChildren(widget, formConfig)
|
||||||
|
return `<RadioGroup ${vModel} ${disabled} ${size}>${radioOptions}</RadioGroup>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'checkbox': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, size} = getElAttrs(widget, formConfig)
|
||||||
|
const checkboxOptions = buildCheckboxChildren(widget, formConfig)
|
||||||
|
return `<CheckboxGroup ${vModel} ${disabled} ${size}>${checkboxOptions}</CheckboxGroup>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'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 `<Select ${vModel} class="full-width-input" ${disabled} ${size} ${clearable} ${filterable}
|
||||||
|
${allowCreate} ${defaultFirstOption} ${automaticDropdown} ${multiple} ${multipleLimit} ${placeholder}
|
||||||
|
${remote}>${selectOptions}</Select>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'time': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, placeholder, clearable, format, editable
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<TimePicker confirm ${vModel} class="full-width-input" ${readonly} ${disabled} ${size} ${format}
|
||||||
|
${placeholder} ${clearable} ${editable}></TimePicker>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'time-range': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, startPlaceholder, endPlaceholder, clearable, format, editable
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<TimePicker confirm type="range" ${vModel} class="full-width-input" ${readonly} ${disabled} ${size} ${format}
|
||||||
|
${startPlaceholder} ${endPlaceholder} ${clearable} ${editable}></TimePicker>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'date': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, type, placeholder, clearable, format, valueFormat, editable
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<DatePicker ${vModel} ${type} class="full-width-input" ${readonly} ${disabled} ${size} ${format}
|
||||||
|
${valueFormat} ${placeholder} ${clearable} ${editable}></DatePicker>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'date-range': (widget, formConfig) => {
|
||||||
|
const {vModel, readonly, disabled, size, type, startPlaceholder, endPlaceholder, clearable, format, valueFormat, editable
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<DatePicker is-range ${vModel} ${type} class="full-width-input" ${readonly} ${disabled} ${size} ${format}
|
||||||
|
${valueFormat} ${startPlaceholder} ${endPlaceholder} ${clearable} ${editable}></DatePicker>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'switch': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, activeText, inactiveText, activeColor, inactiveColor, switchWidth
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<iSwitch ${vModel} ${disabled} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor}
|
||||||
|
${switchWidth}></iSwitch>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'rate': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, rateMax, allowHalf, showText} = getElAttrs(widget, formConfig)
|
||||||
|
return `<Rate ${vModel} ${disabled} ${rateMax} ${allowHalf} ${showText} ></Rate>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'color': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, size
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<ColorPicker ${vModel} ${disabled} ${size}></ColorPicker>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'slider': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, sliderMin, sliderMax, sliderStep, sliderRange, sliderVertical
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<Slider ${vModel} ${disabled} ${sliderMin} ${sliderMax} ${sliderStep} ${sliderRange}
|
||||||
|
></Slider>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'picture-upload': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, uploadAction, withCredentials, multipleSelect, showFileList, limit,
|
||||||
|
fileAccept,fileTypes,fileMaxSize,
|
||||||
|
uploadTipSlotChild, pictureUploadIconChild} = getElAttrs(widget, formConfig)
|
||||||
|
let wop = widget.options
|
||||||
|
return `<Upload
|
||||||
|
:headers="${wop.name}UploadHeaders"
|
||||||
|
:data="${wop.name}UploadData"
|
||||||
|
:action="${uploadAction}"
|
||||||
|
paste
|
||||||
|
${disabled}
|
||||||
|
${uploadAction}
|
||||||
|
${withCredentials}
|
||||||
|
${multipleSelect}
|
||||||
|
${showFileList}
|
||||||
|
${fileAccept}
|
||||||
|
${fileTypes}
|
||||||
|
${fileMaxSize}
|
||||||
|
>${uploadTipSlotChild} ${pictureUploadIconChild}</Upload>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'file-upload': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, uploadAction, withCredentials, multipleSelect, showFileList, limit,
|
||||||
|
fileAccept,fileTypes,fileMaxSize,
|
||||||
|
uploadTipSlotChild, fileUploadIconChild} = getElAttrs(widget, formConfig)
|
||||||
|
let wop = widget.options
|
||||||
|
return `<Upload
|
||||||
|
:headers="${wop.name}UploadHeaders"
|
||||||
|
:data="${wop.name}UploadData"
|
||||||
|
:action="${uploadAction}"
|
||||||
|
paste
|
||||||
|
${disabled}
|
||||||
|
${uploadAction}
|
||||||
|
${withCredentials}
|
||||||
|
${multipleSelect}
|
||||||
|
${showFileList}
|
||||||
|
${fileAccept}
|
||||||
|
${fileTypes}
|
||||||
|
${fileMaxSize}
|
||||||
|
>${uploadTipSlotChild} ${fileUploadIconChild}</Upload>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'rich-editor': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, placeholder
|
||||||
|
} = getElAttrs(widget, formConfig)
|
||||||
|
return `<vue-editor ${vModel} ${disabled} ${placeholder}></vue-editor>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'cascader': (widget, formConfig) => {
|
||||||
|
const {vModel, disabled, size, clearable, filterable, placeholder} = getElAttrs(widget, formConfig)
|
||||||
|
let wop = widget.options
|
||||||
|
const optionsAttr = `:options="${wop.name}Options"`
|
||||||
|
return `<Cascader ${vModel} class="full-width-input" ${optionsAttr} ${disabled} ${size} ${clearable}
|
||||||
|
${filterable} ${placeholder}></Cascader>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'static-text': (widget, formConfig) => {
|
||||||
|
return `<div>${widget.options.textContent}</div>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'html-text': (widget, formConfig) => {
|
||||||
|
return `<div v-html="${widget.options.htmlContent}"></div>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'button': (widget, formConfig) => {
|
||||||
|
const {buttonType, buttonPlain, buttonRound, buttonCircle, buttonIcon, disabled} = getElAttrs(widget, formConfig)
|
||||||
|
return `<Button ${buttonType} ${buttonPlain} ${buttonRound} ${buttonCircle} ${buttonIcon}
|
||||||
|
${disabled}>${widget.options.label}</Button>`
|
||||||
|
},
|
||||||
|
|
||||||
|
'divider': (widget, formConfig) => {
|
||||||
|
const {contentPosition,size,type} = buildDivider(widget, formConfig)
|
||||||
|
return `<Divider ${type} ${size} ${contentPosition}></Divider>`
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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}"` : '')
|
||||||
|
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 {
|
||||||
|
//classArray.push(formConfig.labelAlign || 'label-left-align')
|
||||||
|
formConfig.labelAlign !== 'label-left-align' && classArray.push(formConfig.labelAlign)
|
||||||
|
}
|
||||||
|
const classAttr = (classArray.length > 0) ? `class="${classArray.join(' ')}"` : ''
|
||||||
|
|
||||||
|
let customLabelDom =
|
||||||
|
`<template #label><span class="custom-label">${wop.labelIconPosition === 'front' ?
|
||||||
|
(!!wop.labelTooltip ?
|
||||||
|
`<Tooltip content="${wop.labelTooltip}"><i class="${wop.labelIconClass}"></i></Tooltip>${wop.label}` :
|
||||||
|
`<i class="${wop.labelIconClass}"></i>${wop.label}`
|
||||||
|
)
|
||||||
|
:
|
||||||
|
(!!wop.labelTooltip ?
|
||||||
|
`${wop.label}<Tooltip content="${wop.labelTooltip}"><i class="${wop.labelIconClass}"></i></Tooltip>` :
|
||||||
|
`${wop.label}<i class="${wop.labelIconClass}"></i>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</span></template>`
|
||||||
|
!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 ?
|
||||||
|
`<FormItem label="${label}" ${labelWidthAttr} ${labelTooltipAttr} ${propAttr} ${classAttr} >
|
||||||
|
${customLabelDom}
|
||||||
|
${fwDom}
|
||||||
|
</FormItem>`
|
||||||
|
:
|
||||||
|
`<div class="static-content-item" ${vShowAttr}>${fwDom}</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
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 =
|
||||||
|
` <Form :model="${formConfig.modelName}" ref="${formConfig.refName}" :rules="${formConfig.rulesName}"
|
||||||
|
label-position="${formConfig.labelPosition}" :label-width="${formConfig.labelWidth}"
|
||||||
|
${submitAttr}>
|
||||||
|
${!!childrenList ? childrenList.join('\n') : ''}
|
||||||
|
</Form>`
|
||||||
|
|
||||||
|
return formTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
const genGlobalCSS = function (formConfig) {
|
||||||
|
const globalCssTemplate =`${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(.ivu-form-item-label)` : `::v-deep .ivu-form-item-label`} {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-center-align ${!!vue3Flag ? `:deep(.ivu-form-item-label)` : `::v-deep .ivu-form-item-label`} {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-right-align ${!!vue3Flag ? `:deep(.ivu-form-item-label)` : `::v-deep .ivu-form-item-label`} {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-label {
|
||||||
|
}
|
||||||
|
|
||||||
|
.static-content-item {
|
||||||
|
min-height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
${!!vue3Flag ? `:deep(.ivu-divider-horizontal)` : `::v-deep .ivu-divider-horizontal`} {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
return cssTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
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 `<!--
|
||||||
|
Codes Generated By VForm:
|
||||||
|
http://www.vform666.com
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
${html}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
${js}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
${globalCss}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
${scopedCss}
|
||||||
|
</style>`
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
import {isNotNull, traverseContainWidgets, traverseFieldWidgets} from "@/utils/util";
|
||||||
|
import {translate} from "@/utils/i18n";
|
||||||
|
import FormValidators, {getRegExp} from "@/utils/validators";
|
||||||
|
|
||||||
|
export function buildDefaultValueListFn(formConfig, widgetList, resultList) {
|
||||||
|
return function(fieldWidget) {
|
||||||
|
const fop = fieldWidget.options
|
||||||
|
const fd = fop.defaultValue
|
||||||
|
if (isNotNull(fd)) {
|
||||||
|
resultList.push(`${fop.name}: ${JSON.stringify(fd)},`)
|
||||||
|
} else {
|
||||||
|
resultList.push(`${fop.name}: null,`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildRulesListFn(formConfig, widgetList, resultList) {
|
||||||
|
return function(fieldWidget) {
|
||||||
|
const fop = fieldWidget.options
|
||||||
|
let fieldRules = []
|
||||||
|
if (!!fop.required) {
|
||||||
|
fieldRules.push(`{
|
||||||
|
required: true,
|
||||||
|
message: '${translate('render.hint.fieldRequired')}',
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!fop.validation) {
|
||||||
|
let vldName = fop.validation
|
||||||
|
if (!!FormValidators[vldName]) {
|
||||||
|
fieldRules.push(`{
|
||||||
|
pattern: ${eval( getRegExp(vldName) )},
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
message: '${fop.validationHint}'
|
||||||
|
}`)
|
||||||
|
} else {
|
||||||
|
fieldRules.push(`{
|
||||||
|
pattern: '${eval(vldName)}',
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
message: '${fop.validationHint}'
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: 自定义校验函数
|
||||||
|
|
||||||
|
fieldRules.length > 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}',`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverseContainWidgets(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
|
||||||
|
}
|
|
@ -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.ctx.$refs['vForm'].validate(valid => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
//TODO: 提交表单
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
instance.ctx.$refs['vForm'].resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
submitForm,
|
||||||
|
resetForm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})`
|
||||||
|
|
||||||
|
return v3JSTemplate
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import 'babel-polyfill'
|
||||||
|
import './utils/debug-console'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import axios from "axios";
|
||||||
|
import App from './App-iview.vue'
|
||||||
|
import ViewUI from 'view-design';
|
||||||
|
import './utils/directive'
|
||||||
|
import './icons'
|
||||||
|
|
||||||
|
import ContainerWidget from "@/components-iview/form-designer/form-widget/container-widget";
|
||||||
|
import ContainerItem from "@/components-iview/form-render/container-item";
|
||||||
|
|
||||||
|
import 'view-design/dist/styles/iview.css';
|
||||||
|
import '@/styles/index.scss'
|
||||||
|
import '@/iconfont/iconfont.css'
|
||||||
|
|
||||||
|
|
||||||
|
import {i18n} from '@/components-iview/utils/i18n.js'
|
||||||
|
|
||||||
|
/* 递归组件如需在递归组件的嵌套组件中使用,必须注册为全局组件,原因不明?? begin */
|
||||||
|
Vue.component('container-widget', ContainerWidget)
|
||||||
|
Vue.component('container-item', ContainerItem)
|
||||||
|
/* end */
|
||||||
|
Vue.locale = () => {};
|
||||||
|
Vue.use(ViewUI, {size:'small'});
|
||||||
|
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.axios = axios
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
i18n,
|
||||||
|
el: "#app",
|
||||||
|
render: h => h(App),
|
||||||
|
})
|
Loading…
Reference in New Issue