5.2 KiB
Array / Object 字段类型 —— 设计草案(暂未实现)
状态:搁置待需求。当前没有字段需要它(powers 已用自定义类型
powers解决,dropList 用自定义 type + Mixed 解决)。 等出现「既要结构化数组/对象、又要框架级递归校验 + 自动表单渲染」的真实字段时,再按本文档实现。
背景:为什么有这份文档
触发点:powers 字段类型错误
角色权限字段 powers 实际存的是权限 key 数组(string[]),但 roleFields 里错标成 type: "string"。在「字段前置验证」(ShrimpTableModule.validateField)加上类型校验后,String 分支 typeof value !== 'string' 把数组判为类型错误。
已采用的解法(v0.6.2,已上线)
把 powers 改成自定义类型 type: "powers"。原理:validateField 的类型校验是 switch-case,只认 string/number/boolean/date 四个内置类型,任何自定义 type 不匹配任何 case → 自动跳过类型校验;且未注册 typeHandler → 无 reqFilter/resFilter → 数组原样存取,不破坏前端权限树。required/rules 仍生效。
这是最小解,powers 问题已闭环。本文档的 array/object 不是为 powers——powers 不需要它。
何时才该实现本设计
满足以下条件之一再做,否则属于过度设计:
- 出现一个字段,值是结构化数组(如
[{itemId,count,weight}]),且希望框架级递归校验 + 自动按子字段渲染表单(而非像 dropList 那样手写专属前端组件 + 跳过校验)。 - 多个字段重复出现「对象数组」形态,自定义类型方案的手写成本变得不划算。
判别:需求会把"要不要去重 / 要不要嵌套 / 摘要怎么显示"这些问题自然定下来,比现在凭空猜更准。
设计要点(实现时参考)
与 multiple 的关系(关键,A 方案)
multiple 与 array 是两个正交维度,不合并:
multiple:选择基数修饰符,依附 select/relation,含义"从固定候选集里选几个",集合语义(不可重复)。保持现状不动。array:值形态类型,开放列表,元素类型由item定,可嵌套。- 判别标准:值来自固定选项集→ select/relation + multiple;值是开放列表→ array。
后端 shrimp-server
1. IShrimpType.ts
// ShrimpFieldTypes 增加
Array: "array", Object: "object"
// IFieldConfig 增加(复用 IFieldConfig 自身递归 —— tab 的 tabs[].fields 已有先例)
item?: IFieldConfig // array 元素的类型定义
fields?: IFieldConfig[] // object 的子字段定义
unique?: boolean // array 是否去重(集合语义,默认 false)
2. ShrimpTableModule.validateField 递归校验
array分支:值必须是数组 → 对每个元素递归validateField(field.item, 元素, item);unique:true查重报错;min/max 已支持数组长度。object分支:值必须是对象 → 对field.fields每个子字段递归validateField(子字段, value[子key], value)。- multiple 特殊处理:
multiple:true字段值是数组,但元素校验走字段自己的 options/relation;在类型 switch 里 multiple 字段跳过标量类型检查(防止 powers 那类问题复发)。
3. typeHandler
- array/object 不注册 resFilter(不同于 json 会
JSON.stringify)——结构原样进出,前端直接拿到结构化数据。
前端 shrimp-admin
字段渲染机制:约定式自动注册 —— components/fields/Field<X>.vue → 自动注册类型 <x>(首字母小写)。vueAdmin.getFieldComponent(moduleKey, type, key) 按 type 取组件,未知 type 兜底为 string。范本见 FieldTab.vue:用 getFieldComponent(childField.type) 递归渲染子字段。
1. components/fields/FieldArray.vue(新) 类型 array
- edit:列表渲染,每项
getFieldComponent(field.item.type)递归 + 增/删行。 - display:摘要(JSON 截断,或「N 项」)。
- props 标准接口:
{field, modelValue, mode:'display'|'edit'|'filter', disabled}。
2. components/fields/FieldObject.vue(新) 类型 object
- edit:按
field.fields逐个el-form-item+ 递归组件(≈ FieldTab 去掉 tab 选择层)。 - display:摘要。
递归天然支持嵌套:array 的 item 可以是 {type:"object",fields:[...]}(对象数组),object 的某字段可以是 {type:"array",...},任意层级。
powers 若迁移到 array(可选,非必须)
{ key:"powers", type:"array", item:{type:"string"}, unique:true }(集合去重)。但 powers 现用 "powers" 自定义类型已工作,无强需求迁移。
发版
实现并前后端验证(后端 tsc --noEmit、前端 vue-tsc/build)通过后,shrimp-server + shrimp-admin 一起升 0.7.0,业务项目引用同步更新。
关联
- 另一个已知待办:
field.validate(字段级自定义验证函数,IFieldConfig.validate)在IShrimpType.ts定义了,但prepareAddItem/prepareEditItem只调了handler.validate(typeHandler 级),field.validate从未被调用 —— 漏接,待补。