# 2026-09-17
## 修复:合作伙伴分类删除报「order_by 不能为空」
- 现象:`base/othercompany` 删除分类 → 「分类数据检查失败:order_by 不能为空」。
- 根因:`onCategoryDelete` 的守卫查询调 `pageDataApi`(`/data/page`)**漏传 `orderBy`**;
后端 `DataService.page` 用 `getRequiredString(params,"order_by")`,`DbUtils.buildPageOrderBy`
对空值抛「order_by 不能为空」。`/data/loaddata` 的 order_by 是可选的,`/data/page` 是必填
(分页需要确定性排序),所以同类守卫在 `views/system/user` 用 loaddata 没问题、这里用 page 就炸。
- 改动:`src/views/base/othercompany/index.vue` 守卫查询加 `orderBy: 'b_id ASC'`;
`tests/views/othercompany.spec.js` 新增回归用例断言 pageDataApi 的入参(含 orderBy)。
全套 10 个用例通过;oxlint 干净(oxfmt 全仓库既有 format issues,未动)。
- 已核对仓库内另 3 处 `pageDataApi` 调用(FileListPanel / useLookupOptions / FmsModuleListPage)都传了 orderBy。
## 新增:UI 组件库全局配置(uiConfig)
- 背景:组件库此前**没有任何全局配置层**(`ui/index.js` 只是具名导出,无 `install`/`app.use`;
组件间只有 form/form-group/radio-group/tree/dropdown 五个局部 provide)。Form 的
`layout` 等默认值硬编码在 prop 里,想改只能逐处传。
- 新增 `fms-vue/src/components/ui/config.js`:`uiConfig`(reactive)+ `setUIConfig(patch)`(按组件维度浅合并)
+ `resetUIConfig()`;三者也从 `@/components/ui` 具名导出。
- 回落顺序统一为 **自身 prop > 全局配置 > 组件内置默认**。凡要支持全局覆盖的 prop,
`default` 必须改成 `undefined` 且 validator 放行 `undefined`(Vue 会校验 default 值)。
- Form(`form/form.vue`):`layout`/`labelAlign`/`labelWidth`/`validateTrigger` 改为经
`resolvedXxx` computed 回落,provide 下发的是**回落后的最终值**,FormItem 不用再管全局配置;
FormItem 独立使用(无 Form 包裹)时也读一次 `uiConfig`。
- 语言联动写在 `src/App.vue`:`watch(() => i18nStore.locale)` → `layout = /^zh/i.test(locale) ? 'horizontal' : 'vertical'`
(中文横排、其他语言竖排),`immediate: true` 覆盖持久化 locale 的刷新场景。
- 存量 8 处 `layout="vertical"` 已全部删除(login、base/othercompany、system/user、
menu-management/MenuBasicPanel、module-management 的 Basic/AutoCode/FormPreview、FmsModuleEditModal),
统一跟随语言;`ui/demo/form.vue` 保留(它是组件展示页,故意演示各布局)。
风险点:FmsModuleEditModal(columns=24 网格)与 FormPreview(columns=48)在中文下变横排可能拥挤,待目测。
- 测试:`tests/components/form.spec.js` 新增 5 例(全局默认、prop 覆盖、局部合并、validateTrigger、独立 FormItem),
15 例全通过;oxlint `--deny-warnings` 干净;`vite build` 通过。
- 已知非本次引入:`tests/views/login.spec.js` 套件级失败(favicon.svg URL 解析,环境问题,收集 0 用例);
`module-list-components.spec.js` 的 `.filter-joiner`/`.filter-row` 断言是过期基线。
## 修复:顶栏语言切换不生效(i18n 只有 locale,没有译文)
- 根因:`setMessages` 在 `src/` 下**从无调用点** → `messagesRef` 永远为空,`t(key, fallback)` 恒返回中文兜底;
顶栏按钮只是把 `locale` 在 zh-CN/en-US 间翻一下,界面毫无变化。
另查库:`s_i18n_type` 有 zh-CN(默认)/en-US;`s_i18n` **134 行全是 zh-CN,en-US 为 0 行**。
- 改动:
- `stores/i18n.js` 新增 `loadMessages(locale, force)`:查 `s_i18n`(`b_canuse = 1 and b_locale = N'..'`,
`sqlQuote` 转义单引号),按 locale 缓存 + 并发 Promise 复用 + 失败静默;`applyMessages` 只认**当前** locale
(防止请求回来时语言已被切走导致串台),合并顺序 **前端内置 < 库表译文**;`clearMessages` 顺带清缓存。
- 新增 `src/i18n/builtin-messages.js`:内置 en-US 的 `common.*`(45 条,代码里已用 `t('common.x','中文')` 约定)。
中文不内置(fallback 即中文)。库里登记同名 key 可覆盖内置。
- `App.vue` 的 locale watcher 增加 `i18nStore.loadMessages(locale)`;`router/index.js` 守卫在
`loadLanguages()` 后再 `loadMessages()` 兜首次进入。
- 顶栏:按钮改为 Dropdown,语种取自 `s_i18n_type` 清单(不再硬编码互切),当前项用 `shortcut: '✓'` 标出
(Dropdown item 没有 active 态)。
- 测试新增 12 例:`tests/stores/i18n-messages.spec.js`(7)、`tests/components/app-topbar-language.spec.js`(3)、
`tests/unit/app-locale-sync.spec.js`(2)。全量 497 通过 / 13 失败(**均为既有基线**:
login 套件 + router-auth 3 例都是 `favicon.svg` 环境问题——`views/login/index.vue:98` 的 ``;
其余 9 例是查询面板改版后的过期断言)。
- 注:`vite build` 冒烟本次被用户拒绝执行(含 find -delete),改用 vitest 挂载 App/AppTopbar 验证编译与行为。
## 查询区改用 Form/FormItem(跟随语言布局)
- 现象:切中文后查询条件区仍是上下结构。原因:`FmsQueryToolbar.vue` **没用 Form**,是自己写的
`