Ver 2.2.0 2022.01.27更新:

1. 表单设计器新增widgetNameReadonly属性,用于禁止唯一名称修改;
2. 统一Boolean类型属性编辑器为el-switch;
3. 表单设计器、表单渲染器新增两个重要API方法:getFieldWidgets()、getContainerWidgets(),可获取表单全部字段组件或全部容器组件。
master
vdpAdmin 2022-01-27 12:24:16 +08:00
parent 40d0abdc30
commit 7f161f5c4f
42 changed files with 156 additions and 55 deletions

View File

@ -25,6 +25,13 @@ export default {
methods: {
//--------------------- 组件内部方法 begin ------------------//
getPropName() {
if (this.subFormItemFlag && !this.designState) {
return this.subFormName + "." + this.subFormRowIndex + "." + this.field.options.name + ""
} else {
return this.field.options.name
}
},
initFieldModel() {
if (!this.field.formItemFlag) {
@ -304,7 +311,7 @@ export default {
}
},
handleChangeEvent(value) {
handleChangeEvent(value) { /* input的清除输入小按钮会同时触发handleChangeEvent、handleInputCustomEvent */
this.syncUpdateFormModel(value)
this.emitFieldDataChange(value, this.oldFieldValue)
@ -312,7 +319,7 @@ export default {
this.oldFieldValue = deepClone(value) /* oldFieldValue需要在initFieldModel()方法中赋初值!! */
/* 主动触发表单的单个字段校验,用于清除字段可能存在的校验错误提示 */
this.dispatch('VFormRender', 'fieldValidation', [this.field.options.name])
this.dispatch('VFormRender', 'fieldValidation', [this.getPropName()])
},
handleFocusCustomEvent(event) {
@ -335,7 +342,7 @@ export default {
this.syncUpdateFormModel(value)
/* 主动触发表单的单个字段校验,用于清除字段可能存在的校验错误提示 */
this.dispatch('VFormRender', 'fieldValidation', [this.field.options.name])
this.dispatch('VFormRender', 'fieldValidation', [this.getPropName()])
if (!!this.field.options.onInput) {
let customFn = new Function('value', this.field.options.onInput)
@ -429,11 +436,12 @@ export default {
},
resetField() {
if (!!this.subFormItemFlag) { //跳过子表单组件
return
}
let defaultValue = this.field.options.defaultValue
this.setValue(defaultValue)
this.$nextTick(() => {
//
})
//清空上传组件文件列表
if ((this.field.type === 'picture-upload') || (this.field.type === 'file-upload')) {
@ -519,7 +527,7 @@ export default {
reloadOptions(options) {
this.field.options.optionItems = deepClone(options)
},
/**
* 返回radio/checkbox/select/cascader的选项数据
* @returns 选择项数组

View File

@ -65,7 +65,13 @@
import SettingPanel from './setting-panel/index'
import VFormWidget from './form-widget/index'
import {createDesigner} from "@/components/form-designer/designer"
import {addWindowResizeHandler, deepClone, getQueryParam} from "@/utils/util"
import {
addWindowResizeHandler,
deepClone,
getAllContainerWidgets,
getAllFieldWidgets,
getQueryParam
} from "@/utils/util"
import {MOCK_CASE_URL, VARIANT_FORM_VERSION} from "@/utils/config"
import i18n, { changeLocale } from "@/utils/i18n"
import axios from "axios"
@ -101,12 +107,15 @@
externalLink: true, //GitHub
formTemplates: true, //
eventCollapse: true, //
widgetNameReadonly: false, //
clearDesignerButton: true, //
previewFormButton: true, //
importJsonButton: true, //JSON
exportJsonButton: true, //JSON
exportCodeButton: true, //
generateSFCButton: true, //SFC
toolBarMaxWidth: 420, //
presetCssCode: '', //CSS
}
@ -327,6 +336,22 @@
this.$refs.toolbarRef.generateSFC()
},
/**
* 获取所有字段组件
* @returns {*[]}
*/
getFieldWidgets() {
return getAllFieldWidgets(this.designer.widgetList)
},
/**
* 获取所有容器组件
* @returns {*[]}
*/
getContainerWidgets() {
return getAllContainerWidgets(this.designer.widgetList)
},
//TODO:
}
@ -334,6 +359,14 @@
</script>
<style lang="scss" scoped>
.el-container.main-container {
::v-deep aside { /* 防止aside样式被外部样式覆盖 */
margin: 0;
padding: 0;
background: inherit;
}
}
.el-container.full-height {
height: 100%;
overflow-y: hidden;

View File

@ -49,7 +49,7 @@ export const createBooleanEditor = function (propName, propLabelKey) {
render(h) {
return (
<el-form-item label={translate(propLabelKey)}>
<el-checkbox v-model={this.optionModel[propName]} />
<el-switch v-model={this.optionModel[propName]} />
</el-form-item>
)
}

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.allowCreate')">
<el-checkbox v-model="optionModel.allowCreate"></el-checkbox>
<el-switch v-model="optionModel.allowCreate"></el-switch>
</el-form-item>
</template>

View File

@ -4,7 +4,7 @@
<el-divider class="custom-divider">{{i18nt('designer.setting.inputButton')}}</el-divider>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.appendButton')">
<el-checkbox v-model="optionModel.appendButton"></el-checkbox>
<el-switch v-model="optionModel.appendButton"></el-switch>
</el-form-item>
</div>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.appendButtonDisabled')">
<el-checkbox v-model="optionModel.appendButtonDisabled"></el-checkbox>
<el-switch v-model="optionModel.appendButtonDisabled"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.automaticDropdown')">
<el-checkbox v-model="optionModel.automaticDropdown"></el-checkbox>
<el-switch v-model="optionModel.automaticDropdown"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.border')">
<el-checkbox v-model="optionModel.border"></el-checkbox>
<el-switch v-model="optionModel.border"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.buttonStyle')">
<el-checkbox v-model="optionModel.buttonStyle"></el-checkbox>
<el-switch v-model="optionModel.buttonStyle"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.clearable')">
<el-checkbox v-model="optionModel.clearable"></el-checkbox>
<el-switch v-model="optionModel.clearable"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.responsive')">
<el-checkbox v-model="optionModel.responsive"></el-checkbox>
<el-switch v-model="optionModel.responsive"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showBlankRow')">
<el-checkbox v-model="optionModel.showBlankRow"></el-checkbox>
<el-switch v-model="optionModel.showBlankRow"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showRowNumber')">
<el-checkbox v-model="optionModel.showRowNumber"></el-checkbox>
<el-switch v-model="optionModel.showRowNumber"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.disabled')">
<el-checkbox v-model="optionModel.disabled"></el-checkbox>
<el-switch v-model="optionModel.disabled"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.editable')">
<el-checkbox v-model="optionModel.editable"></el-checkbox>
<el-switch v-model="optionModel.editable"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.circle')">
<el-checkbox v-model="optionModel.circle"></el-checkbox>
<el-switch v-model="optionModel.circle"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.plain')">
<el-checkbox v-model="optionModel.plain"></el-checkbox>
<el-switch v-model="optionModel.plain"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.round')">
<el-checkbox v-model="optionModel.round"></el-checkbox>
<el-switch v-model="optionModel.round"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.multiple')">
<el-checkbox v-model="optionModel.multiple"></el-checkbox>
<el-switch v-model="optionModel.multiple"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.checkStrictly')">
<el-checkbox v-model="optionModel.checkStrictly"></el-checkbox>
<el-switch v-model="optionModel.checkStrictly"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.allowHalf')">
<el-checkbox v-model="optionModel.allowHalf"></el-checkbox>
<el-switch v-model="optionModel.allowHalf"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showScore')">
<el-checkbox v-model="optionModel.showScore"></el-checkbox>
<el-switch v-model="optionModel.showScore"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showText')">
<el-checkbox v-model="optionModel.showText"></el-checkbox>
<el-switch v-model="optionModel.showText"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.range')">
<el-checkbox v-model="optionModel.range"></el-checkbox>
<el-switch v-model="optionModel.range"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showStops')">
<el-checkbox v-model="optionModel.showStops"></el-checkbox>
<el-switch v-model="optionModel.showStops"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.vertical')">
<el-checkbox v-model="optionModel.vertical"></el-checkbox>
<el-switch v-model="optionModel.vertical"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.filterable')">
<el-checkbox v-model="optionModel.filterable"></el-checkbox>
<el-switch v-model="optionModel.filterable"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.hidden')">
<el-checkbox v-model="optionModel.hidden"></el-checkbox>
<el-switch v-model="optionModel.hidden"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.labelHidden')">
<el-checkbox v-model="optionModel.labelHidden"></el-checkbox>
<el-switch v-model="optionModel.labelHidden"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.multiple')">
<el-checkbox v-model="optionModel.multiple" @change="onMultipleSelected"></el-checkbox>
<el-switch v-model="optionModel.multiple" @change="onMultipleSelected"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.multipleSelect')">
<el-checkbox v-model="optionModel.multipleSelect"></el-checkbox>
<el-switch v-model="optionModel.multipleSelect"></el-switch>
</el-form-item>
</template>

View File

@ -4,11 +4,11 @@
<el-tooltip effect="light" :content="i18nt('designer.setting.editNameHelp')">
<i class="el-icon-info"></i></el-tooltip>
</span>
<template v-if="!!selectedWidget.category || noFieldList">
<el-input type="text" v-model="optionModel.name" @change="updateWidgetNameAndRef"></el-input>
<template v-if="(!!selectedWidget.category && (selectedWidget.type !== 'sub-form')) || noFieldList">
<el-input type="text" v-model="optionModel.name" :readonly="widgetNameReadonly" @change="updateWidgetNameAndRef"></el-input>
</template>
<template v-else>
<el-select v-model="optionModel.name" allow-create filterable @change="updateWidgetNameAndRef"
<el-select v-model="optionModel.name" allow-create filterable :disabled="widgetNameReadonly" @change="updateWidgetNameAndRef"
:title="i18nt('designer.setting.editNameHelp')">
<el-option v-for="(sf, sfIdx) in serverFieldList" :key="sfIdx" :label="sf.label" :value="sf.name"></el-option>
</el-select>
@ -28,7 +28,7 @@
selectedWidget: Object,
optionModel: Object,
},
inject: ['serverFieldList'],
inject: ['serverFieldList', 'getDesignerConfig'],
data() {
return {
nameRequiredRule: [{required: true, message: 'name required'}],
@ -39,6 +39,10 @@
return !this.serverFieldList || (this.serverFieldList.length <= 0)
},
widgetNameReadonly() {
return !!this.getDesignerConfig().widgetNameReadonly
},
},
methods: {
updateWidgetNameAndRef(newName) {

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.readonly')">
<el-checkbox v-model="optionModel.readonly"></el-checkbox>
<el-switch v-model="optionModel.readonly"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.remote')">
<el-checkbox v-model="optionModel.remote" @change="onRemoteChange"></el-checkbox>
<el-switch v-model="optionModel.remote" @change="onRemoteChange"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.required')">
<el-checkbox v-model="optionModel.required"></el-checkbox>
<el-switch v-model="optionModel.required"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showFileList')">
<el-checkbox v-model="optionModel.showFileList"></el-checkbox>
<el-switch v-model="optionModel.showFileList"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showPassword')" v-if="optionModel.type === 'password'">
<el-checkbox v-model="optionModel.showPassword"></el-checkbox>
<el-switch v-model="optionModel.showPassword"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.showWordLimit')">
<el-checkbox v-model="optionModel.showWordLimit"></el-checkbox>
<el-switch v-model="optionModel.showWordLimit"></el-switch>
</el-form-item>
</template>

View File

@ -1,6 +1,6 @@
<template>
<el-form-item :label="i18nt('designer.setting.withCredentials')">
<el-checkbox v-model="optionModel.withCredentials"></el-checkbox>
<el-switch v-model="optionModel.withCredentials"></el-switch>
</el-form-item>
</template>

View File

@ -236,7 +236,7 @@
{label: '333', value: 3},
]
},
}
},
computed: {
@ -272,12 +272,13 @@
},
mounted() {
let maxTBWidth = this.designerConfig.toolBarMaxWidth
let newTBWidth = window.innerWidth - 260 - 300 - 320 - 80
this.toolBarWidth = newTBWidth >= 420 ? 420 : (newTBWidth <= 300 ? 300 : newTBWidth)
this.toolBarWidth = newTBWidth >= maxTBWidth ? maxTBWidth : (newTBWidth <= 300 ? 300 : newTBWidth)
addWindowResizeHandler(() => {
this.$nextTick(() => {
let newTBWidth2 = window.innerWidth - 260 - 300 - 320 - 80
this.toolBarWidth = newTBWidth2 >= 420 ? 420 : (newTBWidth2 <= 300 ? 300 : newTBWidth2)
this.toolBarWidth = newTBWidth2 >= maxTBWidth ? maxTBWidth : (newTBWidth2 <= 300 ? 300 : newTBWidth2)
})
})
},

View File

@ -42,7 +42,7 @@
import './container-item/index'
import FieldComponents from '@/components/form-designer/form-widget/field-widget/index'
import {
deepClone,
deepClone, getAllContainerWidgets, getAllFieldWidgets,
insertCustomCssToHead,
insertGlobalFunctionsToHtml,
traverseFieldWidgets
@ -549,6 +549,7 @@
subFormNames.forEach(sfName => {
if (!!this.subFormRefList[sfName].resetSubForm) {
this.subFormRefList[sfName].resetSubForm()
this.subFormRefList[sfName].addSubFormRow()
}
})
@ -623,7 +624,23 @@
})
}
}
}
},
/**
* 获取所有字段组件
* @returns {*[]}
*/
getFieldWidgets() {
return getAllFieldWidgets(this.formJsonObj.widgetList)
},
/**
* 获取所有容器组件
* @returns {*[]}
*/
getContainerWidgets() {
return getAllContainerWidgets(this.formJsonObj.widgetList)
},
//--------------------- API end ------------------//

View File

@ -210,8 +210,8 @@ function handleWidgetForTraverse(widget, handler) {
/**
* 遍历容器内的字段组件
* @param con
* @param handler
* @param con
* @param handler
*/
export function traverseFieldWidgetsOfContainer(con, handler) {
if (con.type === 'grid') {
@ -245,6 +245,44 @@ export function traverseFieldWidgetsOfContainer(con, handler) {
}
}
/**
* 获取所有字段组件
* @param widgetList
* @returns {[]}
*/
export function getAllFieldWidgets(widgetList) {
let result = []
let handlerFn = (w) => {
result.push({
type: w.type,
name: w.options.name,
field: w
})
}
traverseFieldWidgets(widgetList, handlerFn)
return result
}
/**
* 获取所有容器组件
* @param widgetList
* @returns {[]}
*/
export function getAllContainerWidgets(widgetList) {
let result = []
let handlerFn = (w) => {
result.push({
type: w.type,
name: w.options.name,
container: w
})
}
traverseContainWidgets(widgetList, handlerFn)
return result
}
export function copyToClipboard(content, clickEvent, $message, successMsg, errorMsg) {
const clipboard = new Clipboard(clickEvent.target, {
text: () => content