Files
new-api-key-usage/docs/design/交互设计.md
T
skycurtain 0e9db439ba docs: 收尾(阶段 8)——部署文档 + 端到端走查 + 文档状态
- docs/delivery/部署文档.md:前置条件 / 环境变量 / 一条命令链 / systemd 与 Nginx 示例 / 首次运行检查 / 运行约束(后端单实例 · 3 天窗口 · 主密钥 · 401 摘除)/ 升级 / 排查
- 从零端到端走查:清库 → db:push → 启动(空转 + QPU 基线)→ 管理面登记 → 引擎立即补一轮(984 条)→ 查看面查询(未折算「—」)→ 补录购买记录 → 金额出现 → 换算参数时间线;无 pageerror
- 8 篇设计文档状态 → 已实现(2026-09-15)
- README:状态行 / 文档地图(含 delivery)/ 下一步(部署 + 待补验与裁决清单)
- AGENTS.md:项目速览更新
2026-09-15 01:10:55 +08:00

141 lines
11 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.
# 交互设计
> 本工具的界面与页面设计——**方向层**(页面集合 + 职责)+ **细节层**(跨页共性 + 逐页细节)。历史旧稿已删除(2026-09-14,内容已全部并入本文与 README / API 契约)。
| 项目 | 内容 |
|------|------|
| 状态 | 已实现(2026-09-15) |
| 关联 | `README.md`(决策与事实)· `docs/design/API契约.md`(数据形状依据) |
| 范围 | 方向层(页面集合 + 职责)+ 细节层(跨页共性 + 逐页细节) |
---
## 0. 大前提(2026-09-13)
### 0.1 路由分区与可见性
- **`/manage` 前缀 = 管理面**:管理通道的页面路由
- **无前缀 = 查看面**:给使用者直接查看的页面路由
- **管理面入口隐藏**:查看面不放任何指向管理面的入口——使用者无论拥有多少查看 / 统计功能,都无法从界面中进入管理面;直连管理面 URL 则由口令门帘拦截(两层:入口找不到 + 撞门要口令)
### 0.2 查看面职责:纯查询(对 key 只读)
- 查看面输入 key **仅用于查询**——凭手里已登记的 key 看用量;不再触发登记 / 轮询;输入未登记的 key → 提示「未收录」(措辞归细节层)
- key 的登记与启停全部在管理面完成——**写操作只发生在管理面**(2026-09-13 定,取代原「输入即注册」流程)
## 1. 页面集合(方向层 · 已收口)
### 1.1 查看面(无前缀)
| 页面 | 路由 | 职责 | 阶段 |
|------|------|------|------|
| 用量日志 | `/usage-log` | 指定时间范围查看模型请求日志(数据表格呈现);表格上方以数据卡片显示该时段累计用量、预估消费等汇总 | 一期 |
| 数据看板 | `/dashboard` | 固定时间窗口的统计视图(如模型消耗分布) | 二期候选(一期可不实现) |
- key 维度:输入式(一次可输入多个——延续核心需求「合并查询」);输入未登记的 key → 提示「未收录」
- 根路径 `/` 重定向到 `/usage-log`
- 卡片口径:累计用量与预估消费直接呈现折算数值(延续全项目「不标注币种」的基调)
### 1.2 管理面(`/manage` 前缀)
| 页面 | 路由 | 职责 |
|------|------|------|
| key 管理 | `/manage/api-keys` | 登记 / 清单 / 启停 |
| 购买记录 | `/manage/purchases` | 登记 / 编辑删除 / 核对提示 |
| 换算参数时间线 | `/manage/qpu` | 只读查看 |
- 根路径 `/manage` 重定向到 `/manage/api-keys`
- 能力边界见 API 契约 §2.2–2.4 与本文件 §3
## 2. 跨页共性(细节层,2026-09-13 起逐条推)
| # | 项 | 定法 | 状态 |
|---|----|------|------|
| 1 | 使用场景与适配 | **桌面优先**(使用者 2–3 人、以看报表为主);不做移动适配 | 已定 |
| 2 | 视觉基调 | **浅色单主题**(不做暗色切换);组件库默认主题 + 少量调色 | 已定 |
| 3 | UI 组件库 | **naive-ui**(2026-09-13 定)——成熟度 / 文档 / 主题变量系统更稳,表格与日期快捷选项能力已验证满足本项目;antdv-next 未采纳(社区新项目:上游为 React 版 antd 6、非 antdv 继任,生态与文档面未成熟) | 已定 |
| 4 | 时间显示格式 | **统一到秒**:`2026-09-11 19:37:12`(当天语境可简写 `HH:mm:ss`) | 已定 |
| 5 | 金额与数字格式 | **分层**:折算金额汇总 2 位小数 / 明细行 4 位小数;token 数与 quota 原值千分位(`53,939,847`);不标币种不变 | 已定 |
| 6 | 通用状态模式(加载 / 空 / 未收录 / 错误) | 见下方细则(2026-09-13 定) | 已定 |
| 7 | 导航 | 查看面:一期单页**不做导航**;管理面:**侧边栏菜单 + 内容区**(传统后台布局);「查看面 →」入口的位置形态随逐页细节 | 已定 |
| 8 | 表格通用交互 | **分页器**:页码器 + 每页条数选择(20/50/100/200,默认 50);**行详情**:展开行(`n-data-table` 内置),展示完整字段 | 已定 |
| 9 | 时间范围控件 | `n-date-picker type="datetimerange"` + 快捷项(今天 / 近 7 天 / 近 30 天);**默认 = 最近 7 天** | 已定 |
| 10 | key 输入控件与前端持久化 | 标签式(`n-dynamic-tags`),粘贴多行 / 逗号自动拆分;localStorage 记住上次输入、打开自动带出;**点「查询」才发请求** | 已定 |
**状态模式细则(#6,2026-09-13 定)**
| 状态 | 呈现 |
|------|------|
| 加载中 | 表格 `n-data-table` 内置 loading;卡片区 `n-skeleton` |
| 初始(未输入 key) | 页面中央引导「输入你的 key 开始查询」——不渲染卡片与表格 |
| 未收录 | 卡片区下方 warning 提示条:「以下 key 未收录:sk-…a1b2(请联系管理员登记)」;已收录部分照常展示 |
| 时段无记录 | 表格空态「该时段没有记录」;卡片数值显示 0 |
| 新 key 未首同步 | `data_until` 为 `null` → 卡片「数据截止」位显示「数据同步中…」 |
| 查询失败(网络 / 5xx) | 页面内 error 提示条「查询失败:<message>」+「重试」按钮 |
| 操作反馈(管理面) | `n-message` toast(登记 / 启停 / 保存的成功与失败) |
## 3. 逐页细节(细节层)
### 3.1 `/usage-log`(推导中)
**布局(2026-09-13 定)**
```
┌────────────────────────────────────────────────────────────┐
│ 筛选区:[key 标签输入框] [时间范围 datetimerange] [查询] │
├────────────────────────────────────────────────────────────┤
│ 卡片区:┌调用次数┐ ┌Token 用量┐ ┌预估消费┐ 数据截止 21:10:00 │
│ │ 1,873 │ │1,469,134│ │ 23.45 │ (小字 + 悬停明细)│
│ │消费·错误│ │ 缓存 89k │ │额度 53.9M│ │
├────────────────────────────────────────────────────────────┤
│ (如有)未收录提示条 │
├────────────────────────────────────────────────────────────┤
│ 明细表格(展开行详情) │
│ 分页器(页码 + 每页条数) │
└────────────────────────────────────────────────────────────┘
```
- 「数据截止」不进卡片(它是注记不是指标):卡片区右端小字 + **悬停弹出 per-key 明细**(各 key 同步进度 / 未收录 / 已停用)——`meta` 信息完整呈现且不挤占卡片位
- 卡片主 / 副行:调用次数(主总行数,副「消费 X · 错误 Y」)· Token 用量(主 prompt+completion,副缓存命中)· 预估消费(主金额,副「额度记账」quota 原值,供对账)
**明细列与呈现(2026-09-13 定)**
- **默认 8 列**:时间(到秒)· key(`token_name`)· 模型 · 类型(Tag)· 输入 tokens · 输出 tokens · 消费(`amount_rmb`,4 位小数)· 耗时
- **展开行**(点行展开):完整字段——缓存 tokens · 流式 · `request_id` · `content`(错误详情)· 请求路径 · quota 原值 · **订阅归属完整组**
- **错误行**:类型列 `n-tag`(消费 = 中性 / 错误 = 红),**无整行底色**;错误详情在展开行
- **订阅归属**:默认列展示订阅名(`other.subscription_plan_title`,如「Max」);完整归属(额度实况 / 期间范围)在展开行——`purchases` §6 的「明细展示订阅归属」完整落地
### 3.2 `/manage/api-keys`(2026-09-13 推完)
- **清单 8 列**:key 名(`token_name`)· 状态徽章 · 累计已用 · 累计剩余(无限额度显示「不限」)· 到期 · 登记时间 · 数据截止 · 操作;其余字段(总额度 / 模型限制 / `consecutive_401s` / 额度新鲜度)归展开行
- **状态徽章三面孔**:运行中(绿)· 已停用(灰,`manual`)· 已失效(红,`auto_401`,tooltip 注明「连续 401 自动摘除」);数据未首同步的状态体现在「数据截止」列(「同步中」)
- **登记**:页头「+ 登记 key」→ `n-modal` 弹窗(粘贴 sk-key)→ 提交;三类失败各有文案——上游拒绝该 key / 该 key 已登记 / 上游暂不可用(稍后重试);成功后 toast + 清单插入新行(`log_synced_at: null` → 「同步中」)
- **启停**:行内按钮;**停用弹确认**(`n-popconfirm`,防误点);**启用直接执行**(安全动作,成功后 toast + 行内状态更新;失败按错误码提示)
- 「查看面 →」入口放侧边栏底部(低调,见 §2 #7)
### 3.3 其余页(待推)
| 页 | 重点 | 状态 |
|----|------|------|
| `/manage/purchases` | — | **2026-09-13 推完** |
| `/manage/qpu` | — | **2026-09-13 推完** |
| 口令门帘 | — | **2026-09-13 推完** |
**`/manage/purchases` 细节(2026-09-13 定)**
- **清单 8 列**:时间 · 类型(订阅 / 充值)· 实付金额 · 订阅(`#N`)· 期间 · 额度(`quota_granted`)· 备注 · 操作(编辑 · 删除;删除弹确认)
- **表单用右侧抽屉**(`n-drawer`):`kind` 单选 · `paid_at` · `paid_amount`(必填)· `quota_granted`(必填)· `subscription_id` · 期间(`period_end` 未结束可空)· `note`;校验含期间顺序(start < end)
- **核对提示与补录**:存在「观测到但未登记」时清单上方提示条「观测到 N 个未登记的订阅」;下方折叠区列出全部观测项(`#N` · 名称 · 日志条数 · 时间范围 · 观测额度 · 已登记 / 未登记);点「补录」开表单,**预填 `subscription_id` 与 `quota_granted`(观测值)**,期间不预填(观测范围 ≠ 订阅期间)
**`/manage/qpu` 细节(2026-09-13 定)**
- 表格两列:生效时间 · QPU 值(`id` 不显示);最新一行标「当前生效」
- 页首说明文案:「quota → 额度美元的换算比率历史;引擎自动采样、只记变化点;当前生效值 = 最近一行」
- 只有基线一行也属正常态(无特殊空态)
**口令门帘(2026-09-13 定)**
- 形态:访问 `/manage/*` 未验证时 → 全屏居中卡片(口令输入 + 「进入」);进入后跳转原目标页
- 校验:直接用任一管理面请求校验(401 即口令错)——**不需新端点**;口令存 **sessionStorage**(关标签即忘、刷新仍在),请求拦截器自动附加 `X-Manage-Token`
- 错误:401 → 输入框下方「口令不正确」;不做「记住我」