From ad5f7f18f545f7191514e4dd63668d1505be4c25 Mon Sep 17 00:00:00 2001 From: wc24 Date: Mon, 29 Jun 2026 21:47:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(export):=20=E5=8E=BB=E6=8E=89=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E5=8F=AF=E9=80=86=E5=BA=8F=E5=88=97=E5=8C=96(resLabel?= =?UTF-8?q?=20+=20export=20stringify)=EF=BC=8C=E5=BA=8F=E5=88=97=E5=8C=96?= =?UTF-8?q?=E4=BA=A4=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit export 只返回原始数据;结构化字段的字符串化交前端(生成 xlsx 时)。 删: tab resLabel、IFieldTypeHandler.resLabel、processResItem 的 _label 段、 export() 的 stringify 折叠、IFieldConfig.format/stringify。保留 tab reqFilter(import parse) + IFieldConfig.parse。彻底清掉 tab 显示搬前端后那个 _label 遗留。 --- package.json | 2 +- src/IShrimpType.ts | 8 +------- src/Shrimp/ShrimpTableModule.ts | 21 +++------------------ 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 591c861..7ea3721 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shrimp-server", - "version": "0.6.9", + "version": "0.6.10", "description": "Shrimp 框架后端 — 声明式字段驱动的 Node.js 管理后台框架(Express + Mongoose + JWT)", "main": "dist/main/index.js", "types": "dist/main/index.d.ts", diff --git a/src/IShrimpType.ts b/src/IShrimpType.ts index 341f7d1..af6903d 100644 --- a/src/IShrimpType.ts +++ b/src/IShrimpType.ts @@ -248,8 +248,6 @@ export interface IFieldTypeHandler { reqFilter?: (value: any, field: IFieldConfig, item: Record) => any | Promise /** 响应过滤:数据库取出 → 返回前端前转换(如密码脱敏为 ***、日期格式化) */ resFilter?: (value: any, field: IFieldConfig, item: Record) => any - /** 响应附带的展示文本:返回值写入 `${key}_label`,本体不变。用于结构化字段(如 tab)给人看的字符串,前端表格直接显示,不必自己序列化 */ - resLabel?: (value: any, field: IFieldConfig, item: Record) => string /** 验证:保存前校验,返回错误 ILabel 或 null(可直接返回字面字符串、{t}、{tc} 等) */ validate?: (value: any, field: IFieldConfig, item: Record) => ILabel | null | Promise /** 新增时的默认值(静态值或工厂函数) */ @@ -278,11 +276,7 @@ export interface IFieldConfig { rules?: IFieldRule[] // 通用验证规则(前后端共用) /** tab 类型专用:每个 type 对应的子字段配置 */ tabs?: { key: any; label: ILabel; fields: IFieldConfig[] }[] - /** tab 类型专用:展示文本(表格列 `${key}_label`),给人看、可有损好看,默认 JSON.stringify */ - format?: (value: any) => string - /** tab 类型专用:export 序列化,须与 parse 可逆(机器往返),默认 JSON.stringify */ - stringify?: (value: any) => string - /** tab 类型专用:import 反序列化,须与 stringify 可逆,默认 JSON.parse */ + /** tab 类型专用:import 反序列化(把字符串还原成对象,如 xlsx 导入),默认 JSON.parse */ parse?: (text: string) => any } diff --git a/src/Shrimp/ShrimpTableModule.ts b/src/Shrimp/ShrimpTableModule.ts index 3d52eb7..1779000 100644 --- a/src/Shrimp/ShrimpTableModule.ts +++ b/src/Shrimp/ShrimpTableModule.ts @@ -60,8 +60,6 @@ export const defaultTypeHandlers: Record = { } return value }, - // 本体原样下发对象(不再 stringify);展示文本走 _label,用 format(给人看,可有损) - resLabel: (value, field) => (field.format ?? JSON.stringify)(value), }, } @@ -221,9 +219,6 @@ export class ShrimpTableModule = 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 } @@ -424,19 +419,9 @@ export class ShrimpTableModule = ITableCrudProto delete shrimpQuery.limit const result = await this.dataSource.list(shrimpQuery) result.list = await this.prepareResponseItems(result.list) - // 导出是机器往返的边界:结构化字段(如 tab)用可逆的 stringify 序列化(默认 JSON), - // 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] !== undefined && typeof row[f.key] === "object") { - row[f.key] = (f.stringify ?? JSON.stringify)(row[f.key]) - } - delete row[`${f.key}_label`] - } - return row - }) + // export 只取全量原始数据,不做序列化;结构化字段(如 tab)的字符串化交前端(生成 xlsx 时)。 + // import 端如传字符串,由各类型 reqFilter(如 tab 的 parse)还原。 + return result.list.map(item => this.filterHidden(item)) } async import(data?: T[], ownerId?: string): Promise { if (!data || data.length === 0) return