现象
Console「新建共享规则」(sys_sharing_rule) 弹窗有两个问题:
- 部分文案未国际化 — 字段标签全是中文(来自
plugin-sharing 的翻译包),但控件内部文案是硬编码英文:Select an object、Select an object first.、Select a user、Search…、No matches。 - 「接收方」永远显示
No matches — 无论接收方类型选什么,下拉里一个候选都没有,规则无法创建。
根因
A. 三个自定义 widget 从未接入 i18n(objectui)
sys_sharing_rule 用 widget: 指定了三个自定义控件(packages/plugins/plugin-sharing/src/objects/sys-sharing-rule.object.ts):object-ref / filter-condition / recipient-picker。实现都在 objectui packages/fields/src/widgets/,全部硬编码英文字面量:
ObjectRefField.tsx — Loading objects… / Select an object / Search objects… / No objects foundRecipientPickerField.tsx — Select a recipient type first. / `Select a ${type}` / Search… / No matches / Loading…FilterConditionField.tsx — Select an object first. / All records / Invalid JSON — … / Edit as JSON / Use visual builder
同目录下已有 useFieldTranslation(LookupField / SelectField / RecordPickerDialog / CapabilityMultiSelectField 都在用),这三个是漏网的。
另外 RecipientPickerField 把枚举值拼进英文句子(Select a ${recipientType.replace(/_/g,' ')}),这种写法天生不可翻译,必须改成按 type 取键。
B. 字符串 $orderby 被逐字符展开(objectui)— 接收方为空的直接原因
RecipientPickerField.tsx 调用:
awaitdataSource.find(mapping.object,{$top: 500,$orderby: 'name asc'})但 QueryParams['$orderby'] 的类型是 Record<string,'asc'|'desc'> | string[] | Array<{field,order}>(packages/types/src/data.ts),不含裸字符串。ApiDataSource.queryParamsToRecord(packages/core/src/adapters/ApiDataSource.ts)在 else 分支对字符串做 Object.entries,逐字符展开成:
$orderby=0 n,1 a,2 m,3 e,4 ,5 a,6 s,7 c
服务端按不存在的列排序 → 返回空数组 → widget 渲染 emptyText='No matches'。与权限、与 sys_user 数据无关。
同一个坑还波及 plugin-grid/src/ObjectGrid.tsx(三处把 sort 写成字符串赋给 $orderby),视图里用字符串 sort 会静默空表。
C. 未知排序列被静默吞成空结果(framework)— 放大器
packages/plugins/driver-sql/src/sql-driver.ts 的 find() 捕获 no such column 后,只有 query.fields(SELECT 投影)那条路会重试;ORDER BY 引起的同一个错误落到 else { return []; },静默返回空页。count 走另一条 SQL,所以 total 仍是真实值 —— UI 上表现为「有数据但一条都不显示」。和 #2926 修掉的 $ 参数静默变过滤器是同一族 footgun。
D. zh-CN 翻译包相对源码过期(framework)
packages/plugins/plugin-sharing/src/translations/zh-CN.objects.generated.ts:recipient_type.help 仍写 department(枚举里是 business_unit),recipient_id.help 仍写「队列名」(queue 已按 ADR-0078 移除)。同文件的 en 版本是新的。
复现(本地 showcase + sqlite,--seed-admin)
GET /api/v1/data/sys_user?$top=5&$orderby=name asc → records 3, total 3 ✅
GET /api/v1/data/sys_user?$top=5&$orderby=0 n,1 a,2 m,... → records 0, total 3 ❌ HTTP 200,不报错
GET /api/v1/data/sys_user?$top=5&$orderby=nosuchfield asc → records 0, total 3 ❌ 同上
第二行正是 widget 实际发出的请求。
修复范围
objectui
packages/core/src/adapters/ApiDataSource.ts — $orderby 归一化增加字符串直通分支(服务端 metadata-protocol 本来就能解析 'name asc')packages/types/src/data.ts — $orderby 联合类型补 stringpackages/fields/src/widgets/{ObjectRefField,RecipientPickerField,FilterConditionField}.tsx — 接入 useFieldTranslation,接收方占位按 type 取键packages/fields/src/widgets/useFieldTranslation.ts + packages/i18n/src/locales/*.ts — 补齐各语言键
framework
packages/plugins/driver-sql/src/sql-driver.ts — 未知 ORDER BY 列不再静默返回空(降级为不排序,或明确报错)packages/plugins/plugin-sharing/src/translations/zh-CN.objects.generated.ts — 对齐源码
验收
在 framework showcase (examples/app-showcase) 的 Console 里打开「新建共享规则」:
- 控件内文案随界面语言切换,中文界面无残留英文
- 接收方类型选「用户 / 团队 / 业务单元 / 岗位」时,下拉都能列出候选并可选中保存
现象
Console「新建共享规则」(
sys_sharing_rule) 弹窗有两个问题:plugin-sharing的翻译包),但控件内部文案是硬编码英文:Select an object、Select an object first.、Select a user、Search…、No matches。No matches— 无论接收方类型选什么,下拉里一个候选都没有,规则无法创建。根因
A. 三个自定义 widget 从未接入 i18n(objectui)
sys_sharing_rule用widget:指定了三个自定义控件(packages/plugins/plugin-sharing/src/objects/sys-sharing-rule.object.ts):object-ref/filter-condition/recipient-picker。实现都在 objectuipackages/fields/src/widgets/,全部硬编码英文字面量:ObjectRefField.tsx—Loading objects…/Select an object/Search objects…/No objects foundRecipientPickerField.tsx—Select a recipient type first./`Select a ${type}`/Search…/No matches/Loading…FilterConditionField.tsx—Select an object first./All records/Invalid JSON — …/Edit as JSON/Use visual builder同目录下已有
useFieldTranslation(LookupField/SelectField/RecordPickerDialog/CapabilityMultiSelectField都在用),这三个是漏网的。另外
RecipientPickerField把枚举值拼进英文句子(Select a ${recipientType.replace(/_/g,' ')}),这种写法天生不可翻译,必须改成按 type 取键。B. 字符串
$orderby被逐字符展开(objectui)— 接收方为空的直接原因RecipientPickerField.tsx调用:但
QueryParams['$orderby']的类型是Record<string,'asc'|'desc'> | string[] | Array<{field,order}>(packages/types/src/data.ts),不含裸字符串。ApiDataSource.queryParamsToRecord(packages/core/src/adapters/ApiDataSource.ts)在 else 分支对字符串做Object.entries,逐字符展开成:服务端按不存在的列排序 → 返回空数组 → widget 渲染
emptyText='No matches'。与权限、与sys_user数据无关。同一个坑还波及
plugin-grid/src/ObjectGrid.tsx(三处把 sort 写成字符串赋给$orderby),视图里用字符串 sort 会静默空表。C. 未知排序列被静默吞成空结果(framework)— 放大器
packages/plugins/driver-sql/src/sql-driver.ts的find()捕获no such column后,只有query.fields(SELECT 投影)那条路会重试;ORDER BY 引起的同一个错误落到else { return []; },静默返回空页。count 走另一条 SQL,所以total仍是真实值 —— UI 上表现为「有数据但一条都不显示」。和 #2926 修掉的$参数静默变过滤器是同一族 footgun。D. zh-CN 翻译包相对源码过期(framework)
packages/plugins/plugin-sharing/src/translations/zh-CN.objects.generated.ts:recipient_type.help仍写department(枚举里是business_unit),recipient_id.help仍写「队列名」(queue已按 ADR-0078 移除)。同文件的 en 版本是新的。复现(本地 showcase + sqlite,
--seed-admin)第二行正是 widget 实际发出的请求。
修复范围
objectui
packages/core/src/adapters/ApiDataSource.ts—$orderby归一化增加字符串直通分支(服务端metadata-protocol本来就能解析'name asc')packages/types/src/data.ts—$orderby联合类型补stringpackages/fields/src/widgets/{ObjectRefField,RecipientPickerField,FilterConditionField}.tsx— 接入useFieldTranslation,接收方占位按 type 取键packages/fields/src/widgets/useFieldTranslation.ts+packages/i18n/src/locales/*.ts— 补齐各语言键framework
packages/plugins/driver-sql/src/sql-driver.ts— 未知 ORDER BY 列不再静默返回空(降级为不排序,或明确报错)packages/plugins/plugin-sharing/src/translations/zh-CN.objects.generated.ts— 对齐源码验收
在 framework showcase (
examples/app-showcase) 的 Console 里打开「新建共享规则」: