Files
mes-ui-d2/docs/布局迁移报告.md
sheng 96fe85b3be docs: 更新文档并新增业务模块路由配置
1. 重命名README中的文档链接为中文名称
2. 新增数据中台、质量管理、系统设置、设备管理、计划与生产5个业务路由模块
3. 新增CRUD页面迁移、Sass废弃修复、布局组件迁移、国际化规范、登录页面搬迁、变更日志等文档
2026-05-28 16:15:57 +08:00

15 KiB
Raw Blame History

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 optionssizeOptions 确保语言切换时下拉选项也跟随更新
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.jslibs/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-sizetabs 组件中原来在 data() 里的中文文本数组已经移到 computed,确保 $t() 是响应式的。未来如果要在 data() 中放翻译文本,必须使用 computed 或确保使用者是组件内部翻译。


六、回滚指南

如需回滚任一组件的改动:

  1. 布局组件git checkout 对应的组件文件即可,所有改动互相独立
  2. 语言包 — 回滚 src/locales/*.json,新增的 page.layout 命名空间不影响业务页面
  3. 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() 包装(客户端翻译)
语言切换 ──────────┤
                    └─ 方案 BmenuReload重新拉取后端菜单

方案 A渲染层 $t() 包装

将菜单渲染节点中的所有 menu.title 包裹 $t()

修改文件

文件 改动
src/layout/header-aside/components/libs/util.menu.js elMenuItemelSubmenumenu.titlethis.$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.jsonChangeLocale 末尾追加:

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.titlethis.$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 验证方法

  1. 启动项目,确认当前展示的菜单文字正确(方案 A 生效)
  2. 切换语言,观察侧边栏和顶部导航栏文字是否更新(方案 A + B 双重生效)
  3. 如果切换语言后菜单文字不变,检查:
    • 后端 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.vue20+ 个) 全局共享 1 个 module-index.vue
模板代码 每个文件 20 行重复代码 0 行重复
root 值 硬编码在每个 data() 中 路由 meta.root 动态读取
i18n 通过 $t(menu.title) 自动翻译
新增模块 创建目录 + 创建 index.vue + 写重复代码 仅需添加路由配置行

9.5 验证方法

  1. 访问 /production_configuration(不带子路径),应显示"生产配置"模块的二级和三级模块导航
  2. 切换语言banner 标题和菜单项应跟随翻译
  3. 点击菜单项,正确跳转到对应页面对应的三级模块页面