检索过渡界面与关系面板方案.md 6.4 KB

检索过渡界面与关系面板按需显示方案

创建日期:2026-08-21 状态:已完成(tsc 通过;vitest 343 全绿,其中新增 15 例) 范围:纯前端(无后端改动)

1. 需求

  1. 关系面板按需显示:未点击(选中)主案时,主页面下方"配套方案 / 支撑成果"等区域整体隐藏,不再显示"请选择主案"占位。
  2. 检索重构:检索激活时,中央列表切换为过渡平铺界面:
    • 主案、配套方案(子案)、支撑成果等全部命中文档平铺到一个列表;
    • 列与"主体方案"列表一致(序号 / 名称 / 密级 / 提供单位 / 操作);
    • 名称之后用方框标注方案类型(主体方案 / 配套方案 / 支撑成果 / 方案训考资料 / 典型案例);
    • 保留单击预览(右侧栏)、双击查看(弹窗)能力;
    • 操作列新增定位按钮:
      • 主案:跳转到其分类,选中该主案,展开下方关系面板;
      • 子案 / 附件:先定位到其主案(跳主案分类 + 选中主案 + 展开面板);
      • 附件挂多个主案时,弹框让用户选择目标主案。
  3. 分页:命中过多时平铺列表分页展示(每页 20 条)。
  4. 后台管理页(方案数据管理后台 → 主体方案模式)做同样改动。

2. 现状分析

项 现状 位置
关系面板 !hideRelations 即渲染,未选中时显示占位 App.tsx RealMainPage / RealAdminPage
检索 主案列表 documentType=MAIN + aggregateHits=true,子案/附件命中仅以徽章(子案 N / 附件 N)聚合到主案行 App.tsx:2712-2723、F4DocumentTable
后端 documents 接口 documentType 支持 MAIN,SUB_PLAN 逗号组合 document_query_service.py:424
后端 attachments 接口 支持 keyword / contentKeyword / securityLevels / organizationKeyword 全部检索条件 attachment_query_service.py
附件反查主案 attachmentApi.mainPlans(id) → MainPlanBrief[](id/documentName/categoryName/securityLevel,无 categoryId) attachmentApi.ts:58
pageSize 上限 后端 100/页 common/pagination.py:14

结论:两个既有接口即可覆盖平铺数据,无需后端改动;附件定位需经 documentApi.detail(mainPlanId) 补全 categoryId。

3. 设计

3.1 数据层:features/documents/globalSearchResults.ts(新建)

useGlobalSearchResults(active: boolean, keyword: string | undefined, advanced: AdvancedQuery)
  • active 为 false 时直接返回空态(不发请求);
  • 并行两路,各自循环拉全量(pageSize=100,翻页直到拉完,单源上限 10 页 / 1000 条防失控,超限截断):
    • documentApi.list({ documentType: "MAIN,SUB_PLAN", keyword, ...advanced, sortBy: "updatedAt", sortDirection: "desc" })
    • attachmentApi.list({ keyword, ...advanced, sortBy: "updatedAt", sortDirection: "desc" })
  • 请求版本号 + AbortController 防竞态(参照 AttachmentBindingModal 既有模式);
  • 纯函数(可单测):
    • mergeSearchResults(plans: DocumentSummary[], attachments: DocumentSummary[]): DocumentSummary[] —— 合并、按 updatedAt 降序稳定排序;
    • paginateFlat<T>(items: T[], page: number, pageSize: number): { items: T[]; totalPages: number; total: number } —— 前端分页。

3.2 表格扩展:F4DocumentTable 增加可选 props(DRY,不新建表格)

  • showTypeBadge?: boolean:名称单元格后渲染类型方框(border + 主题蓝小字号方框):
    • MAIN → "主体方案";SUB_PLAN → "配套方案";ATTACHMENT → attachmentTypes 字典标签(支撑成果 / 方案训考资料 / 典型案例);
  • onLocate?: (item: DocumentSummary) => void:操作列末尾追加"定位"按钮。

3.3 检索过渡视图(RealMainPage / RealAdminPage 对称接入)

  • searchActive = simpleKeyword !== "" || 任一高级条件非空;
  • searchActive 时中央列表区整体替换为检索视图:
    • 标题栏:全局检索结果(命中合计、类型构成、超 1000 截断提示);
    • 平铺表格:showTypeBadge + onLocate + onView(双击)、onSelect(单击预览);
    • F4Pager 前端分页(每页 20);
    • 检索态下左侧分类树与检索栏保持可用;分页/翻页不清空检索条件;
  • 关系面板显示条件统一改为:!hideRelations && selected?.documentType === MAIN(需求 1 与检索态共用,占位分支删除)。

3.4 定位逻辑(页面内 locateItem(item))

目标主案 resolution:
  MAIN     → item 自身
  SUB_PLAN → 结果集中找 id === item.parentDocumentId 的 MAIN;找不到 → documentApi.detail(parentDocumentId)
  ATTACHMENT → attachmentApi.mainPlans(item.id):
               0 个 → 错误提示"该资料未挂载任何主案,无法定位"
               1 个 → documentApi.detail(mainPlanId)
               多个 → 弹 LocateTargetModal 选择 → detail(选定 id)
跳转动作(统一):
  resetGlobalSearch()          // 清空检索,退出过渡界面
  setCategoryId(主案.categoryId)
  setSelected(主案 summary)    // 关系面板随 selectedMainId 展开
  setHideRelations(false)
  setPage/setPlanPage(1)
  • 后台页:admin 模式下 detail 可能受可见性限制,失败时给出 HttpError 原始 message 提示。

3.5 新组件:LocateTargetModal(多主案选择弹框)

  • props:attachment: DocumentSummary、candidates: MainPlanBrief[]、onConfirm(mainPlanId)、onClose;
  • 样式仿 F4ReverseModal(NAVY 标题栏 + 白底列表 + 单选确认);
  • 列显示:主案名称 / 分类(categoryName)/ 密级。

4. 实施阶段

阶段 内容 验证
1 关系面板按需显示(主页 + 后台页) tsc;未选中时面板隐藏
2 globalSearchResults hook(全量拉取/合并/分页纯函数) 纯函数单测
3 F4DocumentTable 扩展 + 检索视图 + locateItem + LocateTargetModal(两页接入) tsc + scope 断言
4 测试补充 + 全量回归 vitest(基线 328+)、tsc

5. 风险

  • 低:附件 summary categoryId 为 null —— 定位统一走主案 categoryId,已规避;
  • 低:命中超 1000 条/源 —— 截断并提示,不做深度聚合;
  • 低:后台页 detail 权限受限 —— 失败展示原始错误信息;
  • 低:检索请求竞态 —— 版本号 + AbortController 既有模式。