# Admin 前端唯一执行方案

更新时间：2026-03-19

## 1. 文档定位

本文是当前唯一有效的 Admin 前端执行方案，后续前端实现、拆分任务、接口联调、验收都以本文为准。

事实来源仅限以下文件：

- `docs/API.md`
- `internal/api/router/router.go`
- `docs/status/REAL_PROJECT_STATUS.md`
- `docs/PROJECT_REVIEW_REPORT.md`

约束原则：

1. 只实现仓库内已经真实接线、可以联调的 API。
2. PRD 与当前后端实现不一致时，以当前后端事实为准，缺口明确列为延期项或前置依赖。
3. 页面、类型、认证流、API 服务层必须统一，不再允许文档内同时存在 Vue / React、Axios / Fetch、假接口 / 真接口两套口径。

---

## 2. 当前真实约束

### 2.1 项目现状

- 仓库中目前没有真实前端项目，Admin 前端尚未开始。
- 后端当前已经通过 `go build ./...`、`go vet ./...`、`go test ./...`。
- 当前项目真实状态是“后端核心链路可用，前端未开始，PRD 仍有未完成范围”。

### 2.2 必须接受的后端事实

- 用户管理没有 `POST /api/v1/users`，因此当前不支持“管理员单个创建用户”页面。
- 没有批量启用、批量禁用、批量删除接口，因此不做批量用户操作。
- `PUT /api/v1/users/:id/password` 当前只允许本人改自己的密码，不支持管理员重置他人密码。
- `/api/v1/users/:id/avatar` 的处理逻辑实际上只更新当前登录用户头像，因此头像上传只放到“个人中心”，不放到用户管理页。
- 仪表盘统计只提供：
  - 用户总数、各状态人数
  - 今日 / 本周 / 本月新增用户
  - 今日成功 / 失败登录数
  - 本周成功登录数
- 没有系统设置配置接口，因此不做 `/settings` 页面。
- 没有“当前用户权限快照”接口。前端可稳定获取的是：
  - `GET /api/v1/auth/userinfo`
  - `GET /api/v1/users/:id/roles`
- OAuth 回调当前由后端直接返回 JSON，不是标准 SPA redirect 完成态，因此社交登录 / 绑定不纳入当前 MVP。
- 设备接口没有“全局设备列表”，只有：
  - `GET /api/v1/devices` 当前用户设备
  - `GET /api/v1/devices/users/:id` 指定用户设备
  因此前端不做全局设备管理页面。

---

## 3. 单一技术栈

当前只接受以下一套前端技术栈：

| 关注点 | 统一方案 | 说明 |
|--------|----------|------|
| 框架 | React 18 + TypeScript | 统一组件模型和类型系统 |
| 构建工具 | Vite | 快速启动，适合从零搭建 |
| 路由 | React Router 6 | 足够覆盖登录、后台布局、受保护路由 |
| UI 组件 | Ant Design 5 | 后台场景成熟，减少重复造轮子 |
| 请求层 | 浏览器原生 `fetch` + 自建请求客户端 | 避免 Axios 二次封装分叉 |
| 状态管理 | React Context 仅管理会话态 | 不引入 Redux / Zustand / Pinia |
| 页面数据 | 页面局部状态 + 受控请求 | 首版不引入 React Query |
| 表单 | Ant Design Form | 与 UI 组件一致 |
| 样式 | CSS Modules + CSS Variables + AntD Theme Token | 不引入 `styled-components` |
| 图表 | MVP 不引入额外图表库 | 当前统计接口不足以支撑复杂图表 |

明确不采用：

- Vue 3
- Pinia
- Axios
- styled-components
- React Query
- “HTML + CSS + JavaScript 手写管理后台”

### 3.1 前端技术架构总览

前端统一采用单体 SPA 架构，不拆微前端，不做 SSR，不做多应用并行。

运行时分层固定为：

1. `App Shell`
   - 应用启动、主题、路由挂载、全局异常边界
2. `Session Layer`
   - 会话恢复、刷新令牌、登录态广播、路由准入
3. `HTTP Client Layer`
   - 鉴权头注入、统一解包、401 重试、下载上传
4. `Service Layer`
   - 按资源拆分 API 方法，不放 UI 逻辑
5. `Page / Feature Layer`
   - 页面编排、表格、表单、弹窗、抽屉
6. `Shared UI Layer`
   - 布局、通用表格状态、空态、错误态、确认弹窗

数据流固定为：

`Page -> Service -> HTTP Client -> API`

禁止反向依赖：

- `services` 依赖页面组件
- 页面直接拼接 URL 自己发请求
- 多套 token 管理并存
- 多套类型定义并存

### 3.2 项目目录架构

前端项目统一新建在：

- `frontend/admin`

目录结构固定为：

```text
frontend/admin/
  package.json
  tsconfig.json
  vite.config.ts
  .env.example
  src/
    app/
      App.tsx
      main.tsx
      router.tsx
      providers/
        AuthProvider.tsx
        ThemeProvider.tsx
    layouts/
      AdminLayout/
      AuthLayout/
    pages/
      auth/
        LoginPage/
        ForgotPasswordPage/
        ResetPasswordPage/
      admin/
        DashboardPage/
        UsersPage/
        RolesPage/
        PermissionsPage/
        LoginLogsPage/
        OperationLogsPage/
        WebhooksPage/
        ImportExportPage/
        ProfilePage/
        ProfileSecurityPage/
    features/
      auth/
      users/
      roles/
      permissions/
      logs/
      webhooks/
      profile/
      import-export/
      devices/
      totp/
    services/
      auth.ts
      users.ts
      roles.ts
      permissions.ts
      stats.ts
      logs.ts
      webhooks.ts
      import-export.ts
      profile.ts
      devices.ts
    lib/
      http/
        client.ts
        auth-session.ts
        errors.ts
      storage/
        token-storage.ts
    components/
      common/
      feedback/
      forms/
    types/
      http.ts
      auth.ts
      user.ts
      role.ts
      permission.ts
      device.ts
      log.ts
      webhook.ts
      stats.ts
    styles/
      tokens.css
      global.css
```

目录原则：

- `pages` 只放路由页面，不放复用业务组件
- `features` 放页面内复用的业务组件与交互逻辑
- `services` 只放接口调用
- `types` 只放接口类型与领域类型
- `components/common` 只放通用 UI，不感知业务资源

### 3.3 依赖白名单

首版允许引入的前端运行时依赖只保留：

- `react`
- `react-dom`
- `react-router-dom`
- `antd`
- `@ant-design/icons`
- `dayjs`

首版允许引入的开发依赖只保留：

- `typescript`
- `vite`
- `@vitejs/plugin-react`
- `eslint`
- `@types/react`
- `@types/react-dom`
- `vitest`

依赖准入规则：

1. 没有明确解决当前问题的依赖，不引入。
2. 能用浏览器原生能力解决的问题，不引入包装库。
3. 同类库只保留一套。

### 3.4 技术简化结论

这版前端技术架构刻意做了收缩，最终结论如下：

- 不做微前端
- 不做 monorepo 拆分
- 不做 SSR / SSG
- 不做 Redux / Zustand / Pinia
- 不做 React Query
- 不做 Axios
- 不做 CSS-in-JS
- 不做图表库优先接入
- 不做权限码驱动的复杂前端元编程

首版只保留四个必须的技术支点：

1. React 页面和路由
2. Context 会话管理
3. `fetch` 请求客户端
4. Ant Design 后台组件

### 3.5 工程准则

前端开发时必须遵守以下准则：

1. 一个 API 资源对应一个 service 文件。
2. 一个路由页面对应一个 page 目录。
3. 一个复杂弹窗 / 抽屉对应一个独立 feature 组件。
4. 一个任务只解决一类问题，不混做“页面 + 所有依赖 + 所有接口”。
5. 所有新代码默认 TypeScript 严格模式。
6. 所有请求先写类型，再写 service，再接页面。

---

## 4. 页面与路由范围

### 4.1 MVP 页面

| 路由 | 访问级别 | 页面范围 | 对应 API |
|------|----------|----------|----------|
| `/login` | 公开 | 账号密码登录、邮箱验证码登录、短信验证码登录 | `/auth/login` `/auth/send-email-code` `/auth/login/email-code` `/auth/send-code` `/auth/login/code` |
| `/forgot-password` | 公开 | 发起密码重置 | `/auth/forgot-password` |
| `/reset-password` | 公开 | 校验重置 token、提交新密码 | `/auth/reset-password` |
| `/dashboard` | 管理员 | 统计卡片与简表，不做假趋势图 | `/admin/stats/dashboard` `/admin/stats/users` |
| `/users` | 管理员 | 用户列表、筛选、详情抽屉、编辑、状态切换、删除、分配角色 | `/users` `/users/:id` `/users/:id` `/users/:id/status` `/users/:id/roles` `/users/:id` |
| `/roles` | 管理员 | 角色列表、创建、编辑、删除、启停、分配权限 | `/roles` `/roles/:id` `/roles/:id/status` `/roles/:id/permissions` |
| `/permissions` | 管理员 | 权限列表、树、创建、编辑、删除、启停 | `/permissions` `/permissions/tree` `/permissions/:id/status` |
| `/logs/login` | 管理员 | 登录日志列表 | `/logs/login` |
| `/logs/operation` | 管理员 | 操作日志列表 | `/logs/operation` |
| `/webhooks` | 已登录 | Webhook 列表、创建、编辑、删除、投递记录 | `/webhooks` `/webhooks/:id/deliveries` |
| `/import-export` | 管理员 | 导入、导出、模板下载 | `/admin/users/import` `/admin/users/export` `/admin/users/import/template` |
| `/profile` | 已登录 | 个人资料查看与编辑 | `/auth/userinfo` `/users/:id` |
| `/profile/security` | 已登录 | 修改密码、TOTP、头像上传、本人设备、本人日志 | `/users/:id/password` `/auth/2fa/*` `/users/:id/avatar` `/devices` `/logs/login/me` `/logs/operation/me` |

### 4.2 页面能力边界

`/users` 当前只允许实现：

- 列表分页、关键字筛选、状态筛选、角色筛选、时间筛选、排序
- 查看用户详情
- 编辑用户资料
- 删除用户
- 修改用户状态
- 分配角色

`/users` 当前明确不做：

- 新建用户
- 批量操作
- 管理员代改他人密码
- 为其他用户上传头像

`/dashboard` 当前只允许展示真实统计字段，不做以下伪需求：

- 在线用户
- 近 7 / 30 天时序趋势图
- 地域分布图
- 最近注册用户
- 最近登录用户

### 4.3 延期或阻塞页面

以下页面或功能不进入当前前端实施范围：

- `/settings`
- 全局 `/devices` 管理页
- 用户创建页
- 用户批量操作
- 管理员重置他人密码
- “记住我 / 记住设备”
- 社交登录回调页
- 社交账号绑定页
- 依赖权限码的细粒度按钮控制

---

## 5. 统一类型模型

### 5.1 基础响应模型

所有请求统一先经过响应解包，禁止页面直接假定不同接口的 `data` 结构。

```ts
export interface ApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

export interface PaginatedData<T> {
  items: T[];
  total: number;
  page: number;
  page_size: number;
}
```

注意：

- 分页列表真实格式是 `data.items + total + page + page_size`。
- 不是所有列表都是分页返回。
- `GET /webhooks`、`GET /users/:id/roles`、`GET /roles/:id/permissions`、`GET /permissions/tree` 等接口返回的不是 `PaginatedData<T>`。

### 5.2 会话与认证类型

```ts
export interface SessionUser {
  id: number;
  username: string;
  email: string;
  phone: string;
  nickname: string;
  avatar: string;
  status: 0 | 1 | 2 | 3;
}

export interface TokenBundle {
  access_token: string;
  refresh_token: string;
  expires_in: number;
  user: SessionUser;
}
```

关键约束：

- `POST /auth/login` 返回 `TokenBundle`
- `POST /auth/refresh` 也返回 `TokenBundle`
- 不允许再定义“刷新后只返回 access_token”的前端假类型

### 5.3 领域类型

前端类型定义以当前后端 JSON 字段为准，至少包含以下文件：

- `src/types/http.ts`
- `src/types/auth.ts`
- `src/types/user.ts`
- `src/types/role.ts`
- `src/types/permission.ts`
- `src/types/device.ts`
- `src/types/log.ts`
- `src/types/webhook.ts`
- `src/types/stats.ts`

必须保留的真实枚举约束：

- `UserStatus`: `0 | 1 | 2 | 3`
- `RoleStatus`: `0 | 1`
- `PermissionStatus`: `0 | 1`

---

## 6. 认证与会话流

### 6.1 统一会话策略

采用以下单一策略：

1. `refresh_token` 持久化到 `localStorage`
2. `access_token` 保存在内存态
3. 应用启动时优先尝试用 `refresh_token` 换取新的 `TokenBundle`
4. 刷新成功后再加载角色信息
5. 刷新失败则清空会话并回到登录页

这样做的原因：

- 当前后端刷新接口已经返回完整 `TokenBundle`
- 可以避免前端长期持久化过期 `access_token`
- 页面刷新后可以稳定恢复登录态

### 6.2 启动流程

应用启动流程固定如下：

1. 读取本地 `refresh_token`
2. 若存在，则调用 `POST /api/v1/auth/refresh`
3. 刷新成功后，拿到新的 `access_token`、`refresh_token`、`user`
4. 调用 `GET /api/v1/users/{user.id}/roles`
5. 从角色列表中推导：
   - `roleCodes`
   - `isAdmin = roleCodes.includes("admin")`
6. 会话完成后才渲染受保护路由

补充规则：

- 不以 `GET /auth/userinfo` 作为唯一启动入口，避免浪费一次可能会被 401 的请求
- `GET /auth/userinfo` 作为“刷新当前资料”能力保留给 Profile 页面或手动重载

### 6.3 登录与登出流

登录页支持三种真实入口：

- 账号密码登录
- 邮箱验证码登录
- 短信验证码登录

登录成功后统一执行：

1. 写入新的 `refresh_token`
2. 将新的 `access_token` 放入内存
3. 直接使用返回体中的 `user`
4. 再请求当前用户角色
5. 完成后跳转后台首页

登出统一执行：

1. 调用 `POST /api/v1/auth/logout`
2. 请求体优先带上当前 `access_token` 和 `refresh_token`
3. 无论接口成功与否，都清空本地会话
4. 跳回 `/login`

### 6.4 401 处理

请求客户端必须实现单次刷新机制：

1. 普通业务请求收到 `401`
2. 若当前存在 `refresh_token`，触发一次全局中的刷新流程
3. 刷新成功后重放原请求
4. 刷新失败则清会话并跳转 `/login`

禁止旧方案中的以下行为：

- 收到 `401` 直接跳登录，不尝试刷新
- 刷新后只更新 `access_token`，不更新 `refresh_token`

### 6.5 暂不纳入 MVP 的认证能力

以下能力有 API，但当前不纳入首版登录流：

- 社交登录按钮直连回调完成登录
- 社交账号绑定
- remember me

原因不是“前端不做”，而是“当前后端协议不适合 SPA 稳定落地”。

---

## 7. API 服务层统一方案

### 7.1 目录结构

目录结构以 `3.2 项目目录架构` 为准。

本节只强调 service / lib / types 三层在该目录中的职责：

- `lib/http`
  - 统一请求客户端、会话刷新、错误模型
- `services`
  - 资源级 API 访问层
- `types`
  - 统一接口类型和领域类型

### 7.2 请求客户端职责

`lib/http/client.ts` 只做以下事情：

- 拼接基础 URL
- 注入 `Authorization: Bearer <access_token>`
- 统一解包 `ApiResponse<T>`
- 统一处理 `401`
- 统一处理 `Blob` 下载和 `FormData` 上传
- 将后端业务错误统一转成前端可消费的 `AppError`

禁止：

- 页面组件内手写 `fetch("/api/v1/...")`
- 每个页面自己处理 token 注入与刷新
- 把分页响应和普通响应混成一个类型

### 7.3 服务层模块边界

`services/auth.ts`

- `loginByPassword`
- `sendEmailCode`
- `loginByEmailCode`
- `sendSmsCode`
- `loginBySmsCode`
- `refreshSession`
- `logout`
- `forgotPassword`
- `validateResetToken`
- `resetPassword`
- `getUserInfo`
- `getTwoFactorStatus`
- `setupTwoFactor`
- `enableTwoFactor`
- `disableTwoFactor`
- `verifyTwoFactor`

`services/users.ts`

- `listUsers`
- `getUser`
- `updateUser`
- `deleteUser`
- `updateUserStatus`
- `getUserRoles`
- `assignUserRoles`

`services/profile.ts`

- `getMyProfile`
- `updateMyProfile`
- `changeMyPassword`
- `uploadMyAvatar`
- `getMyLoginLogs`
- `getMyOperationLogs`

`services/roles.ts`

- `listRoles`
- `createRole`
- `getRole`
- `updateRole`
- `deleteRole`
- `updateRoleStatus`
- `getRolePermissions`
- `assignRolePermissions`

`services/permissions.ts`

- `listPermissions`
- `getPermissionTree`
- `createPermission`
- `getPermission`
- `updatePermission`
- `deletePermission`
- `updatePermissionStatus`

`services/stats.ts`

- `getDashboardStats`
- `getUserStats`

`services/logs.ts`

- `getLoginLogs`
- `getOperationLogs`

`services/webhooks.ts`

- `listWebhooks`
- `createWebhook`
- `updateWebhook`
- `deleteWebhook`
- `getWebhookDeliveries`

`services/import-export.ts`

- `downloadUserExport`
- `uploadUserImport`
- `downloadImportTemplate`

`services/devices.ts`

- `getMyDevices`
- `getUserDevices`
- `getDevice`
- `createDevice`
- `updateDevice`
- `deleteDevice`
- `updateDeviceStatus`

### 7.4 下载与上传约束

- 用户导出必须走 `Blob` 下载
- 导入必须走 `multipart/form-data`
- 头像上传必须走 `multipart/form-data`
- 头像上传虽然路径是 `/users/:id/avatar`，但前端只允许传当前用户 ID

---

## 8. 权限与路由守卫

### 8.1 现阶段只做两级守卫

当前前端守卫只做：

- `RequireAuth`
- `RequireAdmin`

判断依据：

1. 会话中必须有有效 `access_token`
2. 启动后必须已成功拉到当前用户角色
3. `role.code === "admin"` 视为管理员

### 8.2 当前不做权限码按钮系统

原因：

- 后端没有“当前用户权限快照”公开接口
- 中间件中的 `permission_codes` 仅存在于服务端请求上下文，不会自动返回给前端

因此当前只允许：

- 管理员页面用 `RequireAdmin`
- 用户自己的页面按“本人”场景控制

当前不允许：

- 以前端硬编码方式伪造整套按钮级 `permission code` 控制体系

---

## 9. 实施阶段与最小任务拆解

### 9.1 交付顺序

前端按以下顺序交付，不允许跳步：

1. 工程骨架
2. 会话与请求底座
3. 认证页面
4. 管理后台框架
5. 核心管理页
6. 运营与个人安全页
7. 收尾验证

### 9.2 任务拆解规则

最小任务定义：

- 一个任务只产出一个明确可验证的结果
- 一个任务的完成标准必须能被构建、页面行为或接口联调验证
- 一个任务不能横跨多个资源域

任务粒度控制：

- 基础设施任务：一个文件组或一个运行能力
- 服务层任务：一个资源服务文件
- 页面任务：一个路由页面
- 复杂交互任务：一个弹窗、一个抽屉、一个上传、一个下载流

### 9.3 M0 工程骨架

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M0-01 | 初始化 `frontend/admin` Vite 项目 | 基础工程目录 | 能本地启动空白 React 页面 |
| M0-02 | 建立 `tsconfig` 路径别名 | `@/` 别名 | 导入路径不再依赖相对路径层层回退 |
| M0-03 | 建立 `.env.example` | `VITE_API_BASE_URL` 规范 | 请求基地址可配置 |
| M0-04 | 接入 Ant Design 5 和全局样式 | `global.css` / `tokens.css` | 页面样式正常加载 |
| M0-05 | 建立 `AuthLayout` 与 `AdminLayout` 骨架 | 两套布局组件 | 登录页和后台页布局可区分 |
| M0-06 | 建立应用路由骨架 | `router.tsx` | 公开页和受保护页都可访问 |
| M0-07 | 建立错误页与 404 页 | 错误反馈页面 | 未匹配路由可落到 404 |
| M0-08 | 建立全局 `AppError` 模型 | 统一错误结构 | 页面可接收统一错误对象 |

### 9.4 M1 会话与请求底座

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M1-01 | 实现 `token-storage.ts` | 刷新令牌读写封装 | `refresh_token` 可稳定存取 |
| M1-02 | 实现 `auth-session.ts` | 内存态 access token 管理 | 页内请求可拿到当前 access token |
| M1-03 | 实现 `client.ts` 基础请求能力 | `GET/POST/PUT/DELETE` 封装 | 页面不再手写原生请求细节 |
| M1-04 | 实现统一响应解包 | `ApiResponse<T>` 解包 | service 可直接返回业务数据 |
| M1-05 | 实现统一业务错误映射 | 后端错误转 `AppError` | 页面可稳定提示错误信息 |
| M1-06 | 实现 401 单次刷新机制 | 请求重试逻辑 | access token 过期后可自动刷新一次 |
| M1-07 | 实现并发刷新锁 | 单飞刷新 | 多请求 401 时只触发一次刷新 |
| M1-08 | 实现 `AuthProvider` | 全局会话上下文 | 页面可读取用户、角色、登录状态 |
| M1-09 | 实现应用启动会话恢复 | 启动自动 refresh | 刷新页面后能恢复登录态 |
| M1-10 | 实现 `RequireAuth` | 登录守卫 | 未登录不可进入受保护页面 |
| M1-11 | 实现 `RequireAdmin` | 管理员守卫 | 非 admin 不可进入后台管理页 |
| M1-12 | 定义 `http.ts` / `auth.ts` / `user.ts` 基础类型 | 通用类型文件 | 认证和用户请求不再使用 `any` |

### 9.5 M2 认证页

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M2-01 | 实现 `services/auth.ts` | 认证 service | 登录、刷新、登出、密码重置接口可调用 |
| M2-02 | 实现密码登录表单 | 登录页密码 Tab | 可调用 `/auth/login` 并进入后台 |
| M2-03 | 实现邮箱验证码发送 | 邮箱验证码发送流程 | 可调用 `/auth/send-email-code` |
| M2-04 | 实现邮箱验证码登录表单 | 邮箱验证码登录 Tab | 可调用 `/auth/login/email-code` |
| M2-05 | 实现短信验证码发送 | 短信验证码发送流程 | 可调用 `/auth/send-code` |
| M2-06 | 实现短信验证码登录表单 | 短信验证码登录 Tab | 可调用 `/auth/login/code` |
| M2-07 | 实现忘记密码页 | `ForgotPasswordPage` | 可调用 `/auth/forgot-password` |
| M2-08 | 实现重置密码页 | `ResetPasswordPage` | 可校验并提交重置密码 |
| M2-09 | 实现登出动作 | 退出登录入口 | 退出后会话被清空并跳转登录页 |
| M2-10 | 实现会话启动加载态 | 启动屏 / 骨架态 | 启动恢复期间不闪跳错误页面 |

### 9.6 M3 后台框架

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M3-01 | 实现后台菜单配置 | 路由与菜单映射 | 菜单只出现当前方案允许的页面 |
| M3-02 | 实现后台头部区域 | 顶栏组件 | 能展示当前用户和退出入口 |
| M3-03 | 实现侧边栏导航 | 菜单导航 | 页面间可跳转 |
| M3-04 | 实现面包屑和页面容器 | 页面框架组件 | 后台页面布局一致 |
| M3-05 | 实现页面级加载 / 空态 / 错误态组件 | 反馈组件 | 所有列表页使用统一反馈模式 |

### 9.7 M4 Dashboard

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M4-01 | 定义 `stats.ts` 类型 | 统计类型文件 | 用户统计和登录统计字段齐全 |
| M4-02 | 实现 `services/stats.ts` | 统计 service | 可请求 dashboard / users stats |
| M4-03 | 实现 Dashboard 统计卡片 | Dashboard 页面基础版 | 展示真实统计卡片 |
| M4-04 | 实现 Dashboard 简表区块 | 简单文本 / 数字区块 | 不引入假图表但信息完整 |

### 9.8 M5 Users

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M5-01 | 完成 `user.ts` 领域类型 | 用户列表 / 详情类型 | 用户页所有字段有类型约束 |
| M5-02 | 实现 `services/users.ts` | 用户 service | 列表、详情、编辑、状态、角色分配可调用 |
| M5-03 | 实现用户筛选表单 | UsersFilter | 支持关键字、状态、角色、时间、排序 |
| M5-04 | 实现用户列表表格 | UsersTable | 分页和列表展示正常 |
| M5-05 | 实现用户详情抽屉 | UserDetailDrawer | 可查看用户详情 |
| M5-06 | 实现编辑用户抽屉 | UserEditDrawer | 可提交 `/users/:id` 更新 |
| M5-07 | 实现用户状态切换动作 | 状态更新交互 | 可提交 `/users/:id/status` |
| M5-08 | 实现用户删除动作 | 删除确认流 | 可提交 `/users/:id` 删除 |
| M5-09 | 实现用户角色分配弹窗 | AssignRolesModal | 可提交 `/users/:id/roles` |

### 9.9 M6 Roles

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M6-01 | 定义 `role.ts` 类型 | 角色类型文件 | 列表、详情、权限关联字段齐全 |
| M6-02 | 实现 `services/roles.ts` | 角色 service | 增删改查、启停、权限分配可调用 |
| M6-03 | 实现角色列表页 | RolesPage | 列表、分页、状态展示正常 |
| M6-04 | 实现创建 / 编辑角色弹窗 | RoleFormModal | 可提交创建和更新 |
| M6-05 | 实现角色状态切换 | 状态操作 | 可提交 `/roles/:id/status` |
| M6-06 | 实现角色删除动作 | 删除确认流 | 可删除角色 |
| M6-07 | 实现角色权限分配弹窗 | RolePermissionsModal | 可读取并提交角色权限 |

### 9.10 M7 Permissions

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M7-01 | 定义 `permission.ts` 类型 | 权限类型文件 | 权限树和列表字段齐全 |
| M7-02 | 实现 `services/permissions.ts` | 权限 service | 列表、树、增删改查、启停可调用 |
| M7-03 | 实现权限列表页 | PermissionsPage | 列表和树切换正常 |
| M7-04 | 实现创建 / 编辑权限弹窗 | PermissionFormModal | 可提交创建和更新 |
| M7-05 | 实现权限状态切换 | 状态操作 | 可提交 `/permissions/:id/status` |
| M7-06 | 实现权限删除动作 | 删除确认流 | 可删除权限 |

### 9.11 M8 Logs

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M8-01 | 定义 `log.ts` 类型 | 日志类型文件 | 登录日志和操作日志字段齐全 |
| M8-02 | 实现 `services/logs.ts` | 日志 service | 登录日志、操作日志接口可调用 |
| M8-03 | 实现登录日志页 | LoginLogsPage | 列表、分页、筛选正常 |
| M8-04 | 实现操作日志页 | OperationLogsPage | 列表、分页、筛选正常 |

### 9.12 M9 Webhooks

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M9-01 | 定义 `webhook.ts` 类型 | Webhook 类型文件 | 列表和投递记录字段齐全 |
| M9-02 | 实现 `services/webhooks.ts` | Webhook service | 列表、增删改、投递记录可调用 |
| M9-03 | 实现 Webhook 列表页 | WebhooksPage | 列表可展示 |
| M9-04 | 实现创建 / 编辑 Webhook 弹窗 | WebhookFormModal | 可提交创建和更新 |
| M9-05 | 实现删除 Webhook 动作 | 删除确认流 | 可删除 Webhook |
| M9-06 | 实现投递记录抽屉 | DeliveriesDrawer | 可查看 `/webhooks/:id/deliveries` |

### 9.13 M10 Import / Export

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M10-01 | 实现 `services/import-export.ts` | 导入导出 service | 下载模板、上传导入、导出下载可调用 |
| M10-02 | 实现导出表单 | ExportPanel | 可设置格式和筛选条件 |
| M10-03 | 实现导出下载流 | Blob 下载 | CSV / XLSX 文件可正常下载 |
| M10-04 | 实现导入上传表单 | ImportPanel | 可上传 `.csv` / `.xlsx` |
| M10-05 | 实现模板下载动作 | TemplateDownload | 可下载导入模板 |

### 9.14 M11 Profile / Security

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M11-01 | 实现 `services/profile.ts` | 个人中心 service | 当前用户资料与安全接口可调用 |
| M11-02 | 实现 `services/devices.ts` | 设备 service | 本人设备接口可调用 |
| M11-03 | 实现个人资料页 | ProfilePage | 可查看并编辑个人资料 |
| M11-04 | 实现修改密码表单 | ChangePasswordForm | 可提交本人密码修改 |
| M11-05 | 实现 TOTP 状态与 setup 流程 | TOTPSetupPanel | 可拉取状态并展示 setup 信息 |
| M11-06 | 实现 TOTP 启用 / 关闭 / 验证 | TOTPActionPanel | 可完整跑通 2FA 管理流 |
| M11-07 | 实现头像上传 | AvatarUploadPanel | 可上传并刷新当前头像 |
| M11-08 | 实现本人设备列表 | MyDevicesPanel | 可展示和操作 `/devices` |
| M11-09 | 实现本人登录日志 | MyLoginLogsPanel | 可展示 `/logs/login/me` |
| M11-10 | 实现本人操作日志 | MyOperationLogsPanel | 可展示 `/logs/operation/me` |

### 9.15 M12 收尾与验证

| ID | 任务 | 产出 | 完成定义 |
|----|------|------|----------|
| M12-01 | 统一菜单权限与路由准入复核 | 菜单守卫检查 | 管理页不会暴露给非 admin |
| M12-02 | 统一分页交互复核 | 分页行为检查 | 所有分页页都按真实结构工作 |
| M12-03 | 统一错误提示与空态复核 | 反馈体验检查 | 页面失败时不出现白屏 |
| M12-04 | 添加关键基础测试 | `vitest` 用例 | 至少覆盖请求客户端和会话恢复逻辑 |
| M12-05 | 执行前端构建验证 | `npm run build` | 生产构建通过 |
| M12-06 | 执行联调烟雾验证 | 手工联调清单 | 每个已纳入页面至少完成一次真实接口验证 |

### 9.16 当前不拆解的延期项

以下项在后端前置条件未补齐前，不进入任务拆解：

- 社交登录 / 绑定
- 系统设置
- 全局设备管理
- 用户创建
- 批量操作
- 细粒度权限码前端控制
- 富图表仪表盘

---

## 10. 后端补齐前置依赖

以下接口或协议补齐后，前端范围才能继续扩张：

1. `POST /api/v1/users`，用于管理员单个创建用户
2. 用户批量操作接口
3. 当前用户角色码 / 权限码快照接口
4. SPA 友好的 OAuth callback redirect 协议
5. 社交账号绑定握手协议，而不是直接要求前端提交 `open_id`
6. 系统设置读写接口
7. 全局设备列表与检索接口
8. 时间序列统计、最近活动、地域分布等仪表盘接口

---

## 11. 验收基线

后续前端实现必须满足以下验收条件：

1. 每个页面都能映射到当前真实后端 API，不出现“文档有、后端无”的按钮。
2. 所有分页页都按真实结构处理 `items / total / page / page_size`。
3. 刷新会话必须轮换 `refresh_token`，不能只更新 `access_token`。
4. Admin 页面必须以当前用户角色中的 `admin` 作为准入条件。
5. 个人头像上传只能从个人中心进入，不能放进用户管理页。
6. Dashboard 只展示当前统计接口真实提供的数据。
7. 不新增任何与本文冲突的前端栈或页面规划文档。
