# 前端开发

本文档说明 DiAL Dashboard 的 React、TDesign、TanStack Query、组件、样式和前端测试约定。

适用场景：修改 `src/web/`、前端共享类型使用方式、Dashboard 数据流、组件结构、样式规则或前端测试。

## 技术栈

| 层面   | 技术                                  | 用途                                           |
| ------ | ------------------------------------- | ---------------------------------------------- |
| 框架   | React 19                              | UI 组件开发                                    |
| 构建   | Bun HTML import + Vite dev server     | 开发服务与生产构建                             |
| 语言   | TypeScript 6                          | 类型安全                                       |
| UI 库  | TDesign React + tdesign-icons-react   | UI 组件与图标                                  |
| 数据层 | TanStack Query + React Query Devtools | 服务端状态管理与自动轮询                       |
| 图表   | Recharts                              | 拨测趋势图                                     |
| 动画   | @number-flow/react                    | 倒计时数字滚动过渡                             |
| 路由   | 无                                    | 单页面 Dashboard，通过 Drawer/Tab 做页面内导航 |

不引入 React Router 或额外状态管理库。TanStack Query 承担服务端状态，组件内状态使用 `useState`。

## 组件树与数据流

```text
main.tsx
└── StrictMode
    └── ErrorBoundary
        └── QueryClientProvider
            ├── App
            │   ├── useThemePreference()
            │   ├── useDashboard(refreshInterval)
            │   ├── SummaryCards
            │   └── TargetBoard
            │       └── TargetGroup[]
            │           └── PrimaryTable
            │   └── TargetDetailDrawer
            │       └── useTargetDetail()
            │           ├── OverviewTab
            │           └── HistoryTab
            └── ReactQueryDevtools
```

## TanStack Query 规范

Query key 使用 structured array，排序为 scope -> id -> 参数。

```typescript
const queryKeys = {
  dashboard: () => ["dashboard", "24h", 30] as const,
  meta: () => ["meta"] as const,
  metrics: (targetId: number, from: string, to: string, bucket: "auto" | MetricsBucket) =>
    ["metrics", targetId, from, to, bucket] as const,
  history: (targetId: number, from: string, to: string, page: number) => ["history", targetId, from, to, page] as const,
};
```

全局面板级查询可持续刷新，详情级查询必须按 Drawer 状态和 Tab 状态条件启用。

## fetch 封装

统一使用 `fetch`，不引入 axios。错误抛异常，由 TanStack Query 的 `error` 状态承接。

```typescript
async function fetchJson<T>(url: string): Promise<T> {
  const response = await fetch(url);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json() as Promise<T>;
}
```

## 组件开发规范

- 每个 React 组件一个 `.tsx` 文件，文件名使用 PascalCase。
- 组件 props 定义为 `interface XxxProps`，紧邻组件函数声明。
- 类型从 `../../shared/api` 导入，使用 `import type`。
- 展示组件放在 `components/`，通过 props 接收数据，通过回调返回事件。
- 容器逻辑放在 hooks 中，组件只做数据消费。
- 列定义、排序器、筛选器、颜色阈值等常量放在 `constants/`。
- 时间处理等纯函数放在 `utils/`。

## 现有组件

| 组件                 | 用途                                                              |
| -------------------- | ----------------------------------------------------------------- |
| `App`                | 根组件，Layout + HeadMenu 骨架、主题模式、刷新控制、Skeleton 加载 |
| `ErrorBoundary`      | React 错误边界                                                    |
| `SummaryCards`       | 总览统计卡片                                                      |
| `TargetBoard`        | 按分组渲染目标表格列表                                            |
| `TargetGroup`        | 单个分组 Card + PrimaryTable                                      |
| `TargetDetailDrawer` | 目标详情抽屉                                                      |
| `OverviewTab`        | 目标详情概览                                                      |
| `HistoryTab`         | 目标历史记录表格和分页                                            |
| `TrendChart`         | 趋势折线图                                                        |
| `StatusDot`          | 圆形状态指示点                                                    |
| `StatusBar`          | 最近采样状态条                                                    |
| `RefreshCountdown`   | Header 刷新倒计时和手动刷新按钮                                   |

## 样式规范

前端基于 TDesign React 构建 UI，样式开发优先级：

1. TDesign 组件
2. TDesign 组件 props
3. TDesign CSS tokens（`--td-*`）
4. `styles.css` CSS 类
5. 自行开发组件

红线：

- 严禁在组件中使用 `style` 属性内联调整样式。
- 严禁通过 CSS 覆盖 TDesign 组件内部类名。
- 严禁使用 `!important`。
- 颜色统一使用 TDesign CSS tokens，不使用硬编码色值。

## 前端测试与验证

- 测试目录为 `tests/web/`，结构对应 `src/web/`。
- 单元测试重点覆盖 `constants/`、`utils/` 和 hooks 中的纯逻辑。
- 组件测试使用 jsdom 和 `@testing-library/react`。
- 测试用户行为而非实现细节。
- 只 mock 系统边界，例如 `fetch`。
- 使用真实的 QueryClientProvider 包裹依赖 TanStack Query 的组件。
- 异步错误断言使用 helper 或显式 try/catch，避免依赖 Bun `expect(...).rejects` 与 `await-thenable` 规则的类型不匹配。
- 组件测试环境由 `tests/setup.ts` 和 `bunfig.toml` preload 提供，包含 ResizeObserver、IntersectionObserver、matchMedia、attachEvent 和 Recharts mock。

前端逻辑变更通常需要运行 `bun run check`。影响生产静态资源、前后端集成或构建流程时运行 `bun run verify`。

## 更新触发条件

修改前端技术栈、组件边界、数据流、样式规则、测试环境或前端验证方式时，必须更新本文档。
