发布日期:2023年11月20日
一、项目背景与核心功能
在现代企业级应用中,动态表单是高频需求场景。本教程将实现一个基于Vue3的完整动态表单解决方案,包含:
- 可视化表单设计器
- JSON Schema解析引擎
- 动态表单渲染器
- 自定义验证系统
- 多主题支持
技术栈:Vue3 + TypeScript + Vite + Element Plus
二、架构设计与初始化
1. 项目结构
src/
├── core/
│ ├── form-renderer/ # 表单渲染核心
│ ├── form-designer/ # 设计器核心
│ └── schema/ # Schema处理
├── components/
│ ├── field-widgets/ # 字段组件库
│ └── common/ # 通用组件
├── stores/ # Pinia状态管理
├── types/ # TS类型定义
└── utils/ # 工具函数
2. 初始化项目
npm create vite@latest vue3-form-builder --template vue-ts
cd vue3-form-builder
npm install pinia element-plus @vueuse/core lodash-es
三、核心模块实现
1. Schema类型定义
// types/schema.ts
export interface FormSchema {
type: 'object'
properties: Record<string, FormField>
required?: string[]
layout?: FormLayout
}
export interface FormField {
type: 'string' | 'number' | 'boolean' | 'array' | 'object'
title: string
description?: string
widget?: string
default?: any
rules?: FormRule[]
props?: Record<string, any>
hidden?: boolean | ((values: any) => boolean)
}
export interface FormRule {
pattern?: RegExp
message: string
required?: boolean
validator?: (value: any) => boolean
}
2. 动态表单渲染器
<!-- core/form-renderer/FormRenderer.vue -->
<template>
<form @submit.prevent="handleSubmit">
<template v-for="(field, name) in schema.properties" :key="name">
<FormItem
v-if="!isHidden(field)"
:field="field"
:name="name"
:value="modelValue[name]"
@update:value="handleChange(name, $event)"
/>
</template>
<slot name="actions"></slot>
</form>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import FormItem from './FormItem.vue'
const props = defineProps<{
schema: FormSchema
modelValue: Record<string, any>
}>()
const emit = defineEmits(['update:modelValue', 'submit'])
const isHidden = (field: FormField) => {
if (typeof field.hidden === 'function') {
return field.hidden(props.modelValue)
}
return field.hidden
}
const handleChange = (name: string, value: any) => {
emit('update:modelValue', { ...props.modelValue, [name]: value })
}
const handleSubmit = () => {
emit('submit', props.modelValue)
}
</script>
3. 字段组件动态解析
<!-- core/form-renderer/FormItem.vue -->
<template>
<div class="form-item">
<component
:is="resolveComponent(field)"
:modelValue="value"
@update:modelValue="$emit('update:value', $event)"
v-bind="field.props || {}"
/>
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
import type { FormField } from '../../types/schema'
const props = defineProps<{
field: FormField
name: string
value: any
}>()
const componentMap = {
string: 'InputWidget',
number: 'InputNumberWidget',
boolean: 'CheckboxWidget',
// 其他字段类型映射...
}
const resolveComponent = (field: FormField) => {
const widgetName = field.widget || componentMap[field.type]
return defineAsyncComponent(() =>
import(`../../components/field-widgets/${widgetName}.vue`)
)
}
</script>
四、高级功能实现
1. 可视化设计器实现
<!-- core/form-designer/FormDesigner.vue -->
<template>
<div class="designer-container">
<div class="widget-panel">
<div
v-for="widget in widgets"
:key="widget.type"
draggable="true"
@dragstart="handleDragStart($event, widget)"
>
{{ widget.title }}
</div>
</div>
<div
class="design-area"
@dragover.prevent
@drop="handleDrop"
>
<FormRenderer
:schema="currentSchema"
v-model="formData"
/>
</div>
<div class="property-panel">
<SchemaEditor
:field="selectedField"
@update="handleFieldUpdate"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import FormRenderer from '../form-renderer/FormRenderer.vue'
import SchemaEditor from './SchemaEditor.vue'
const widgets = [
{ type: 'text', title: '单行文本' },
{ type: 'textarea', title: '多行文本' },
// 其他控件类型...
]
const currentSchema = ref<FormSchema>({
type: 'object',
properties: {}
})
const selectedField = ref<FormField|null>(null)
const handleDragStart = (e: DragEvent, widget: any) => {
e.dataTransfer?.setData('widget', JSON.stringify(widget))
}
const handleDrop = (e: DragEvent) => {
const widget = JSON.parse(e.dataTransfer?.getData('widget') || '{}')
const fieldName = `field_${Date.now()}`
currentSchema.value.properties[fieldName] = {
type: 'string',
title: widget.title,
widget: widget.type
}
}
</script>
2. 自定义验证系统
// utils/validator.ts
export const validateForm = (schema: FormSchema, values: any) => {
const errors: Record<string, string[]> = {}
Object.entries(schema.properties).forEach(([name, field]) => {
if (field.rules) {
const fieldErrors: string[] = []
field.rules.forEach(rule => {
if (rule.required && !values[name]) {
fieldErrors.push(rule.message)
return
}
if (rule.pattern && !rule.pattern.test(values[name])) {
fieldErrors.push(rule.message)
return
}
if (rule.validator && !rule.validator(values[name])) {
fieldErrors.push(rule.message)
}
})
if (fieldErrors.length) {
errors[name] = fieldErrors
}
}
})
return Object.keys(errors).length ? errors : null
}
五、项目优化与扩展
1. 性能优化方案
- 使用
shallowRef
处理大型表单数据 - 实现字段组件的懒加载
- 添加表单缓存机制
2. 企业级扩展方向
- 集成低代码平台
- 添加版本控制功能
- 实现多语言支持
- 开发表单模板市场
六、总结
本教程实现了:
- 基于JSON Schema的动态表单解析引擎
- 可视化表单设计器核心功能
- 企业级表单验证解决方案
- 可扩展的组件架构设计
通过本项目,您将掌握Vue3在企业级复杂表单场景下的最佳实践,能够应对各种动态表单需求。