Appearance
子应用菜单翻译与图标方案
状态:已实现(代码完成,待部署) 日期:2026-07-03 目标:子应用新增/修改菜单时,翻译与图标随子应用自己的发布走,主应用零改动。
现状与耦合点
菜单数据本身已经是动态的:
子应用 vite.config UploadPlugin(menus) ──发版──▶ nuwa ──▶ OSS hiapi-cloud/{project}/{version}/menus.json
平台安装/升级应用 ──▶ cloud-public AppStoreLogic.reloadMenus 拉取入库(hiapi_core_system_menu)
主应用 LayoutAside ──▶ GET /cloud-api/{accountType}/system-basic/menu/tree 渲染但两样东西耦合在主应用:
- 翻译:
menuLabel用te(langCode) ? t(langCode) : label,langCode 必须存在于主应用src/i18n/*/*.json(vem 的菜单翻译现在就写在主应用i18n/*/menu.json里) - 图标:
<component :is="menu.icon"/>依赖主应用全局注册的组件名,自定义 SVG(VemAlarm、VemRestock 等)必须放主应用assets/svg/
设计原则
- 一个子项目一个语言包:翻译集中在子应用自己的
src/i18n/<lang>/*.json(与主应用同构),菜单翻译和页面翻译同一套,新增语言 = 加语言文件夹,全线动作一致 - 构建期强校验防疏忽:发布时校验「每个菜单 langCode 在每个语言下都有翻译」,缺失直接发布失败
- 图标随子应用仓库管理:svg 文件放子应用
src/assets/svg/,发布时自动收集上报 - 不动数据库:i18n/icons 走 OSS 旁路(与 menus.json 同级),菜单表结构零改动
数据流(新增部分)
UploadPlugin 发版时:
menus[].langCode ──▶ 从 src/i18n/<lang>/ 抽取 ──校验──▶ form: i18n = {lang: {key: text}}
menus[].icon ──▶ 若 src/assets/svg/{icon}.svg 存在 ──▶ form: icons = {name: "<svg .../>"}
micro 选项 ──▶ form: micro = {name, activeRule}
nuwa UploadAdminRelease 上传 OSS(版本目录 + latest 双份):
hiapi-cloud/{project}/{version}/menus.json | i18n.json | icons.json | {jsFile} | {cssFile}
hiapi-cloud/{project}/{version}/admin.json ← 汇总清单(见下)
hiapi-cloud/{project}/... ← latest 副本(向后兼容)
版本目录对象设 Cache-Control: public, max-age=31536000, immutable
latest 副本设 Cache-Control: no-cache
admin.json 结构:
{
"name": "HiapiCloudVemAdmin", // qiankun 应用名
"activeRule": "/micro-apps/vem", // qiankun 激活规则
"version": "0.0.3",
"scripts": ["https://oss/.../0.0.3/hiapi-cloud-vem-admin.umd.js"],
"styles": ["https://oss/.../0.0.3/vem-admin.css"],
"i18n": "https://oss/.../0.0.3/i18n.json",
"icons": "https://oss/.../0.0.3/icons.json",
"menus": "https://oss/.../0.0.3/menus.json"
}
cloud-public 平台安装/升级(AppStoreLogic.loadApp):
拉取 {adminVersion}/admin.json ──▶ 存 CloudApplication.adminEntry(新增 TEXT 列)
cloud-public 新增公开接口(无需登录):
GET /cloud-api/public/micro-apps
→ [{appId, project, version, name, activeRule, scripts, styles, i18n, icons}]
主应用启动时:
fetch /cloud-api/public/micro-apps
├─▶ registerMicroApps(动态清单 ∪ 静态兜底,动态优先) + start()
├─▶ 各 app fetch i18n.json → i18n.global.mergeLocaleMessage(lang, pack)
└─▶ 各 app fetch icons.json → 注入 iconRegistry(响应式 Map)主应用渲染改动
menuLabel不变:mergeLocaleMessage 后te(langCode)直接命中;pack 拉取失败时兜底显示label- 新增
MenuIcon.vue,三形态渲染,替换 LayoutAside 中所有<component :is="menu.icon"/>:- iconRegistry 命中(子应用上报的 SVG)→ 内联渲染(继承 currentColor)
http(s)开头 →<img>- 其它 →
<component :is>(Element Plus 图标 / 主应用自有 SVG 组件,现状不变)
子应用接入约定(以 vem-admin 为例)
ts
UploadPlugin({
...,
micro: {name: 'HiapiCloudVemAdmin', activeRule: '/micro-apps/vem'},
// i18nDir 默认 'src/i18n',iconDir 默认 'src/assets/svg',可省略
menus: [...]
})- 菜单 langCode 的翻译写进子应用
src/i18n/<lang>/menu.json(缺失 → 发布报错) - 菜单用到的自定义图标 svg 放子应用
src/assets/svg/{icon}.svg;Element Plus 图标名无需任何文件 - langCode 命名保持
hiapi_cloud_{project}_*前缀约定,避免 merge 进主应用时撞 key
迁移与兼容
- vem 菜单翻译从主应用
i18n/*/menu.json复制到 vem-adminsrc/i18n/*/menu.json;vem 专属图标 svg 复制到 vem-admin。主应用中的旧副本暂保留(兜底),待 vem 发版 + 平台升级验证后删除 - 未上报 i18n/icons 的旧应用(如 design):接口里无对应字段,主应用继续走静态兜底注册 + 现有渲染路径,行为不变
- adminEntry 为空的应用不出现在 /public/micro-apps,主应用静态兜底列表覆盖(design、未重发版的 vem)
改动清单
| 位置 | 改动 |
|---|---|
| hiapi-cloud-components-upload | options 增 i18nDir/iconDir/micro;构建后抽取+校验+上报 |
| hiapi-cloud-nuwa | AdminRelease 接收 i18n/icons/micro;上传版本化+latest 双份;生成 admin.json;uploader 支持 Cache-Control |
| hiapi-cloud-public | CloudApplication 增 adminEntry 列;loadApp 拉 admin.json;新增 GET /public/micro-apps |
| hiapi-cloud-admin-ts | 动态注册微应用;merge 语言包;iconRegistry + MenuIcon.vue;LayoutAside 替换图标渲染 |
| vem-admin | UploadPlugin 配置 micro;i18n 增 menu.json;拷贝菜单 svg;版本 0.0.3 |
已完成的代码改动(2026-07-03)
- hiapi-cloud-components-upload → 0.2.0:新增
micro/i18nDir/iconDir选项;extractMenuI18n(缺翻译抛错)、collectMenuIcons(内联 svg);发版时 form 追加i18n/icons/micro。已验证:vem 20 条菜单 langCode 在 zh-cn/en 全部命中,内联图标 = VemAlarm/VemRestock/VemMap/VemReport。 - hiapi-cloud-nuwa:
AdminReleaseInput增 I18n/Icons/Micro;UploadAdminRelease改为版本目录(强缓存)+ latest(no-cache)双写,生成admin.json;ObjectUploader增WithCacheControl。go build ./...通过。 - hiapi-cloud-public:
CloudApplication增adminEntry(text 列);AppStoreLogic.reloadAdminEntry安装/升级时拉 admin.json 存入;新增GET /public/micro-apps。四模块mvn -o compile通过。 - hiapi-cloud-admin-ts:
plugins导出i18nInstance;新增utils/icon-registry.ts、components/MenuIcon.vue、utils/micro-apps.ts(动态注册+merge i18n+load icons);main.ts挂window.Vue/window.ElementPlus并改用bootstrapMicroApps;LayoutAside图标改MenuIcon。vue-tsc通过。 - vem-admin:vite.config 加
micro+ external;src/main.ts删 element-plus CSS import;index.html加 dev-only CDN;新增i18n/{zh-cn,en}/menu.json;拷入 4 个 Vem* svg;依赖临时指向本地 0.2.0 插件。vue-tsc -b通过。
翻译/图标怎样才会生效(关键)
翻译要从 vem 仓库「走到」主应用页面,链路是 vem 构建抽取 → nuwa 写 OSS → 主应用 fetch 合并,缺一不可。主应用侧改用 OSS 根目录 latest 地址(hiapi-cloud/hiapi-cloud-vem/i18n.json、icons.json)作为静态兜底,所以不依赖 cloud-public 的 /public/micro-apps 与数据库也能加载 vem 的翻译/图标。
让 vem 菜单翻译生效的最小步骤(三件,缺一不可):
- 部署 nuwa(新版):否则它收不到 UploadPlugin 传的
i18n/icons字段,OSS 上不会有i18n.json。 - 重新构建发布 vem-admin(
pnpm build,disable:false):把 i18n.json/icons.json 上传到 OSS。 - 重新部署主应用:
bootstrapMicroApps会 fetch OSS 的 i18n.json 并mergeLocaleMessage,i18nVersion触发侧边栏重渲染。
⚠️ 主应用
i18n/*/menu.json里的 vem 菜单翻译副本已被移除。在上面三步完成前,vem 菜单会显示中文原始 label(中英文都是),这是预期现象,不是 bug。若想在部署前保持旧翻译可用,可临时把 vem 的 langCode 恢复到主应用 menu.json。
其它部署前置
- 发布 UploadPlugin 0.2.0:当前 vem-admin 依赖临时改为
file:本地路径以便验证。正式上线应npm publish发布 0.2.0,并把各子应用依赖改回版本范围^0.2.0(store-web、public-web 等用到本插件的项目同步)。 - cloud-public 数据库迁移(仅"动态注册新子应用"需要,翻译/图标不依赖它):
hiapi_core_application表新增admin_entry列(TEXT)。若 Hibernateddl-auto非 update/create,手动ALTER TABLE hiapi_core_application ADD COLUMN admin_entry TEXT;。装机/升级触发 reloadAdminEntry 写入后,/public/micro-apps才会输出该应用。 - OSS CORS:主应用浏览器
fetch()OSS 的 i18n.json/icons.json 需要 CORS。qiankun 现在就是 fetch 方式加载 umd.js 的(已能工作),说明 OSS 对管理后台域名的 CORS 已开,故无需额外配置。 - 收尾:全部上线验证后,已可从主应用
assets/svg删除仅 vem 使用的 Vem* 图标(翻译副本已删)。