# Dashboard 筛选与多选过滤逻辑详细设计

这个进阶方案在前面“禁止同级攀附”的基础上，完善了对**关联底座的优化**、**中间环节双向过滤**以及**跨层多选**的逻辑定义。这是接下来对 `Dashboard.tsx` 及其附属组件进行重构时的业务指导原则。

## 一、 数据源解耦前置假设（后端配合）

鉴于后续需求与 Node/Pattern 的关联将转为数据库连表记录，这给前端性能和逻辑纯粹性带来了巨大提升：
- **摒弃动态正则比对**：前端无需在 `useMemo` 里处理 `pattern.leaf_names` 与 `req.source_nodes` 的字符串包含、相交计算。
- **图结构一致性**：关联表落地后，在前端看来，**“呈现（Node/Pattern） $\leftrightarrow$ 需求(Req)”** 跟 **“需求(Req) $\leftrightarrow$ 能力(Cap)”** 在数据结构和遍历策略上变得完全一致。全图彻底转化为基于明确 ID 的节点边网络。

---

## 二、 单选中间环节：双向过滤 (Pivot Filtering)

如果用户不仅能点选两端的“源头（Node/Req）”或“底层（Tool）”，而是在中间点选了一项（例如：某一个**能力 (Capability)** 或 某一个**工序 (Proc)**），筛选逻辑应该如何表现？

**设计结论：支持双向严格映射，但绝对禁止路径上的“U型掉头”（U-Turn）。**

以选中【能力(Cap_A)：文案润色】为例，我们把它作为枢轴（Pivot Node），向左右散发：
*   **向左（Upstream / 向上溯源）**：展示“我们要在哪里用到这个能力？”
    *   亮起直接组合了该能力的 **工序 (Proc)**。
    *   亮起直接依赖此能力的 **需求 (Req)**，以及被上述工序满足的需求。
    *   亮起提出这些需求的 **特征/场景 (Node/Pattern)**。
*   **向右（Downstream / 向下找落地球元）**：展示“这个能力由什么具体手段实现？”
    *   亮起具备该能力的 **工具 (Tool)**。

**【禁 U 型掉头原则】的实际表现：**
向左走到【工序 P】时，工序 P 可能同时除了【文案润色 Cap_A】外，还组合了【图片生成 Cap_B】。但我们的搜索路径在这里**只能继续向左（去推导Req）**，**绝不能转个弯向右**去点亮【图片生成 Cap_B】相关的卡片，更不能顺着 Cap_B 把所有绘图工具牵扯出来。
*(这就完美解决了“选拔一个能力，弹出一堆毫不相干的配套能力”的连坐噪音)*。

---

## 三、 超级复选与组合筛选逻辑设计

业务中经常会出现复杂交叉筛选，例如“我想看关于【文旅场景 Node_A】的需求中，有几个是用上了【GPT-4 Tool_B】的”。这就要求支持跨列（甚至同列）多选。

我们采用 **“同维并集 (OR) + 跨维交集 (AND)”** 模型。这套“路径约束图”算法是重构代码时的核心骨架。

### 1. 同列内多选：交集（AND）为主，支持切换并集（OR）
*   **操作**：用户在【特征树/节点列】同时勾选了【节点A】和【节点B】；或在【能力列】同时勾选了【语音识别 Cap_1】和【文字生成 Cap_2】。
*   **业务动机**：
    *   **组装/相交查询（默认 AND）**：“我想找同时包含节点A和节点B这两个特征组合的高频 Pattern”、“我要找同时运用了这两种能力的具体工序（看复杂多模态融合的例子）”。这就要求对**组合关系**作极其精准的收口。
    *   **拓宽盘子（可选 OR 切换）**：“我想看看只要具备这两种能力之一的所有盘子有多大”。
*   **逻辑**：
    *   **默认模式（AND）**：当同列复选多项时，只有那些在下一跳（乃至后续路径）中**同时**通过有效边连接到这些所有选项的节点，才会被保留。例如，只有 `Pattern1 -> NodeA` 且 `Pattern1 -> NodeB` 都成立时，基于这组复选推导出的网络才是以 Pattern1 为根的。
    *   **交互支持**：建议在各个 Column 的表头增加一个隐性或显性的类似 `[AND / OR]` 的小 Toggle 按键，让用户自由决定同维度的查询性质（即“满足所有”还是“满足其一”）。

### 2. 跨列多选：交集（Intersection / AND）
*   **操作**：用户同时选中了【需求 Req_X】和【工具 Tool_Y】。
*   **业务动机**：增加条件限制，做归因分析。“这个需求被很多工具满足，我想单独看 Tool_Y 是怎么介入到 Req_X 这个案子里的”。
*   **逻辑（漏斗交集算法）**：
    1. 前端先依据前面的“单选逻辑”，算出 Req_X 向右衍生出的整个闭包图网络 `Graph_ReqX`。
    2. 算出 Tool_Y 向左逆推衍生出的闭包图网络 `Graph_ToolY`。
    3. 取这两个网络的有效交集节点与连线：`VisibleGraph = Graph_ReqX ∩ Graph_ToolY`。
*   **UI 视觉表现**：
    *   如果在 Req_X 到 Tool_Y 之间完全没有链路关联（比如 Req_X 处理图片，Tool_Y 是搞文本的爬虫），这两者没有任何交集连线。结果是：两者高亮，但中间地带全部一片灰暗。明确告诉用户：“此路不通，这俩组合无解”。
    *   如果两者有关联，那么界面上**只会保留直接把 Req_X 和 Tool_Y 串起来的特定的工序和能力节点**。这极大去除了噪音，能让业务人员一眼看穿这个特定的工具在这个特定的需求流里充当了什么具体的环节。

## 总结：给前端代码重构的算法建议

为了满足上述复杂的全链路有向交互，接下来的重构不要在视图层继续写几十个零散的 `useMemo` 或递归了。我们可以：
1. **统一初始化一张有向图 (Graph)**：使用邻接表（Adjacency List）在浏览器内存中建立 `Node -> Req -> Proc -> Cap -> Tool` 及其反向关系的图。
2. **抽象通用探路器 (Pathfinder)**：实现一个禁止 U-turn 的 `BFS/DFS` 遍历函数。
3. **状态机过滤**：
   维护全局状态：`selections = { reqs: Set, caps: Set, tools: Set ... }`。
   每次选中变动，针对 selections 里的每个层级先取**并集**探路，再将不同层级探出的结果子图取**交集**，最后把结果子图注入给 UI 视图去亮起组件即可。这种方式代码行数能大幅精简 60% 以上，性能和健壮性也将得到指数级改善。

## 四、 卡片状态定义与交互排序定序 (Item States & Sorting)

在复杂的由点及面的探索过程中，卡片如果因为失去焦点、或匹配值变动而在列表中“上下乱飞”（Layout Shift），会严重破坏用户的空间记忆。因此我们需要专门设计状态与排序机制。

### 1. 三层卡片视觉状态
在任何时刻，全盘卡片仅会处于以下三种确定的物理状态之一：
*   **状态 `Active` (枢纽选中)**：用户主动点选的“条件锚点”（你点的那些源按钮）。
    *   *视觉*：强烈的边框高光、指示态样式（例如醒目的 Orange/Sky 边框加亮），表明它是过滤漏斗的发起者。
*   **状态 `Matched` (有效连通)**：没有被主动选中，但在当前的过滤漏斗交集中**幸存下来**的强关联者。
    *   *视觉*：保持原始正常的颜色的清晰度（Opacity 100%），可以正常交互与查看抽屉。
*   **状态 `Dimmed` (掉线无关)**：在交集网络之外，因条件限制或路径不通被舍弃的卡片。
    *   *视觉*：大幅度变为去色的灰阶或透明度降至 30%（Opacity 30%）。**注意：不直接 `display: none` 隐藏**，以防止列表突然塌陷。

### 2. 置顶与防跳排序逻辑 (Pinning & Frozen Ordering)

结合你提出的核心痛点（*上一轮发现感兴趣的节点，取消旧筛选后找不到它*），我们必须对纯冻结做改良，引入**“选中即置顶（Pin to Top）”**机制，这既能防跳跃，又能锁住焦点：

1. **选中项终极置顶（Active Floating）**：只要卡片被你**主动点击选中（变为 Active 状态）**，它必须立刻脱离原本长列表，**置顶吸附**在该列的最顶端！
2. **平滑过渡分析法（解决痛点）**：
   * 原操作导致丢失的原因：看到感兴趣节点 $\rightarrow$ 直接取消旧筛选 $\rightarrow$ 感兴趣节点失去 `Matched` 落回大海深处。
   * **新的标准交互流**：看到感兴趣的节点 $\rightarrow$ **先点击它（它立刻吸顶锁定，变为 Active）** $\rightarrow$ **此时再取消原有的旧筛选条目**。
   * *结果*：由于感兴趣的节点已经被判定为“上浮且置顶的 Active 参数”，当你取消旧条件时，它岿然不动地留在该列顶部，并且仪表盘顺滑地过渡为“围绕这个新节点向上/下推导发散”的全新视角。完全不会有找不到的头疼事。
3. **未选中项的静默（Dimmed Freezing）**：只有处于 `Active` 状态的卡片享有置顶特权。剩下的卡片（包括被连线波及的高亮 `Matched` 项和无关灰阶的 `Dimmed` 项）的相对内部顺序一定要锁死不跳，从而捍卫视觉的空间记忆。配合列头的 `[收起全列无关灰阶卡片]` 按键，不仅防跳跃，更兼顾了密集信息的阅读折叠。
