7.2 KiB
7.2 KiB
name, overview, todos
| name | overview | todos | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 模块管理树节点点击懒加载 | 将模块管理从“挂载时一次性加载全部表”改为“挂载时仅加载树(b_module),点击树节点时才懒加载该模块的字段/权限/翻译/用户分配数据”。 |
|
用户需求
模块管理页面进入时,不要立即把所有模块的全部数据一次性拉取下来;应当在用户点击(或右键)左侧树节点时,才加载该节点对应模块的详情数据。
产品概述
模块管理采用「左侧模块树 + 右侧详情」布局。当前实现在页面挂载时即通过 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() 拆分为「树加载」与「模块详情懒加载」两步:
loadModules():仅loadDataApi("b_module", null, "b_xh ASC")填充data.modules(树、上级选项、编码校验所需)。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。- 树节点
@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不存在而清空新建草稿。
架构设计
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 与级联保存删除逻辑。
关键代码结构
// 按模块编码过滤多语言键(精确前缀,避免误匹配)
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)
}