From 4539bec3a43a4f6e0fbc328643b2bdfbcd4f467d Mon Sep 17 00:00:00 2001 From: sheng <905537351@qq.com> Date: Thu, 28 May 2026 11:30:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(page-table,=20page-dialog-form):=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9B=BD=E9=99=85=E5=8C=96=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=93=8D=E5=BA=94=EF=BC=8C=E7=A7=BB=E9=99=A4=E6=8F=90=E5=89=8D?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 为page-table和page-dialog-form添加内部$t翻译逻辑,支持语言切换自动更新 2. 移除data()中提前翻译的k()辅助函数,改为直接传入i18n key 3. 更新文档说明最新的i18n使用规范,新增迁移指南文档 4. 修复工厂区域页面的国际化调用方式,统一使用this.key()传参 --- docs/i18n-rules.md | 18 +- docs/migration-prompt.md | 88 ++++ docs/表格组件使用说明.md | 405 ++++++++++-------- src/components/page-dialog-form/index.vue | 26 +- src/components/page-table/index.vue | 7 +- .../factory-model/factory-area/index.vue | 26 +- 6 files changed, 351 insertions(+), 219 deletions(-) create mode 100644 docs/migration-prompt.md diff --git a/docs/i18n-rules.md b/docs/i18n-rules.md index f9ff1c86..ad957a10 100644 --- a/docs/i18n-rules.md +++ b/docs/i18n-rules.md @@ -200,16 +200,13 @@ import { i18nMixin } from '@/composables/useI18n' export default { mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')], data () { - const t = this.$t.bind(this) - const k = (s) => t(this.key(s)) // 当前页面翻译 - const ck = (s) => t(this.ckey(s)) // 公共翻译 - return { + // 传入完整 i18n key,不在此处翻译,由子组件内部 $t() 处理 formCols: [ - [{ label: k('code'), placeholder: k('enter_code') }] + [{ label: this.key('code'), placeholder: this.key('enter_code') }] ], rules: { - code: [{ required: true, message: k('enter_code'), trigger: 'blur' }] + code: [{ required: true, message: this.key('enter_code'), trigger: 'blur' }] } } }, @@ -236,12 +233,13 @@ export default { ``` -### 4.3 data() 中翻译的时机说明 +### 4.3 data() 中传参的时机说明 `data()` 在 `created` 之前执行,此时 `this` 已经可用。因此: -- **column label / formCols / rules.message**:在 `data()` 中或 `created()` 中 `this.key()` 都可以 -- **推荐在 `data()` 中用 `k()` 提前翻译**,避免子组件 `$t()` 的 webpack HMR 缓存问题 +- **column label / formCols / rules.message**:在 `data()` 中或 `created()` 中用 `this.key()` 传入完整 i18n key +- **翻译由子组件负责**:`page-table` 和 `page-dialog-form` 内部使用 `$t()` 翻译传入的 key,切换语言时自动响应更新 +- **不要用 `k()` 提前翻译**:`k()` 将 key 翻译成静态字符串,语言切换后不会更新 --- @@ -382,7 +380,7 @@ this.$t('please_enter', { name: '所区编码' }) - [ ] 5. 验证 JSON 合法性:`node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))"` - [ ] 6. 页面中使用 `mixins: [i18nMixin('key前缀')]` - [ ] 7. 模板中用 `$t(key('xxx'))` 替代硬编码文字 -- [ ] 8. data() 中用 `k('xxx')` 提前翻译 formCols / rules +- [ ] 8. data() 中用 `this.key('xxx')` 传入完整 i18n key(不用 `k()` 提前翻译) - [ ] 9. 公共文案(帮助、确定、取消等)用 `ckey('xxx')` 引用 - [ ] 10. `pnpm lint` 无报错 diff --git a/docs/migration-prompt.md b/docs/migration-prompt.md new file mode 100644 index 00000000..c21a3303 --- /dev/null +++ b/docs/migration-prompt.md @@ -0,0 +1,88 @@ +## 任务:迁移旧 CRUD 页面到新版 page-table + page-dialog-form 方案 + +### 输入 +用户会提供一个旧 CRUD 页面的文件路径列表和对照表信息。 + +--- + +### 迁移规则(必须严格遵守) + +#### 1. 组件方案 +- 使用 `` + `` 替代旧版 `` + `` + `` +- 使用 `useTableColumns()` 生成列定义(不再手动分配 idx) +- 使用 `useTableButtons()` 生成工具栏按钮和行内按钮(不再分开写 buttonList / tableButtonList) +- 使用 `i18nMixin(prefix)` 注入 `key()` 和 `ckey()` 方法 +- 参考文档:[表格组件使用说明.md](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md) +- 参考示例:`src/views/production-master-data/factory-model/factory-area/index.vue` + +#### 2. i18n 国际化(重要) +- 使用 `mixins: [i18nMixin('完整i18n前缀')]`,不要在每个页面手动定义 `T` 常量和 `tkey()` 方法 +- `data()` 中用 `this.key('xxx')` 传完整 i18n key,**不要用 `k()` 提前翻译**(翻译由 page-table / page-dialog-form 内部处理,切换语言自动响应) +- 模板搜索区用 `$t(key('xxx'))`,公共 key 用 `$t(ckey('xxx'))` +- 语言包必须中英文同步添加(`zh-chs.json` + `en.json`) +- 参考文档:[i18n-rules.md](file:///d:/code/mes/mes-ui/docs/i18n-rules.md) + +#### 3. 文件夹命名(重要) +- 文件夹名称遵循 [后台Webman界面截图对照表](file:///d:/code/mes/mes-ui/后台Webman界面截图对照表.md) 的 snake_case/kebab-case 命名 +- 目录示例:`src/views/production-master-data/factory-model/factory-area/` +- 路由模块:`src/router/modules/production-master-data.js` +- API 文件:`src/api/production-master-data/factory-area.js` + +#### 4. 路由 path 和 API BASE URL(重要 — 临时方案) +- **路由 path** 和 **API BASE URL** **暂时保留旧项目的命名**,因为后台数据库的路由表还未更新 +- 示例:目录按对照表叫 `production-master-data`,但路由 path 保持旧值 `production_configuration` + ```js + // src/router/modules/production-master-data.js + path: '/production_configuration', // 旧值暂留 + ``` + ```js + // src/api/production-master-data/factory-area.js + const BASE = 'production_configuration/factory_model/factory_area/' // 旧值暂留 + ``` +- 后续统一修改路由后再批量替换 + +#### 5. 旧 key → 新 key 映射 +如果用户提供的是旧页面代码,需要根据对照表做 key 映射。已知映射如下(持续补充): + +| 旧 key 前缀 | 新 key 前缀 | +|------------|------------| +| `page.production_configuration.factory_model.factory_area` | `page.production_master_data.factory_model.factory_area` | +| `page.system_settings.user_management.role` | `page.system_administration.user_management.role` | +| `page.system_settings.user_management.user` | `page.system_administration.user_management.user` | +| `page.system_settings.menu_configuration.menu` | `page.system_administration.menu_management.menu_configuration` | +| `page.system_settings.system_assistant.operate_log` | `page.system_administration.system_utilities.operation_logs` | +| `page.system_settings.system_assistant.api_log` | `page.system_administration.system_utilities.api_logs` | +| `page.system_settings.system_monitoring.system.login` | `page.system_administration.system_monitoring.login` | +| `page.production_configuration.matetial_model.*` | `page.production_master_data.material_model.*` | +| `page.planning_production.production_batch_management.batch` | `page.planning_production.batch_management.batch_list` | +| `page.data_middleground.basic_traceability.*` | `page.data_platform.traceability.*` | + +#### 6. 处理流程(每迁移一个页面执行以下步骤) +1. **确定对照表位置**:从 [后台Webman界面截图对照表](file:///d:/code/mes/mes-ui/后台Webman界面截图对照表.md) 找到对应行的英文名,转换为 snake_case +2. **创建目录结构**:`src/views/{一级snake_case}/{二级snake_case}/{三级snake_case}/` +3. **创建 API 文件**:`src/api/{一级snake_case}/{二级snake_case}/{三级snake_case}.js`,BASE URL 暂用旧值 +4. **添加路由模块**:`src/router/modules/{一级snake_case}.js`,path 暂用旧值 +5. **编写页面代码**:使用 page-table + page-dialog-form 方案 +6. **添加 i18n**:在 `zh-chs.json` 和 `en.json` 中添加对应 key +7. **验证 JSON 合法性**:`node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))"` + +--- + +### 页面模板(通用 CRUD 页面骨架) + +参考 `src/views/production-master-data/factory-model/factory-area/index.vue` 的结构,核心模式: + +- **Script**:`mixins: [i18nMixin('page.{一}.{二}.{三}')]` + `data()` 中用 `this.key('xxx')` 传 key +- **Template**:`` 传 columns/data/loading/toolbarButtons/rowButtons/pagination + `` 传 formCols/formData/rules/title +- **Buttons**:`useTableButtons({ toolbar: [...], row: [...] }, this.$permission)` +- **Columns**:`useTableColumns([...])` +- **Methods**:`fetchData / onSearch / onReset / onPageChange / openAdd / openEdit / onDialogSubmit / handleDelete` +- **i18n key 模板**(每个页面至少要有这些):`search / reset / add / edit / delete / operation / add_title / edit_title / code / name / remark / enter_code / enter_name / remark_length / operation_success / confirm / cancel / tip / confirm_delete` + +--- + +### 输出要求 +- 创建完整的页面文件、路由模块、API 文件 +- 在 `zh-chs.json` 和 `en.json` 中添加对应 i18n key(中文和英文同步) +- 完成后提醒用户验证 JSON 合法性 +- 如涉及对照表中已有映射的 key 前缀变更,自动纠正为新的 key 前缀 diff --git a/docs/表格组件使用说明.md b/docs/表格组件使用说明.md index ab01b04c..19d10bca 100644 --- a/docs/表格组件使用说明.md +++ b/docs/表格组件使用说明.md @@ -1,6 +1,6 @@ # 表格组件使用说明 -> 基于 `page-table` + `page-dialog-form` 的新一?CRUD 表格方案? +> 基于 `page-table` + `page-dialog-form` 的新一代 CRUD 表格方案。 > 源码位置:`src/components/page-table/`、`src/components/page-dialog-form/`、`src/composables/` > 完整可运行示例:`src/views/production-master-data/factory-model/factory-area/index.vue` @@ -10,13 +10,13 @@ 1. [快速开始(三步走)](#1-快速开始三步走) 2. [完整示例代码](#2-完整示例代码) -3. [组件 API 参考](#3-组件-api-参? -4. [Composable API 参考](#4-composable-api-参? -5. [i18n 国际化方案](#5-i18n-国际化方? +3. [组件 API 参考](#3-组件-api-参考) +4. [Composable API 参考](#4-composable-api-参考) +5. [i18n 国际化方案](#5-i18n-国际化方案) 6. [常用场景速查](#6-常用场景速查) 7. [路由配置](#7-路由配置) 8. [API 文件写法](#8-api-文件写法) -9. [旧代码迁移对照](#9-旧代码迁移对? +9. [旧代码迁移对照](#9-旧代码迁移对照) 10. [常见问题排查](#10-常见问题排查) --- @@ -31,12 +31,12 @@ import { useTableButtons } from '@/composables/useTableButtons' import { i18nMixin } from '@/composables/useI18n' export default { - mixins: [i18nMixin('page.模块?二级模块.三级模块')], + mixins: [i18nMixin('page.模块名.二级模块.三级模块')], created () { - // --- 列定?--- - // 只需声明 prop ?label,idx 自动补齐 - // prop: '_actions' 约定为操作列,自动渲?rowButtons + // --- 列定义 --- + // 只需声明 prop 和 label,idx 自动补齐 + // prop: '_actions' 约定为操作列,自动渲染 rowButtons this.columns = useTableColumns([ { prop: 'sort', label: this.key('sort'), width: 80 }, { prop: 'code', label: this.key('code'), minWidth: 120 }, @@ -45,7 +45,7 @@ export default { ]) // --- 按钮定义 --- - // 不再分开?buttonList / tableButtonList + // 不再分开写 buttonList / tableButtonList const btns = useTableButtons({ toolbar: [ { key: 'add', label: this.key('add'), icon: 'el-icon-plus', @@ -57,7 +57,7 @@ export default { { key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/xxx/delete', onClick: this.handleDelete } ] - }, this.$permission) // ?第二个参数传入权限校验函? + }, this.$permission) // 第二个参数传入权限校验函数 this.toolbarButtons = btns.toolbarButtons this.rowButtons = btns.rowButtons } @@ -69,7 +69,7 @@ export default { ```vue ``` -### 第三步:写业务方法(增删改查? +### 第三步:写业务方法(增删改查) + ```js methods: { // 获取列表数据 @@ -159,7 +161,8 @@ methods: { }) }, - // 编辑:回填表? openEdit (row) { + // 编辑:回填表单 + openEdit (row) { this.handleType = 'edit' this.dialogTitle = this.key('edit_title') this.editId = row.id @@ -198,7 +201,7 @@ methods: { await deleteApi({ id: [row.id] }) this.$message.success(this.$t(this.key('operation_success'))) this.fetchData() - } catch (e) { /* 取消或失?*/ } + } catch (e) { /* 取消或失败 */ } } } ``` @@ -208,7 +211,8 @@ methods: { ## 2. 完整示例代码 > 📁 `src/views/production-master-data/factory-model/factory-area/index.vue` -> 这是一?*可直接运行的完整 CRUD 页面**,包含搜索栏、表格、分页、新?编辑弹框、删除确认? +> 这是一个**可直接运行的完整 CRUD 页面**,包含搜索栏、表格、分页、新增/编辑弹框、删除确认。 + ### 模板部分 ```vue @@ -246,6 +250,7 @@ methods: { :row-buttons="rowButtons" :pagination="pagination" help-url="/help/factory-area" + :help-text="$t(ckey('help'))" auto-height @page-change="onPageChange" @selection-change="onSelect" @@ -260,8 +265,8 @@ methods: { :form-data="formData" :rules="rules" :submitting="submitting" - :confirm-text="$t(key('confirm'))" - :cancel-text="$t(key('cancel'))" + :confirm-text="key('confirm')" + :cancel-text="key('cancel')" @submit="onDialogSubmit" @close="onDialogClose" /> @@ -286,8 +291,6 @@ export default { mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')], data () { - const t = this.$t.bind(this) - const k = (s) => t(this.key(s)) return { loading: false, submitting: false, @@ -302,12 +305,12 @@ export default { formData: { code: '', name: '', remark: '' }, rules: { code: [ - { required: true, message: k('enter_code'), trigger: 'blur' }, - { min: 1, max: 100, message: k('remark_length'), trigger: 'blur' } + { required: true, message: this.key('enter_code'), trigger: 'blur' }, + { min: 1, max: 100, message: this.key('remark_length'), trigger: 'blur' } ], name: [ - { required: true, message: k('enter_name'), trigger: 'blur' }, - { min: 1, max: 100, message: k('remark_length'), trigger: 'blur' } + { required: true, message: this.key('enter_name'), trigger: 'blur' }, + { min: 1, max: 100, message: this.key('remark_length'), trigger: 'blur' } ] }, columns: [], @@ -316,18 +319,18 @@ export default { formCols: [ [ { type: 'input', prop: 'code', - label: k('code'), placeholder: k('enter_code'), + label: this.key('code'), placeholder: this.key('enter_code'), clearable: true, style: { width: '90%' } } ], [ { type: 'input', prop: 'name', - label: k('name'), placeholder: k('enter_name'), + label: this.key('name'), placeholder: this.key('enter_name'), clearable: true, style: { width: '90%' } } ], [ { type: 'input', prop: 'remark', inputType: 'textarea', autosize: { minRows: 2, maxRows: 6 }, - label: k('remark'), placeholder: k('remark_required'), + label: this.key('remark'), placeholder: this.key('remark_required'), clearable: true, style: { width: '90%' } } ] ] @@ -436,7 +439,7 @@ export default { Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1 ) this.fetchData() - } catch (e) { /* 取消删除 / 请求失败不处?*/ } + } catch (e) { /* 取消删除 / 请求失败不处理 */ } } } } @@ -444,111 +447,115 @@ export default { --- -## 3. 组件 API 参? -### 3.1 `page-table` ?表格 + 按钮?+ 分页 +## 3. 组件 API 参考 + +### 3.1 `page-table` — 表格 + 按钮栏 + 分页 #### Props -| Prop | 类型 | 默认?| 说明 | +| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| -| `columns` | `Array` | `[]` | 列定义,?`useTableColumns()` 生成 | -| `data` | `Array` | `[]` | 表格行数?| +| `columns` | `Array` | `[]` | 列定义,由 `useTableColumns()` 生成 | +| `data` | `Array` | `[]` | 表格行数据 | | `loading` | `Boolean` | `false` | 是否显示 loading 遮罩 | -| `height` | `String/Number` | ?| 表格高度。不传则随内容撑开;传具体数值固定高度;?`'auto'` 启用自适应 | -| `auto-height` | `Boolean` | `false` | 启用高度自适应,表格自动填满可用空?| -| `border` | `Boolean` | `true` | 是否带边?| +| `height` | `String/Number` | — | 表格高度,传 `'auto'` 启用自适应 | +| `auto-height` | `Boolean` | `false` | 启用高度自适应,填满可用空间 | +| `border` | `Boolean` | `true` | 是否带边框 | | `row-key` | `String` | `'id'` | 行唯一 key | -| `toolbar-buttons` | `Array` | `[]` | 顶部工具栏按钮,?`useTableButtons()` 生成 | +| `toolbar-buttons` | `Array` | `[]` | 顶部工具栏按钮,由 `useTableButtons()` 生成 | | `row-buttons` | `Array` | `[]` | 行内操作按钮,由 `useTableButtons()` 生成 | -| `pagination` | `Object` | `null` | 分页参数 `{ current, size, total }`,传了才显示分页 | -| `table-attrs` | `Object` | `{}` | 额外透传?`el-table` 的属?| -| `table-listeners` | `Object` | `{}` | 额外透传?`el-table` 的事?| -| `help-url` | `String` | `''` | 帮助文档跳转 URL。传了才显示工具栏右侧的问号按钮,点击新窗口打开 | -| `help-text` | `String` | `'帮助'` | 帮助按钮文字,支?i18n key(组件自?`$t()` 翻译?| +| `pagination` | `Object` | `null` | 分页参数 `{ current, size, total }` | +| `table-attrs` | `Object` | `{}` | 额外透传给 `el-table` 的属性 | +| `table-listeners` | `Object` | `{}` | 额外透传给 `el-table` 的事件 | +| `help-url` | `String` | `''` | 帮助文档 URL,传了显示问号按钮 | +| `help-text` | `String` | `'帮助'` | 帮助按钮文字,支持 i18n key | #### 事件 -| 事件?| 参数 | 说明 | +| 事件名 | 参数 | 说明 | |--------|------|------| -| `@page-change` | `{ current, size, total }` | 分页变化(切换页?条数?| -| `@selection-change` | `rows: Array` | 选中行变?| -| `@sort-change` | 透传 el-table 原生事件 | 排序变化 | +| `@page-change` | `{ current, size, total }` | 分页变化 | +| `@selection-change` | `rows: Array` | 选中行变化 | #### 插槽 -| 插槽?| 作用?| 说明 | +| 插槽名 | 作用域 | 说明 | |--------|--------|------| -| `#col-{prop}` | `{ row, index }` | 自定义列的渲染(列定义中 prop 需?`slot: true`?| -| `#toolbar-extra` | ?| 工具栏区域追加自定义内容 | -| `#empty` | ?| 表格空数据时的占?| -| `#append` | ?| 表格最后一行后追加 | -| `#extra` | ?| 页面底部追加区域 | +| `#col-{prop}` | `{ row, index }` | 自定义列渲染(列需设 `slot: true`) | +| `#toolbar-extra` | — | 工具栏追加内容 | +| `#empty` | — | 空数据占位 | +| `#append` | — | 表格末尾追加 | +| `#extra` | — | 页面底部追加 | + +#### 列定义规范 -#### 列定义规? ```js // 普通列 { prop: 'code', label: '编码', minWidth: 120 } -// 操作列(约定:prop === '_actions'?{ prop: '_actions', label: '操作', width: 160, fixed: 'right' } +// 操作列(约定:prop === '_actions') +{ prop: '_actions', label: '操作', width: 160, fixed: 'right' } -// 自定义插槽列(slot: true?{ prop: 'status', label: '状?, slot: true, width: 100 } +// 自定义插槽列(slot: true) +{ prop: 'status', label: '状态', slot: true, width: 100 } -// 复选框?+ 序号列(通过 useTableColumns 第二个参数) +// 复选框 + 序号列(通过 useTableColumns 第二个参数) useTableColumns([...], { selectionWidth: 55, indexWidth: 60 }) ``` --- -### 3.2 `page-dialog-form` ?增删改查弹框 +### 3.2 `page-dialog-form` — 增删改查弹框 #### Props -| Prop | 类型 | 默认?| 说明 | +| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| -| `visible` | `Boolean` | `false` | 弹框显隐,使?`.sync` 修饰?| -| `title` | `String` | `''` | 弹框标题,支?i18n key | +| `visible` | `Boolean` | `false` | 弹框显隐,使用 `.sync` | +| `title` | `String` | `''` | 弹框标题,支持 i18n key | | `width` | `String` | `'35%'` | 弹框宽度 | -| `form-cols` | `Array` | `[]` | 表单字段结构(二维数组,见下方) | +| `form-cols` | `Array` | `[]` | 表单字段结构(二维数组) | | `form-data` | `Object` | `{}` | 表单数据对象 | -| `rules` | `Object` | `{}` | 校验规则,与 `el-form` rules 一?| +| `rules` | `Object` | `{}` | 校验规则,与 `el-form` rules 一致 | | `label-width` | `String` | `'100px'` | label 宽度 | -| `submitting` | `Boolean` | `false` | 提交 loading 状?| -| `confirm-text` | `String` | `'确定'` | 确定按钮文字 | -| `cancel-text` | `String` | `'取消'` | 取消按钮文字 | +| `submitting` | `Boolean` | `false` | 提交 loading 状态 | +| `confirm-text` | `String` | `'确定'` | 确定按钮文字,支持 i18n key | +| `cancel-text` | `String` | `'取消'` | 取消按钮文字,支持 i18n key | #### 事件 -| 事件?| 说明 | +| 事件名 | 说明 | |--------|------| -| `@submit` | 表单验证通过后触发,父组件执行提交逻辑 | -| `@close` | 弹框关闭后触?| +| `@submit` | 表单验证通过后触发 | +| `@close` | 弹框关闭后触发 | + +#### 方法(通过 ref 调用) -#### 方法(通过 ref 调用? | 方法 | 说明 | |------|------| | `reset()` | 重置表单 | -| `validate()` | 手动验证,返?`Promise` | +| `validate()` | 手动验证,返回 `Promise` | #### formCols 数据结构 -**注意:需?`data()` 中用 `k(prop)` 提前完成 i18n 翻译**,不要传 raw key? +传入 i18n key(用 `this.key()` 拼接完整 key),组件内部自动翻译且跟随语言切换: + ```js -// 例:两个普通输入框 + 一个多行文本框 formCols: [ [ { type: 'input', prop: 'code', - label: k('code'), placeholder: k('enter_code'), + label: this.key('code'), placeholder: this.key('enter_code'), clearable: true, style: { width: '90%' } } ], [ { type: 'input', prop: 'name', - label: k('name'), placeholder: k('enter_name'), + label: this.key('name'), placeholder: this.key('enter_name'), clearable: true, style: { width: '90%' } } ], [ { type: 'input', prop: 'remark', inputType: 'textarea', autosize: { minRows: 2, maxRows: 6 }, - label: k('remark'), placeholder: k('remark_required'), + label: this.key('remark'), placeholder: this.key('remark_required'), clearable: true, style: { width: '90%' } } ] ] @@ -556,14 +563,14 @@ formCols: [ **字段支持的属性:** -| 属?| 适用类型 | 说明 | +| 属性 | 适用类型 | 说明 | |------|---------|------| -| `type` | 全部 | `'input'`(文本输入)/ `'select'`(下拉) | +| `type` | 全部 | `'input'`(文本) / `'select'`(下拉) | | `prop` | 全部 | 绑定 `formData` 中的 key | -| `label` | 全部 | 表单项标?| -| `placeholder` | 全部 | 占位提示 | -| `inputType` | `input` | `'textarea'` 多行 / 不传为普?text | -| `autosize` | `input` | textarea ?`{ minRows, maxRows }` | +| `label` | 全部 | 表单项标签,支持 i18n key | +| `placeholder` | 全部 | 占位提示,支持 i18n key | +| `inputType` | `input` | `'textarea'` / 不传为普通 text | +| `autosize` | `input` | textarea 自适应:`{ minRows, maxRows }` | | `clearable` | 全部 | 是否可清空,默认 `true` | | `style` | 全部 | 样式对象 | | `options` | `select` | 选项数组 `[{ label, value }]` | @@ -571,13 +578,15 @@ formCols: [ --- -## 4. Composable API 参? +## 4. Composable API 参考 + ### 4.1 `useTableColumns(rawColumns, options?)` -**作用**:消除手动分?`idx` 序号,自动识别操作列和插槽列? -| 参数 | 类型 | 默认?| 说明 | +**作用**:消除手动分配 `idx` 序号,自动识别操作列和插槽列。 + +| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| -| `rawColumns` | `Array` | ?| 列定?| +| `rawColumns` | `Array` | — | 列定义 | | `options.selectionWidth` | `number` | `55` | 复选框列宽,传 `0` 隐藏 | | `options.indexWidth` | `number` | `0` | 序号列宽,传 `0` 隐藏 | @@ -585,37 +594,40 @@ formCols: [ const columns = useTableColumns([ { prop: 'code', label: '编码', width: 120 }, { prop: 'name', label: '名称' }, - { prop: '_actions', label: '操作', width: 160, fixed: 'right' } // ?操作?]) + { prop: '_actions', label: '操作', width: 160, fixed: 'right' } // 操作列 +]) ``` -**内部约定**:`prop === '_actions'` 自动标记?`slot: '_actions'`,由 `page-table` 渲染为操作列? +**内部约定**:`prop === '_actions'` 自动标记为 `slot: '_actions'`,由 `page-table` 渲染为操作列。 + --- ### 4.2 `useTableButtons(options, permissionCheck?)` -**作用**:统一生成工具栏按钮和行内操作按钮,自动注入权限校验结果? +**作用**:统一生成工具栏按钮和行内操作按钮,自动注入权限校验结果。 + | 参数 | 类型 | 说明 | |------|------|------| | `options.toolbar` | `Array` | 顶部按钮列表 | | `options.row` | `Array` | 行内按钮列表 | -| `permissionCheck` | `Function` | 权限函数,通常?`this.$permission` | +| `permissionCheck` | `Function` | 权限函数,通常为 `this.$permission` | -**返回?*:`{ toolbarButtons, rowButtons }` +**返回值**:`{ toolbarButtons, rowButtons }` -**按钮字段?* +**按钮字段:** | 字段 | toolbar | row | 说明 | |------|:---:|:---:|------| -| `key` | ?| ?| 唯一标识 | -| `label` | ?| ?| 显示文本 | -| `icon` | ?| ?| Element UI 图标 | -| `type` | ?| ?| `primary`/`success`/`warning`/`danger` | -| `color` | ?| ?| `'danger'` 使文字变?| -| `auth` | ?| ?| 权限 key | -| `cssStyle` | ?| ?| 自定义样?| -| `onClick` | ?| ?| 点击回调 | -| `hasPermission` | ?| ?| **自动注入**,由权限函数计算 | -| `needSelection` | ?| ?| 需选中行才能点?| +| `key` | √ | √ | 唯一标识 | +| `label` | √ | √ | 显示文本 | +| `icon` | √ | √ | Element UI 图标 | +| `type` | √ | — | `primary`/`success`/`warning`/`danger` | +| `color` | — | √ | `'danger'` 使文字变红 | +| `auth` | √ | √ | 权限 key | +| `cssStyle` | √ | √ | 自定义样式 | +| `onClick` | √ | √ | 点击回调 | +| `hasPermission` | √ | √ | **自动注入**,由权限函数计算 | +| `needSelection` | √ | — | 需选中行才能点击 | ```js const btns = useTableButtons({ @@ -636,55 +648,63 @@ const btns = useTableButtons({ ### 4.3 `i18nMixin(prefix)` -**作用**:注?`key(suffix)` ?`ckey(suffix)` 两个方法,消除每个页面手?`T` 常量?`tkey` 方法? -| 方法 | 返回?| 说明 | +**作用**:注入 `key(suffix)` 和 `ckey(suffix)` 两个方法,消除每个页面手动定义常量和 `tkey` 方法。 + +| 方法 | 返回值 | 说明 | |------|--------|------| -| `key(suffix)` | `prefix.suffix` | 当前页面?i18n key | -| `ckey(suffix)` | `page.common.suffix` | 公共 i18n key(如"帮助"?确定"?取消"?| +| `key(suffix)` | `prefix.suffix` | 当前页面的 i18n key | +| `ckey(suffix)` | `page.common.suffix` | 公共 i18n key | | 参数 | 类型 | 说明 | |------|------|------| -| `prefix` | `String` | 该页面的 i18n key 前缀,如 `'page.production_master_data.factory_model.factory_area'` | +| `prefix` | `String` | 该页面的 i18n key 前缀 | ```js import { i18nMixin } from '@/composables/useI18n' export default { mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')], - // 此后模板中用 $t(key('code')) 访问当前页面的翻? // ?$t(ckey('help')) 访问公共翻译 'page.common.help' + // 模板中用 $t(key('code')) 访问当前页面翻译 + // 模板中用 $t(ckey('help')) 访问公共翻译 'page.common.help' // JS 中用 this.$t(this.key('code')) / this.$t(this.ckey('help')) } ``` -**`data()` 中翻译文本的技?*? +**`data()` 中 i18n key 传参技巧**: + ```js data () { - const t = this.$t.bind(this) // 绑定 i18n 翻译函数 - const k = (s) => t(this.key(s)) // 当前页面翻译:key + translate - const ck = (s) => t(this.ckey(s)) // 公共翻译:ckey + translate - return { + // 传入完整 i18n key,组件内部 $t() 翻译,切换语言自动响应 formCols: [ - [{ label: k('code'), placeholder: k('enter_code') }] // 页面?key + [{ label: this.key('code'), placeholder: this.key('enter_code') }] ], - helpText: ck('help') // 公共 key + rules: { + code: [{ required: true, message: this.key('enter_code'), trigger: 'blur' }] + } } } ``` +> **原理**:`page-table` / `page-dialog-form` 内部使用 `$t()` 翻译传入的 key。`$t()` 是 Vue I18n 的响应式方法,切换语言时自动返回新的翻译结果,触发组件重新渲染。不要用 `k()` 等辅助函数提前翻译成静态字符串,否则语言切换后不会更新。 + --- -## 5. i18n 国际化方? -### 5.1 语言包文? +## 5. i18n 国际化方案 + +### 5.1 语言包文件 + | 语言 | 文件路径 | |------|---------| -| 简体中?| `src/locales/zh-chs.json` | +| 简体中文 | `src/locales/zh-chs.json` | | 繁体中文 | `src/locales/zh-cht.json` | | 英文 | `src/locales/en.json` | | 日文 | `src/locales/ja.json` | -### 5.2 语言包结? -?`一级模??二级模块 ?三级模块` 三层嵌套? +### 5.2 语言包结构 + +按 `一级模块 > 二级模块 > 三级模块` 三层嵌套: + ```json { "page": { @@ -693,20 +713,23 @@ data () { "factory_area": { "search": "查询", "reset": "重置", - "code": "所区编?, - "name": "所区名?, - "add": "??, - "edit": "??, - "delete": "??, - "enter_code": "请输入所区编?, - "enter_name": "请输入所区名?, - "add_title": "新增所?, - "edit_title": "编辑所?, + "code": "所区编码", + "name": "所区名称", + "add": "新 增", + "edit": "编 辑", + "delete": "删 除", + "enter_code": "请输入所区编码", + "enter_name": "请输入所区名称", + "add_title": "新增所区", + "edit_title": "编辑所区", "confirm": "确定", "cancel": "取消", "operation_success": "操作成功" } } + }, + "common": { + "help": "帮 助" } } } @@ -714,17 +737,22 @@ data () { ### 5.3 组件自动翻译 -`page-table` ?`page-dialog-form` 内置 `$t()`? -- **?label** ?自动翻译 -- **按钮 label** ?自动翻译 -- **表单 label / placeholder** ?自动翻译 -- **弹框 title / confirm / cancel** ?自动翻译 +`page-table` 和 `page-dialog-form` 内部使用 `$t()` 翻译: + +- **列 label** — 自动翻译 +- **按钮 label** — 自动翻译 +- **表单 label / placeholder** — 自动翻译 +- **弹框 title / confirm / cancel** — 自动翻译 +- **验证规则 message** — 自动翻译(`translatedRules` 计算属性) + +页面只需传入 i18n key,组件渲染时自动替换为当前语言的文本。 + +### 5.4 页面中手动翻译 + +搜索区、提示消息、`$confirm` 等 UI 文字需手动 `$t()`: -因此页面只需传入 i18n key 字符串,组件渲染时自动替换为当前语言的文本? -### 5.4 页面中手动翻? -搜索区、校验消息、`$confirm` ?UI 文字需手动?`$t()`? ```vue - + @@ -732,7 +760,8 @@ data () { ``` ```js -// JS ?this.$message.success(this.$t(this.key('operation_success'))) +// JS 中 +this.$message.success(this.$t(this.key('operation_success'))) this.$confirm(this.$t(this.key('confirm_delete')), this.$t(this.key('tip')), { ... }) ``` @@ -740,15 +769,18 @@ this.$confirm(this.$t(this.key('confirm_delete')), this.$t(this.key('tip')), { . ## 6. 常用场景速查 -### 场景 1:自定义列渲染(状?tag? -列定义加?`slot: true`? +### 场景 1:自定义列渲染(状态 tag) + +列定义加 `slot: true`: + ```js useTableColumns([ - { prop: 'status', label: '状?, slot: true, width: 100 } + { prop: 'status', label: '状态', slot: true, width: 100 } ]) ``` -模板中用 `#col-status` 插槽? +模板中用 `#col-status` 插槽: + ```vue