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

7.2 KiB
Raw Blame History

name, overview, todos
name overview todos
模块管理树节点点击懒加载 将模块管理从“挂载时一次性加载全部表”改为“挂载时仅加载树(b_module),点击树节点时才懒加载该模块的字段/权限/翻译/用户分配数据”。
id content status
split-load 拆分 loadAll 为 loadModules 与 loadModuleDetail,新增翻译条件过滤器 pending
id content status dependencies
wire-lazy 将树选中/刷新、onMounted、保存/删除后刷新接入懒加载,保留 draft 与级联逻辑 pending
split-load
id content status dependencies
verify-scopes 回归校验新建/取消/保存/删除/刷新在各场景下数据正确且不触发全量加载 pending
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 不存在而清空新建草稿。

架构设计

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)
}