基于JSON Schema构建企业级动态表单系统的完整指南
一、动态表单系统架构设计
现代企业级表单系统需要解决的核心问题:
- 动态渲染:根据配置实时生成表单界面
- 数据校验:支持复杂校验规则
- 布局控制:灵活的表单布局管理
- 状态管理:处理复杂表单数据流
- 扩展机制:支持自定义表单控件
系统架构图
Schema解析器 → 表单渲染引擎 → 状态管理 → 校验引擎 ↑ ↑ ↑ ↑ JSON Schema 基础组件库 全局状态 校验规则
二、核心模块实现
1. Schema解析器
<script setup>
import { ref, computed } from 'vue'
const props = defineProps({
schema: {
type: Object,
required: true
}
})
// 解析Schema生成表单配置
const formConfig = computed(() => {
return {
fields: parseFields(props.schema.properties),
layout: props.schema.layout || 'vertical'
}
})
function parseFields(properties) {
return Object.entries(properties).map(([name, config]) => {
return {
name,
label: config.title || name,
type: config.type || 'text',
component: getComponent(config.type),
rules: generateRules(config),
options: config.enum || []
}
})
}
function getComponent(type) {
const componentMap = {
string: 'InputText',
number: 'InputNumber',
boolean: 'InputSwitch',
array: 'InputSelect'
}
return componentMap[type] || 'InputText'
}
</script>
2. 动态表单渲染器
<template>
<form class="dynamic-form" :class="`layout-${formConfig.layout}`">
<template v-for="field in formConfig.fields" :key="field.name">
<component
:is="field.component"
v-model="formData[field.name]"
:label="field.label"
:options="field.options"
:rules="field.rules"
/>
</template>
</form>
</template>
<script setup>
import { defineProps, ref } from 'vue'
import InputText from './components/InputText.vue'
import InputNumber from './components/InputNumber.vue'
// 导入其他基础组件...
defineProps({
formConfig: {
type: Object,
required: true
}
})
const formData = ref({})
</script>
三、高级功能实现
1. 条件渲染逻辑
// 在parseFields函数中添加条件渲染支持
function parseFields(properties) {
return Object.entries(properties).map(([name, config]) => {
return {
// ...其他配置
visible: config.visible || true,
dependencies: config.dependencies || [],
condition: config.condition || null
}
})
}
// 在组件中添加条件渲染逻辑
const visibleFields = computed(() => {
return formConfig.value.fields.filter(field => {
if (!field.dependencies.length) return field.visible
const dependsValues = field.dependencies.reduce((obj, dep) => {
obj[dep] = formData.value[dep]
return obj
}, {})
return evaluateCondition(field.condition, dependsValues)
})
})
function evaluateCondition(condition, values) {
if (!condition) return true
// 实现条件表达式解析逻辑
// 示例:return new Function(`return ${condition}`).call(values)
}
2. 复杂校验系统
function generateRules(config) {
const rules = []
if (config.required) {
rules.push({
required: true,
message: `${config.title || '该字段'}是必填项`,
trigger: 'blur'
})
}
if (config.pattern) {
rules.push({
pattern: new RegExp(config.pattern),
message: config.message || '格式不正确',
trigger: 'blur'
})
}
if (config.validator) {
rules.push({
validator: (rule, value) => {
return new Promise((resolve) => {
// 执行自定义校验逻辑
const valid = config.validator(value)
if (valid) resolve()
else reject(new Error(config.message || '校验失败'))
})
},
trigger: 'change'
})
}
return rules
}
// 在表单组件中使用校验
const formRef = ref(null)
const validate = async () => {
try {
await formRef.value.validate()
return true
} catch (e) {
console.error('表单校验失败', e)
return false
}
}
四、性能优化策略
1. 表单数据响应式优化
import { shallowRef, triggerRef } from 'vue'
// 使用shallowRef减少不必要的响应式开销
const formData = shallowRef({})
// 批量更新表单数据
function setFormData(data) {
formData.value = { ...data }
triggerRef(formData)
}
// 单个字段更新优化
function updateField(name, value) {
const newData = { ...formData.value, [name]: value }
formData.value = newData
// 不需要triggerRef,因为解构创建了新对象
}
2. 动态组件懒加载
const componentMap = {
InputText: defineAsyncComponent(() => import('./InputText.vue')),
InputNumber: defineAsyncComponent(() => import('./InputNumber.vue')),
// 其他组件...
}
function getComponent(type) {
return componentMap[type] || componentMap.InputText
}
五、扩展机制设计
1. 自定义组件注册
// 组件注册管理器
const customComponents = new Map()
export function registerComponent(type, component) {
customComponents.set(type, component)
}
export function getComponent(type) {
return customComponents.get(type) || defaultComponents.get(type)
}
// 使用示例
import CustomUploader from './CustomUploader.vue'
registerComponent('uploader', CustomUploader)
2. 插件系统实现
// 插件接口定义
export function createFormPlugin(plugin) {
return {
install(formEngine) {
if (plugin.components) {
Object.entries(plugin.components).forEach(([name, component]) => {
formEngine.registerComponent(name, component)
})
}
if (plugin.mixins) {
formEngine.addMixins(plugin.mixins)
}
}
}
}
// 插件使用示例
const richTextPlugin = createFormPlugin({
components: {
richText: RichTextEditor
},
mixins: [{
beforeSubmit(formData) {
// 提交前处理逻辑
}
}]
})
formEngine.use(richTextPlugin)
六、实战案例:用户注册表单
1. Schema定义
const registerSchema = {
title: "用户注册",
layout: "vertical",
properties: {
username: {
type: "string",
title: "用户名",
minLength: 4,
maxLength: 16,
pattern: "^[a-zA-Z0-9_]+$",
required: true
},
password: {
type: "string",
title: "密码",
format: "password",
minLength: 6,
required: true
},
confirmPassword: {
type: "string",
title: "确认密码",
format: "password",
dependencies: ["password"],
condition: "values.password === value",
required: true
},
// 更多字段...
}
}
2. 表单使用
<template>
<DynamicForm
:schema="registerSchema"
@submit="handleSubmit"
/>
</template>
<script setup>
import { ref } from 'vue'
import DynamicForm from './DynamicForm.vue'
const registerSchema = ref({/* 同上 */})
const handleSubmit = async (formData) => {
const res = await api.register(formData)
// 处理注册结果
}
</script>