# AgentEvalTool 前端开发日志 — 评测执行页改版

**日期**: 2026-07-15  
**基线版本**: v0.2.0-dev  
**状态**: 已部署至 t480 开发线  
**作者**: AgentEval Team

---

## 一、背景

本次开发在 v0.2.0-dev 基础上，对评测执行页（Runs）进行全面 UI 改版，同时修复了若干历史遗留的基础 Bug。

---

## 二、关键 Bug 修复

### 2.1 `index.css` 从未被引入（历史遗留）

**症状**: 所有全局 CSS（动画、hover、布局覆盖规则）完全无效，但无报错提示。

**根因**: `main.tsx` 缺少 `import './index.css'`，Vite 不会自动包含未被引用的 CSS 文件，导致该文件从项目创建以来就从未生效。

**修复**: 在 `main.tsx` 顶部添加：
```tsx
import './index.css'
```

**影响**: 修复后构建产物首次出现独立 `.css` 文件（`dist/assets/index-*.css`），全局 CSS 规则一次性全部生效。

---

### 2.2 UTC 时间戳显示偏差 8 小时

**症状**: 评测记录时间比实际时间早 8 小时（如显示 16:40 实为次日 00:40 北京时间）。

**根因（三层叠加）**:
```
utc_now() → datetime(UTC)
    ↓ SQLite 不保存时区
"2026-07-14T16:40:00"（无 Z 后缀）
    ↓ ECMAScript 规范：无时区字符串按本地时间解析
new Date("2026-07-14T16:40:00") → 北京时间 16:40（比 UTC 多 8 小时）
```

**修复**: 在 `frontend/web/src/utils/date.ts` 添加 `toDate()` 工具函数：
```ts
export function toDate(iso: string): Date {
  if (iso.endsWith('Z') || /[+-]\d{2}:\d{2}$/.test(iso)) return new Date(iso)
  return new Date(iso + 'Z')   // 追加 Z，强制按 UTC 解析
}
```

所有解析后端时间戳的地方统一使用 `toDate()`，不再直接 `new Date(str)`。

**注意**: 此问题根本解决方式是后端序列化时附带时区（如 `datetime.isoformat()` → `+00:00`），前端修复为应急兜底。

---

### 2.3 时间过滤器与显示时区不一致

**症状**: 今天发起的记录显示为正确的北京时间，但选择「当天」时过滤器找不到这些记录。

**根因**: `shortDateTime()` 已使用 `toDate()`（UTC 解析正确），但 `RunList` 过滤器仍用 `new Date(r.started_at)` 按本地时间解析，两者时间基准不一致，差了 8 小时导致过滤失败。

**修复**: 过滤器改用 `toDate()` 统一时间基准：
```ts
const t = r.started_at ? toDate(r.started_at).getTime() : null
```

---

## 三、评测执行页改版详情

### 3.1 全局布局修复

| 问题 | 修复方案 |
|---|---|
| 页面产生外层滚动条 | `App.tsx` tab 容器改为 `height: 100%, boxSizing: border-box`，消除 `minHeight + padding` 叠加导致的溢出 |
| 页面顶部出现空隙 | `html, body { margin: 0; padding: 0 }` + 覆盖 Ant Design `editable-card` Tabs 默认 `paddingTop` |
| Grid 子元素高度失效 | 增加 `gridTemplateRows: '1fr'`，显式让单行撑满 Grid 高度 |
| 魔法数字 `calc(100vh - 152px)` | 用 `PageWrapper fullHeight` + flex 链代替，彻底去除硬编码数字 |
| 内容区未贴边 | `Layout.Content` 及所有容器 padding 归零，内容贴合浏览器边缘 |

### 3.2 左侧面板 — 启动区

- **两行布局**: 第一行「评测对象」Select 全宽；第二行「评测场景 + 启动」并排
- **Select 增强**: `showSearch`（输入过滤）+ `allowClear`（清除按钮）
- **背景色**: 区域改为 `colors.bgSubtle`（`#fafafa`）浅底色，与列表区视觉分隔

### 3.3 左侧面板 — 筛选区

**三行布局:**
```
Row 1: [搜索框 full-width]
Row 2: [全部 进行中 成功 失败]  [当天][1天][7天][30天]  [N条]
```

**时间快捷标签**（替代 DatePicker）:
- 当天：自然日 00:00 ～ 23:59（本地时间）
- 1天：昨天 + 今天
- 7天：最近 7 天含今天
- 30天：最近 30 天含今天
- 默认选中「当天」；再次点击取消（回到全部时间）

**状态分段**: 默认「全部」，选中态蓝底白字（ConfigProvider Segmented token）

**时间标签颜色**: 激活态蓝色实心背景 + 白字；未激活态深灰边框 + 深色文字

### 3.4 左侧面板 — 列表

| 项目 | 改动 |
|---|---|
| 行结构 | 三行固定（名称/元信息/进度条），等高不抖动 |
| 进度条 | pending/running 显示蓝色动态进度（来自 liveProgress）；completed 显示通过率色阶 |
| 分页 | 每页 10 条，底部固定分页栏（始终占位，布局稳定） |
| 排序 | 去掉错误的 `.reverse()`，API 已按 `started_at DESC` 返回 |
| hover | 去掉 `onMouseEnter/Leave` 直接操作 DOM，改为 CSS `.run-row:hover` |

### 3.5 右侧面板 — Tab 滚动

**问题**: 三个 Tab（实时对话、规则总览、用例明细）内容区无法独立滚动。

**根因**: Ant Design v5 使用 CSS-in-JS（emotion）在运行时注入样式，覆盖了静态 CSS 文件中的 `flex`/`height` 规则，导致 Tab 内容区高度链断裂。

**最终方案（导航与内容分离）**:
```
Tabs（.nav-only-tabs）：只渲染导航头，内容区 display: none
  ↓
三个内容 div（直接 flex 子元素）：
  flex: 1, min-height: 0, overflowY: auto
  → 高度由 flex 算法直接确定，不经过 Ant Design 内部 DOM
```

```css
.ant-tabs.nav-only-tabs .ant-tabs-content-holder {
  display: none !important;
}
```

三个 Tab 的滚动各自独立，互不干扰。

---

## 四、其他优化

| 项目 | 内容 |
|---|---|
| 代码去重 | 提取 `startRun()` 函数，消除 `handleStart` 与 `onRerun` 重复逻辑 |
| 死代码清理 | 删除 `sessionReducer` 中从未被 dispatch 的 `SET_RUN` action |
| 工具函数复用 | `RunList` 改用 `shortDateTime` / `elapsedStr`，删除本地重复的 `formatStart` / `durationStr` |
| 类型安全 | `run.summary as any` 改为 `Record<string, unknown>` 类型收窄 |
| Bundle 瘦身 | 用时间标签替代 DatePicker，vendor chunk 缩小约 107KB |
| CSS 组件化 | Segmented 选中态、Run list hover 统一移入 ConfigProvider token / CSS 类，不再散落在组件 inline style |

---

## 五、t480 部署记录

**部署前环境补全**（首次排查发现）:

`.env` 只有 1 个键（`AGENTEVAL_ALLOWED_ORIGINS`），缺失以下配置：

```bash
AGENTEVAL_API_KEY=
AGENTEVAL_OPENCLAW_UPSTREAM=http://openclaw-eval:18789
AGENTEVAL_OPENCLAW_WS_UPSTREAM=ws://openclaw-eval:18789
AGENTEVAL_OPENCLAW_PROXY_USER=agenteval
AGENTEVAL_OPENCLAW_AUTH_TOKEN=change-me-in-production
```

已通过 SSH 补全，OpenClaw 代理功能恢复正常。

---

## 六、遗留问题

| 问题 | 状态 | 说明 |
|---|---|---|
| case-001 无发送气泡 | 待查 | 只有智能体回复，无用户发送内容；需确认 DB 中 `turn.sent_message` 字段内容 |
| UTC 时间后缀 | 待优化 | 当前前端追加 `Z` 兜底；根本修复需后端序列化附带时区标记 |

---

## 七、文件变更清单

**前端**
- `src/main.tsx` — 添加 `import './index.css'`
- `src/index.css` — html/body 重置、nav-only-tabs、TabBar padding 覆盖、Segmented 样式
- `src/App.tsx` — 布局修复（overflow、padding），Segmented token
- `src/components/PageWrapper.tsx` — 新增 `fullHeight` prop
- `src/pages/Runs.tsx` — 完整重构：布局、启动区、内联头部、Tab 分离
- `src/components/RunList.tsx` — 筛选区三行布局、时间标签、toDate 对齐、分页
- `src/utils/date.ts` — 新增 `toDate()`（export），修复时区
- `src/hooks/sessionReducer.ts` — 删除 `SET_RUN` 死代码
