Files
workspace/code/fms/前端元数据运行时重构计划.md
T
2026-08-04 17:31:27 +08:00

263 lines
13 KiB
Markdown

# FMS 前端元数据运行时重构计划
## 1. 目标
本次重构以内部 ERP 快速开发为目标,将模块配置、运行时解析、列表编辑和页面组合能力集中到前端公共层。业务开发优先通过配置和组合完成,只有特殊业务流程编写页面代码。
预期结果:
```text
普通数据页面 配置 Module + Profile 后直接使用公共页面
组合业务页面 配置 PageNode,并通过公共运行时加载多个模块
特殊业务页面 复用公共运行时,只补充业务事件和业务流程
模块配置维护 统一在模块管理中完成,不修改后端
```
## 2. 硬约束
- 不兼容旧系统、旧模块模型和旧业务数据。
- 日常开发和运行期间不修改、不重启后端。
- 不新增业务 Controller、Service 或专用接口。
- 不修改 `/data/loaddata`、`/data/page`、`/data/saveobjt`、`/data/nextid`、`/data/describe` 的协议和处理逻辑。
- 所有多表保存继续通过一次 `/data/saveobjt` 在同一事务中提交。
- SQL Server `bigint` 在前端始终按字符串处理。
- 数据库约束负责主外键、唯一性、类型和树结构的最终完整性。
- 前端负责运行时解析、配置编排、交互校验、保存请求生成和错误定位。
- 后端重启不作为前端功能开发、配置调整或页面接入的前置步骤。
## 3. 保留的核心模型
| 模型 | 稳定职责 |
| --- | --- |
| `Module` | 可复用资源及公共属性 |
| `FunctionNode` | 菜单目录和页面入口 |
| `PageNode` | 页面内部结构和模块引用 |
| `Profile` | 同一数据模块的不同使用方式 |
| `Field` | 模块级字段定义 |
| `Capability` | Profile 级列表、表单、查询等配置及 Module 级操作 |
字段继续属于 Module;列表、表单、查询、导入和导出继续属于 Profile。不得把这些配置重新合并为一张大表。
当前阶段正式支持 `page` 和 `data` 模块。`report`、`dashboard`、`workflow` 只保留数据库契约,在对应运行能力完成前不作为常规配置入口。
## 4. 目标前端结构
```text
src/
module-runtime/
constants.js 元数据表名、视图名、节点类型和能力编码
runtimeResolver.js Module/Profile/PageNode 统一解析
configLoader.js 字段及 Profile 配置统一加载
conditionBuilder.js 固定条件、查询条件和页面参数组合
saveRequestBuilder.js inserts/updates/deletes 请求生成与排序
fieldRegistry.js 字段控件和渲染器注册
actionRegistry.js 通用操作和页面操作注册
useModuleRuntime.js 面向页面的统一组合入口
useModuleDataSource.js 分页、刷新、选择和查询状态
useModuleEditor.js 新增、编辑、删除、临时 ID 和批量保存
```
目录名可在实施时结合现有结构调整,但职责边界不得重新散落到业务页面。
## 5. 公共运行时契约
### 5.1 模块定位
公共运行时支持以下三种明确定位方式:
1. `moduleId`:使用该模块的默认 Profile
2. `profileId`:精确定位 Profile 并反查所属 Module
3. `pageNodeId`
同时提供 `moduleId + profileId` 时只用于校验两者归属关系。新页面不再使用 `moduleCode + profileCode` 定位,也不再通过页面路由后取“第一个 `module_ref`”作为隐式模块。一个页面存在多个模块引用时,必须通过 `pageNodeId` 明确定位。
### 5.2 运行时结果
`useModuleRuntime` 对调用方提供统一对象:
```text
module 模块及数据源
profile 当前使用方式
fields 启用字段
listConfig 列表配置
formConfig 表单配置
queryConfig 查询配置
actions 操作目录及有效权限
groups 布局分组
runtimeKey 当前配置唯一键
loading/error 加载状态
reload() 重新加载配置
```
业务页面不再自行查询 `s_module_field*`、`s_module_profile` 或 `v_s_module_runtime`。
### 5.3 扩展点
通用页面通过明确扩展点承载差异:
- `fixedCondition`
- `beforeLoad`
- `afterLoad`
- `beforeSave`
- `afterSave`
- `rowActions`
- `actionHandlers`
- 表格和表单插槽
扩展点只处理业务差异,不重新实现分页、字段解析、脏数据和批量保存。
## 6. 实施阶段
### 阶段 0:基线和旧设计清理
状态:`completed`
实际结果:旧设计文档已标记为历史方案;公共常量已建立;`otherdata` 只使用正式的 `group/module_ref` 节点类型。
- 以 `新数据库结构.md` 为唯一模块模型规范。
- 将旧的 `s_module_group`、`s_option_source`、旧节点类型和编码型 i18n 设计标记为历史方案。
- 建立前端元数据常量,统一 `directory/entry`、`group/tab/section/module_ref` 等枚举。
- 修正 `otherdata` 中 `category/group`、`module/module_ref` 的混合判断。
- 记录当前普通列表、可编辑列表、其他资料、用户管理、登录日志和多语言页面的行为基线。
验收:代码和有效文档中不再存在会参与运行的旧模型定义。
### 阶段 1:抽取运行时解析和配置加载
状态:`completed`
实际结果:已建立 ID 优先的公共解析、配置加载、并发版本控制和会话缓存;多模块页面必须使用 `pageNodeId`。
- 从 `FmsModuleListPage.vue` 抽取模块定位、Profile 定位和配置加载逻辑。
- 建立 `useModuleRuntime`,统一 ID 字符串转换、加载并发控制和错误状态。
- 建立显式页面节点解析规则,删除“第一个数据节点”约定。
- 保持现有 API 请求数量和运行行为,阶段内不调整页面视觉结构。
- 为同一运行时键增加会话级缓存;模块管理保存后可以定向失效缓存。
验收:普通页面可通过 `moduleId`、`profileId`、`pageNodeId` 或唯一页面引用定位;同一页面可通过不同 `pageNodeId` 明确加载多个数据模块。
### 阶段 2:抽取数据源和编辑器
状态:`completed`
实际结果:分页查询、查询状态、临时 ID、脏行、批量保存、批量删除和拖拽排序已从页面组件抽离;一次保存仍只调用一次 `saveobjt`。
- 从 `FmsModuleListPage.vue` 抽取分页、查询、排序和刷新状态到 `useModuleDataSource`。
- 抽取新增、编辑、删除、临时 ID、自动编码和保存请求生成到 `useModuleEditor`。
- 建立统一 `saveRequestBuilder`,负责表级变更合并及删除、更新、插入顺序。
- 一次用户保存只发起一次 `/data/saveobjt`。
- 保留 `FmsModuleTable`、`FmsQueryToolbar` 和现有页面插槽契约。
验收:`FmsModuleListPage.vue` 主要负责界面组合,不再同时承担元数据加载、数据访问和保存算法。
### 阶段 3:控件和操作注册中心
状态:`completed`
实际结果:字段注册中心已覆盖输入、长文本、数字、金额、日期、日期时间、布尔和选择控件;数字编辑使用字符串模式保护 bigint;操作注册中心已提供通用操作和页面 handler 扩展。
- 建立字段控件注册中心,收敛输入、数字、日期、布尔、选择和自定义渲染器映射。
- 建立操作注册中心,统一 `create/update/delete/export` 等通用行为。
- 特殊业务操作由页面注册 handler,不修改公共运行时主体。
- 注册项使用稳定 key;未知 key 明确报错并指出模块、Profile 和字段。
- 表格行内重型控件继续按需挂载,任意时刻只保留当前编辑实例。
验收:新增一种字段控件或通用操作只修改注册中心及对应实现,不修改所有业务页面。
### 阶段 4:模块管理提效
状态:`completed`
实际结果:创建数据模块自动创建默认 Profile;同步字段自动补齐列表和表单配置;Profile 可完整复制;模块管理已增加实时完整性检查和列表预览;常规新建入口只开放 `page/data`。
- 创建数据模块时自动创建 `default` Profile。
- 同步字段后,新字段自动生成默认列表和表单配置;失效字段只停用。
- 支持复制 Profile,并完整复制分组、列表、表单和查询配置。
- 模块管理增加配置预览和完整性检查结果,但不展示底层保存顺序。
- 常规入口只开放 `page` 和 `data`;预留模块类型放入明确的高级区域或隐藏。
- 保存后定向刷新运行时缓存、菜单和权限数据,不刷新整个应用。
验收:技术人员可以完成“创建模块、同步字段、调整配置、加入页面、立即使用”的完整流程。
### 阶段 5:页面编排和业务页面接入
状态:`completed`
实际结果:`contact`、`othercompany`、`user`、`login-log`、`i18n-type` 已接入公共运行时;`otherdata` 通过 `pageNodeId` 加载所选模块引用,不再按模块编码定位运行时。
- 页面组件通过 `pageNodeId` 或明确的 `moduleId/profileId` 加载数据模块。
- `otherdata` 使用统一 PageNode 枚举和运行时解析,不再维护独立模块解析逻辑。
- `contact`、`othercompany`、`user`、`login-log`、`i18n-type` 按风险从低到高接入公共运行时。
- 普通页面只保留模块声明和必要插槽;特殊页面只保留业务流程。
- 不为了接入公共运行时修改后端或重启后端。
验收:新增普通数据页面只需要配置 Module/Profile 和一个轻量页面入口。
### 阶段 6:清理与稳定性验证
状态:`in_progress`
已完成:公共运行时代码已通过 Node 语法检查、定向格式检查和 Vite 生产构建;异步请求均使用运行时键或版本号丢弃过期结果;bigint ID 和数字编辑按字符串处理。
待完成:连接实际运行数据后回归普通列表、可编辑列表、`otherdata`、多模块页面和机构切换,并确认每个路由页面只有一个启用的数据模块引用。
- 删除已无调用方的重复加载、条件组合和保存函数。
- 删除旧枚举适配、旧模块解析路径和演示数据。
- 检查所有 bigint 比较、Map key、选择值和请求参数均按字符串处理。
- 检查模块切换、路由切换和机构切换时不会应用过期异步结果。
- 检查大列表滚动、编辑器按需挂载和批量保存请求数量。
- 执行 `pnpm fmt:check`;除非用户明确要求,不执行构建、不启动或重启后端。
验收:公共运行时成为唯一模块解析入口,业务页面没有重复实现模块配置加载。
## 7. 优先改造文件
| 优先级 | 文件/目录 | 处理 |
| --- | --- | --- |
| P0 | `src/components/fms-module-list/FmsModuleListPage.vue` | 拆分运行时、数据源和编辑逻辑 |
| P0 | `src/views/base/otherdata/index.vue` | 统一 PageNode 枚举和模块定位 |
| P0 | `src/views/module-management/composables/useModuleResources.js` | 复用请求构造、缓存失效和字段同步能力 |
| P1 | `src/views/module-management/composables/usePageComposition.js` | 使用统一节点规则和运行时定位 |
| P1 | `src/views/module-management/composables/useFunctionStructure.js` | 使用统一节点常量和树校验 |
| P1 | `src/stores/permissions.js` | 与运行时共享模块、节点和操作索引 |
| P2 | 使用 `FmsModuleListPage` 的业务页面 | 逐页删除重复配置加载和保存逻辑 |
## 8. 性能和运行连续性
- 元数据按 `orgId + moduleId + profileId` 缓存,避免同一页面重复请求。
- 模块配置保存后只失效相关缓存,不清空所有模块缓存。
- 页面或机构切换使用版本号丢弃过期请求结果。
- 列表业务数据始终分页;不因元数据重构增加全表读取。
- 多个元数据请求可并行加载,但不得在行循环中发起请求。
- 批量编辑只生成一次保存请求,不逐行调用接口。
- 前端变更通过 Vite 热更新生效;实施期间不重启后端。
## 9. 非目标
- 不建立传统业务 Controller/Service/DAO 分层。
- 不为每个模块生成后端代码。
- 不实现旧模块表兼容、双写或旧数据转换。
- 不在本次重构实现报表引擎、仪表盘设计器或工作流引擎。
- 不重写 `fms-table`、文件组件或已有手写业务流程。
- 不一次性改造所有业务页面;公共能力稳定后逐页接入。
## 10. 执行纪律
- 每个阶段单独完成、验证和交付,不跨阶段进行大范围改写。
- 开始一个阶段前记录受影响页面和当前行为。
- 公共组件改动后优先回归普通列表、可编辑列表和 `otherdata`。
- 发现通用接口无法满足需求时停止该项,说明缺口后再决定是否允许修改后端。
- 每阶段完成后更新本文件状态和实际结果,未通过验收不得进入下一阶段。
## 11. 完成标准
满足以下条件后,本计划完成:
- 技术人员可以不修改后端完成数据模块创建、字段同步、Profile 配置和页面挂载。
- 开发人员可以通过公共组件或 composable 接入模块,不重复编写元数据查询和保存流程。
- 普通模块新增和配置调整不需要后端重启。
- 同一数据模块可被多个页面和多个 Profile 复用。
- 页面包含多个模块时定位明确,不依赖隐式顺序。
- 前端只保留一套有效模块模型和节点枚举。
- 批量保存、权限刷新、缓存失效和异步切换行为可预测。