1. 重命名README中的文档链接为中文名称 2. 新增数据中台、质量管理、系统设置、设备管理、计划与生产5个业务路由模块 3. 新增CRUD页面迁移、Sass废弃修复、布局组件迁移、国际化规范、登录页面搬迁、变更日志等文档
15 KiB
header-aside 布局组件迁移报告
迁移日期:2026-05-28
迁移范围:src/layout/header-aside/
适用项目:MES-UI(基于 D2Admin)
一、迁移概览
| 项目 | 迁改前 | 迁改后 |
|---|---|---|
| 修改文件数 | — | 14 个 |
| 硬编码中文处数 | ~35 处 | 0 处 |
| 删除死代码 | — | 1 处(pageKeepAliveClean) |
| 新增 i18n key | 0 | 47 个(zh-chs/en/ja/zh-cht 各 47 个) |
| 语言包覆盖 | 仅 zh-chs + en | zh-chs / en / ja / zh-cht 四语言完整覆盖 |
二、执行逻辑判断结果
全部 16 个组件均在新项目中生效,无废弃组件
| 文件 | 状态 | 依赖 | 判断依据 |
|---|---|---|---|
layout.vue |
✅ 生效 | d2admin store 全模块 | 全局注册组件齐全 |
menu-side/index.js |
✅ 生效 | d2admin/menu, d2-scrollbar | 组件已全局注册 |
menu-header/index.js |
✅ 生效 | d2admin/menu + resize | throttle 正常 |
tabs/index.vue |
✅ 生效 | d2admin/page, sortablejs | sortablejs 已安装 |
header-search/index.vue |
✅ 生效 | 纯 emit | 无额外依赖 |
header-user/index.vue |
✅ 生效 | d2admin/user + account | store 模块存在 |
header-fullscreen/index.vue |
✅ 生效 | d2admin/fullscreen | store 模块存在 |
header-theme/index.vue |
✅ 生效 | d2admin/theme | store 模块存在 |
header-theme/list/index.vue |
✅ 生效 | d2admin/theme | store 模块存在 |
header-size/index.vue |
✅ 生效 | d2admin/size | store 模块存在 |
header-color/index.vue |
✅ 生效 | d2admin/color | store 模块存在 |
header-locales/index.vue |
✅ 生效 | localeMixin | mixin 正常 |
header-log/index.vue |
✅ 生效 | d2admin/log, 路由 log | log 路由存在 |
contextmenu/index.vue |
✅ 生效 | 纯 UI | 无额外依赖 |
panel-search/index.vue |
✅ 生效 | d2admin/search, fuse.js | fuse.js 已安装 |
locales/mixin.js |
✅ 生效 | $i18n | i18n 已初始化 |
三、各文件改动明细
3.1 模板硬编码 → $t() (共 11 个文件)
| 文件 | 改动项 | 旧值 | 新值 |
|---|---|---|---|
| header-user/index.vue | 用户问候 | `你好 ${info.name}` |
$t('page.layout.user.greeting', { name }) |
| 未登录占位 | '未登录' |
$t('page.layout.user.not_logged_in') |
|
| 登出按钮 | 注销 |
$t('page.layout.user.logout') |
|
| header-fullscreen/index.vue | 全屏 tooltip | '全屏' / '退出全屏' |
$t('page.layout.fullscreen.enter/exit') |
| header-theme/index.vue | 主题按钮 tooltip | content="主题" |
:content="$t('page.layout.theme.title')" |
| 弹框标题 | title="主题" |
:title="$t('page.layout.theme.title')" |
|
| header-theme/list/index.vue | 预览列标题 | label="预览" |
:label="$t('page.layout.theme.preview')" |
| 已激活按钮 | 已激活 |
$t('page.layout.theme.active') |
|
| 使用按钮 | 使用 |
$t('page.common.use') |
|
| header-size/index.vue | 尺寸选项 | '默认'/'中'/'小'/'最小' |
$t('page.layout.size.*') |
| 通知标题 | '提示' |
$t('page.layout.size.notification_title') |
|
| 通知内容 | 硬编码 HTML | $t('page.layout.size.notification_message') |
|
| header-log/index.vue | tooltip 文本 | 模板字符串拼接 | $t('page.layout.log.tooltip/no_log', {...}) |
| menu-side/index.js | 空菜单提示 | 没有侧栏菜单 |
this.$t('page.layout.menu.no_sidebar') |
| tabs/index.vue | Tab 默认名 | '未命名' |
$t('page.layout.tabs.unnamed') |
| 关闭操作文字 | 关闭左侧/右侧/其它/全部 |
$t('page.layout.tabs.close_*') |
|
| 刷新文字 | 刷新 |
$t('page.layout.tabs.refresh') |
|
| 错误提示 | '无效的操作' |
$t('page.layout.tabs.invalid_operation') |
|
| panel-search/index.vue | 搜索 placeholder | placeholder="搜索页面" |
:placeholder="$t('page.layout.search.placeholder')" |
| 快捷键提示 | 含 span 的散装文本 | v-html="$t('page.layout.search.tip', {...})" |
|
| mixin/menu.js | 临时菜单提示 | '临时菜单' |
$t('page.layout.menu.temp_menu') |
| libs/util.menu.js | 菜单 fallback | '未命名菜单' |
this.$t('page.layout.menu.unnamed_menu') |
| locales/mixin.js | 语言切换通知 | '当前语言:...'/'语言变更' |
$t('page.layout.locales.*') |
3.2 data() → computed() 转换(响应式 i18n)
| 文件 | 属性 | 原因 |
|---|---|---|
| header-size/index.vue | options → sizeOptions |
确保语言切换时下拉选项也跟随更新 |
| tabs/index.vue | contextmenuListIndex |
确保右键菜单 title 切换语言时跟随更新 |
contextmenuList |
同上 |
3.3 删除死代码
| 文件 | 删除项 | 原因 |
|---|---|---|
| header-size/index.vue | ...mapMutations({ pageKeepAliveClean }) |
引入后从未被调用 |
同步删除了 import { mapMutations } |
mapMutations 不再需要 |
单一用途,移除了整个 import |
四、新增 i18n Key 清单
所有 key 位于 page.layout.* 命名空间下,四语言同步添加:
page.layout.
├── user.greeting / not_logged_in / logout
├── fullscreen.enter / exit
├── theme.title / preview / active
├── size.default / medium / small / mini / notification_title / notification_message
├── log.no_log / tooltip / tooltip_zero
├── menu.no_sidebar / temp_menu / unnamed_menu
├── tabs.unnamed / refresh / close_left / close_right / close_other / close_all / invalid_operation
├── search.placeholder / tip
└── locales.changed / notification_title / preview_warning / preview_doc
加上 page.common.use,共计 48 个新 key × 4 语言 = 192 条翻译。
五、开发者注意事项
5.1 涉及 4 个语言包文件
⚠️ 所有修改的语言包
zh-chs.json / en.json / ja.json / zh-cht.json已通过JSON.parse()验证合法性。
5.2 JSX 渲染函数的特殊语法
menu-side/index.js 和 libs/util.menu.js 使用 JSX 渲染函数,$t() 调用语法为 { this.$t('...') }(单花括号),与 .vue 模板的 {{ }}(双花括号)不同。
5.3 v-html 使用处
panel-search/index.vue 的提示文本包含内联 <span class="panel-search__key"> 标签,必须使用 v-html 绑定。
5.4 data() → computed() 的 Reactivity
header-size 和 tabs 组件中原来在 data() 里的中文文本数组已经移到 computed,确保 $t() 是响应式的。未来如果要在 data() 中放翻译文本,必须使用 computed 或确保使用者是组件内部翻译。
六、回滚指南
如需回滚任一组件的改动:
- 布局组件 —
git checkout对应的组件文件即可,所有改动互相独立 - 语言包 — 回滚
src/locales/*.json,新增的page.layout命名空间不影响业务页面 - localeMixin — 回滚
src/locales/mixin.js
七、验证清单
| 验证项 | 方法 |
|---|---|
| JSON 合法性 | node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))" |
| 全量 JSON | 逐个检查 zh-chs/en/ja/zh-cht(已通过) |
| 语言切换 | 打开页面 → 切换中文/English/日本語/繁體中文 → 观察全部 header 文字是否跟随变化 |
| 搜索面板 | Ctrl+K 打开搜索面板 → 检查 placeholder + 快捷键提示 |
| 右键 Tab | 右键点击 Tab → 检查菜单文字 |
| 全屏切换 | 鼠标悬停全屏按钮 → 检查 tooltip |
| 主题弹框 | 打开主题弹框 → 检查标题 + "预览"/"已激活"/"使用" |
| 组件尺寸 | 切换组件尺寸 → 检查通知弹框 |
| 用户区 | 检查用户问候语 + 登出按钮 |
八、菜单 i18n 翻译方案(2026-05-28 追加)
8.1 问题背景
旧项目中,当用户切换语言(中文 → English)时,侧边栏菜单和顶部一级导航菜单的文字会跟随切换为英文。当前项目迁移后缺少这个能力,菜单文字始终为后端返回的单一语言文本。
8.2 方案设计
采用双重保障机制:
┌─ 方案 A:$t() 包装(客户端翻译)
语言切换 ──────────┤
└─ 方案 B:menuReload(重新拉取后端菜单)
方案 A:渲染层 $t() 包装
将菜单渲染节点中的所有 menu.title 包裹 $t()。
修改文件:
| 文件 | 改动 |
|---|---|
src/layout/header-aside/components/libs/util.menu.js |
elMenuItem 和 elSubmenu 中 menu.title → this.$t(menu.title) |
src/components/d2-module-index-menu/components/group.vue |
{{menu.title}} → {{ $t(menu.title) }}(2 处) |
src/components/d2-module-index-menu/components/item.vue |
{{menu.title}} → {{ $t(menu.title) }} |
工作原理:
后端返回 title 是 i18n key → $t() 返回对应语言的翻译 ✓
后端返回 title 是纯文本 → $t() 找不到 key,返回原文本(fallback)✓
无需修改后端接口即可兼容两种模式。
方案 B:语言切换时重新拉取菜单
在 src/store/modules/d2admin/modules/menu.js 新增 menuReload action:
async menuReload ({ state, dispatch }) {
// 1. 清空 localStorage 缓存
await dispatch('d2admin/db/set', {
dbName: 'database',
path: '$menu.sourceData',
value: [],
user: true
}, { root: true })
state.sourceData = []
// 2. 重新从后端拉取(后端根据当前语言返回对应文本)
await dispatch('sourceDataLoad')
}
在 src/locales/mixin.js 的 onChangeLocale 末尾追加:
this.$store.dispatch('d2admin/menu/menuReload')
8.3 工作流程
用户点击切换语言
│
├─ this.$i18n.locale = 'en' → 客户端组件重新渲染
│ $t(menu.title) 即时生效 ✓
│
├─ $store.dispatch('menuReload') → 清缓存 → 调后端
│ getMenuAll() 返回英文菜单
│ menu.install() 重建 header/aside
│ 侧栏 + 顶栏全部更新 ✓
│
└─ $notify({ title: 'Language Changed' }) → 通知用户
注意:如果后端
getMenuAll()接口不支持根据语言返回不同内容,方案 B 不会生效,此时仅依赖方案 A($t()包装)。建议后续后端添加多语言菜单数据返回。
8.4 修改清单
| 文件 | 操作 | 行数 |
|---|---|---|
src/layout/header-aside/components/libs/util.menu.js |
menu.title → this.$t(menu.title) |
2 处 |
src/components/d2-module-index-menu/components/group.vue |
{{menu.title}} → {{ $t(menu.title) }} |
2 处 |
src/components/d2-module-index-menu/components/item.vue |
{{menu.title}} → {{ $t(menu.title) }} |
1 处 |
src/store/modules/d2admin/modules/menu.js |
新增 menuReload action |
+15 行 |
src/locales/mixin.js |
onChangeLocale 末尾追加 menuReload |
+1 行 |
8.5 验证方法
- 启动项目,确认当前展示的菜单文字正确(方案 A 生效)
- 切换语言,观察侧边栏和顶部导航栏文字是否更新(方案 A + B 双重生效)
- 如果切换语言后菜单文字不变,检查:
- 后端
getMenuAll()是否支持多语言 - 菜单数据中的
name是否为可翻译的 i18n key 路径
- 后端
九、一级模块首页复用方案(2026-05-28 追加)
9.1 问题背景
旧项目中,每个一级模块(如 planning_production)都有一个 index/index.vue:
<template>
<d2-container>
<page-navi class="cs-m" v-model="root"/>
</d2-container>
</template>
<script>
export default {
name: 'produce-index',
components: {
PageNavi: () => import('@/layout/header-aside/components/header-navi')
},
data() { return { root: '/planning_production' } }
}
</script>
这些文件结构高度重合,仅 root 值不同。随着模块增多(对照表中约 20+ 个一级模块),需要逐个创建这些文件。
9.2 方案:通用 module-index.vue
创建一个共享的路由级组件,动态读取路由 meta 中的 root 来展示模块首页。
文件:src/views/system/function/module-index.vue(已创建)
核心逻辑:
<template>
<d2-container class="module-index-page">
<template #header>
<d2-module-index-banner
:title="$t(headerMenu.title)"
:sub-title="$t(headerMenu.title)" />
</template>
<d2-module-index-menu :menu="asideMenu" />
</d2-container>
</template>
<script>
export default {
computed: {
rootPath () { return this.$route.meta.root || this.$route.path },
headerMenu () {
return this.$store.state.d2admin.menu.header.find(
m => m.path === this.rootPath
) || { title: this.rootPath }
},
asideMenu () {
return this.$store.state.d2admin.menu.aside.find(
m => m.path === this.rootPath
) || { children: [] }
}
}
}
</script>
9.3 使用方式
在路由模块中,为每个一级模块添加一个指向同一组件的空路径路由:
路由示例(src/router/modules/production-master-data.js):
export default {
path: '/production_configuration',
component: layoutHeaderAside,
children: (pre => [
// ★ 模块首页 — 所有一级模块共用同一个组件
{
path: '',
name: `${pre}index`,
meta: { ...meta, title: '生产配置', root: '/production_configuration' },
component: _import('system/function/module-index')
},
// 三级模块页面...
{
path: 'factory_model/factory_area',
name: `${pre}factory_model-factory_area`,
meta: { ...meta, cache: true, title: '工厂区域' },
component: _import('production-master-data/factory-model/factory-area')
}
])('production_configuration-')
}
9.4 对比
| 维度 | 旧方案 | 新方案 |
|---|---|---|
| 文件数 | 每个一级模块 1 个 index.vue(20+ 个) | 全局共享 1 个 module-index.vue |
| 模板代码 | 每个文件 20 行重复代码 | 0 行重复 |
| root 值 | 硬编码在每个 data() 中 | 路由 meta.root 动态读取 |
| i18n | 无 | 通过 $t(menu.title) 自动翻译 |
| 新增模块 | 创建目录 + 创建 index.vue + 写重复代码 | 仅需添加路由配置行 |
9.5 验证方法
- 访问
/production_configuration(不带子路径),应显示"生产配置"模块的二级和三级模块导航 - 切换语言,banner 标题和菜单项应跟随翻译
- 点击菜单项,正确跳转到对应页面对应的三级模块页面