Files
user-system/docs/plans/ADMIN_FRONTEND_EXECUTION_PLAN.md

887 lines
29 KiB
Markdown
Raw Permalink 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.
# 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. 不新增任何与本文冲突的前端栈或页面规划文档。