Files
workspace/code/fms/.codebuddy/plans/模块管理树节点点击懒加载_09a97df4(未完成).md
T
2026-07-22 22:25:10 +08:00

121 lines
7.2 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: 将模块管理从“挂载时一次性加载全部表”改为“挂载时仅加载树(b_module),点击树节点时才懒加载该模块的字段/权限/翻译/用户分配数据”。
todos:
- id: split-load
content: 拆分 loadAll 为 loadModules 与 loadModuleDetail,新增翻译条件过滤器
status: pending
- id: wire-lazy
content: 将树选中/刷新、onMounted、保存/删除后刷新接入懒加载,保留 draft 与级联逻辑
status: pending
dependencies:
- split-load
- id: verify-scopes
content: 回归校验新建/取消/保存/删除/刷新在各场景下数据正确且不触发全量加载
status: pending
dependencies:
- wire-lazy
---
## 用户需求
模块管理页面进入时,不要立即把所有模块的全部数据一次性拉取下来;应当在用户点击(或右键)左侧树节点时,才加载该节点对应模块的详情数据。
## 产品概述
模块管理采用「左侧模块树 + 右侧详情」布局。当前实现在页面挂载时即通过 `loadAll()` 一次性并发加载 6 张表(b_module / b_module_field / b_module_power / b_i18n / b_user_module / b_user_power)的全部数据并缓存,再按选中节点从内存过滤。需要改为:挂载时只加载树所需的 b_module;选中某个节点时才按该模块懒加载其字段、权限、翻译及用户分配数据。
## 核心特性
- 页面挂载仅加载模块树(b_module),用于渲染树、上级模块选项与编码重复校验。
- 点击/右键树节点时才加载该模块的字段定义、操作权限、多语言翻译、用户模块/权限分配,并构建编辑草稿。
- 保存、删除、刷新后只刷新树与「当前选中模块」的详情,不再全量加载。
- 新建模块、取消编辑保持原交互;保存/删除的级联逻辑在懒加载下依然正确。
## 技术栈
- 前端:Vue 3 `<script setup>` + Vite + antdv-next + Pinia(沿用现有项目栈)
- 数据访问:复用通用端点 `loadDataApi(viewName, searchCondition, orderBy)`(POST `/api/data/loaddata`),不新增后端 URL
- 复用现有组件 `ModuleTreePanel` / `ModuleBasicInfoPanel` / `ModuleFieldDefinitionPanel` / `ModulePowerPanel`,仅调整 `index.vue` 的加载编排
## 实现方案
### 策略
将当前的 `loadAll()` 拆分为「树加载」与「模块详情懒加载」两步:
1. `loadModules()`:仅 `loadDataApi("b_module", null, "b_xh ASC")` 填充 `data.modules`(树、上级选项、编码校验所需)。
2. `loadModuleDetail(moduleCode)`:先定位 `data.modules` 中的模块,再按 `b_module_id` 条件并发加载 `b_module_field`、`b_module_power`、`b_i18n`(按模块编码过滤)、`b_user_module`;随后以已加载的权限 id 列表为条件加载 `b_user_power`。加载完成后调用现有 `selectModule(moduleCode)` 从已加载的 `data` 构建 `draft`。
3. 树节点 `@select` / 右键 `@select` 改为调用 `loadModuleDetail`;`@refresh`、保存后、删除后统一复用 `loadModules()` +(必要时)`loadModuleDetail(selectedKey)`。
### 关键技术决策
- **翻译按模块过滤**:多语言键规则为 `module.<code>`、`field.<code>.<字段>`、`power.<code>.<权限>`。用条件 `b_key = 'module.<code>' OR b_key LIKE 'field.<code>.%' OR b_key LIKE 'power.<code>.%'` 在后端过滤,避免一次性加载全量 b_i18n。`.%` 后缀确保不会被 `m_page` 误匹配到 `m_page_x` 等。code 来自数据库且为小写字母数字下划线,仍做单引号转义防御。
- **用户分配按当前模块加载**:`b_user_module` 用 `b_module_id = <id>` 过滤;`b_user_power` 依赖权限 id,故在 powers 加载完成后用 `b_power_id IN (...)` 二次加载。保证 `saveModule` 的权限回收与 `deleteModule` 的级联删除逻辑数据完整。
- **保留草稿构建逻辑**:`selectModule` 仅负责从已加载的 `data`(fields/powers/translations)按模块过滤并构建 `draft`,不含网络请求,最小化改动面;`resetDraft` 直接复用它从已加载数据重建。
- **不回归**:`parentOptions`/`directoryParentOptions`/`validate()` 依赖 `data.modules`(已加载);新建、取消、保存、删除交互保持不变。
### 性能与可靠性
- 挂载请求由 6 个并发降为 1 个(b_module),点击节点时按模块加载 4~5 个按需查询,随模块规模线性而非全量。
- `loading` 复用现有 `a-spin` 同时驱动树刷新按钮与右侧工作区,加载期间提供反馈。
- 对 `handleSelect` 增加 `loading` 守卫,避免并发重复加载。
- bigint 的 `b_module_id`/`b_power_id` 以字符串拼接条件(与原 `diffRows` 中 `String(item.b_id)` 处理一致),避免精度问题。
## 实现注意
- 仅修改 `fms-vue/src/views/module-management/index.vue`,不触碰 `api.js`、通用端点、全局组件与其他页面。
- `data` 响应式结构(modules/fields/powers/translations/userModules/userPowers)保持不变;`clearDraft` 仍负责清空选中态与详情。
- 删除当前模块后 `selectedKey` 对应的模块已从 `data.modules` 移除,应走 `clearDraft` 分支;删除非当前模块则重新加载当前详情。
- 新建模块期间刷新:只刷新树(loadModules),不要因 `selectedKey` 不存在而清空新建草稿。
## 架构设计
```mermaid
flowchart TD
A[onMounted] --> B[loadModules 仅 b_module]
B --> C[渲染模块树]
D[点击/右键树节点] --> E[loadModuleDetail 按模块懒加载]
E --> F[并发: b_module_field / b_module_power / b_i18n / b_user_module]
F --> G[按权限id加载 b_user_power]
G --> H[selectModule 构建 draft]
H --> I[右侧展示详情]
J[保存/删除/刷新] --> B
J --> K{有选中且非新建?}
K -->|是| E
K -->|否| L[clearDraft]
```
## 目录结构
```
fms-vue/src/views/module-management/
└── index.vue # [MODIFY] 模块管理主视图。拆分 loadAll 为 loadModules(仅加载 b_module 树数据)与
# loadModuleDetail(按选中模块懒加载字段/权限/翻译/用户分配并构建 draft);
# 新增 translationSearchCondition 按模块编码过滤 b_i18n;
# 树 @select/@refresh、onMounted、saveModule/deleteModule 末尾统一调用新加载函数;
# 保留 selectModule/resetDraft/clearDraft 与级联保存删除逻辑。
```
## 关键代码结构
```js
// 按模块编码过滤多语言键(精确前缀,避免误匹配)
function translationSearchCondition(code) {
const c = String(code).replace(/'/g, "''");
return `b_key = 'module.${c}' OR b_key LIKE 'field.${c}.%' OR b_key LIKE 'power.${c}.%'`;
}
// 树加载:仅 b_module
async function loadModules() { /* loadDataApi("b_module", null, "b_xh ASC") -> data.modules */ }
// 详情懒加载:选中节点时按模块加载字段/权限/翻译/用户分配
async function loadModuleDetail(moduleCode) {
const module = data.modules.find((i) => i.b_code === moduleCode);
if (!module) return;
// 并发加载 b_module_field(b_module_id=id)、b_module_power(b_module_id=id)、
// b_i18n(translationSearchCondition(code))、b_user_module(b_module_id=id)
// 再以权限 id 列表加载 b_user_power(b_power_id IN (...))
// 写入 data 后调用 selectModule(moduleCode)
}
```