Files
iotlcm/vue/apps/web-antd/src/template/iotlcm-crud-style/README.md
T
MiFaZhan 644cbe164d docs: 修复 crud-style 模板 README 的悬空引用
MEMORY.md 已从开源仓移除,README 仍要求「必须同步阅读 MEMORY.md」并列出其内容。
改为直接用「常见踩坑」小节承载这些纠错要点,目录树中同步去掉 MEMORY.md 条目。
2026-10-30 11:50:18 +08:00

19 KiB
Raw Blame History

IOTLCM CRUD 新风格模板

本目录用于把默认生成或旧风格的 IOTLCM CRUD 页面迁移为当前统一风格。模板是可复制使用的示例,迁移时先复制文件,再按真实模块替换类型名、API 路径、字段、主键、权限码和表格标题。

目录说明

iotlcm-crud-style/
├── README.md
├── template/
│   ├── api/
│   │   ├── index.ts  # 类型化 API 调用模板
│   │   └── type.ts   # API 参数和响应类型模板
│   └── view/
│       ├── index.vue # 列表页模板
│       ├── edit.vue  # 新增/编辑弹窗模板
│       ├── view.vue  # 查看弹窗模板
│       └── model.tsx # 查询表单、表格列、编辑表单、查看描述配置
└── compare/
    ├── before/       # 修改前完整代码快照
    └── after/        # 修改后完整代码快照

compare 是完整对照组,不是片段示例:

  • 对照文件统一使用 .txt 后缀保存完整源码,避免旧代码被 ESLint/TypeScript 当作真实业务代码扫描。
  • 迁移时必须同时看 before/api -> after/api 和 before/view -> after/view,不要只改视图。

常见踩坑

模板迁移中的典型纠错,动手前先过一遍:

  • 接口返回未知时不要凭前端字段伪造响应类型,保留 any 并明确说明。
  • 查看弹窗内新增 Tab/Grid 时,相关列和查询表单应放在 model.tsx 的 ViewDescSchema 后面。
  • 弹窗内 Grid 应使用 BetterVben.grid.gridOptions.modal。
  • 弹窗内 Grid 的数据加载应使用 proxyConfig.ajax.query,打开弹窗后调用 gridApi.query(),不要手动 setGridOptions({ data })。

迁移前检查

  1. 确认视图目录和 API 目录是一一对应的,例如:
    • 视图:apps/web-antd/src/views/iotlcm/tLifeCycle/tExample
    • API:apps/web-antd/src/api/iotlcm/tLifeCycle/tExample
  2. 先打开真实模块的 type.ts,确认主键字段。不要默认所有模块都用 id,也可能是 appKey、deptId、regionId 等。
  3. 记录原代码里的业务差异:自定义按钮、特殊 API、权限码、查询参数转换、插槽渲染、弹窗传参、提交前后处理。
  4. 检查本地 Skill 和项目参考:优先参考 C:\Users\HuanTian\.claude\skills 下的 vben-page-guide、vben-grid-guide、vben-form-guide、vben-modal-guide、vben-form-grid。

硬性执行边界

视图模块和 API 模块是一个迁移单元。即使用户只点名 apps/web-antd/src/views/iotlcm/...,也必须同步检查并改造对应的 apps/web-antd/src/api/iotlcm/...:

用户给出的路径 必须同步处理的路径
apps/web-antd/src/views/iotlcm/tLifeCycle/tBatch apps/web-antd/src/api/iotlcm/tLifeCycle/tBatch
apps/web-antd/src/views/iotlcm/tChannel/tSimChannels apps/web-antd/src/api/iotlcm/tChannel/tSimChannels

跳过 API 文件属于迁移未完成。最终结果至少要覆盖:

  • API:index.ts、type.ts
  • 视图:index.vue、edit.vue、view.vue、model.tsx

不要做“半旧半新”的局部改造。旧代码中的业务字段、接口路径、权限码、查询参数可以保留,但面板骨架必须收敛到本模板。尤其是从旧 Drawer 页面迁移时,不能只把 useVbenDrawer 改成 useVbenModal 后继续保留旧写法。

目标参考

新人迁移时可以把 template/view/、template/api/ 和 compare/after/ 当作最终形态参考。迁移后的结果不要求字段完全相同,但代码组织和基础逻辑应基本一致:

  • index.vue 使用 Page + Grid + EditModal + ViewModal。
  • index.vue 中表格标题、导出文件名、权限码直接替换为真实模块值。
  • index.vue 中 formOptions.schema 使用 IndexGridSchema,gridOptions.columns 使用 IndexGridColumns。
  • index.vue 操作插槽使用 #action="{ row }",查看、修改、删除方法都接收 row。
  • edit.vue 使用 ModalProps { data, isUpdate }、handleOpen、handleConfirm、handleCancel。
  • edit.vue 编辑回显调用 View,提交调用 Edit(value as XxxModel) 或真实编辑参数类型。
  • view.vue 使用 ViewDescSchema + Description + useDescription。
  • 基础 CRUD 的 model.tsx 只导出 IndexGridSchema、IndexGridColumns、EditFormSchema、ViewDescSchema 这四类核心配置。
  • 如果查看弹窗内新增业务 Tab/Grid,相关列配置和查询表单属于查看弹窗扩展配置,允许追加在 ViewDescSchema 后面。
  • model.tsx 内部顺序固定为:表格查询 IndexGridSchema -> 表格列 IndexGridColumns -> 新增/编辑表单 EditFormSchema -> 查看描述 ViewDescSchema。
  • api/index.ts 从同级 type.ts 导入参数和响应类型,不再使用 any。

以 compare/after 中的 tSimChannels 完整对照为例,模板占位替换应类似这样:

模板内容 真实模块内容
TemplateCrudModel TSimChannelsModel
TemplateCrudListParam TSimChannelsListParam
TemplateCrudListRes TSimChannelsListRes
TemplateCrudEditParam TSimChannelsEditParam
TemplateCrudViewParam TSimChannelsViewParam
TemplateCrudViewRes TSimChannelsViewRes
TemplateCrudDeleteParam TSimChannelsDeleteParam
TemplateCrudExportParam TSimChannelsExportParam
iot/templateCrud iot/tSimChannels
示例业务 通道管理
cpm:iot:templateCrud cpm:iot:tSimChannels

路径一:基于默认生成代码应用新模板

这种情况适用于刚生成、还没有明显业务改动的页面。处理目标是把默认生成代码统一为新结构。

1. 检查 API 类型

先检查对应 API 目录下是否存在 type.ts。

  • 如果 type.ts 存在:继续按下面规则修正 API 类型和 index.ts。
  • 如果 type.ts 不存在:跳过 API 类型化流程,不要临时编造类型文件;在最终输出中明确提示用户“对应 API 缺少 type.ts,需要先补全类型后才能完成 API 类型化迁移”。

必须包含这些类型:

XxxModel
XxxListParam
XxxListRes
XxxExportParam
XxxEditParam
XxxViewParam
XxxViewRes
XxxDeleteParam

规则:

  • XxxListRes 使用 GlobalListRes<XxxModel>。
  • XxxExportParam 通常复用 XxxListParam。
  • XxxViewParam 写真实主键字段。
  • XxxDeleteParam 按后端约定写单值或数组。
  • 字段注释保留或补齐,便于后续判断主键和业务字段。

2. 替换 API index.ts

用 template/api/index.ts 的结构改造真实 API 文件。

必须满足:

  • 从 ./type 导入请求参数和响应类型。
  • List(params: XxxListParam) 返回 requestClient.get<XxxListRes>(...)。
  • View(params: XxxViewParam) 返回 requestClient.get<XxxViewRes>(...)。
  • Edit/Delete/Export 使用明确参数类型。
  • 保留函数名 List/Delete/Edit/View/Export。
  • 不再使用 params: any、requestClient.get<any> 这类旧写法。

API 验收时必须同时满足:

不存在:params: any
不存在:requestClient.get<any>
存在:List(params: XxxListParam)
存在:requestClient.get<XxxListRes>
存在:View(params: XxxViewParam)
存在:requestClient.get<XxxViewRes>

3. 改造 model.tsx

把旧导出名统一为新导出名:

旧风格 新风格 用途
querySchema IndexGridSchema 表格查询表单
columns IndexGridColumns 表格列
editSchema EditFormSchema 编辑表单
viewSchema ViewDescSchema 查看描述

必须清理旧残留:

querySchema
editSchema
viewSchema
State
newState
BasicDrawer
useVbenDrawer
rules: null
export const columns
import { columns

注意:columns 是 useVbenVxeGrid 的合法配置项,不要裸搜 columns 后全部清理。只清理旧导出 export const columns、旧导入 import { columns } 或把 columns 当表格列变量直接传入的旧写法。

字段配置不要照抄模板里的 name/code/status/remark 示例字段。真实模块要从旧 model.tsx、接口字段或 type.ts 迁移字段。可以参考 compare/after/view/model.tsx.txt 的最终形态:保留真实业务字段,同时清理旧生成器残留。

model.tsx 必须满足这些结构和样式规则:

  • 导出顺序固定为 IndexGridSchema -> IndexGridColumns -> EditFormSchema -> ViewDescSchema。
  • IndexGridColumns 中删除旧生成器残留的 align: 'left'。
  • IndexGridColumns 中删除 width: -1,根据字段补合适宽度,普通字段优先用 minWidth: 120。
  • 操作列使用固定宽度,例如 { title: '操作', width: 120, slots: { default: 'action' }, fixed: 'right' }。
  • IndexGridSchema 和 EditFormSchema 中删除旧生成器残留的 formItemClass: 'col-span-1'。
  • EditFormSchema 只放新增/编辑字段,不要混入查看字段。
  • ViewDescSchema 只放查看字段,顺序应跟详情展示一致,不要插到 EditFormSchema 前面。

model.tsx 禁止残留:

align: 'left'
width: -1
formItemClass: 'col-span-1'
formItemClass: "col-span-1"

4. 改造 index.vue

复制 template/view/index.vue 的结构,再替换真实模块信息。

必须替换:

  • TemplateCrudModel -> 真实 XxxModel
  • ../api/type -> 真实 API 类型路径
  • ../api -> 真实 API 路径
  • 示例业务 -> 真实表格标题和导出文件名
  • cpm:iot:templateCrud -> 真实权限前缀
  • 删除和批量删除里的主键字段

模板为了在 src/template 目录下自包含,使用了 ../api 相对导入。迁移到真实业务模块后,API 导入应改成 #/api/iotlcm/... 形式,并放回 API 接口导入分组。

结构要求:

  • 使用 Page + useVbenVxeGrid + useVbenModal。
  • 代码区域顺序固定为 常量 -> 数据 -> 方法 -> 组件 -> 周期。
  • formOptions.schema 使用 IndexGridSchema。
  • gridOptions.columns 使用 IndexGridColumns。
  • 操作列插槽统一写 #action="{ row }"。
  • 列表查询中清空 selectRows.value。
  • 保留新增、批量删除、导出、查看、编辑、删除的标准入口。
  • 工具栏按钮必须放在 <div class="flex items-center justify-center gap-2"> 中,按钮自身使用 class="flex items-center"。
  • 操作列按钮必须放在 <div class="flex items-center justify-center gap-2"> 中,链接按钮自身使用 class="border-none p-0"。
  • 按钮组间距统一由父容器 gap-2 控制,不使用按钮自身的 mr-2 维护间距。

useVbenVxeGrid 配置必须以模板为准:

  • formOptions 默认只保留 ...(BetterVben.grid.formOptions.default as any) 和 schema: IndexGridSchema。
  • gridOptions 默认只保留 ...(BetterVben.grid.gridOptions.default as any)、columns: IndexGridColumns 和 proxyConfig。
  • 引入 BetterVben 默认配置后,不要继续叠加旧生成器默认项:fieldMappingTime: [['date', ['start', 'end']]]、submitOnChange、submitOnEnter、checkboxConfig、rowConfig 都要默认删除。
  • 如果业务确实依赖时间字段映射、行主键或复选框展示配置,必须按真实字段重新写,不能保留示例 date/start/end/id。最终汇报里要说明这是业务保留项。
  • 判断标准:旧默认面板带来的配置删除;真实业务字段转换、交互或后端参数需要的配置保留并验证。

5. 改造 edit.vue

复制 template/view/edit.vue 的结构,再替换真实模块信息。

必须替换:

  • TemplateCrudModel -> 真实 XxxModel
  • View({ id: data.data.id }) -> 真实详情主键参数
  • Edit(value as TemplateCrudModel) -> 真实模型类型或编辑参数类型,默认生成页通常使用真实 XxxModel

结构要求:

  • 使用 ModalProps { data, isUpdate }。
  • 打开编辑弹窗时调用 View 获取最新详情。
  • 提交时使用 BetterVben.function.formValidate 和 BetterVben.function.formSubmit。
  • onConfirm 和 onOpenChange 使用 BetterVben.function.modalAutoLoadAsync 包裹。
  • 取消时关闭弹窗并 resetForm。
  • 表单组件必须命名为 const [Form, formApi] = useVbenForm(...),模板中必须使用 <Form />。
  • useVbenForm 必须使用 ...BetterVben.form.default 作为基础配置,除非已有业务明确需要保留特殊布局。

edit.vue 禁止残留这些旧写法:

DrawerProps
BasicDrawer
BasicForm
EditDrawer
editDrawerApi
reload
useVbenDrawer
formApi.validate()
formApi.getValues()
modalApi.setState({ confirmLoading
drawerApi
showConfirmButton
readonly
only-read

说明:

  • 如果旧页面有查看态 view,新风格应交给独立 view.vue 处理,不要把查看态混进 edit.vue。
  • 如果旧页面有 id/update/view 传参,迁移后统一改成 ModalProps { data, isUpdate },由 index.vue 调用 editModalApi.setData({ data: row, isUpdate: !!row })。
  • 如果旧页面用 reload 事件,迁移后统一改为 confirm 事件,父组件使用 <EditModal @confirm="handleRefresh" />。
  • 如果旧页面手写表单校验和 loading,迁移后必须改为 BetterVben.function.formValidate、formSubmit、modalAutoLoadAsync。

6. 改造 view.vue

复制 template/view/view.vue 的结构,再替换真实模块信息。

必须替换:

  • TemplateCrudModel -> 真实 XxxModel
  • View({ id: row.id }) -> 真实详情主键参数
  • ViewDescSchema 保持新命名

结构要求:

  • 使用 Description + useDescription。
  • 打开弹窗时从 modalApi.getData() 取 { row }。
  • schema 使用 ViewDescSchema。
  • 查看弹窗不提交,模板中保留 :footer="false"。

7. 扫描旧风格残留

迁移后在目标模块执行:

rg -n "querySchema|editSchema|viewSchema|State|newState|BasicDrawer|BasicForm|DrawerProps|useVbenDrawer|rules:\s*null|export const columns|import .*columns|align:\s*'left'|width:\s*-1|formItemClass:\s*['\"]col-span-1['\"]|fieldMappingTime|submitOnChange|submitOnEnter|checkboxConfig|rowConfig|formApi\.validate\(|formApi\.getValues\(|modalApi\.setState\(\{ confirmLoading|drawerApi|emit = defineEmits<\{ reload" apps/web-antd/src/views/iotlcm/目标模块路径

如果扫描结果命中真实旧变量或旧默认配置,继续清理;如果命中确实需要保留的业务配置,必须确认字段名和行为仍然正确,不能直接忽略。

路径二:不影响既有逻辑应用新面板

这种情况适用于旧代码已经被人改过,不能简单整体覆盖。处理目标是只统一外层风格,不改变现有功能。

1. 先做业务盘点

改动前先列出旧代码中的特殊逻辑:

  • 工具栏额外按钮,例如导入、审核、同步、分配、启停。
  • 操作列额外按钮,例如详情跳转、重置、启用、禁用。
  • 特殊 API,例如 Enable、Disable、Sync、Import。
  • 查询参数转换,例如时间范围拆分、枚举转数字、过滤空字符串。
  • 编辑回显处理,例如 JSON 字符串转对象、数组合并、字段补默认值。
  • 提交处理,例如提交前字段清洗、提交后刷新其他数据。
  • 权限码和路由跳转。
  • 插槽渲染,例如字典、成员摘要、可点击字段、自定义组件。

2. 只替换面板骨架

不要直接删除旧文件后重写。推荐顺序:

  1. 先把旧文件中所有特殊逻辑标记出来。
  2. 复制新模板骨架,不要从旧 Drawer 文件上局部改。
  3. 把旧特殊逻辑按区域放回,只保留业务逻辑,不保留旧面板写法。
  4. 对比原文件确认功能入口没有少。

区域放置规则:

内容 放置区域
defineEmits、静态标题、权限码、常量枚举 常量
ref、computed、reactive、inject 数据
CRUD 方法、自定义业务方法、参数转换 方法
useVbenVxeGrid、useVbenModal、useVbenForm、useDescription 组件
watch、onMounted 等生命周期 周期

3. 保持业务行为不变

以下内容不能因套模板而擅自改变:

  • API URL。
  • 后端参数名。
  • 主键字段。
  • 权限码。
  • 表格字段顺序和必要插槽。
  • 新增、编辑、查看的传参结构。
  • 提交前后的数据转换。
  • 用户已经新增的按钮和业务方法。

如果新模板和旧业务逻辑冲突,以旧业务逻辑为准,只把代码组织方式调成新风格。

这里的“以旧业务逻辑为准”只指业务行为,不包括旧代码结构。下面这些不是业务逻辑,必须按新模板替换:

  • BasicDrawer/useVbenDrawer
  • BasicForm
  • DrawerProps
  • reload 事件
  • 手写 loading
  • 手写 formApi.validate/getValues
  • 编辑弹窗里的查看态只读逻辑
  • 旧生成器默认 fieldMappingTime/submitOnChange/submitOnEnter
  • 旧生成器默认 checkboxConfig/rowConfig
  • 按钮自身使用 mr-2 维护按钮组间距

如果旧代码里有真正的时间范围查询、行主键或复选框业务要求,可以在新模板里重新加回,但必须替换成真实字段并验证效果。

4. 逐项回归

完成后人工核对:

  • 搜索条件是否都还在。
  • 表格列和操作列是否都还在。
  • 新增、编辑、查看、删除、批量删除、导出是否可触发。
  • 自定义按钮是否仍可触发原方法。
  • 编辑回显参数是否还是原主键。
  • 提交 payload 是否没有少字段。

命名和代码风格要求

导入顺序:

  1. Vue 相关
  2. UI 组件
  3. API 接口
  4. 自定义组件
  5. 常量配置
  6. 工具函数
  7. 相对导入

代码区域顺序:

/** ====================  常量  ==================== */
/** ====================  数据  ==================== */
/** ====================  方法  ==================== */
/** ====================  组件  ==================== */
/** ====================  周期  ==================== */

如果存在类型声明,可放在 常量 前的 类型 区域:

/** ====================  类型  ==================== */

验证命令

在 vue 目录下执行:

pnpm exec prettier --check apps/web-antd/src/template/iotlcm-crud-style
pnpm exec eslint apps/web-antd/src/template/iotlcm-crud-style --ext .ts,.tsx,.vue

迁移真实模块后,额外执行目标模块扫描:

rg -n "querySchema|editSchema|viewSchema|State|newState|BasicDrawer|BasicForm|DrawerProps|useVbenDrawer|rules:\s*null|export const columns|import .*columns|align:\s*'left'|width:\s*-1|formItemClass:\s*['\"]col-span-1['\"]|fieldMappingTime|submitOnChange|submitOnEnter|checkboxConfig|rowConfig|formApi\.validate\(|formApi\.getValues\(|modalApi\.setState\(\{ confirmLoading|drawerApi|emit = defineEmits<\{ reload" apps/web-antd/src/views/iotlcm/目标模块路径

全量 typecheck 如果命中项目既有错误,需要再过滤目标模块路径,确认本次迁移目标没有新增类型错误。

API 也必须扫描:

rg -n "params:\s*any|requestClient\.(get|post)<any>" apps/web-antd/src/api/iotlcm/目标模块路径

如果目标 API 目录不存在 type.ts,最终回复必须包含:

未完成 API 类型化:目标 API 目录缺少 type.ts,请先补全类型文件。