Files
workspace/code/fms/.codebuddy/plans/模块管理迁移计划_e4a461a6.md
T
2026-08-11 16:51:11 +08:00

264 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: 模块管理迁移计划
overview: 将 fms-vue2(antdv)的模块管理页面迁移到 fms-vue(shadcn-vue + reka-ui + tailwindcss),核心面板包括:基本信息、字段定义、权限配置、多语言。沿用 originals+drafts+diff 数据流模式,复用通用数据 API。
todos:
- id: create-tool-functions
content: 创建工具函数:dataChanges.ts(diffRows/tableChange/allocateTemporaryIds/cloneData/hasChanges)、tempId.ts(createTempId)、i18n.ts(validateModuleCode/buildModuleI18nKey 等),TypeScript 化旧系统代码
status: completed
- id: create-types-and-service
content: 创建类型定义 types/module.ts 和 API 服务 services/moduleManagement.ts(封装 loadTree/loadModuleConfig/saveModule/deleteModule/syncFields)
status: completed
- id: create-loading-overlay
content: 创建 FmsLoadingOverlay.vue 加载遮罩组件
status: completed
- id: rewrite-index-page
content: 重写 index.vue 主页面:左侧树(FmsTree + 搜索 + 右键菜单 + 拖拽分隔线)+ 右侧详情(Header + section nav + 面板切换)+ 数据加载/保存/删除/字段同步完整逻辑
status: completed
dependencies:
- create-tool-functions
- create-types-and-service
- create-loading-overlay
- id: create-basic-panel
content: 使用 [skill:shadcn-vue] 创建 ModuleBasicPanel.vue:基于 shadcn-vue Form 组件(vee-validate + zod)构建表单,按模块类型动态显示字段,含图标选择器
status: completed
dependencies:
- create-tool-functions
- create-types-and-service
- id: create-fields-powers-i18n-panels
content: 创建 ModuleFieldsPanel.vue(StkTable + 同步字段)、ModulePowersPanel.vue(StkTable + 增删改)、ModuleI18nPanel.vue(StkTable + 受管键 + I18nQuickSetModal),从旧系统迁移完整逻辑
status: completed
dependencies:
- create-tool-functions
- create-types-and-service
---
## 用户需求
将 fms-vue2 旧系统的模块管理页面迁移到 fms-vue 新系统,实现模块元数据的管理功能。
## 核心功能
### 1. 模块树管理
- 左侧树形面板展示所有启用模块(s_module 表),支持虚拟根节点
- 支持按模块名称/编码搜索过滤
- 右键菜单:添加目录、添加页面、添加数据分类、添加数据、删除节点
- 删除节点前校验无子节点,级联删除所有子表数据
- 左侧面板宽度可拖拽调整(210px ~ 440px)
- 点击树节点加载右侧配置详情
### 2. 基本信息面板(所有模块类型)
- 表单字段:b_code(编码)、b_name(名称)、b_module_type(类型:目录/页面/数据分类/数据)、b_parent_id(上级模块,只读展示父名称)、b_xh(排序号)
- 目录/页面可见:b_icon(图标选择)、b_route(路由,页面必填)、b_canmenu(显示菜单开关)
- 数据可见:b_viewtable(查询视图,必填)、b_savetable(保存表)、b_keyfield(主键字段)
- 模块类型变更时自动清空不适用的字段
- Header 区域:模块名称 + 类型标签 + 启用开关 + 显示菜单开关 + 保存按钮
### 3. 字段定义面板(data 类型)
- 表格展示字段列表(b_field 字段名、b_name 显示名、b_type 控件类型、b_canuse 启用)
- 工具栏:同步字段按钮(调用 describeApi 拉视图列,与现有字段合并)
- 支持行内编辑、上移/下移、删除行
- 同步逻辑:视图已有列保留原配置、新增列追加默认行、已删列移除并清理关联配置
### 4. 权限配置面板(page 类型)
- 表格展示操作权限列表(b_code 权限编码、b_name 权限名称、b_canuse 启用)
- 支持上移/下移、新增行、删除行
### 5. 多语言面板(非 directory 类型)
- 表格展示翻译条目(b_key 多语言键、各语言列的值)
- 受管键(模块/字段/权限的自动生成键)只读展示
- 非受管键支持手动编辑
- 支持快速设置弹窗
### 6. 统一保存
- 点击保存按钮,通过 diffRows 比较 drafts 与 originals 计算增量
- 子表临时负数 ID 通过 allocateTemporaryIds 批量替换为真实雪花 ID
- 调用 saveObjectApi 在同一事务中批量提交所有表的变更
- 保存成功后更新 originals 快照、刷新树节点、刷新权限数据
## 技术栈
- 前端框架:Vue 3 + TypeScript + Vite
- UI 组件:shadcn-vue(reka-ui)+ Tailwind CSS
- 表单验证:vee-validate + zod
- 表格:stk-table-vue(StkTable)
- 状态管理:Pinia
- Toast:vue-sonner
- 图标:lucide-vue
- API 层:复用现有 api.ts(loadDataApi / saveObjectApi / nextIdApi / describeApi / loadDataBySqlApi)
## 实现方案
### 整体策略
从旧系统完整迁移核心逻辑,适配新系统的 TypeScript + shadcn-vue + tailwindcss 技术栈。复用新系统已有的 FmsTree 树组件和 buildTree/sortTree 工具,新建缺失的工具函数和面板组件。
### 数据流架构
沿用旧系统 originals + drafts + diff 模式:
```mermaid
flowchart TD
A[loadTree: loadDataApi s_module] --> B[buildTree 构建树结构]
B --> C[用户点击树节点]
C --> D[loadModuleConfig: 按模块类型并行加载子表]
D --> E[originals = 加载的快照]
E --> F[drafts = cloneData originals]
F --> G[子面板编辑 drafts]
G --> H[用户点击保存]
H --> I[diffRows 比较 drafts vs originals]
I --> J[allocateTemporaryIds 替换临时负数ID]
J --> K[saveObjectApi 批量提交]
K --> L[originals = cloneData drafts]
L --> M[刷新树节点 + 权限数据]
```
### 关键设计决策
1. **TypeScript 化**:所有工具函数和组件使用 TypeScript 编写,类型安全
2. **表单方案**:使用 shadcn-vue 的 Form 组件(vee-validate + zod),参考 login 页面的使用模式
3. **表格方案**:使用 stk-table-vue 的 StkTable,保持与新系统一致
4. **临时 ID**:子表新增行使用前端临时负数 ID,保存时通过 allocateTemporaryIds 批量替换
5. **模块类型路由**:section nav 根据 b_module_type 动态显示面板入口,使用 v-if 切换而非路由跳转
6. **权限控制**:通过 permissionStore.canPower('m_metadata', actionCode) 控制按钮可用性
### 文件拆分原则
- index.vue:主协调层(树交互、数据加载/保存、面板切换、权限校验),目标控制在 400 行以内
- 各面板组件:独立文件,通过 props 接收数据、emit 通知变更
- 工具函数:按职责拆分到 utils/ 下独立文件
- 类型定义:统一放在 types/ 目录
## 实现细节
### 性能考量
- 树加载:一次性加载全部 s_module 行(b_canuse = 1),前端 buildTree 构建,无需多次请求
- 配置加载:按模块类型并行请求(Promise.all),非 data 类型只加载必需表
- 保存:一次 saveObjectApi 调用包含所有表的变更,事务保证一致性
- diffRows 比较:使用 Map 按 b_id 索引,O(n) 复杂度
- 树搜索:前端 filterTree 递归过滤,无需后端请求
### 日志与错误处理
- 加载失败:toast.error 提示用户,不清空已有数据
- 保存失败:toast.error 提示,drafts 不回退(保留用户编辑)
- 权限不足:按钮 disabled,不显示 toast
### 兼容性
- b_id 始终序列化为字符串
- boolean 列使用 int 0/1
- API 请求格式与后端 DataController 完全一致
- 不修改任何通用端点
## 目录结构
```
fms-vue/src/
├── utils/
│ ├── dataChanges.ts # [NEW] 数据变更工具。导出 cloneData、diffRows、allocateTemporaryIds、remapForeignKeys、tableChange、hasChanges 函数。TypeScript 化旧系统 dataChanges.js,适配新系统 API 类型。
│ ├── tempId.ts # [NEW] 临时 ID 生成。导出 createTempId 函数,生成会话内唯一的负数字符串 ID("-1"、"-2"…)。TypeScript 化旧系统 tempId.js。
│ └── i18n.ts # [NEW] 多语言工具。导出 validateModuleCode、buildModuleI18nKey、buildModuleItemI18nKey、syncModuleItemI18nKeys、groupI18nRows、mergeI18nDefaults、consolidatePivotRows 函数。TypeScript 化旧系统 utils/i18n.js。
├── types/
│ └── module.ts # [NEW] 模块管理类型定义。定义 ModuleData、ModuleField、ModulePower、I18nRow 等接口,以及 SectionName、ModuleType 等类型。
├── services/
│ └── moduleManagement.ts # [NEW] 模块管理 API 服务。封装 loadTree、loadModuleConfig、saveModule、deleteModule、syncFields 等业务方法,内部调用 api.ts 通用端点。
├── views/system/module-management/
│ ├── index.vue # [MODIFY] 主页面。完整重写:左侧树 + 可拖拽分隔线 + 右侧 Header + section nav + 面板区。实现树交互、数据加载/保存、面板切换、权限校验。
│ └── components/
│ ├── ModuleBasicPanel.vue # [NEW] 基本信息面板。使用 shadcn-vue Form 组件(vee-validate + zod)构建表单,根据模块类型动态显示/隐藏字段。支持图标选择器(复用项目已有的 lucideIcons 工具)。
│ ├── ModuleFieldsPanel.vue # [NEW] 字段定义面板。使用 StkTable 展示字段列表,支持行内编辑、上移/下移、删除。工具栏包含"同步字段"按钮(调用 describeApi)。
│ ├── ModulePowersPanel.vue # [NEW] 权限配置面板。使用 StkTable 展示权限列表,支持上移/下移、新增行、删除行。
│ ├── ModuleI18nPanel.vue # [NEW] 多语言面板。使用 StkTable 展示翻译条目(b_key + 各语言列),支持受管键只读展示、非受管键行内编辑。包含 I18nQuickSetModal 快速设置弹窗。
│ ├── I18nQuickSetModal.vue # [NEW] 多语言快速设置弹窗。使用 Dialog 组件,表单输入各语言翻译值,提交后合并到 i18n 草稿中。
│ └── FmsLoadingOverlay.vue # [NEW] Loading 遮罩层组件。使用 shadcn-vue 的 Skeleton 或自定义 spinner,覆盖在内容区域上方显示加载状态。新系统中不存在此组件。
```
## 关键代码结构
### 类型定义(types/module.ts)
```typescript
export type ModuleType = 'directory' | 'page' | 'data_category' | 'data';
export interface ModuleData {
b_id: string;
b_code: string;
b_name: string;
b_module_type: ModuleType;
b_parent_id: string | null;
b_i18n: string | null;
b_xh: number;
b_icon: string | null;
b_route: string | null;
b_viewtable: string | null;
b_savetable: string | null;
b_keyfield: string | null;
b_canmenu: number;
b_canuse: number;
[key: string]: unknown;
}
export interface ModuleField {
b_id: string;
b_module_id: string;
b_field: string;
b_name: string;
b_i18n: string | null;
b_type: string;
b_default_value: string | null;
b_options: string | null;
b_canuse: number;
[key: string]: unknown;
}
export interface ModulePower {
b_id: string;
b_module_id: string;
b_code: string;
b_name: string;
b_i18n: string | null;
b_xh: number;
b_canuse: number;
[key: string]: unknown;
}
export interface Originals {
module: ModuleData | null;
fields: ModuleField[];
powers: ModulePower[];
}
export type SectionName = 'module' | 'fields' | 'powers';
```
### dataChanges 核心函数签名(utils/dataChanges.ts)
```typescript
export function cloneData<T>(value: T): T;
export function diffRows<T extends { b_id: string }>(current: T[], previous: T[]): { inserts: T[]; updates: T[]; deletes: { b_id: string }[] };
export function tableChange(table: string, changes: { inserts?: unknown[]; updates?: unknown[]; deletes?: unknown[] }): SaveRequest;
export function hasChanges(request: SaveRequest): boolean;
export async function allocateTemporaryIds(rowGroups: unknown[][]): Promise<Map<string, string>>;
export function remapForeignKeys(rows: Record<string, unknown>[], fields: string[], mapping: Map<string, string>): void;
```
## Agent Extensions
### Skill
- **shadcn-vue**
- 用途:在构建 Form 表单组件和 Dialog 弹窗时,查询 shadcn-vue 组件的 API 文档、props/slots/events 定义
- 预期结果:确保 Form、FormField、FormItem、Dialog 等组件的使用方式与 shadcn-vue 官方文档一致
### SubAgent
- **code-explorer**
- 用途:在实现过程中需要快速定位旧系统面板组件的具体实现细节时,用于搜索和读取旧系统代码
- 预期结果:获取 ModuleFieldsPanel、ModulePowersPanel、ModuleI18nPanel 的完整实现逻辑作为迁移参考