# 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 { code: number; message: string; data: T; } export interface PaginatedData { 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`。 ### 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 ` - 统一解包 `ApiResponse` - 统一处理 `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` 解包 | 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. 不新增任何与本文冲突的前端栈或页面规划文档。