16 KiB
16 KiB
name, overview, todos
| name | overview | todos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| module-management-full-implementation | 基于新数据库结构.md和开发规范.md完整实现模块管理页面。左侧模块树+右侧7个子Tab(基本信息、字段定义、列表配置、编辑配置、查询配置、权限配置、多语言),子Tab操作按钮靠左。 |
|
用户需求
根据新数据库结构.md和开发规范.md,完整实现模块管理页面。当前模块管理页面只有一个空占位(<el-button>),需要构建完整的左侧模块树+右侧子Tab布局。
核心功能
主页面布局
- 左侧模块树:以树形结构展示所有模块(directory/page/data),支持选中节点后在右侧加载详情
- 右侧详情区:使用
a-tabs展示子Tab,根据选中模块的b_module_type动态显示对应Tab
模块类型与Tab对应关系
| 模块类型 | 可用Tab |
|---|---|
| directory | 基本信息、权限配置、多语言 |
| page | 基本信息、权限配置、多语言 |
| data | 基本信息、字段定义、列表配置、编辑配置、查询配置、权限配置、多语言 |
各子Tab功能
- 基本信息:编辑
b_module的核心字段(b_code、b_name、b_module_type、b_route、b_viewtable、b_savetable、b_keyfield、b_orderfield、b_parent_id、b_canmenu、b_canuse、b_xh),表单保存 - 字段定义:展示
b_module_field列表,不可手工增删。提供「同步字段」按钮,调用describeApi获取b_viewtable的列结构,与已有字段合并(保留已有配置、追加新列、标记已删列为不可用)。支持编辑b_name、b_type、b_default_value、b_options、b_canuse - 列表配置:管理
b_module_field_list(字段列配置)和b_module_field_group(多级表头分组)。表格展示列表配置行,支持新增/删除。分组管理弹窗支持树形分组增删改 - 编辑配置:管理
b_module_field_edit(编辑属性)和b_module_field_group(编辑分组)。表格展示编辑配置行,支持新增/删除。共享分组管理组件 - 查询配置:管理
b_module_field_query,表格展示查询条件配置,支持新增/删除 - 权限配置:管理
b_module_power,表格展示操作权限(b_code、b_name),支持新增/删除 - 多语言:管理
b_i18n,表格展示翻译条目(b_key、b_locale、b_value),支持新增/删除
操作按钮排列
所有子Tab面板内,新增、删除、保存等操作按钮栏靠左对齐显示。
数据保存策略
- 主表 b_module:选中模块后直接编辑表单并保存,新增模块先调用
nextIdApi获取雪花ID - 子表(b_module_field/b_module_field_list等):新增行使用
createTempId()生成临时负数ID,调用saveObjectApi统一保存变更 - 保存时:使用
diffRows()对比草稿与原始数据,通过tableChange()构建变更请求,多表变更一次提交到saveObjectApi
Tech Stack
- 前端框架:Vue 3(
<script setup>SFCs) - 状态管理:组件内部 ref/reactive(不引入额外 Pinia store)
- UI 组件库:antdv-next(自动导入)+ 项目封装组件(FmsTable、FmsTree、FmsSelect)
- 样式:SCSS scoped + CSS 变量主题(
--fms-*) - 数据获取:复用现有通用接口
loadDataApi、saveObjectApi、nextIdApi、describeApi - 工具函数:
createTempId(临时ID)、diffRows/allocateTemporaryIds/remapForeignKeys/tableChange/hasChanges(变更追踪)
Implementation Approach
整体策略
采用声明式数据驱动模式:每个子Tab面板作为独立组件接收模块ID和完整配置数据,内部维护编辑态草稿(cloneData深拷贝),通过 diffRows 计算变更后交给父组件统一保存。
关键设计决策
-
数据加载集中在 Service 层:
moduleManagement.js封装loadModuleTree()(加载树)、loadModuleConfig(moduleId)(加载单个模块的完整配置)、saveModuleConfig(moduleId, changes)(保存变更),各面板组件调用 Sem 方法获取和回存数据。 -
字段同步采用合并策略(与记忆 ID: 93228844 一致):调用
describeApi(b_viewtable)获取视图列 → 按列名匹配已有b_module_field→ 视图中已存在的保留原配置不覆盖、新增列追加默认行、视图中不存在的已有字段标记b_canuse=0。 -
子Tab操作按钮左对齐:每个面板组件内部在表格上方设置工具栏,使用
justify-content: flex-start确保按钮靠左排列。 -
分组管理复用:列表配置和编辑配置共享
b_module_field_group,通过FieldGroupManager.vue组件统一管理分组树增删改。 -
保存事务性:所有变更通过
saveObjectApi一次提交多表变更数组,后端在同一事务内执行,任何表失败整体回滚。
Implementation Notes
- b_id 全部字符串化:后端返回的 bigint 在
http.js拦截器中统一转为字符串,前端所有ID比较使用String()包裹 - 布尔字段为 int 0/1:b_canuse、b_visible、b_sortable、b_required、b_readonly、b_disabled、b_canmenu 均使用 0/1
- b_module_field 同步后不覆盖已有配置:b_name、b_type、b_options 等由管理员手动维护的值在同步时保留
- 临时ID流转:新增子表行 →
createTempId()→ 保存时allocateTemporaryIds()请求后端真实ID → 保存完成后loadAll重建草稿
Architecture Design
flowchart TB
subgraph 主页面
A[index.vue<br/>左侧树 + 右侧Tab]
end
subgraph Service
S[moduleManagement.js<br/>loadModuleTree<br/>loadModuleConfig<br/>saveModuleConfig]
end
subgraph 组件
B1[ModuleBasicPanel<br/>基本信息表单]
B2[ModuleFieldsPanel<br/>字段定义表格+同步]
B3[ModuleListConfigPanel<br/>列表配置表格]
B4[ModuleEditConfigPanel<br/>编辑配置表格]
B5[ModuleQueryConfigPanel<br/>查询配置表格]
B6[ModulePowersPanel<br/>权限表格]
B7[ModuleI18nPanel<br/>多语言表格]
G[FieldGroupManager<br/>共用分组管理]
end
subgraph API
API1[loadDataApi]
API2[saveObjectApi]
API3[nextIdApi]
API4[describeApi]
end
A --> S
S --> API1
S --> API2
S --> API3
S --> API4
A --> B1
A --> B2
A --> B3
A --> B4
A --> B5
A --> B6
A --> B7
B3 --> G
B4 --> G
Directory Structure
fms-vue/src/
├── services/
│ └── moduleManagement.js # [NEW] 模块管理服务层
│ # - loadModuleTree(): 加载所有模块并构建树形数据
│ # - loadModuleConfig(moduleId): 加载单个模块的完整配置
│ # (b_module + b_module_field + b_module_field_list
│ # + b_module_field_edit + b_module_field_query
│ # + b_module_field_group + b_module_power + b_i18n)
│ # - saveModuleConfig(moduleId, changes, oldData): 保存模块配置
│ # 对比草稿和原始数据,构建多表变更请求
│ # - syncFields(moduleId, viewTable): 同步字段
│ # 调用describeApi获取列结构并合并
│ # - typeFromSqlType(sqlType): SQL类型到b_type映射
├── views/
│ └── module-management/
│ ├── index.vue # [MODIFY] 主页面容器
│ │ # - 左侧 FmsTree 展示模块树(directory/page/data)
│ │ # - 右侧 a-tabs 根据 b_module_type 动态显示子Tab
│ │ # - 选中节点加载完整配置,传递给各子面板
│ │ # - 统一收集各面板变更并调用保存
│ └── components/
│ ├── ModuleBasicPanel.vue # [NEW] 基本信息面板
│ │ # - 表单编辑 b_module 核心字段
│ │ # - b_code/b_name/b_module_type/b_parent_id 必填
│ │ # - directory类型隐藏 route/viewtable/savetable
│ │ # - page类型必须填写 route,data类型必须填写 viewtable
│ │ # - 新增模块先调用 nextIdApi 获取ID
│ ├── ModuleFieldsPanel.vue # [NEW] 字段定义面板
│ │ # - FmsTable 展示 b_module_field 列表
│ │ # - 工具栏「同步字段」按钮 + 保存按钮(靠左)
│ │ # - 同步逻辑:describeApi获取列 → 合并已有字段
│ │ # - 不可手工新增/删除行,仅可编辑属性
│ ├── ModuleListConfigPanel.vue # [NEW] 列表配置面板
│ │ # - FmsTable 展示 b_module_field_list
│ │ # - 工具栏新增/删除/保存按钮(靠左)
│ │ # - 可选字段来自 b_module_field(b_canuse=1)
│ │ # - 分组列可打开 FieldGroupManager 弹窗
│ ├── ModuleEditConfigPanel.vue # [NEW] 编辑配置面板
│ │ # - FmsTable 展示 b_module_field_edit
│ │ # - 工具栏新增/删除/保存按钮(靠左)
│ │ # - 可选字段来自 b_module_field(b_canuse=1)
│ │ # - 分组列可打开 FieldGroupManager 弹窗
│ ├── ModuleQueryConfigPanel.vue # [NEW] 查询配置面板
│ │ # - FmsTable 展示 b_module_field_query
│ │ # - 工具栏新增/删除/保存按钮(靠左)
│ │ # - b_component和b_operator使用下拉选择
│ ├── ModulePowersPanel.vue # [NEW] 权限配置面板
│ │ # - FmsTable 展示 b_module_power
│ │ # - 工具栏新增/删除/保存按钮(靠左)
│ │ # - b_code 唯一校验
│ ├── ModuleI18nPanel.vue # [NEW] 多语言面板
│ │ # - FmsTable 展示 b_i18n
│ │ # - 工具栏新增/删除/保存按钮(靠左)
│ │ # - 支持 zh-CN/en-US 等语言切换筛选
│ └── FieldGroupManager.vue # [NEW] 共用分组管理组件
│ # - 弹窗内展示分组树(b_module_field_group)
│ # - 支持新增/编辑/删除分组节点
│ # - 被 ModuleListConfigPanel 和 ModuleEditConfigPanel 引用
Key Code Structures
moduleManagement.js 核心接口
// 加载模块树(用于左侧树展示)
export async function loadModuleTree()
// 加载单个模块的完整配置(所有关联表数据)
export async function loadModuleConfig(moduleId)
// 返回: { module, fields, fieldList, fieldEdit, fieldQuery, groups, powers, i18n }
// 保存模块配置变更
export async function saveModuleConfig(moduleId, draftData, originalData)
// draftData 和 originalData 结构同 loadModuleConfig 返回值
// 内部使用 diffRows 对比各子表,构建 saveObjectApi 请求
// 同步字段:从 b_viewtable 拉取列结构,与已有字段合并
export async function syncFields(moduleId, viewTable, existingFields)
// SQL类型到 b_type 的映射
export function typeFromSqlType(sqlType)
// varchar/char/nvarchar → input, text/ntext → textarea
// int/bigint/decimal/numeric/float → number, money/smallmoney → money
// date → date, datetime/datetime2 → datetime, bit → switch
各面板组件统一 Props/Emits 模式
// 所有面板组件遵循相同的接口约定
defineProps({
moduleId: { type: String, required: true }, // 当前模块ID
data: { type: Array, required: true }, // 面板对应的数据行数组
loading: { type: Boolean, default: false }, // 加载状态
})
const emit = defineEmits(['update']) // 数据变更时触发,传递新的行数组
数据流
index.vue 选中模块
→ loadModuleConfig(moduleId) 获取原始数据
→ cloneData 生成 draftData(编辑态草稿)
→ 各面板接收 draftData 对应部分,用户编辑后 emit('update', newRows)
→ index.vue 更新 draftData
→ 用户点击「保存全部」
→ saveModuleConfig(moduleId, draftData, originalData)
→ diffRows 对比各子表变更
→ allocateTemporaryIds 分配临时ID为真实ID
→ 构建 saveObjectApi 请求数组
→ 提交保存
→ 重新 loadModuleConfig 刷新原始数据
Agent Extensions
Skill
- antdv-next
- Purpose: 查询 antdv-next 组件库的 API 文档,确认 a-tree、a-tabs、a-form、a-modal、a-select、a-button、a-space 等组件的 props/events/slots 用法
- Expected outcome: 获取准确的组件属性定义和事件签名,确保模板中使用正确的 API
SubAgent
- code-explorer
- Purpose: 在实现过程中需要快速搜索项目中的代码模式(如 FmsTable 使用方式、dataChanges.js 调用示例、现有表单校验模式等)时使用
- Expected outcome: 获得准确的现有代码引用,确保新代码风格一致