1
0
Files
nex/openspec/specs/frontend-config-ui/spec.md
lanyuanxiaoyao 870004af23 feat: 现代化 UI 布局,实现侧边栏导航和统计仪表盘
- 重构 AppLayout 为可折叠侧边栏导航布局
- 实现统计仪表盘:统计摘要卡片 + 请求趋势图表
- Provider 页面使用 Card 包裹优化视觉层次
- 主题切换按钮移至侧边栏底部,支持折叠态
- Header 适配暗色主题,添加分隔线优化视觉过渡
- 添加全局样式重置(SCSS)
- 完善组件测试和 E2E 测试覆盖
- 同步 OpenSpec 规范到主 specs
2026-04-16 19:24:02 +08:00

326 lines
12 KiB
Markdown
Raw 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.
# 前端配置界面
## Purpose
TBD - 提供供应商、模型配置和用量统计的前端管理界面
## Requirements
### Requirement: 提供供应商管理页面
前端 SHALL 使用 Ant Design 组件提供供应商管理页面。
#### Scenario: 显示供应商列表
- **WHEN** 加载供应商管理页面
- **THEN** 前端 SHALL 使用 Ant Design Table 显示所有已配置供应商
- **THEN** 每个供应商 SHALL 显示 name、base_url 和 enabled 状态(使用 Tag 组件)
- **THEN** API Key SHALL 被脱敏显示(掩码处理)
- **THEN** 表格 SHALL 支持展开行以显示关联模型
#### Scenario: 添加新供应商
- **WHEN** 用户点击"添加供应商"按钮
- **THEN** 前端 SHALL 使用 Ant Design Modal + Form 显示输入表单
- **THEN** 表单 SHALL 包含 id、name、api_key、base_url 字段,带校验规则
- **WHEN** 用户提交包含有效数据的表单
- **THEN** 前端 SHALL 通过 useMutation 调用创建 API
- **THEN** 成功后 SHALL 关闭 Modal 并刷新供应商列表
- **THEN** 失败 SHALL 使用 message.error() 提示
#### Scenario: 编辑现有供应商
- **WHEN** 用户点击供应商的"编辑"按钮
- **THEN** 前端 SHALL 使用 Ant Design Modal + Form 显示预填充数据的表单
- **WHEN** 用户提交包含更新数据的表单
- **THEN** 前端 SHALL 通过 useMutation 调用更新 API
- **THEN** 成功后 SHALL 关闭 Modal 并刷新供应商列表
#### Scenario: 删除供应商
- **WHEN** 用户点击供应商的"删除"按钮
- **THEN** 前端 SHALL 使用 Ant Design Popconfirm 弹出确认
- **WHEN** 用户确认删除
- **THEN** 前端 SHALL 通过 useMutation 调用删除 API
- **THEN** 成功后 SHALL 刷新供应商列表
### Requirement: 提供模型管理界面
前端 SHALL 在供应商页面展开行中提供模型管理。
#### Scenario: 显示供应商的模型
- **WHEN** 展开供应商行
- **THEN** 前端 SHALL 显示该供应商的模型列表
- **THEN** 每个模型 SHALL 显示 model_name 和 enabled 状态
#### Scenario: 为供应商添加模型
- **WHEN** 用户在展开行中点击"添加模型"
- **THEN** 前端 SHALL 显示 Ant Design Modal + Form
- **THEN** provider_id SHALL 自动关联当前供应商
- **WHEN** 用户提交表单
- **THEN** 前端 SHALL 通过 useMutation 调用创建 API
- **THEN** 成功后 SHALL 刷新模型列表
#### Scenario: 编辑模型
- **WHEN** 用户点击模型的"编辑"
- **THEN** 前端 SHALL 显示编辑表单
- **WHEN** 用户提交表单
- **THEN** 前端 SHALL 通过 useMutation 调用更新 API
- **THEN** 成功后 SHALL 刷新模型列表
#### Scenario: 删除模型
- **WHEN** 用户点击模型的"删除"
- **THEN** 前端 SHALL 使用 Popconfirm 弹出确认
- **WHEN** 用户确认删除
- **THEN** 前端 SHALL 通过 useMutation 调用删除 API
- **THEN** 成功后 SHALL 刷新模型列表
### Requirement: 提供统计查看页面
前端 SHALL 使用 Ant Design 组件提供统计仪表盘页面。
#### Scenario: 显示统计概览
- **WHEN** 加载统计页面
- **THEN** 前端 SHALL 在顶部显示统计摘要卡片(总请求量、活跃模型数、活跃供应商数、今日请求量)
- **THEN** 统计摘要数据 SHALL 从 stats API 返回数据中前端聚合
- **THEN** 前端 SHALL 显示请求趋势折线图
- **THEN** 前端 SHALL 使用 Ant Design Table 显示统计数据
- **THEN** 统计数据 SHALL 按供应商和模型显示请求计数
#### Scenario: 按供应商过滤统计
- **WHEN** 用户从 Ant Design Select 选择供应商
- **THEN** 前端 SHALL 自动查询并过滤统计
- **THEN** 统计摘要卡片和趋势图表 SHALL 同步更新
#### Scenario: 按日期范围过滤统计
- **WHEN** 用户使用 Ant Design DatePicker.RangePicker 选择日期范围
- **THEN** 前端 SHALL 自动查询并过滤统计
- **THEN** 统计摘要卡片和趋势图表 SHALL 同步更新
### Requirement: 优雅处理 API 错误
前端 SHALL 处理 API 错误并显示用户友好的消息。
#### Scenario: API 请求失败
- **WHEN** API 请求失败网络错误、4xx、5xx
- **THEN** 前端 SHALL 使用 Ant Design 的 message.error() 显示全局错误提示
- **THEN** 错误消息 SHALL 具有描述性
#### Scenario: 验证错误
- **WHEN** 用户提交包含无效数据的表单
- **THEN** 前端 SHALL 在相关字段旁显示验证错误
- **THEN** 前端 SHALL 阻止表单提交
### Requirement: 提供响应式布局
前端 SHALL 使用 Ant Design Layout 提供侧边栏导航布局。
#### Scenario: 桌面布局
- **WHEN** 在桌面屏幕上查看前端(宽度 >= 992px
- **THEN** 布局 SHALL 使用 Ant Design `Layout.Sider` + `Menu`inline 模式)
- **THEN** 侧边栏 SHALL 显示导航菜单,包含图标和文字标签
- **THEN** 侧边栏 SHALL 可通过折叠按钮折叠为仅图标模式
#### Scenario: 响应式折叠
- **WHEN** 屏幕宽度小于 992px
- **THEN** 侧边栏 SHALL 自动折叠为图标模式
- **THEN** 折叠后侧边栏宽度 SHALL 为 80px
- **THEN** 菜单项 SHALL 仅显示图标
#### Scenario: 页面内容区域
- **WHEN** 显示页面内容
- **THEN** 内容区域 SHALL 在 `Layout.Content` 中渲染
- **THEN** 页面之间 SHALL 通过 React Router Outlet 渲染
#### Scenario: Header 区域
- **WHEN** 渲染页面 Header
- **THEN** Header SHALL 仅显示当前页面标题
- **THEN** Header SHALL 不包含导航菜单
### Requirement: 使用无组件库的最小 UI
前端 SHALL 使用 Ant Design 6 作为 UI 组件库。
#### Scenario: Ant Design 组件使用
- **WHEN** 实现前端
- **THEN** 它 SHALL 使用 Ant Design 6 组件Table、Form、Modal、Menu、Tag、Popconfirm、DatePicker、Button、Select 等)
- **THEN** 它 SHALL 使用 @ant-design/icons 提供图标
- **THEN** 图标 SHALL 优先使用图标库中已有的图标
#### Scenario: Ant Design 主题支持
- **WHEN** 配置 Ant Design 主题
- **THEN** 前端 SHALL 支持亮色和暗色模式切换
- **THEN** 前端 SHALL 使用 Ant Design v6 的 theme.darkAlgorithm 和 theme.defaultAlgorithm
- **THEN** 前端 SHALL 通过 ConfigProvider 动态切换主题
### Requirement: 样式体系
前端样式 SHALL 优先使用 Ant Design 样式体系SCSS 作为补充工具。
#### Scenario: Ant Design 样式优先
- **WHEN** 实现组件样式
- **THEN** 前端 SHALL 优先使用 Ant Design 组件的 style prop
- **THEN** 前端 SHALL 优先使用 Ant Design v6 的语义化样式classNames 和 styles props
- **THEN** 前端 SHALL 使用 Ant Design Layout 组件处理布局
#### Scenario: SCSS 补充使用
- **WHEN** Ant Design 样式体系无法满足需求
- **THEN** 前端 MAY 使用 SCSS 作为补充
- **THEN** SCSS 文件 SHALL 使用 *.module.scssSCSS Modules
- **THEN** 前端 SHALL NOT 使用纯 CSS 文件(*.css
#### Scenario: 移除冗余 SCSS
- **WHEN** SCSS 文件仅实现 Ant Design 已有的功能
- **THEN** 前端 SHALL 移除该 SCSS 文件
- **THEN** 前端 SHALL 使用 Ant Design 内置功能替代
### Requirement: 提供侧边栏导航
前端 SHALL 使用 Ant Design `Layout.Sider` 提供侧边栏导航。
#### Scenario: 侧边栏内容
- **WHEN** 渲染侧边栏
- **THEN** 侧边栏顶部 SHALL 显示应用名称/Logo
- **THEN** 侧边栏 SHALL 包含 inline 模式的导航菜单
- **THEN** 导航菜单项 SHALL 包含供应商管理CloudServerOutlined 图标、用量统计BarChartOutlined 图标)
- **THEN** 侧边栏底部 SHALL 放置主题切换按钮
#### Scenario: 侧边栏折叠
- **WHEN** 用户点击侧边栏折叠按钮
- **THEN** 侧边栏 SHALL 切换折叠/展开状态
- **THEN** 折叠状态下菜单项 SHALL 仅显示图标
- **THEN** 折叠状态下应用名称/Logo SHALL 隐藏或缩小
- **THEN** 折叠状态下主题切换按钮 SHALL 保持可见可点击
#### Scenario: 导航菜单交互
- **WHEN** 用户点击导航中的"供应商管理"
- **THEN** 前端 SHALL 导航到 `/providers` 并高亮当前菜单项
- **WHEN** 用户点击导航中的"用量统计"
- **THEN** 前端 SHALL 导航到 `/stats` 并高亮当前菜单项
### Requirement: 提供导航
前端 SHALL 使用 React Router v7 提供导航。
#### Scenario: 路由配置
- **WHEN** 应用启动
- **THEN** 前端 SHALL 使用 React Router v7 Library 模式BrowserRouter
- **THEN** `/providers` 路径 SHALL 显示供应商管理页面
- **THEN** `/stats` 路径 SHALL 显示用量统计页面
- **THEN** `/` 路径 SHALL 重定向到 `/providers`
- **THEN** 不存在的路径 SHALL 显示 404 页面
#### Scenario: 导航菜单
- **WHEN** 用户点击导航中的"供应商管理"
- **THEN** 前端 SHALL 导航到 `/providers` 并高亮当前菜单项
- **WHEN** 用户点击导航中的"用量统计"
- **THEN** 前端 SHALL 导航到 `/stats` 并高亮当前菜单项
#### Scenario: URL 同步
- **WHEN** 用户在供应商页面刷新浏览器
- **THEN** 前端 SHALL 保持在供应商页面URL 持久化)
- **WHEN** 用户使用浏览器后退按钮
- **THEN** 前端 SHALL 正确导航到上一个页面
### Requirement: 使用 React 和 TypeScript
前端 SHALL 使用 React 和 TypeScript 实现,遵循 strict 模式。
#### Scenario: TypeScript strict 模式
- **WHEN** 编写前端代码
- **THEN** TypeScript 配置 SHALL 开启 strict: true
- **THEN** TypeScript 配置 SHALL 开启 noUncheckedIndexedAccess
- **THEN** 所有代码 SHALL NOT 使用 any 类型
- **THEN** tsconfig SHALL 合并为单文件(不使用 project references
#### Scenario: React 函数组件
- **WHEN** 实现 UI
- **THEN** 它 SHALL 使用 React 函数组件
- **THEN** 它 SHALL 使用自定义 Hooks 封装业务逻辑
### Requirement: 使用 Vite 构建
前端 SHALL 使用 Vite 作为构建工具。
#### Scenario: 开发服务器
- **WHEN** 在开发模式下启动前端
- **THEN** Vite SHALL 使用热模块替换服务应用
#### Scenario: 生产构建
- **WHEN** 为生产构建前端
- **THEN** Vite SHALL 生成优化的静态文件
### Requirement: 与后端 API 通信
前端 SHALL 使用 TanStack Query v5 和统一 API 客户端与后端通信。
#### Scenario: API 基础 URL 配置
- **WHEN** 前端发起 API 请求
- **THEN** 开发环境 SHALL 通过 Vite proxy 转发 /api 请求到后端
- **THEN** 生产环境 SHALL 使用环境变量 VITE_API_BASE 配置基础 URL
- **THEN** 前端 SHALL NOT 硬编码 API 基础 URL
#### Scenario: 统一 API 客户端
- **WHEN** 进行 API 调用
- **THEN** 所有调用 SHALL 通过 api/client.ts 的 request<T>() 方法
- **THEN** 错误处理 SHALL 统一抛出 ApiError包含 status 和 message
- **THEN** 开发环境 SHALL 使用 Vite proxy 转发 API 请求
#### Scenario: 字段名转换
- **WHEN** 接收后端 API 响应
- **THEN** API 层 SHALL 将 snake_case 字段转换为 camelCase
- **WHEN** 发送请求到后端 API
- **THEN** API 层 SHALL 将 camelCase 字段转换为 snake_case
- **THEN** hooks 和组件 SHALL 仅使用 camelCase 字段
#### Scenario: TanStack Query 数据管理
- **WHEN** 页面加载数据
- **THEN** 前端 SHALL 使用 TanStack Query 的 useQuery hook
- **THEN** 前端 SHALL 自动缓存请求结果
- **THEN** 前端 SHALL 自动处理加载和错误状态
#### Scenario: TanStack Query 写操作
- **WHEN** 用户执行创建、更新或删除操作
- **THEN** 前端 SHALL 使用 TanStack Query 的 useMutation hook
- **THEN** 操作成功后 SHALL 自动失效相关查询缓存
- **THEN** 操作失败 SHALL 使用 Ant Design message.error() 显示错误提示
#### Scenario: 错误提示
- **WHEN** API 请求失败网络错误、4xx、5xx
- **THEN** 前端 SHALL 使用 Ant Design 的 message.error() 显示全局错误提示
- **THEN** 错误消息 SHALL 具有描述性