ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

vue-vben-admin Form Array 数组字段:`type: ‘array‘ + children` Schema 实战指南

vue-vben-admin Form Array 数组字段:`type: ‘array‘ + children` Schema 实战指南 vue-vben-admin Form Array 数组字段type: array childrenSchema 实战指南【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. Its fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin导读在 vue-vben-admin 的core/ui-kit/form-ui表单体系中数组字段如“联系人列表”“明细行”这类可增删的结构化数据推荐使用type: array children的 Schema 描述方式而无需关心底层渲染组件名。本文以 playground/src/views/demos/form-array/index.vue 这个官方 Demo 为线索完整讲解数组字段的 Schema 写法、arrayProps配置、子字段校验、行内dependencies联动、提交值codec转换、updateSchema动态更新并结合 schema.ts、form-field-array.vue 等核心源码剖析其底层实现链路。读完本文你将能独立在 vue-vben-admin 中搭建可增删行、可联动、可校验、可提交转换的完整数组表单。快速开始一个最小的数组字段 Schema数组字段的推荐写法是把type设为array用children描述每一行的子字段用arrayProps控制行编辑器的行为const schema: VbenFormSchema[] [ { type: array, fieldName: contacts, label: 联系人, formItemClass: col-span-1 md:col-span-2, defaultValue: [ { enabled: true, name: 张三, phone: 10086, role: owner, }, ], rules: z.array(z.any()).min(1, 请至少添加一个联系人), arrayProps: { addButtonText: 添加联系人, min: 1, max: 5, createRow: () ({ enabled: true, name: , phone: , role: member, }), }, children: [ { component: Input, fieldName: name, label: 姓名, rules: z.string().min(1, 请输入姓名), }, { component: Select, fieldName: role, label: 角色, rules: selectRequired, componentProps: { options: [ { label: 负责人, value: owner }, { label: 成员, value: member }, ], }, }, ], }, ];官方 Demo 中还演示了一个完整的“值班联络人配置”表单顶部是依赖联动的基础字段planName方案名称、description方案说明下面是可增删的contacts联系人数组每个联系人包含姓名、角色、电话、状态四个子字段见 playground/src/views/demos/form-array/index.vue。字段职责详解type: array声明这是一个数组字段。渲染前会被内部转换为VbenFormFieldArray所以业务 Schema 不需要写具体组件名。从源码看isFormArraySchema会同时识别三种形态type array、component VbenFormFieldArray、或者存在非空的childrenschema.ts这保证了新旧写法都能被统一处理。fieldName数组字段名。假设为contacts第 1 行子字段name会被转换成contacts[0].name;行路径的拼接在createArrayChildSchema中完成rowPath ${arrayField}[${index}]然后通过resolveArrayChildFieldName解析出每个子字段的真实路径schema.ts。children数组每一行的子字段定义。每个 child 都是完整的FormSchema可以继续使用componentcomponentPropsrulesdependenciesdefaultValuehelpsuffixrenderComponentContentformFieldPropsdisabledhidelabelClasscontrolClasschild 最终仍然走统一的FormField渲染见 form-field-array.vue所以现有 FormSchema 的全部能力在数组行内都不会丢失。arrayProps传给数组编辑器VbenFormFieldArray的配置。其 Props 定义与默认值见 form-field-array.vue字段说明addButtonText新增按钮文案默认添加一行actionText操作列表头文案默认操作emptyText空数据文案默认暂无数据min最少行数达到后禁用删除默认0max最多行数达到后禁用新增默认Number.POSITIVE_INFINITYshowIndex是否显示序号默认truecreateRow新增行时生成默认数据的工厂函数缺省时按 child 的defaultValue生成没有则给null其中min/max的禁用逻辑由计算属性canRemove arrayLength props.min、canAdd arrayLength props.max驱动form-field-array.vue新增与删除分别调用表单运行时的pushFieldValue/removeFieldValueform-runtime.ts底层基于tanstack/vue-form的useForm管理行数据。校验建议数组级规则与子字段规则分层数组父级rules建议只写数组级规则例如至少一行rules: z.array(z.any()).min(1, 请至少添加一个联系人);每个子字段的必填、长度、格式校验写在 children 自己的rules{ component: Input, fieldName: name, label: 姓名, rules: z.string().min(1, 请输入姓名), }不要在父级数组规则里重复写z.object({ name: ... })否则某一行子字段失败时父级数组也会失败容易出现重复错误提示。dependencies 用法行内联动与根字段联动默认的“当前行相对路径”children 里的dependencies.triggerFields默认是“当前行相对路径”。例如{ component: Input, fieldName: phone, label: 电话, dependencies: { triggerFields: [role], componentProps: (_values, _form, _api, ctx) ({ disabled: ctx?.row?.role viewer, placeholder: ctx?.row?.role viewer ? 观察员无需电话 : 请输入电话, }), }, }在第 1 行中triggerFields: [role]会被转换成contacts[0].role。路径作用域由scopeRowFieldName实现schema.ts普通字段名会自动拼上rowPath前缀。回调的ctx参数由于行内字段的componentProps/dependencies回调需要知道自己属于哪一行框架额外注入了一个可选的ctx参数类型为FormSchemaContext见 schema.ts字段说明ctx.row当前行数据ctx.rowIndex当前行索引ctx.rowPath当前行路径例如contacts[0]ctx.arrayField数组字段名例如contactsctx.fieldName当前真实字段名例如contacts[0].phonectx.originalFieldName原始 child 字段名例如phonectx.rootValues表单完整值这些信息在createArrayChildSchema中组装为baseContext并通过wrapComponentProps、wrapDependencyFn、wrapCustomParamsRender等包装函数透传给各个回调schema.ts。官方 Demo 里姓名输入框的占位文案第 ${(ctx.rowIndex ?? 0) 1} 行姓名就是利用rowIndex的典型例子index.vue。前缀语法$root.与$row.如果 child 需要依赖表单根字段可以使用$root.前缀dependencies: { triggerFields: [$root.planName], componentProps: (values) ({ disabled: !values.planName, }), }如果想显式写当前行字段也可以使用$row.前缀triggerFields: [$row.role];在scopeRowFieldName中$root.前缀会被直接剥离以指向表单根路径$row.前缀会被替换为rowPathschema.ts。同理setValue等 API 的路径前缀解析在 field-name.ts 的resolveValueFormatFieldName中实现。提交值转换表单级 codec数组字段的提交转换使用表单级codec一次处理完整表单值。官方 Demo 中encode负责提交前把姓名去除首尾空格、把空电话归一化为undefinedfunction encodeArrayFormValues(values: ReadonlyArrayFormValues) { return { ...values, contacts: values.contacts.map((contact) ({ ...contact, name: contact.name.trim(), phone: contact.phone?.trim() || undefined, })), }; } const [Form] useVbenForm({ codec: { decode: decodeArrayFormValues, encode: encodeArrayFormValues, }, schema, });decode则负责回填表单时把undefined还原为空字符串避免受控组件告警index.vue。codec的执行由 form-codec.ts 统一调度任一阶段抛错都会被包装为携带phase信息的FormCodecError。在数组行内调用setValue时同样支持路径前缀写根字段用$root.显式写当前行字段用$row.setValue($root.firstContactPhone, value); setValue($row.phone, value);updateSchema 用法动态更新子字段可以用父级路径更新 child schemaformApi.updateSchema([ { fieldName: contacts.phone, rules: z.string().min(5, 电话至少 5 位), }, ]);如果传入带索引路径也会更新对应 child 定义formApi.updateSchema([ { fieldName: contacts[0].phone, rules: z.string().min(5, 电话至少 5 位), }, ]);注意updateSchema更新的是 schema 定义不是单行实例。因此contacts[0].phone这种写法目前会解析到 childphone实际影响所有行的该列。这一点可以从updateFormSchemaList的递归逻辑得到印证——它会把contacts[0].phone通过resolveChildUpdateFieldName归一化为子字段名phone再下发给children递归更新schema.ts而路径归一化逻辑在 field-name.ts。updateSchema的入口在 form-api.ts要求数组中每一项都必须带非空fieldName否则打印错误并拒绝更新。官方 Demo 中的“更新电话规则”按钮正是这一能力的直接演示index.vue。新增行默认值优先使用arrayProps.createRowarrayProps: { createRow: () ({ name: , role: member, phone: , enabled: true, }), }没有createRow时会根据 children 的defaultValue生成行数据没有 defaultValue 的字段会给null。该逻辑在buildDefaultRow中实现form-field-array.vuecreateRow存在时直接调用否则遍历列定义读取defaultValueundefined时给null若该列本身是嵌套数组则给空数组[]。内部流程从 schema 到渲染的完整链路数组字段从 schema 到渲染大致是这条链路几个关键点外层type: array只是语义声明。createFormFieldSchema在渲染前调用isFormArraySchema识别数组 schema再由createArrayFieldSchema把component替换为VbenFormFieldArray并把children合并进componentProps.schemaschema.ts。具体展示仍复用内部VbenFormFieldArray。行数据的增删由formActions.pushFieldValue/removeFieldValue驱动form-field-array.vue。child 最终仍然走FormField所以现有 FormSchema 能力不会丢form-field-array.vue。dependencies不改核心调用链而是在createArrayChildSchema里做路径和 ctx 适配——scopeDependencies会把triggerFields作用域化并把componentProps、rules、required等回调包装成能拿到FormSchemaContext的形式schema.ts。updateSchema在FormApi里递归处理 children提交转换由表单级codec统一完成。小屏幕展示form-field-array在大屏下按表格式 grid 展示在小屏下每行转为纵向堆叠并显示每个 child 的 label。业务侧通常不需要额外处理移动端布局。模板中表头与行体都使用sm:grid断点小屏时每行以p-3纵向布局并渲染sm:hidden的字段 label大屏时按gridTemplateColumns序号列3rem 各字段minmax(0, 1fr) 操作列4rem排列form-field-array.vue。兼容旧写法旧写法仍可用{ component: VbenFormFieldArray, fieldName: contacts, componentProps: { schema: [...], }, }新代码推荐{ type: array, fieldName: contacts, children: [...], }两种写法会被getFormArraySchemaChildren统一识别——它优先读取children否则回退到componentProps.schemaschema.ts因此存量代码无需改造即可平滑迁移到新的声明式写法。【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. Its fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表