Files
sanguo_moziplus_v2/docs/design/polling-ux-proposal.md
T
2026-05-20 21:03:51 +08:00

104 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 轮询 UX 优化方案
> 日期:2026-05-20
> 作者:庞统
> 状态:待评审
> 问题:5 秒全局轮询导致输入框失焦,用户无法在搜索框、新建对话框等输入文字
## 一、现状
### 当前轮询机制
- `setInterval` 每 1 秒减 countdowncountdown 归零时 `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. 右上角不再有跳动数字