状态:✅ 已完成(2026-09-17 归档到
completed/) 结果:面板新增cardList模式,卡片区「更多」打开「政策列表」侧边栏 (本会话卡片跨消息聚合、去重、按匹配度排序;保留搜索框与热门搜索,无部门/分类筛选); 详情面板的「返回政策事项列表」仍走旧列表(按要求只换「更多」入口)。 回归脚本harness/tools/verify-policy-card-list.mjs31/31 通过。 ⚠️ 未做:浏览器人工点验。
用户要求:点卡片区「更多」按钮,打开一套新侧边栏——标题「政策列表」, 下方是搜索框 + 热门搜索,没有部门筛选和分类筛选,内容为本会话内所有消息 出现过的卡片(跨消息聚合、去重)。样式与现有列表一致(复用原样式类,不新建 UI 组件)。
PolicyMatch.vue:29(无 v-if);openPolicyList(:1206-1223) 是旧列表入口panelMode = ref<"list" | "detail">("list")(:530);旧列表数据源 fetchPolicyList(:988-1048)
有守卫 panelMode !== "list" 直接返回 —— 新增第三模式即可自然短路旧逻辑message.content 的 POLICY_TABLE 块恢复
(parsePolicyTableContent,utils/stream-xml-filter.ts:518-531)useBusinessAssistantChat 内有 provide 先例(provide('submitQuestionAnswers', …) :1627),
PC/Mobile 都在 setup 里调用该 composable —— 在那里 provide 即可两处生效、零改动declaration_item(2026-09-17 起 = 「政策名 - 申报事项」,每条唯一)src/components/Chat/policy-match-utils.ts(追加,可被 harness 测试)parsePolicyTableContent(utils/stream-xml-filter,无循环依赖)collectPolicyCardsFromMessages(messages) —— 遍历 AI 消息 content,parse 出全部 POLICY_TABLE raw 条目(不去重、保消息顺序)buildPolicyCardDedupKey(item) —— 主键 decl:<declaration_item>;占位符「查看政策详情」(精简格式)时退化为 标题|匹配度|匹配理由|id 组合键dedupePolicyItems(items, keyOf) —— 首见优先去重sortPolicyCardsByMatchScore(items) —— match_score 降序 + 原顺序稳定 tie-break(与卡片区 panelData 语义一致)matchesPolicyCardKeyword(item, keyword) —— 检索面 title/desc/declaration_item/match_reason/source/enterprise_benefit,大小写不敏感,空词恒 true(与 buildRecordPolicyList 的 haystack 一致)src/components/business-assistant/useBusinessAssistantChat.ts在 provide('submitQuestionAnswers', …)(:1627) 后新增:
provide('collectSessionPolicyCards', (): Record<string, any>[] =>
collectPolicyCardsFromMessages(currentSession.value?.messages ?? [])
);
currentSession.value → 切会话天然跟随;只采集,不去重/排序(组装留在组件)src/components/Chat/PolicyMatch.vue — scriptpanelMode 类型加第三值 "cardList";inject collectSessionPolicyCards(可空)cardListItems(聚合重建:provider() ?? 回退 props.policyData || parsePolicyTableContent(props.policyContent) → mergeWithPublicPolicies → normalizePolicy → 去重 → 排序)cardListFiltered(按 searchKeyword 过滤,computed)、cardListTotal、cardListPaginatedtotalPages / paginatedPolicies 加 panelMode === 'cardList' 分支(两模式互斥共享 currentPage/itemsPerPage/visiblePages,安全)openCardList()(放 openPolicyList 前):先切 panelMode="cardList" 再重置共享状态(让 watcher 触发的 fetchPolicyList 被守卫短路)→ rebuildCardList → requestOpen/nextTick/互斥校验 → panelVisible → emitPanelWidthsyncPolicies(:969-984) 里面板正开着 cardList 时顺带 rebuildCardList(流式刷新,可选两行)src/components/Chat/PolicyMatch.vue — template(6 处)| 位置 | 改动 |
|---|---|
| :29 「更多」 | @click.stop="openCardList" |
| :37-40 背景类 | panelMode === 'detail' ? 'detail-mode-bg' : 'list-mode-bg' |
| :42-44 头部标题 | panelMode === 'cardList' ? '政策列表' : '智能政策匹配' |
| :61 list body | v-if="panelMode !== 'detail'"(list 与 cardList 共用 body 与样式) |
| :64-67 record-list-header | 标题 cardList 时「政策列表」+ 副标题「汇总本会话内出现的全部政策事项」,否则旧文案 |
| :69 search-filter-bar | `v-if="isZhaoshangLLM && (recordMode |
| :80 filter-selects(部门/分类) | 加 v-if="panelMode === 'list'" —— cardList 隐藏筛选,搜索框 + 热门词保留 |
harness/docs/exec-plans/active/policy-card-list-sidebar.md(CLAUDE.md 硬规则:计划只能放这里)harness/progress.md(Session 026)、harness/feature_list.json(新条目,passing 需带 evidence)README.md 20260917 节追加npm run build 通过后按提交约定提交推送resolvePolicyDetailIndex 对象同一性失配 → 字段比对:本消息里有同 declaration_item 则命中本消息条目;否则 -1 → override 兜底直接展示被点条目(它是完整 PolicyItem,originalData 含 conditions/match_basis,详情完整可用)npm run build;npx vue-tsc --noEmit(panelMode 联合类型改动过类型检查)harness/tools/_entry-policy-cards.ts + verify-policy-card-list.mjs(照 _entry-policy-match.ts 模式):
聚合并集保序 / user 消息跳过 / 坏 JSON 不抛错;去重键主键与占位符退化;首见去重;
match_score 降序稳定排序;六个检索面与大小写/空词node harness/tools/validate-harness.mjs 退出码 0