feat(tab): 结构化字段端到端传对象,给人看的边界走 _label/stringify

tab 字段原把对象 JSON.stringify 成字符串下发(复用 json 的 resFilter),
导致前端 FieldTab 收到字符串:表格 display 显示 '-'、编辑回退默认值。

改为:
- 库/响应/编辑全程对象(去掉 tab 的 resFilter stringify)
- 新增 resLabel 钩子 → 写入 ${key}_label,承载给人看的字符串文本
- export 边界用 _label 文本替换对象列(可读且可被 import 的 parse 还原)
- 字段级可配 stringify/parse,默认 JSON
This commit is contained in:
2026-06-29 15:45:58 +08:00
parent f94a01e0a1
commit c5c06bee13
3 changed files with 26 additions and 11 deletions
+19 -10
View File
@@ -48,24 +48,20 @@ export const defaultTypeHandlers: Record<string, IFieldTypeHandler> = {
},
},
[ShrimpFieldTypes.Tab]: {
reqFilter: (value) => {
// tab 是结构化字段:库里/响应/编辑全程是对象,只在"给人看"的边界(import/export/label)走字符串。
reqFilter: (value, field) => {
if (value == null || value === "") return undefined
if (typeof value === "string") {
try {
return JSON.parse(value)
return (field.parse ?? JSON.parse)(value)
} catch {
throw new ShrimpError({ t: 'shrimp.invalidJson' })
}
}
return value
},
resFilter: (value) => {
if (value == null) return value
if (typeof value === "object") {
return JSON.stringify(value)
}
return value
},
// 本体原样下发对象(不再 stringify);展示文本走 _label
resLabel: (value, field) => (field.stringify ?? JSON.stringify)(value),
},
}
@@ -225,6 +221,9 @@ export class ShrimpTableModule<T, P extends ITableCrudProto<T> = ITableCrudProto
if (handler?.resFilter && result[field.key] !== undefined) {
result[field.key] = await handler.resFilter(result[field.key], field, result)
}
if (handler?.resLabel && result[field.key] !== undefined) {
result[`${field.key}_label`] = handler.resLabel(result[field.key], field, result)
}
}
return result
}
@@ -396,7 +395,17 @@ export class ShrimpTableModule<T, P extends ITableCrudProto<T> = ITableCrudProto
delete shrimpQuery.limit
const result = await this.dataSource.list(shrimpQuery)
result.list = await this.prepareResponseItems(result.list)
return result.list.map(item => this.filterHidden(item))
// 导出是"给人看"的边界:把带 resLabel 的结构化字段(如 tab)用其字符串文本替换对象本体,
// 列保持可读,且能被 import 的 reqFilter(parse)原样还原;去掉额外的 _label 列。
const labelFields = this.fields.filter(f => this.getTypeHandler(f)?.resLabel)
return result.list.map(item => {
const row = this.filterHidden(item)
for (const f of labelFields) {
if (row[`${f.key}_label`] !== undefined) row[f.key] = row[`${f.key}_label`]
delete row[`${f.key}_label`]
}
return row
})
}
async import(data?: T[], ownerId?: string): Promise<void> {
if (!data || data.length === 0) return