diff --git a/README.md b/README.md
index 8cd12b0..95db881 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Variant Form 3 For Vue 3.x
#### 一款高效的Vue 3低代码表单,可视化设计,一键生成源码,享受更多摸鱼时间。
-![image](https://vform2021.oss-cn-beijing.aliyuncs.com/vform_demo.gif?versionId=CAEQGBiBgIDst4zj4hciIDQyYTkyOGY1ZGJiODQ4YTk5ZjkxMGIwMDY0MmY2M2Ri)
+![image](https://ks3-cn-beijing.ksyuncs.com/vform-static/img/vform_demo.gif)
diff --git a/package.json b/package.json
index 85f31ce..2b018f9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "variant-form3",
- "version": "3.0.3",
+ "version": "3.0.4",
"private": false,
"scripts": {
"serve": "vite",
diff --git a/src/components/form-designer/form-widget/field-widget/fieldMixin.js b/src/components/form-designer/form-widget/field-widget/fieldMixin.js
index d22800b..ce66de6 100644
--- a/src/components/form-designer/form-widget/field-widget/fieldMixin.js
+++ b/src/components/form-designer/form-widget/field-widget/fieldMixin.js
@@ -34,6 +34,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) {
@@ -201,7 +208,7 @@ export default {
required: true,
//trigger: ['blur', 'change'],
trigger: ['blur'], /* 去掉change事件触发校验,change事件触发时formModel数据尚未更新,导致radio/checkbox必填校验出错!! */
- message: this.i18nt('render.hint.fieldRequired'),
+ message: this.field.options.requiredHint || this.i18nt('render.hint.fieldRequired'),
})
}
@@ -320,8 +327,7 @@ export default {
this.oldFieldValue = deepClone(value) /* oldFieldValue需要在initFieldModel()方法中赋初值!! */
/* 主动触发表单的单个字段校验,用于清除字段可能存在的校验错误提示 */
- this.dispatch('VFormRender', 'fieldValidation', [this.field.options.name])
- // eventBus.$emit('fieldValidation', [this.field.options.name])
+ this.dispatch('VFormRender', 'fieldValidation', [this.getPropName()])
},
handleFocusCustomEvent(event) {
@@ -343,6 +349,9 @@ export default {
handleInputCustomEvent(value) {
this.syncUpdateFormModel(value)
+ /* 主动触发表单的单个字段校验,用于清除字段可能存在的校验错误提示 */
+ this.dispatch('VFormRender', 'fieldValidation', [this.getPropName()])
+
if (!!this.field.options.onInput) {
let customFn = new Function('value', this.field.options.onInput)
customFn.call(this, value)
diff --git a/src/components/form-designer/index.vue b/src/components/form-designer/index.vue
index a6f0b27..61dfd76 100644
--- a/src/components/form-designer/index.vue
+++ b/src/components/form-designer/index.vue
@@ -68,7 +68,8 @@
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, getQueryParam, getAllContainerWidgets,
+ getAllFieldWidgets} from "@/utils/util"
import {MOCK_CASE_URL, VARIANT_FORM_VERSION} from "@/utils/config"
import i18n, { changeLocale } from "@/utils/i18n"
import axios from 'axios'
@@ -243,7 +244,8 @@
return
}
- axios.get(this.fieldListApi.URL).then(res => {
+ let headers = this.fieldListApi.headers || {}
+ axios.get(this.fieldListApi.URL, {'headers': headers}).then(res => {
let labelKey = this.fieldListApi.labelKey || 'label'
let nameKey = this.fieldListApi.nameKey || 'name'
@@ -340,6 +342,22 @@
this.$refs.toolbarRef.generateSFC()
},
+ /**
+ * 获取所有字段组件
+ * @returns {*[]}
+ */
+ getFieldWidgets() {
+ return getAllFieldWidgets(this.designer.widgetList)
+ },
+
+ /**
+ * 获取所有容器组件
+ * @returns {*[]}
+ */
+ getContainerWidgets() {
+ return getAllContainerWidgets(this.designer.widgetList)
+ },
+
//TODO: 增加更多方法!!
}
diff --git a/src/components/form-designer/setting-panel/property-editor/requiredHint-editor.vue b/src/components/form-designer/setting-panel/property-editor/requiredHint-editor.vue
new file mode 100644
index 0000000..d44c442
--- /dev/null
+++ b/src/components/form-designer/setting-panel/property-editor/requiredHint-editor.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/form-designer/setting-panel/propertyRegister.js b/src/components/form-designer/setting-panel/propertyRegister.js
index f653cd9..303b439 100644
--- a/src/components/form-designer/setting-panel/propertyRegister.js
+++ b/src/components/form-designer/setting-panel/propertyRegister.js
@@ -24,6 +24,7 @@ const COMMON_PROPERTIES = {
'labelHidden' : 'labelHidden-editor',
'rows' : 'rows-editor',
'required' : 'required-editor',
+ 'requiredHint' : 'requiredHint-editor',
'validation' : 'validation-editor',
'validationHint' : 'validationHint-editor',
'readonly' : 'readonly-editor',
diff --git a/src/components/form-designer/toolbar-panel/index.vue b/src/components/form-designer/toolbar-panel/index.vue
index 7ec3b1c..26cc26d 100644
--- a/src/components/form-designer/toolbar-panel/index.vue
+++ b/src/components/form-designer/toolbar-panel/index.vue
@@ -62,6 +62,7 @@
{{i18nt('designer.hint.disableForm')}}
{{i18nt('designer.hint.enableForm')}}
{{i18nt('designer.hint.closePreview')}}
+ Test Load
@@ -594,6 +595,23 @@
this.$refs['preForm'].enableForm()
},
+ testLoadForm() {
+ axios.get('https://www.fastmock.site/mock/e9710039bb5f11262d1a0f2f0bbe08c8/vform3/getFS').then(res => {
+ let newFormJson = res.data
+ this.$refs.preForm.setFormJson(newFormJson)
+ // let newFormData = {'input30696': '668899'}
+ // this.$refs.preForm.setFormData(newFormData)
+
+ console.log('test', 'aaaaaaaa')
+ this.$nextTick(() => {
+ let newFormData = {'input30696': '668899'}
+ this.$refs.preForm.setFormData(newFormData)
+ })
+ }).catch(err => {
+ //
+ })
+ },
+
handleFormChange(fieldName, newValue, oldValue, formModel) {
/*
console.log('---formChange start---')
diff --git a/src/components/form-designer/widget-panel/widgetsConfig.js b/src/components/form-designer/widget-panel/widgetsConfig.js
index c2785f5..3783645 100644
--- a/src/components/form-designer/widget-panel/widgetsConfig.js
+++ b/src/components/form-designer/widget-panel/widgetsConfig.js
@@ -118,6 +118,7 @@ export const basicFields = [
clearable: true,
showPassword: false,
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -163,6 +164,7 @@ export const basicFields = [
disabled: false,
hidden: false,
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -201,6 +203,7 @@ export const basicFields = [
disabled: false,
hidden: false,
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -247,6 +250,7 @@ export const basicFields = [
{label: 'radio 3', value: 3},
],
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -286,6 +290,7 @@ export const basicFields = [
{label: 'check 3', value: 3},
],
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -330,6 +335,7 @@ export const basicFields = [
{label: 'select 3', value: 3},
],
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -370,6 +376,7 @@ export const basicFields = [
editable: false,
format: 'HH:mm:ss', //时间格式
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -410,6 +417,7 @@ export const basicFields = [
editable: false,
format: 'HH:mm:ss', //时间格式
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -451,6 +459,7 @@ export const basicFields = [
format: 'YYYY-MM-DD', //日期显示格式
valueFormat: 'YYYY-MM-DD', //日期对象格式
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -493,6 +502,7 @@ export const basicFields = [
format: 'YYYY-MM-DD', //日期显示格式
valueFormat: 'YYYY-MM-DD', //日期对象格式
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -557,6 +567,7 @@ export const basicFields = [
disabled: false,
hidden: false,
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -594,6 +605,7 @@ export const basicFields = [
disabled: false,
hidden: false,
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
@@ -625,6 +637,7 @@ export const basicFields = [
disabled: false,
hidden: false,
required: false,
+ requiredHint: '',
validation: '',
validationHint: '',
//-------------------
diff --git a/src/components/form-render/container-item/containerItemMixin.js b/src/components/form-render/container-item/containerItemMixin.js
index 6b26579..f0163f2 100644
--- a/src/components/form-render/container-item/containerItemMixin.js
+++ b/src/components/form-render/container-item/containerItemMixin.js
@@ -1,4 +1,4 @@
-import {generateId} from "@/utils/util";
+import { traverseFieldWidgetsOfContainer } from "@/utils/util";
export default {
computed: {
@@ -14,6 +14,9 @@ export default {
},
},
+ mounted() {
+ this.callSetHidden()
+ },
methods: {
unregisterFromRefList() { //销毁容器组件时注销组件ref
@@ -23,11 +26,33 @@ export default {
}
},
+ /* 主动触发setHidden()方法,以清空被隐藏容器内字段组件的校验规则!! */
+ callSetHidden() {
+ if (this.widget.options.hidden === true) {
+ this.setHidden(true)
+ }
+ },
+
//--------------------- 以下为组件支持外部调用的API方法 begin ------------------//
/* 提示:用户可自行扩充这些方法!!! */
setHidden(flag) {
this.widget.options.hidden = flag
+
+ /* 容器被隐藏后,需要同步清除容器内部字段组件的校验规则 */
+ let clearRulesFn = (fieldWidget) => {
+ let fwName = fieldWidget.options.name
+ let fwRef = this.getWidgetRef(fwName)
+ if (flag && !!fwRef && !!fwRef.clearFieldRules) {
+ fwRef.clearFieldRules()
+ }
+
+ if (!flag && !!fwRef && !!fwRef.buildFieldRules) {
+ fwRef.buildFieldRules()
+ }
+ }
+
+ traverseFieldWidgetsOfContainer(this.widget, clearRulesFn)
},
activeTab(tabIndex) { //tabIndex从0计数
diff --git a/src/components/form-render/index.vue b/src/components/form-render/index.vue
index 7ee9477..e8302e2 100644
--- a/src/components/form-render/index.vue
+++ b/src/components/form-render/index.vue
@@ -41,7 +41,8 @@
import emitter from '@/utils/emitter'
import './container-item/index'
import FieldComponents from '@/components/form-designer/form-widget/field-widget/index'
- import {deepClone, insertCustomCssToHead, insertGlobalFunctionsToHtml} from "@/utils/util"
+ import {deepClone, insertCustomCssToHead, insertGlobalFunctionsToHtml, getAllContainerWidgets,
+ getAllFieldWidgets} from "@/utils/util"
import i18n, { changeLocale } from "@/utils/i18n"
import eventBus from "@/utils/event-bus"
@@ -549,7 +550,23 @@
})
}
}
- }
+ },
+
+ /**
+ * 获取所有字段组件
+ * @returns {*[]}
+ */
+ getFieldWidgets() {
+ return getAllFieldWidgets(this.formJsonObj.widgetList)
+ },
+
+ /**
+ * 获取所有容器组件
+ * @returns {*[]}
+ */
+ getContainerWidgets() {
+ return getAllContainerWidgets(this.formJsonObj.widgetList)
+ },
//--------------------- 以上为组件支持外部调用的API方法 end ------------------//
diff --git a/src/extension/extension-loader.js b/src/extension/extension-loader.js
index 54055ab..f80a47d 100644
--- a/src/extension/extension-loader.js
+++ b/src/extension/extension-loader.js
@@ -76,7 +76,7 @@ export const loadExtension = function (app) {
{label: 'info', value: 'info'},
{label: 'error', value: 'error'},
]
- PERegister.registerCPEditor(app, 'alert-type', 'alert-type-editor',
+ PERegister.registerCPEditor(app, 'type', 'alert-type-editor',
PEFactory.createSelectEditor('type', 'extension.setting.alertType',
{optionItems: typeOptions}))
diff --git a/src/lang/en-US.js b/src/lang/en-US.js
index 71a4c1f..cf3af28 100644
--- a/src/lang/en-US.js
+++ b/src/lang/en-US.js
@@ -190,6 +190,7 @@ export default {
rows: 'Rows',
labelHidden: 'Hide Label',
required: 'Required',
+ requiredHint: 'Failure Hint',
validation: 'Validation',
validationHelp: 'Regular expressions supported',
validationHint: 'Validation Hint',
diff --git a/src/lang/zh-CN.js b/src/lang/zh-CN.js
index 9749426..6a9898e 100644
--- a/src/lang/zh-CN.js
+++ b/src/lang/zh-CN.js
@@ -190,6 +190,7 @@ export default {
rows: '行数',
labelHidden: '隐藏字段标签',
required: '必填字段',
+ requiredHint: '必填校验提示',
validation: '字段校验',
validationHelp: '支持输入正则表达式',
validationHint: '校验失败提示',
diff --git a/src/utils/config.js b/src/utils/config.js
index c414379..9601034 100644
--- a/src/utils/config.js
+++ b/src/utils/config.js
@@ -8,7 +8,7 @@ export const DESIGNER_OPTIONS = {
}
-export const VARIANT_FORM_VERSION = '3.0.3'
+export const VARIANT_FORM_VERSION = '3.0.4'
//export const MOCK_CASE_URL = 'https://www.fastmock.site/mock/2de212e0dc4b8e0885fea44ab9f2e1d0/vform/'
export const MOCK_CASE_URL = 'https://ks3-cn-beijing.ksyuncs.com/vform-static/vcase/'
diff --git a/src/utils/emitter.js b/src/utils/emitter.js
index e705c85..21b1822 100644
--- a/src/utils/emitter.js
+++ b/src/utils/emitter.js
@@ -2,7 +2,6 @@
function _broadcast(componentName, eventName, params) {
this.$children.forEach(function (child) {
let name = child.$options.componentName;
-
if (name === componentName) {
//child.$emit.apply(child, [eventName].concat(params));
if (!!child.emit$) {
@@ -72,7 +71,28 @@ export default {
},
broadcast: function broadcast(componentName, eventName, params) {
- _broadcast.call(this, componentName, eventName, params);
+ /* Vue3移除了$children属性,_broadcast方法已不能使用!! */
+ //_broadcast.call(this, componentName, eventName, params);
+
+ if (!!this.widgetRefList) { //FormRender只需遍历自身的widgetRefList属性
+ Object.keys(this.widgetRefList).forEach(refName => {
+ let cmpName = this.widgetRefList[refName].$options.componentName
+ if (cmpName === componentName) {
+ let foundRef = this.widgetRefList[refName]
+ foundRef.emit$.call(foundRef, eventName, params)
+ }
+ })
+ }
+
+ if (!!this.refList) { //其他组件遍历inject的refList属性
+ Object.keys(this.refList).forEach(refName => {
+ let cmpName = this.refList[refName].$options.componentName
+ if (cmpName === componentName) {
+ let foundRef = this.refList[refName]
+ foundRef.emit$.call(foundRef, eventName, params)
+ }
+ })
+ }
}
}
};
diff --git a/src/utils/util.js b/src/utils/util.js
index 71c3c75..aa04395 100644
--- a/src/utils/util.js
+++ b/src/utils/util.js
@@ -200,6 +200,89 @@ export function traverseAllWidgets(widgetList, handler) {
})
}
+function handleWidgetForTraverse(widget, handler) {
+ if (!!widget.category) {
+ traverseFieldWidgetsOfContainer(widget, handler)
+ } else if (widget.formItemFlag) {
+ handler(widget)
+ }
+}
+
+/**
+ * 遍历容器内的字段组件
+ * @param con
+ * @param handler
+ */
+export function traverseFieldWidgetsOfContainer(con, handler) {
+ if (con.type === 'grid') {
+ con.cols.forEach(col => {
+ col.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ })
+ } else if (con.type === 'table') {
+ con.rows.forEach(row => {
+ row.cols.forEach(cell => {
+ cell.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ })
+ })
+ } else if (con.type === 'tab') {
+ con.tabs.forEach(tab => {
+ tab.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ })
+ } else if (con.type === 'sub-form') {
+ con.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ } else if (con.category === 'container') { //自定义容器
+ con.widgetList.forEach(cw => {
+ handleWidgetForTraverse(cw, handler)
+ })
+ }
+}
+
+/**
+ * 获取所有字段组件
+ * @param widgetList
+ * @returns {[]}
+ */
+export function getAllFieldWidgets(widgetList) {
+ 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
diff --git a/vite.config.js b/vite.config.js
index 7ffcf75..076cb03 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -52,7 +52,7 @@ export default defineConfig({
},
build: {
- minify: false,
+ //minify: false,
commonjsOptions: {
exclude: [
'lib/vuedraggable/dist/vuedraggable.umd.js,', //引号前的逗号不能删,不知何故??