12、表单系统设计:动态表单 + 校验 + 可配置化

🪜 写在前面

表单,是前端系统最基础但最复杂的模块之一。 它常常包含:

  • 大量字段、交互逻辑、联动控制
  • 多种校验规则、字段显示控制、动态字段生成
  • 提交、校验、重置、草稿保存、只读模式等状态管理
  • 表单配置平台化(低代码配置出页面)

本篇我们将一步步实现一套通用、动态、可扩展的表单系统,覆盖以下能力:

✅ 表单字段动态渲染

✅ 多类型字段支持(输入框/下拉/日期/开关/上传等)

✅ 校验规则可配置

✅ 字段联动控制(显示/禁用/值变化)

✅ 表单模式(编辑/只读)切换

✅ 支持后续平台化配置表单模板


🧱 一、表单渲染核心设计思想

用 JSON Schema + 渲染组件映射 实现动态表单:

ts 复制代码
export interface FormSchema {
  label: string
  field: string
  type: 'input' | 'select' | 'switch' | 'date' | 'upload'
  required?: boolean
  options?: Array<{ label: string; value: any }>
  defaultValue?: any
  rules?: Array<any>
  showWhen?: (formModel) => boolean
  disabledWhen?: (formModel) => boolean
}

📦 二、核心目录结构设计

bash 复制代码
components/
├── DynamicForm.vue         # 主表单组件
├── fields/                 # 各类型组件
│   ├── InputField.vue
│   ├── SelectField.vue
│   ├── SwitchField.vue
│   └── ...
hooks/
├── useFormRender.ts        # 渲染逻辑封装
├── useFormModel.ts         # 状态管理封装

✅ 三、基础组件结构(DynamicForm)

vue 复制代码
<!-- DynamicForm.vue -->
<template>
  <Form ref="formRef" :model="formModel" :rules="rules">
    <FormItem
      v-for="schema in visibleSchemas"
      :label="schema.label"
      :prop="schema.field"
    >
      <component
        :is="getFieldComponent(schema.type)"
        v-model="formModel[schema.field]"
        v-bind="getFieldProps(schema)"
        :disabled="isDisabled(schema)"
      />
    </FormItem>
  </Form>
</template>
ts 复制代码
// 渲染辅助逻辑
function getFieldComponent(type: string) {
  return {
    input: InputField,
    select: SelectField,
    switch: SwitchField,
  }[type]
}

🧩 四、支持动态联动(showWhen、disabledWhen)

ts 复制代码
const visibleSchemas = computed(() =>
  props.schema.filter((schema) =>
    schema.showWhen ? schema.showWhen(formModel.value) : true
  )
)

function isDisabled(schema: FormSchema) {
  return schema.disabledWhen ? schema.disabledWhen(formModel.value) : false
}

配置示例:

ts 复制代码
{
  field: 'username',
  label: '用户名',
  type: 'input',
  required: true
},
{
  field: 'adminCode',
  label: '管理员编号',
  type: 'input',
  showWhen: (form) => form.role === 'admin'
}

🔐 五、校验规则支持

支持默认 + 自定义:

ts 复制代码
{
  field: 'email',
  label: '邮箱',
  type: 'input',
  rules: [
    { required: true, message: '请输入邮箱' },
    { type: 'email', message: '格式不正确' },
  ],
}

表单验证封装:

ts 复制代码
async function validate() {
  return formRef.value.validate()
}

🧲 六、只读模式/编辑模式统一处理

方式1:组件接收 readonly 属性控制渲染

vue 复制代码
<!-- InputField.vue -->
<template>
  <span v-if="readonly">{{ modelValue }}</span>
  <Input v-else v-model:value="modelValue" />
</template>

方式2:统一在 DynamicForm.vue 中处理

vue 复制代码
<component
  :is="getFieldComponent(schema.type)"
  v-model="formModel[schema.field]"
  :readonly="props.readonly"
  :disabled="props.readonly || isDisabled(schema)"
/>

📦 七、支持插槽 & 自定义组件注册

vue 复制代码
<FormItem v-if="schema.slot">
  <slot :name="schema.slot" :form="formModel" />
</FormItem>

允许使用:

vue 复制代码
<DynamicForm :schema="formSchema">
  <template #customSlot="{ form }">
    <Input v-model:value="form.customField" />
  </template>
</DynamicForm>

🎯 八、平台化配置建议(支持后台配置)

后端返回:

json 复制代码
[
  {
    "field": "username",
    "label": "用户名",
    "type": "input",
    "required": true
  },
  {
    "field": "role",
    "label": "角色",
    "type": "select",
    "options": [
      { "label": "管理员", "value": "admin" },
      { "label": "普通用户", "value": "user" }
    ]
  }
]

前端直接传入 schema,完成渲染。


🧠 九、性能优化建议

优化点 建议方案
schema 频繁变动 使用 v-memo 或 shallowReactive
只读表单渲染 使用只读组件(纯文本)+ 懒加载字段
联动过多 使用 computed 缓存联动状态
多类型组件动态渲染 使用缓存组件工厂 (defineAsyncComponent)

💡 十、组件封装建议(进阶)

类型 说明
DynamicForm.vue 渲染主组件
FormRender.ts 生成组件映射表,支持自定义注册
useForm.ts 暴露 validate/reset/setFields 等方法
useFormSchema.ts 拆分 schema 过滤/联动/权限逻辑
FieldWrapper.vue 可扩展插槽 + 错误提示包装器

🧠 总结一句话

表单系统的通用化设计,是支撑前端"配置化平台"、"低代码编辑器"的核心引擎。


下一篇我们进入高复用组件系统的核心场景:

👉 《表格系统架构:列配置、嵌套数据、复杂交互》

相关推荐
一斤代码1 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子1 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年1 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子1 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina1 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路2 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_2 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
伍哥的传说3 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409193 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app
我在北京coding3 小时前
element el-table渲染二维对象数组
前端·javascript·vue.js