一、背景:为什么表单最难维护?
在广告平台中,表单遍布各个业务模块:新建广告、编辑素材、审批流表单、充值申请、内容提交……
问题在于:
- 字段多达几十个,表单嵌套层级深
- 联动逻辑复杂,部分字段按角色/状态动态显示
- 校验规则五花八门,重复性高
因此,我主导对表单系统进行全面重构。
二、目标:构建 schema 驱动、逻辑解耦、组件可插拔的表单系统
核心思路:数据驱动 UI + 逻辑分离 + 插槽灵活扩展
三、表单 schema 抽象设计
基础结构:
interface FormSchemaField {
label: string
field: string
component: string
props?: Record<string, any>
rules?: Rule[]
ifRender?: () => boolean
}
示例 schema:
const adPlanFormSchema: FormSchemaField[] = [
{
label: '广告名称',
field: 'name',
component: 'n-input',
rules: [{ required: true, message: '请输入广告名称' }]
},
{
label: '投放时间',
field: 'range',
component: 'n-date-picker',
props: { type: 'daterange' }
}
]
四、动态渲染组件逻辑
封装一个 SchemaForm.vue:
<template>
<n-form :model="formData" ref="formRef">
<n-form-item
v-for="field in schema"
:label="field.label"
:path="field.field"
v-if="!field.ifRender || field.ifRender()"
>
<component :is="field.component" v-model:value="formData[field.field]" v-bind="field.props" />
</n-form-item>
</n-form>
</template>
五、插槽机制支持扩展需求
支持传入插槽覆盖字段渲染:
<template #custom-range>
<n-date-picker v-model:value="formData.range" type="daterange" clearable />
</template>
在 Schema 中标记:
{
label: '投放时间',
field: 'range',
component: 'custom-slot'
}
六、联动逻辑设计(useFormModel)
抽象状态联动逻辑到 hook:
export const useFormModel = () => {
const formData = reactive({ name: '', range: [], type: 'CPC' })
const schema = computed(() => {
return [
...baseSchema,
...(formData.type === 'CPM' ? extraCPMSchema : [])
]
})
return { formData, schema }
}
七、表单校验封装与提交
在组件内部暴露 validate 方法供外部调用:
const validate = async () => {
if (!formRef.value) return false
return await formRef.value.validate()
}
defineExpose({ validate })
八、结果:高复用 + 高灵活 + 低耦合
- 所有表单结构配置化,业务字段易于维护
- 表单逻辑抽离后更易测试与复用
- 插槽机制让灵活场景无痛嵌入
九、总结:表单是中后台的“根”,不重构注定混乱
前端架构的核心就是识别“高重复 + 高变形”的部分并优雅抽象。
表单系统就是其中最典型的代表。