# sct-base-table 组件架构重构方案(方案 B) > 设计人:前端团队 > 日期:2026-05-26 > 状态:待评审(RFC) --- ## 目录 1. [背景与现状](#1-背景与现状) 2. [方案总览](#2-方案总览) 3. [架构对照:旧 vs 新](#3-架构对照旧-vs-新) 4. [新组件详细设计](#4-新组件详细设计) 5. [页面迁移对照示例](#5-页面迁移对照示例) 6. [文件清单](#6-文件清单) 7. [风险与回滚策略](#7-风险与回滚策略) 8. [工时估算](#8-工时估算) --- ## 1. 背景与现状 ### 1.1 当前使用规模 `src/components/sct-base-table/index.vue` 是一个基于 `el-table` 的二次封装组件,在旧项目中**被 100+ 个页面引用**,是使用频率最高的公共组件。 ### 1.2 现有问题 | # | 问题 | 严重程度 | 说明 | |---|------|:---:|------| | 1 | 引入了 `yargs`(Node.js CLI 库)到浏览器代码 | 🔴 致命 | `import { boolean } from 'yargs'` 会导致打包体积膨胀,且 yargs 依赖 Node API | | 2 | 60% CSS 为注释掉的死代码 | 🔴 | 100+ 行 `/* ... */` 注释占据组件内容 | | 3 | 包含备份文件 `index - 副本.vue` | 🔴 | 源码应无备份文件 | | 4 | `v-bind="$attrs"` 无白名单过滤 | 🟡 | 任何父组件属性都会污染 el-table DOM | | 5 | 按钮栏与表格强耦合 | 🟡 | 无法单独复用表格或按钮栏 | | 6 | 每个页面手动分配 `idx` 序号 | 🟡 | 100+ 个页面都在重复 `{ idx: 0, attrs: {...} }` | | 7 | 每个页面重复定义 `buttonList` / `tableButtonList` | 🟡 | 结构完全相同,仅在权限和回调上有差异 | | 8 | `__judge` 命名不规范 | 🟢 | 双下划线命名、语义不清 | | 9 | 无分页能力 | 🟡 | 100+ 页面需要额外包装 `` | | 10 | 与 `sct-base-dialog`、`sct-base-form`、`sct-back-to-top` 四件套重复组合 | 🟡 | 每个页面都要手动组装这四个组件 | ### 1.3 当前使用模式 ``` 每个页面的 PageMain 组件: ├── ← 表格(含顶部按钮栏) ├── ← 新增/编辑弹框 │ └── ← 表单 └── ← 回到顶部 ``` --- ## 2. 方案总览 ### 2.1 核心理念 **从「一个万能组件」拆分为「组合式工具函数 + 职责单一的组件」。** ### 2.2 三层架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ 组合层:各页面 PageMain 组件 │ │ 通过 + 自由组合 │ ├─────────────────────────────────────────────────────────────┤ │ 组件层:8 个职责单一的 UI 组件 │ │ sct-table / sct-toolbar / sct-action-buttons / sct-expand │ │ sct-batch-actions / page-dialog-form / page-search / │ │ page-table(便捷组合体) │ ├─────────────────────────────────────────────────────────────┤ │ 逻辑层:5 个可复用的 composable 函数 │ │ useTableColumns / useTableButtons / useTableSelection / │ │ usePagination / usePageTable │ └─────────────────────────────────────────────────────────────┘ ``` ### 2.3 改造范围 | 不改造 | 改造 | |--------|------| | API 调用的业务逻辑 (methods) | 表格/按钮/列的组装方式 | | sct-base-form(表单组件) | sct-base-table → 拆为多个小组件 | | sct-base-dialog(弹框组件) | 页面模板结构 | | sct-back-to-top | columns/buttonList 的定义方式 | --- ## 3. 架构对照:旧 vs 新 ### 3.1 组件拆分对照 ``` 旧架构(一个组件包揽一切): sct-base-table/index.vue ├── 模板:按钮栏+表格(215 行) ├── 逻辑:权限校验+按钮显隐+表头样式 └── 样式:60% 注释掉 新架构(按职责拆分): src/ ├── components/ │ ├── sct-table/index.vue 纯表格封装(~40 行) │ ├── sct-toolbar/index.vue 顶部工具栏(按钮+筛选,~60 行) │ ├── sct-action-buttons/index.vue 行内操作按钮(~30 行) │ ├── sct-expand/index.vue 展开行内容(~40 行) │ ├── sct-batch-actions/index.vue 批量操作栏(~30 行) │ ├── page-dialog-form/index.vue 增删改查弹框一体(~80 行) │ └── page-table/index.vue 便捷组合体(表格+工具栏+分页,~80 行) └── composables/ ├── useTableColumns.js 列定义工厂(~30 行) ├── useTableButtons.js 按钮定义工厂(~50 行) ├── useTableSelection.js 选中行管理(~30 行) ├── usePagination.js 分页逻辑(~40 行) └── usePageTable.js 表格数据+分页+刷新(~60 行) ``` ### 3.2 数据流对照 ``` 旧模式(单向强耦合): data() 手写 { idx, attrs: { prop, label, ... } } → columns prop → el-table-column 遍历 新模式(声明式 + 自动推导): columns: [{ prop: 'code', label: '编码' }] → composable 自动补全 idx → sct-table ``` --- ## 4. 新组件详细设计 ### 4.1 `sct-table` — 纯表格封装 **定位**:最底层的表格组件,只负责渲染列和数据,没有任何业务逻辑。 **Props:** | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | columns | Array | `[]` | 列定义列表,直接透传 el-table-column 属性 | | data | Array | `[]` | 表格数据 | | loading | Boolean | false | 加载状态 | | height | String/Number | — | 表格高度 | | border | Boolean | true | 是否带边框 | | rowKey | String | `'id'` | 行数据的 Key | | expandable | Boolean | false | 是否启用展开行 | **Slots:** | 插槽名 | 作用 | 作用域 | |--------|------|--------| | `#col-{prop}` | 自定义列的默认插槽 | `{ row, $index }` | | `#col-{prop}-header` | 自定义列表头 | `{ column }` | | `#expand` | 展开行内容 | `{ row, $index }` | | `#empty` | 空数据占位 | — | | `#append` | 表格最后一行追加内容 | — | **事件:** 与 `el-table` 一致,透传 `@selection-change`、`@sort-change` 等。 **示例:** ```vue ``` --- ### 4.2 `sct-toolbar` — 顶部工具栏 **定位**:表格上方的操作按钮区域,支持权限控制、loading、下拉菜单分组。 **Props:** | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | buttons | Array | `[]` | 按钮列表(定义见下方) | | selectedCount | Number | 0 | 当前选中行数 | | showSelectedHint | Boolean | true | 是否显示"已选 N 项"文字 | **按钮定义结构:** ```js const toolbarButtons = [ { key: 'add', label: '新增', icon: 'el-icon-plus', type: 'primary', auth: '/xxx/create', onClick: handleAdd, }, { key: 'delete', label: '批量删除', icon: 'el-icon-delete', type: 'danger', auth: '/xxx/batch-delete', needSelection: true, // 需要选中行才能点击 confirmMsg: '确认删除选中项?', onClick: handleBatchDelete, }, { key: 'export', label: '导出', icon: 'el-icon-download', type: 'success', auth: '/xxx/export', onClick: handleExport, }, ] ``` **示例:** ```vue ``` --- ### 4.3 `sct-action-buttons` — 行内操作按钮 **定位**:表格每行末尾的"编辑/删除/查看"等操作按钮。 **Props:** | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | row | Object | — | 当前行数据 | | buttons | Array | `[]` | 按钮列表 | | maxVisible | Number | 3 | 超过此数量折叠到"更多"下拉菜单 | **按钮定义结构:** ```js const rowButtons = [ { key: 'edit', label: '编辑', icon: 'el-icon-edit', auth: '/xxx/edit', onClick: handleEdit }, { key: 'delete', label: '删除', icon: 'el-icon-delete', color: 'danger', auth: '/xxx/delete', confirm: true, onClick: handleDelete }, { key: 'print', label: '打印', icon: 'el-icon-printer', auth: '/xxx/print', onClick: handlePrint }, ] ``` --- ### 4.4 `sct-expand` — 展开行内容 **定位**:表格展开行中以 form 布局展示的详情信息。 ```vue ``` --- ### 4.5 `page-table` — 便捷组合体(向下兼容) **定位**:为快速迁移提供一个与旧组件心智模型接近的便捷组合体。在 `sct-table` 基础上内置了工具栏和分页。 ```vue ``` `page-table` 只服务于"标准 CRUD 页面"场景(占比 ~80% 的页面)。特殊页面(如检验单管理那种自定义工具栏)直接使用 `sct-table` + `sct-toolbar` 组合。 --- ### 4.6 Composable 层:消除重复代码 #### `useTableColumns(columns)` ```js // 旧方式:每个页面手写 this.columns = [ { idx: 0, attrs: { prop: 'sort', label: '序号' } }, { idx: 1, attrs: { prop: 'code', label: '编码' } }, ] ``` ```js // 新方式:声明列定义,idx 自动生成 import { useTableColumns } from '@/composables/useTableColumns' const columns = useTableColumns([ { prop: 'sort', label: '序号', width: 80 }, { prop: 'code', label: '编码', minWidth: 120 }, { prop: 'name', label: '名称' }, { prop: '_actions', label: '操作', width: 200, fixed: 'right' }, ]) // 自动补齐 idx,_actions 约定为操作列插槽 ``` #### `useTableButtons(...)` ```js // 生成 toolbar 按钮 + row 按钮,自动注入权限判断 const { toolbarButtons, rowButtons } = useTableButtons({ toolbar: [ { key: 'add', label: '新增', ... }, ], row: [ { key: 'edit', label: '编辑', ... }, ], }, permissionCheck) ``` #### `usePagination()` ```js const { pagination, onPageChange, resetPage } = usePagination({ defaultPageSize: 20, }) ``` #### `usePageTable(fetchFn)` ```js // 封装表格数据 + 分页 + loading + 刷新 const { data, loading, pagination, refresh, search } = usePageTable(fetchList) ``` --- ## 5. 页面迁移对照示例 ### 5.1 标准 CRUD 页面(产线管理) > 源文件:`views/production_configuration/factory_model/factory_line/components/PageMain/index.vue` #### 旧代码(简化) ```vue ``` #### 新代码(使用 `page-table` 便捷组合体) ```vue ``` ### 5.2 复杂自定义页面(检验单管理) > 源文件:`views/quality_control/xqc/inspection_order_manage/components/PageMain/index.vue` 这个页面工具栏有 10 个自定义按钮 + 列筛选器 + 多处自定义事件,旧代码直接用了 `vxe-table` 绕过了 `sct-base-table`。 #### 新代码(使用底层组件自由组合) ```vue ``` ### 5.3 带展开行的页面(库位管理) ```vue ``` --- ## 6. 文件清单 ### 6.1 新增文件 | 文件 | 类型 | 行数估计 | 说明 | |------|:---:|:---:|------| | `src/composables/useTableColumns.js` | 逻辑 | ~40 | 列定义工厂,自动补 idx | | `src/composables/useTableButtons.js` | 逻辑 | ~60 | 按钮定义工厂,自动权限过滤 | | `src/composables/useTableSelection.js` | 逻辑 | ~35 | 选中行管理 | | `src/composables/usePagination.js` | 逻辑 | ~50 | 分页逻辑 | | `src/composables/usePageTable.js` | 逻辑 | ~70 | 表格数据+分页+刷新一站式 | | `src/composables/index.js` | 逻辑 | ~10 | 统一导出 | | `src/components/sct-table/index.vue` | 组件 | ~60 | 纯表格 | | `src/components/sct-toolbar/index.vue` | 组件 | ~80 | 工具栏 | | `src/components/sct-action-buttons/index.vue` | 组件 | ~50 | 行内操作按钮 | | `src/components/sct-expand/index.vue` | 组件 | ~50 | 展开行详情 | | `src/components/sct-batch-actions/index.vue` | 组件 | ~40 | 批量操作栏 | | `src/components/page-dialog-form/index.vue` | 组件 | ~100 | 增删改查弹框一体 | | `src/components/page-table/index.vue` | 组件 | ~100 | 便捷组合体(80% 页面用这个) | **总计:约 745 行新代码**(旧 sct-base-table 215 行,删除 ~100 行死代码后实际上只有 ~100 行有效代码) ### 6.2 修改文件 | 文件 | 修改内容 | |------|---------| | `src/components/sct-base-table/index.vue` | 删除或标记 `@deprecated`,重定向到新组件 | | 100+ 个 PageMain/index.vue | 按页面类型批量替换为 page-table / sct-table+toolbar | --- ## 7. 风险与回滚策略 ### 7.1 风险 | 风险 | 概率 | 影响 | 缓解措施 | |------|:---:|:---:|------| | 新组件 bug 影响线上 | 低 | 高 | 分模块灰度迁移,先迁移"系统设置"模块验证 | | 性能回退(多一层组件) | 极低 | 低 | 新组件均为无渲染函数式组件,零额外开销 | | 开发者学习成本 | 中 | 中 | `page-table` API 刻意靠近旧组件,降低迁移心智负担 | ### 7.2 回滚策略 - 旧 `sct-base-table` 保留不删,标记 `@deprecated` - 每个模块迁移后在路由配置中可独立切回旧组件 - 使用 Vue 的 `defineAsyncComponent` 做新旧切换的 feature flag --- ## 8. 工时估算 | 阶段 | 内容 | 预估时间 | |------|------|:---:| | 阶段 0 | 创建 composables + 核心组件 | 0.5 天 | | 阶段 1 | 创建 page-table 便捷组合体 | 0.5 天 | | 阶段 2 | 迁移「系统设置」模块(6 页)验证 | 0.5 天 | | 阶段 3 | 迁移「生产配置」(15 页) | 1 天 | | 阶段 4 | 迁移「设备模型」(14 页) | 1 天 | | 阶段 5 | 迁移「计划与生产」(10 页) | 1 天 | | 阶段 6 | 迁移「质量管理」(26 页) | 2 天 | | 阶段 7 | 迁移「数据中台」(8 页) | 0.5 天 | | 阶段 8 | 迁移「仓储管理」(25 页) | 2 天 | | 阶段 9 | 迁移「SCADA 管理」(10 页) | 1 天 | | 阶段 10 | 迁移特殊页面(检验单、鹰眼等) | 1 天 | | **合计** | | **约 11 天** | > 注:与搬迁工作并行进行,每个模块搬迁时顺便替换。 --- ## 附录 A:完整的 buttonList 到 toolbarButtons 映射 ```js // 旧 buttonList 结构 buttonList: [ { label: '新增', size: 'mini', icon: 'el-icon-plus', type: 'primary', auth: '/xxx/create', handle: this.openDialog, cssStyle: { background: '#3CBA92' }, noShow: this.someCondition, } ] // 新 toolbarButtons 结构 toolbarButtons: [ { key: 'add', label: this.$t('新增'), icon: 'el-icon-plus', type: 'primary', auth: '/xxx/create', onClick: openDialog, // cssStyle 通过 type 预设色系,自定义色系用 color prop visible: computed(() => someCondition), } ] ``` ## 附录 B:完整的 columns 结构变化 ```js // 旧 —— 命令式 { idx: 0, attrs: { prop: 'sort', label: '排序' } } { idx: 5, slot: 'handle', attrs: { label: '操作' } } { idx: 3, slot: 'status', headerSlot: 'status_header', attrs: { prop: 'status', label: '状态' } } // 新 —— 声明式 { prop: 'sort', label: '排序' } { prop: '_actions', label: '操作' } // _actions 约定为操作列 { prop: 'status', label: '状态', slot: true, headerSlot: 'status_header' } // slot: true 启用插槽 ``` --- ## 📖 使用指南 > 本节基于已落地的实际代码编写。源码位置:`src/components/page-table/`、`src/components/page-dialog-form/`、`src/composables/`。 > > 完整可运行示例参考:`src/views/production-configuration/factory-model/factory-area/index.vue` --- ### 快速开始(三步走) **第一步**:在 `created()` 中用 `useTableColumns` 声明列、`useTableButtons` 声明按钮: ```js import { useTableColumns } from '@/composables/useTableColumns' import { useTableButtons } from '@/composables/useTableButtons' created () { // 列定义 —— 不再手动分配 idx this.columns = useTableColumns([ { prop: 'sort', label: '排序', width: 80 }, { prop: 'code', label: '编码', minWidth: 120 }, { prop: 'name', label: '名称', minWidth: 120 }, { prop: 'remark', label: '备注' }, { prop: '_actions', label: '操作', width: 160, fixed: 'right' } // ← 操作列 ]) // 按钮定义 —— 不再分开写 buttonList / tableButtonList const btns = useTableButtons({ toolbar: [ { key: 'add', label: '新 增', icon: 'el-icon-plus', type: 'primary', auth: '/xxx/create', onClick: this.openAdd } ], row: [ { key: 'edit', label: '编辑', icon: 'el-icon-edit', auth: '/xxx/edit', onClick: this.openEdit }, { key: 'delete', label: '删除', icon: 'el-icon-delete', color: 'danger', auth: '/xxx/delete', onClick: this.handleDelete } ] }, this.$permission) this.toolbarButtons = btns.toolbarButtons this.rowButtons = btns.rowButtons } ``` **第二步**:模板中直接用 `` + ``: ```vue ``` **第三步**:写业务方法(fetchData / 增删改),和旧代码完全一致: ```js methods: { async fetchData () { this.loading = true try { const res = await getList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size }) this.tableData = res.data || [] this.pagination.total = res.count || 0 } finally { this.loading = false } }, onPageChange (page) { this.pagination.current = page.current this.pagination.size = page.size this.fetchData() }, // 新增 openAdd () { this.handleType = 'create' this.dialogTitle = '新 增' this.$nextTick(() => { this.$refs.dialogForm.reset() this.formData = { code: '', name: '' } this.dialogVisible = true }) }, // 编辑 openEdit (row) { this.handleType = 'edit' this.dialogTitle = '编 辑' this.editId = row.id this.formData = { code: row.code, name: row.name } this.dialogVisible = true }, // 提交 async onDialogSubmit () { this.submitting = true try { if (this.handleType === 'create') { await create(this.formData) } else { await edit({ ...this.formData, id: this.editId }) } this.$message.success('操作成功') this.dialogVisible = false this.fetchData() } finally { this.submitting = false } }, // 删除 async handleDelete (row) { try { await this.$confirm('确定要执行该操作吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) await del({ id: [row.id] }) this.$message.success('操作成功') this.fetchData() } catch (e) { /* 用户取消不处理 */ } } } ``` --- ### 完整可运行示例 > 📁 `src/views/production-configuration/factory-model/factory-area/index.vue` ```vue ``` --- ### API 速查 #### `useTableColumns(rawColumns, options?)` | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | rawColumns | Array | — | 列定义,字段同 `el-table-column` 属性 | | options.selectionWidth | number | 55 | 复选框列宽,传 0 不显示 | | options.indexWidth | number | 0 | 序号列宽,传 0 不显示 | **约定**:`prop: '_actions'` 自动映射为操作列插槽,由 `page-table` 自动渲染 `rowButtons`。 #### `useTableButtons(options, permissionCheck?)` | 参数 | 类型 | 说明 | |------|------|------| | options.toolbar | Array | 顶部工具栏按钮 | | options.row | Array | 行内操作按钮 | | permissionCheck | Function | 权限函数,默认 `() => true`,通常传 `this.$permission` | 返回值 `{ toolbarButtons, rowButtons }`。 **按钮字段**: | 字段 | 说明 | |------|------| | key | 唯一标识 | | label | 显示文本 | | icon | Element UI 图标名 | | type | `primary` / `success` / `warning` / `danger` | | color | 行内按钮专用:`'danger'` 显示红色 | | auth | 权限 key(传给 `$permission` 检查) | | onClick | 点击回调 | | hasPermission | **自动注入**,由 `permissionCheck(auth)` 计算 | #### `page-table` — 组件 Props | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | columns | Array | `[]` | 列定义(由 useTableColumns 生成) | | data | Array | `[]` | 表格数据 | | loading | Boolean | false | loading 遮罩 | | height | String/Number | — | 表格高度 | | toolbarButtons | Array | `[]` | 顶部按钮(由 useTableButtons 生成) | | rowButtons | Array | `[]` | 行内按钮(由 useTableButtons 生成) | | pagination | Object | null | 传 `{ current, size, total }` 才显示分页 | | border | Boolean | true | 是否带边框 | | rowKey | String | `'id'` | 行 key | **事件**:`@page-change`、`@selection-change`,同时透传 `el-table` 原生事件。 **插槽**:`#col-{prop}` 自定义列渲染、`#toolbar-extra` 工具栏追加、`#empty` / `#append` / `#extra`。 #### `page-dialog-form` — 组件 Props | Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | visible | Boolean | false | `.sync` 绑定 | | title | String | `''` | 弹框标题 | | width | String | `'35%'` | 弹框宽度 | | formCols | Array | `[]` | 表单字段(二维数组,每行一个子数组) | | formData | Object | `{}` | 表单数据 | | rules | Object | `{}` | 校验规则(el-form rules) | | labelWidth | String | `'100px'` | label 宽度 | | submitting | Boolean | false | 提交中 loading | | confirmText | String | `'确定'` | 确认按钮文字 | | cancelText | String | `'取消'` | 取消按钮文字 | **方法**(通过 `ref` 调用):`reset()` 重置表单、`validate()` 返回 Promise。 **formCols 字段项**: | 字段 | 说明 | |------|------| | type | `'input'`(默认 input)/ `'select'` | | prop | 绑定 `formData.prop` 的 key | | label | 表单项标签 | | placeholder | 占位文本 | | inputType | `textarea` 时启用多行输入 | | autosize | textarea 的 `{ minRows, maxRows }` | | style | 样式对象,如 `{ width: '90%' }` | | options | type 为 `'select'` 时的选项数组 `[{ label, value }]` | --- ### 常用场景速查 **场景 1:自定义列渲染(如状态显示 tag)** ```vue ``` 列定义中加入 `slot: true`: ```js useTableColumns([ { prop: 'status', label: '状态', slot: true, width: 100 }, ]) ``` **场景 2:工具栏追加自定义内容** ```vue ``` **场景 3:序号列** ```js useTableColumns( [{ prop: 'code', label: '编码' }], { selectionWidth: 55, indexWidth: 60 } // 开启复选框 + 序号列 ) ``` ### 路由配置 ```js // src/router/modules/production-configuration.js import layoutHeaderAside from '@/layout/header-aside' const meta = { auth: true } const _import = require('@/libs/util.import.' + process.env.NODE_ENV) export default { path: '/production_configuration', component: layoutHeaderAside, children: (pre => [ { path: 'factory_model/factory_area', name: `${pre}factory_model-factory_area`, meta: { ...meta, cache: true, title: '工厂区域' }, component: _import('production-configuration/factory-model/factory-area') } ])('production_configuration-') } ``` ### 旧代码迁移对照 | 旧写法 | 新写法 | |--------|--------| | 手动 `columns: [{ idx: 0, attrs: { prop, label } }]` | `useTableColumns([{ prop, label }])` | | `buttonList: [...]` + `tableButtonList: [...]` 分两套 | `useTableButtons({ toolbar: [...], row: [...] })` | | `sct-base-table` + `sct-base-dialog` + `SctBaseForm` 三层 | `page-table` + `page-dialog-form` 两层 | | `sct-form-search` 组件 | 原生 `el-form :inline` | | `sct-back-to-top` 组件 | 需要时自行添加 | | 分页需额外 `page-footer` 组件 | `page-table` 内置分页 | --- ### 国际化 (i18n) 使用说明 **组件层自动翻译**:`page-table` 和 `page-dialog-form` 对所有 `label`、`placeholder`、`title` 等文本属性自动调用 `$t()` 翻译。因此页面只需传入 i18n key,组件会自动渲染翻译后的文本。如果传入的是普通字符串(非 i18n key),`$t()` 会原样返回,不会报错。 **页面层用法**: ```js // 约定:每个页面定义一个 T 常量作为该页面的 i18n key 前缀 const T = 'page.production_configuration.factory_model.factory_area' // 列定义 —— 传 i18n key,组件自动翻译 columns = useTableColumns([ { prop: 'sort', label: T + '.sort', width: 80 }, { prop: 'code', label: T + '.code', minWidth: 120 }, { prop: '_actions', label: T + '.operation', width: 160, fixed: 'right' } ]) // 按钮定义 —— 传 i18n key const btns = useTableButtons({ toolbar: [{ key: 'add', label: T + '.add', icon: 'el-icon-plus', ... }], row: [{ key: 'edit', label: T + '.edit', ... }] }) // 表单定义 —— 传 i18n key formCols: [ [{ type: 'input', prop: 'code', label: T + '.code', placeholder: T + '.enter_code' }] ] // 模板中直接用 $t() 或 $t(tkey('key')) methods: { tkey (key) { return T + '.' + key } } ``` ```vue ``` **语言包文件**:在 `src/locales/zh-chs.json` 和 `src/locales/en.json` 中按页面维护: ```json { "page": { "production_configuration": { "factory_model": { "factory_area": { "search": "查询", "code": "所区编码", "name": "所区名称", "add": "新 增", "edit": "编 辑", "delete": "删 除", "operation_success": "操作成功" } } } } } ``` **语言包目录命名规则**: | 语言 | 文件名 | |------|--------| | 简体中文 | `src/locales/zh-chs.json` | | 繁体中文 | `src/locales/zh-cht.json` | | 英文 | `src/locales/en.json` | | 日文 | `src/locales/ja.json` | > 注意:`page-table` 和 `page-dialog-form` 已内置 `$t()` 调用,页面传 i18n key 即可自动翻译。搜索区等其他 UI 文字需要用 `$t(tkey('...'))` 手动处理。