# Clover 前端逻辑诊断 · 数据流与交互（第一次扒·另一窗口改完需重扒）

> 你的直觉对了："功能呈现和数据流转交互太弱"——但弱在**数据流架构**，不只在界面长相。
> 扒的是：3个zustand store(generation/task/preference) + api.ts + useSse + sse.ts。
> ⚠️ 另一窗口正在改前端，本次结论是当前快照，改完重扒对比。

---

## 一、先画出前端真实的数据流

```
┌─ 进任务页 ─────────────────────────────────────────────┐
│ useSse(taskId) 启动                                      │
│   ① 立即 GET /tasks/{id} → hydrateFromTask (回填历史候选) │
│   ② 建 SSE: 先POST换ticket → EventSource连stream         │
│   ③ SSE事件 → handleEvent → 写 generationStore           │
└──────────────────────────────────────────────────────┘
        ↓ 组件从 store 订阅渲染
   text/images页 读 generationStore.textCandidates 等
        ↓ 用户选择
   taskStore.selectedTextIds (纯本地数组)
        ↓ 点"下一步"
   for循环逐个 POST select → router.push 下一页
```

**三个store各管一摊：**
- `generationStore`：SSE进度+候选+飞轮注入（生成态）
- `taskStore`：当前任务id+已选id数组（选择态）
- `preferenceStore`：飞轮偏好摘要（注入态）

<!-- 后续Edit补充：二、数据流的7个弱点 / 三、交互弱的根因 / 四、对照俊达该补什么 / 五、待重扒清单 -->

---

## 二、数据流的7个弱点（每条带代码证据）

### 弱点1：策略维度A/B/C在前端被彻底抹平 🔴最关键
- 后端生图按3套策略各N张（`pipeline_io.py:201`），SSE的`image_candidate`事件**带了strategy字段**(`useSse.ts:104`接收了)。
- 但 `generationStore.imageCandidates` 是**一维平铺数组**(store注释明写"image_candidate无batch字段所以平铺")，images页直接`grid`铺开。
- **后果**：后端辛苦做的"3个叙事方向×N张给你对比挑选"，前端变成一堆看不出分组的散图。用户不知道哪些是同一策略、为什么有18张。这是"交互弱"最硬的证据——**信息结构在展示层塌掉了**。

### 弱点2：文案五维分要二次请求补全，且每条都重拉整个task 🔴
- `useSse.ts:75-94`：SSE的`text_candidate`只推total分，五维明细**靠再次 GET /tasks/{id} 补全**。
- 致命：**每来一条文案候选就GET一次整个task**(line88)，8条文案=8次全量请求，每次返回所有候选。N条候选→N次重复拉全量，越往后越浪费。
- **后果**：五维分有延迟跳变（先0后补），且网络浪费。交互上是"分数闪一下才出来"。

### 弱点3：选择态是纯本地数组，不落后端、刷新即丢 🟡
- `taskStore.selectedTextIds/selectedImageIds` 只在内存(line13)。
- 选文案时`toggleTextSelection`只改本地数组，**点"下一步"才 for循环逐个POST select**(`text/page.tsx:29-32`)。
- **后果**：①选到一半刷新页面，选择全没了 ②多选N条就发N个请求(不是批量) ③选中状态和后端`is_selected`字段不实时同步。

### 弱点4：跳转用 router.push，候选数据靠"重新拉" 🟡
- 每个环节(text→images→confirm)是独立页面，靠`router.push`跳转。
- 进每页 `useSse` 都`store.reset()`再`hydrateFromTask`重新GET(`useSse.ts:21,30`)。
- **后果**：环节间无"连续流"感，每跳一页是一次重新加载+重连SSE。confirm页要的已选数据，靠`generationStore`里还留着的候选`filter`出来(`confirm/page.tsx:22-23`)——**万一store被reset就空了**。

### 弱点5：confirm页数据来源脆弱 🟡
- `confirm/page.tsx:22` 用`textCandidates.filter(selectedTextIds.includes)`拿已选内容。
- 但`selectedTextIds`在taskStore、`textCandidates`在generationStore，**两个store没绑定**。SSE的reset只清generationStore，selectedTextIds还在taskStore——两者生命周期不一致，容易错位。

### 弱点6：错误处理静默吞掉 🟡
- `useSse.ts:37,93` 多处`.catch(() => {})`空吞错误；`preferenceStore:31`无偏好"不阻塞流程"也静默。
- **后果**：数据没拉到时用户无感知，界面就是"空着"，不知道是没数据还是出错了。这放大了"空"的感受。

### 弱点7：SSE的task_done/error事件没驱动UI 🟡
- `useSse.ts:123-127`：`error`/`task_done`/`analyze_done`/`heartbeat`全部`break`空处理。
- **后果**：生成完成了(task_done)前端不弹提示、不自动跳转；出错了(error)也不显式告诉用户。状态机的关键节点在前端是哑的。

---

## 三、为什么"交互弱"——根因归纳

| 表象 | 数据流根因 |
|---|---|
| 图一堆看不出门道 | 策略维度被平铺抹平(弱点1) |
| 分数闪一下才出 | 五维分二次请求补全(弱点2) |
| 选着选着没了 | 选择态纯本地不持久(弱点3) |
| 翻页像重新加载 | 页面级跳转+每页重连重拉(弱点4) |
| 完成/出错没反馈 | 关键SSE事件空处理(弱点7)+错误静默(弱点6) |

> **本质**：前端是"**被动渲染后端推什么显示什么**"的薄层，没有自己的"数据组织能力"。后端给的多维数据(策略/评分/角色/理由)，前端要么拍平、要么二次拉、要么不展示。**不是界面不好看，是数据进了前端没被'组织'成有结构的交互。**

---

## 四、对照俊达：他的前端为什么"饱满流畅"

俊达界面给人"饱满"感，本质是**数据被组织出了层次**：
- 同一组图按策略/角色分组对比 → Clover平铺(弱点1)
- 每个产出带可见的评分/理由/标签 → Clover评分要补拉、理由没全显(弱点2)
- 操作即时反馈、状态流转有动效 → Clover关键事件空处理(弱点7)
- 进化页/复盘页是"数据看板" → Clover dashboard是3个按钮

> 借鉴点(不抄原文)：**把"后端已有的多维数据"在前端组织成有结构的呈现**——分组、即时反馈、状态可见。这是纯前端重构，不依赖后端加功能，也不碰俊达的knowhow。

---

## 五、待重扒清单（另一窗口改完后）

本次基于当前快照。另一窗口改完前端后，重点复查这几项有没有变：
1. `generationStore` 的 imageCandidates 有没有改成按strategy分组
2. `useSse` 的五维分补全有没有改掉"每条拉全量task"
3. taskStore 选择态有没有改成持久化/批量提交
4. 关键SSE事件(task_done/error)有没有接UI反馈
5. dashboard 有没有加数据看板
6. images页有没有按A/B/C分组展示

> 重扒方法：对这6个文件做 diff，对照本文档逐条核实改了没。本文档是"改之前"的基线。
