- 迁移静态 message 到 App.useApp() 模式,使 message 感知 ConfigProvider 上下文 - Button type/danger 迁移为 variant/color 新 API - Space direction 迁移为 vertical 布尔属性 - Select.Option 子组件迁移为 options 属性 - AppLayout 硬编码颜色替换为 antd design token - 优化 useThemeConfig:default/dark 改为静态导出,减少不必要的 hook 调用 - 同步更新 openspec 主规范文档
14 KiB
14 KiB
前端配置界面
Purpose
TBD - 提供供应商、模型配置和用量统计的前端管理界面
Requirements
Requirement: 提供供应商管理页面
前端 SHALL 使用 Ant Design 组件提供供应商管理页面,所有组件 SHALL 遵循 antd v6 API 规范。
Scenario: 显示供应商列表
- WHEN 加载供应商管理页面
- THEN 前端 SHALL 使用 Ant Design Table 显示所有已配置供应商
- THEN 每个供应商 SHALL 显示 name、base_url 和 enabled 状态(使用 Tag 组件)
- THEN API Key SHALL 被脱敏显示(掩码处理)
- THEN 表格 SHALL 支持展开行以显示关联模型
- THEN 表格 SHALL 设置
scroll={{ x: 'max-content' }}防止窄屏溢出
Scenario: 表格列宽约束
- WHEN 渲染供应商表格
- THEN 名称列 SHALL 固定宽度 180px 并启用 ellipsis(超长文本显示省略号,hover 显示 Tooltip)
- THEN Base URL 列 SHALL 不设固定宽度(浮动填充剩余空间)并启用 ellipsis + Tooltip
- THEN API Key 列 SHALL 固定宽度 120px 并启用 ellipsis
- THEN 状态列 SHALL 固定宽度 80px
- THEN 操作列 SHALL 固定宽度 160px
Scenario: 表格空状态
- WHEN 供应商列表为空
- 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 使用
App.useApp()提供的message实例显示错误提示
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 在供应商页面展开行中提供模型管理,所有组件 SHALL 遵循 antd v6 API 规范。
Scenario: 显示供应商的模型
- WHEN 展开供应商行
- THEN 前端 SHALL 显示该供应商的模型列表
- THEN 每个模型 SHALL 显示 model_name 和 enabled 状态
- THEN 模型名称列 SHALL 启用 ellipsis(超长文本显示省略号,hover 显示 Tooltip)
Scenario: 模型表格空状态
- WHEN 模型列表为空
- THEN 表格 SHALL 显示自定义空状态文案 "暂无模型,点击上方按钮添加"
Scenario: 为供应商添加模型
- WHEN 用户在展开行中点击"添加模型"
- THEN 前端 SHALL 显示 Ant Design Modal + Form
- THEN provider_id SHALL 自动关联当前供应商
- THEN 供应商选择 SHALL 使用
options属性而非Select.Option子组件 - 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 按供应商和模型显示请求计数
- THEN 统计表格 SHALL 设置
scroll={{ x: 'max-content' }}防止窄屏溢出
Scenario: 统计表格列宽约束
- WHEN 渲染统计表格
- THEN 供应商列 SHALL 固定宽度 180px 并启用 ellipsis + Tooltip
- THEN 模型列 SHALL 固定宽度 250px 并启用 ellipsis + Tooltip
- THEN 日期列 SHALL 固定宽度 120px
- THEN 请求数列 SHALL 固定宽度 100px 并右对齐
Scenario: 统计表格空状态
- WHEN 统计数据为空
- 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 错误并显示用户友好的消息,message 组件 SHALL 通过 App.useApp() 获取。
Scenario: API 请求失败
- WHEN API 请求失败(网络错误、4xx、5xx)
- THEN 前端 SHALL 使用
App.useApp()提供的message实例显示全局错误提示 - 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 组件库,所有组件 SHALL 使用 antd v6 推荐的 API。
Scenario: Ant Design 组件使用
- WHEN 实现前端
- THEN Button 组件 SHALL 使用
variant和color属性替代已废弃的type(link/text/dashed)和danger属性 - THEN Button
type="link"SHALL 替换为variant="link" - THEN Button
danger属性 SHALL 替换为color="danger" - THEN Space 组件 SHALL 使用
vertical布尔属性替代已废弃的direction属性 - THEN Select 组件 SHALL 使用
options属性替代Select.Option子组件 - THEN 图标 SHALL 优先使用图标库中已有的图标
Scenario: App 组件包裹
- WHEN 应用初始化
- THEN
App.tsxSHALL 在ConfigProvider内部使用 Ant Design<App>组件包裹应用 - THEN hooks 中 SHALL 使用
App.useApp()获取message实例 - THEN
message实例 SHALL NOT 通过静态import { message } from 'antd'获取
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.scss(SCSS 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() 方法
- 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 使用
App.useApp()提供的message实例显示错误提示
Scenario: 错误提示
- WHEN API 请求失败(网络错误、4xx、5xx)
- THEN 前端 SHALL 使用
App.useApp()提供的message实例显示全局错误提示 - THEN 错误消息 SHALL 具有描述性