创建日期:2026-08-21 状态:已完成(tsc 通过;vitest 343 全绿,其中新增 15 例) 范围:纯前端(无后端改动)
| 项 | 现状 | 位置 |
|---|---|---|
| 关系面板 | !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。
features/documents/globalSearchResults.ts(新建)useGlobalSearchResults(active: boolean, keyword: string | undefined, advanced: AdvancedQuery)
active 为 false 时直接返回空态(不发请求);documentApi.list({ documentType: "MAIN,SUB_PLAN", keyword, ...advanced, sortBy: "updatedAt", sortDirection: "desc" })attachmentApi.list({ keyword, ...advanced, sortBy: "updatedAt", sortDirection: "desc" })mergeSearchResults(plans: DocumentSummary[], attachments: DocumentSummary[]): DocumentSummary[] —— 合并、按 updatedAt 降序稳定排序;paginateFlat<T>(items: T[], page: number, pageSize: number): { items: T[]; totalPages: number; total: number } —— 前端分页。showTypeBadge?: boolean:名称单元格后渲染类型方框(border + 主题蓝小字号方框):
MAIN → "主体方案";SUB_PLAN → "配套方案";ATTACHMENT → attachmentTypes 字典标签(支撑成果 / 方案训考资料 / 典型案例);onLocate?: (item: DocumentSummary) => void:操作列末尾追加"定位"按钮。searchActive = simpleKeyword !== "" || 任一高级条件非空;searchActive 时中央列表区整体替换为检索视图:
全局检索结果(命中合计、类型构成、超 1000 截断提示);showTypeBadge + onLocate + onView(双击)、onSelect(单击预览);F4Pager 前端分页(每页 20);!hideRelations && selected?.documentType === MAIN(需求 1 与检索态共用,占位分支删除)。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 提示。LocateTargetModal(多主案选择弹框)attachment: DocumentSummary、candidates: MainPlanBrief[]、onConfirm(mainPlanId)、onClose;| 阶段 | 内容 | 验证 |
|---|---|---|
| 1 | 关系面板按需显示(主页 + 后台页) | tsc;未选中时面板隐藏 |
| 2 | globalSearchResults hook(全量拉取/合并/分页纯函数) | 纯函数单测 |
| 3 | F4DocumentTable 扩展 + 检索视图 + locateItem + LocateTargetModal(两页接入) | tsc + scope 断言 |
| 4 | 测试补充 + 全量回归 | vitest(基线 328+)、tsc |
categoryId 为 null —— 定位统一走主案 categoryId,已规避;