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

12 KiB
Raw Blame History

name, overview, todos
name overview todos
模块管理迁移计划 将 fms-vue2(antdv)的模块管理页面迁移到 fms-vue(shadcn-vue + reka-ui + tailwindcss),核心面板包括:基本信息、字段定义、权限配置、多语言。沿用 originals+drafts+diff 数据流模式,复用通用数据 API。
id content status
create-tool-functions 创建工具函数:dataChanges.ts(diffRows/tableChange/allocateTemporaryIds/cloneData/hasChanges)、tempId.ts(createTempId)、i18n.ts(validateModuleCode/buildModuleI18nKey 等),TypeScript 化旧系统代码 completed
id content status
create-types-and-service 创建类型定义 types/module.ts 和 API 服务 services/moduleManagement.ts(封装 loadTree/loadModuleConfig/saveModule/deleteModule/syncFields) completed
id content status
create-loading-overlay 创建 FmsLoadingOverlay.vue 加载遮罩组件 completed
id content status dependencies
rewrite-index-page 重写 index.vue 主页面:左侧树(FmsTree + 搜索 + 右键菜单 + 拖拽分隔线)+ 右侧详情(Header + section nav + 面板切换)+ 数据加载/保存/删除/字段同步完整逻辑 completed
create-tool-functions
create-types-and-service
create-loading-overlay
id content status dependencies
create-basic-panel 使用 [skill:shadcn-vue] 创建 ModuleBasicPanel.vue:基于 shadcn-vue Form 组件(vee-validate + zod)构建表单,按模块类型动态显示字段,含图标选择器 completed
create-tool-functions
create-types-and-service
id content status dependencies
create-fields-powers-i18n-panels 创建 ModuleFieldsPanel.vue(StkTable + 同步字段)、ModulePowersPanel.vue(StkTable + 增删改)、ModuleI18nPanel.vue(StkTable + 受管键 + I18nQuickSetModal),从旧系统迁移完整逻辑 completed
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 模式:

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)

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)

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 的完整实现逻辑作为迁移参考