# 轮询 UX 优化方案

> 日期：2026-05-20
> 作者：庞统
> 状态：待评审
> 问题：5 秒全局轮询导致输入框失焦，用户无法在搜索框、新建对话框等输入文字

## 一、现状

### 当前轮询机制
- `setInterval` 每 1 秒减 countdown，countdown 归零时 `loadAll()`
- `loadAll()` 每 5 秒执行：`loadLive()` → `loadProjects()` → `loadV2Tasks()` → 可选 `loadAgentConfig()`
- 右上角显示 `⟳ 5s` 倒计时

### 问题分析
1. **输入失焦**：`set({ v2tasks: ... })` 触发 EdictBoard 重渲染，所有受控输入框失焦
2. **请求浪费**：用户无操作时仍每 5 秒全量刷新
3. **全部任务模式**：聚合 N 个项目的请求，5 秒一次压力大
4. **视觉干扰**：倒计时数字不停跳动，分散注意力

## 二、方案：智能轮询 + 输入保护

### 2.1 输入焦点保护（核心修复）

**原理**：轮询前检测是否有输入焦点，有则延迟刷新。

```typescript
// 判断是否有活跃输入
function hasActiveInput(): boolean {
  const el = document.activeElement;
  if (!el) return false;
  const tag = (el as HTMLElement).tagName;
  return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'
    || (el as HTMLElement).isContentEditable;
}
```

**轮询逻辑调整**：
- 有输入焦点 → 跳过本次 `loadV2Tasks()`，只刷新轻量数据（ticker）
- 输入结束 2 秒后补刷一次任务列表（用 `blur` 事件 + debounce）
- 倒计时继续走，不暂停计时器本身

### 2.2 轮询间隔分级

| 场景 | 间隔 | 说明 |
|------|------|------|
| 活跃操作后 | 10 秒 | 用户刚点击/切换 tab |
| 空闲状态 | 30 秒 | 无操作，静默刷新 |
| 全部任务模式 | 30 秒 | 聚合请求多，降低频率 |

**实现**：记录上次用户操作时间戳，动态调整间隔。

### 2.3 视觉优化

| 改动 | 说明 |
|------|------|
| 去掉秒级倒计时 | 改为状态指示器：`●` 绿色=正常 `●` 黄色=刷新中 `●` 红色=连接失败 |
| 手动刷新按钮 | 点击立即刷新，重置计时器 |
| 刷新时卡片闪烁 | 用 CSS transition 平滑过渡，不要整块替换 |

### 2.4 增量刷新（可选优化）

当前每次轮询全量替换 `v2tasks` 数组。优化为：
- 比对新旧数据，只有状态变化时才更新对应任务
- 减少不必要的重渲染

```typescript
// 简单增量合并
const merged = newTasks.map(nt => {
  const old = oldTasks.find(ot => ot.id === nt.id);
  return (old && JSON.stringify(old) === JSON.stringify(nt)) ? old : nt;
});
```

## 三、改动范围

| 文件 | 改动 | 行数估计 |
|------|------|---------|
| `store.ts` | 轮询逻辑 + hasActiveInput + 分级间隔 + 增量合并 | ~50 行 |
| `App.tsx` | 倒计时 UI → 状态指示器 + 手动刷新按钮 | ~20 行 |
| `EdictBoard.tsx` | 搜索框添加 onFocus/onBlur 事件（可选） | ~5 行 |

**总计**：~75 行改动，不涉及后端。

## 四、不做的事

1. ❌ 不改 WebSocket（当前 HTTP 轮询够用，未来迁移成本不高）
2. ❌ 不改后端 API（轮询是前端优化）
3. ❌ 不做 Service Worker 缓存（过度设计）

## 五、实施优先级

1. **P0**：输入焦点保护（解决失焦问题）
2. **P1**：轮询间隔 10→30 秒分级（减少请求）
3. **P2**：视觉优化（倒计时→状态指示器）
4. **P3**：增量刷新（性能优化，当前数据量不需要）

## 六、验收标准

1. 在搜索框、新建项目对话框输入时，5 秒内不失焦
2. 空闲 30 秒后自动刷新任务列表
3. 手动刷新按钮可用
4. 右上角不再有跳动数字
