- endpoints:AuthMode 扩展 'none'(完全公开),追加 /api/status 定义(考证实证文案 + 实测示例响应),置于列表首位 - endpoint-info-line:鉴权徽标三态化(公开/宽松/严格,map 驱动 + none 中性样式) - endpoint-panel:requiresKey——无鉴权接口隐藏 sk-key 输入行、按钮不因空 key 禁用、空态文案区分 - spec:接口数 5→6、顺序、鉴权分布(none+strict×3+lenient×2)断言更新 - docs:接口文档同步 479 行新版(/api/status 详解 + QuotaPerUnit 考证);交互设计 §1 清单加第 0 行;README 与 side-nav 计数更新 - 验证:test 64/64 + type-check + lint + build 四绿
12 KiB
bench 交互设计与施工方案
new-api-key-bench:NewAPI sk-key 接口的「文档 + 调试」二合一前端工具。
本文档是施工蓝图:所有交互与技术决策已定案,按此施工,不再讨论结构性问题。
1. 项目定位
替代 Postman 的专用调试台(bench):左侧选接口 → 右侧 tab 内看文档、填 key、发请求、看原始响应。对接 6 个接口,服务 karpool 拼车项目的接口调试与换算验证。
接口清单(数据源:docs/NewAPI-sk-key接口文档.md,按此顺序展示,不分组):
| 序 | 路径 | 鉴权 | 响应特征 |
|---|---|---|---|
| 0 | GET /api/status |
无(完全公开) | 站点配置与计费参数:quota_per_unit(换算比率权威来源)、quota_display_type、汇率等 |
| 1 | GET /v1/dashboard/billing/subscription |
TokenAuth(严格) | 小 JSON,美元额度(无限额时 1 亿占位) |
| 2 | GET /v1/dashboard/billing/usage |
TokenAuth(严格) | 小 JSON,累计美分 |
| 3 | GET /v1/models |
TokenAuth(严格) | 中等 JSON,模型数组 |
| 4 | GET /api/usage/token |
TokenAuthReadOnly(宽松) | 小 JSON,累计 quota |
| 5 | GET /api/log/token |
TokenAuthReadOnly(宽松) | 大响应:最近 1000 条日志,pretty-print 2~4MB |
0 完全公开(无需任何凭证,面板隐藏 sk-key 输入行);4、5 宽松鉴权(key 过期/耗尽仍可查);1~3 严格鉴权(key 无效直接被拒)。tab 内标出该差异,避免误判「接口坏了」。 4 与 2 构成换算交叉验证对(total_used ÷ 500000 ≈ total_usage ÷ 100),Tabs 并行对账是核心工作流。0 是换算比率的权威来源(quota_per_unit 可被管理员修改,500000 只是默认值)。
2. 布局
┌────────────────────────────────────────────────────────────────────────────┐
│ new-api-key-bench (暗色默认) │
├──────────────────────────┬─────────────────────────────────────────────────┤
│ 接口列表 │ ┌──────────────┬──────────────┬───────────┐ │
│ │ │/v1/dashboar… │/api/log/token│/api/usage…│ × │
│ /v1/dashboard/billing/ │ ├──────────────┴──────────────┴───────────┴──┤ │
│ subscription │ │ GET /v1/dashboard/billing/subscription │ │
│ /v1/dashboard/billing/ │ ├─────────────────────────────────────────────┤ │
│ usage │ │ sk-key ┌─────────────────────────┐ │ │
│ /v1/models │ │ │ sk-xxxxxxxxxxxx 👁 │ [发送请求]│ │
│ /api/usage/token │ │ └─────────────────────────┘ │ │
│ /api/log/token │ ├─────────────────────────────────────────────┤ │
│ │ │ 200 OK · 156ms · 234 KB [复制] [⤓] │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ │ 1 { │ │ │
│ │ │ │ 2 "object": "billing_subscription", │ │ │
│ │ │ │ 3 "hard_limit_usd": 100000000, │ │ │
│ │ │ │ (虚拟滚动 JSON · DOM 恒 ~40 行) │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │
└──────────────────────────┴─────────────────────────────────────────────────┘
~300px sider(纯路径列表, 自适应(tab 内自带 sk-key 框)
无分组无编号,按清单顺序)
- 左 sider(~300px):接口列表,不分组、不编号,按 §1 清单顺序平铺 6 个路径;选中态高亮
- 右主区:
t-tabs顶部,激活 tab 内容 = 请求行(method + 路径 + 鉴权徽标)→ 调试行(sk-key 输入 + 发送按钮)→ 响应区(元信息 + 虚拟滚动 JSON) - 暗色默认(TDesign dark theme + CSS 变量)
3. 交互细则
3.1 Tabs
- 点击 sider 接口:该接口无 tab 则新开,有则激活(Postman 语义)
- 关闭 tab:丢弃该 tab 状态(响应缓存随之释放);若关的是激活 tab,激活相邻 tab;全部关光 → 空态引导页(提示从左侧选择接口)
- tab 标签直接显示接口路径(如
/v1/models、/api/log/token),与 sider 列表一致;宽度不够时省略号截断 - tab 上限即 5(接口总数),无需溢出策略
3.2 sk-key(每 tab 独立)
- 每个 tab 自带 key 输入框(
t-inputtype=password + 显隐切换),互不影响,可并行调试多把 key - 不做持久化:key 仅存 Pinia 内存——切 tab 不丢,F5 刷新随整页状态一并重置(tabs、key、响应缓存同生共死,语义干净);不写 localStorage / sessionStorage,sk-key 不落盘
3.3 请求
- 纯手动:点「发送」才发请求;TanStack Query
staleTime: Infinity,切走再切回 tab 不自动重发 - queryKey:
[endpointId, skKey]——同 tab 换 key 视为新查询;不同 tab(不同 key)缓存天然隔离 - 发送前校验:key 为空 → 输入框标红提示,不发请求
- 请求中按钮 loading;响应元信息记录:状态码、耗时 ms、响应体字节数
3.4 响应区
- 虚拟滚动 JSON 文本视图(对齐 DevTools Network Response):
JSON.stringify(data, null, 2)→ 按\n切行 → 虚拟滚动窗口渲染- 行号列 + 等宽字体;语法高亮仅对可视区行做正则着色(行级懒高亮)
- DOM 恒定 ~40 行,1000 条日志滚动帧率与 10 条无异
- JSON 解析失败(非 JSON 响应)→ 原样文本展示,不高亮
- 空态(未发送):占位提示「填写 sk-key 后发送请求」
- 错误态:网络错误(fetch reject)与 HTTP 非 2xx 分开展示;非 2xx 时仍展示响应体(调试 401/403 场景是刚需)
- 操作:复制(格式化 JSON)、下载
.json文件
4. 数据与状态设计
4.1 端点元数据(静态)
src/api/endpoints.ts——接口定义(按清单顺序)、鉴权方式、注意事项文案、示例响应。纯数据无逻辑,不含分组概念。
4.2 Pinia stores
useBenchStore:openTabs: EndpointId[](开启顺序)、activeTab: EndpointId | null;tab 的 key 由 queryKey 直接承载,无需单独 store- sk-key 不做独立 store 与持久化:作为 EndpointPanel 内的组件局部状态(
ref),生命周期与 tab 内容一致,随刷新重置
4.3 请求层
src/api/client.ts:薄 fetch wrapper——拼 Authorization 头、计时、返回{ status, ms, bytes, text, json };无拦截器无重试(调试工具要看到真实失败)src/api/queries.ts:TanStack Query 接入,useQuery({ queryKey: [epId, skKey], enabled: 手动触发 })
4.4 组件结构
src/
├── api/
│ ├── endpoints.ts # 5 接口静态元数据
│ ├── client.ts # fetch wrapper(计时/字节数/解析)
│ └── queries.ts # TanStack Query 查询定义
├── stores/
│ └── bench.ts # tabs 状态
├── components/
│ ├── side-nav.vue # 左侧接口菜单(平铺路径列表)
│ ├── request-tabs.vue # t-tabs 容器(开/关/激活)
│ ├── endpoint-panel.vue # 单接口完整面板(请求行+调试+响应)
│ ├── doc-card.vue # 请求行/鉴权徽标/注意事项(tab 内文档信息)
│ └── json-text-view.vue # 虚拟滚动 JSON 文本视图(核心性能组件)
├── lib/
│ └── jsonView.ts # 行窗口计算、行级高亮(纯函数,vitest 覆盖)
├── App.vue # 双栏布局骨架
└── main.ts # 装配 pinia / vue-query / tdesign / router
5. 工程配置
5.1 代理(vite.config.ts)
const GATEWAY = process.env.GATEWAY ?? 'https://ai-gateway.furtherverse.com'
server: {
proxy: {
'/api': { target: GATEWAY, changeOrigin: true },
'/v1': { target: GATEWAY, changeOrigin: true },
},
}
只配 /api、/v1 两条(5 接口仅命中此前缀);GATEWAY 环境变量可覆盖。changeOrigin: true 改写 Host 头,网关虚拟主机路由才正确。
5.2 质量
- 命名规范:文件名一律 kebab-case 且语义化完整,宁长勿缩写(如
json-text-view.vue而非jtv.vue,endpoint-panel.vue而非ep.vue;变量/函数同理,endpointList优于epList);模板中引用组件用大驼峰(<SideNav />、<JsonTextView />) - prettier:
semi: true、单引号、100 宽(已生效并提交) - oxlint + eslint 双引擎(脚手架自带链路)
- vitest:纯函数层测试(
lib/jsonView.ts的窗口/高亮计算、client.ts的 URL 与头拼装);.spec.ts与源码同目录 build保持type-check && build-only(脚手架默认)
5.3 依赖(已安装)
tdesign-vue-next@1.20.7、@tanstack/vue-query@5.102.8、@vueuse/core@14.4.0、vitest@5.0.0 + 脚手架自带(vue-router / pinia / oxlint / eslint / prettier)
VueUse 使用边界(避免与既有职责重叠):
json-text-view.vue用useElementSize(容器尺寸监听)、useThrottleFn(滚动节流);响应区复制按钮用useClipboard。loading/error 态归 TanStack Query,tab 状态归 Pinia,输入显隐归 TDesign——这些场景不用 VueUse。
vue-router:空 routes 保留(单页应用,路由层暂不启用,留作后续扩展位)
6. 施工清单(按序)
- vite.config 补代理 + vitest include;
src/清模板残留(counter store、HelloWorld 引用) api/endpoints.ts静态数据 +lib/jsonView.ts纯函数(含单测)stores/bench.ts(tabs 状态)+api/client.ts+api/queries.ts(sk-key 为 EndpointPanel 局部状态,无持久化)- App 骨架 + SideNav + RequestTabs + EndpointPanel + DocCard
- JsonTextView 虚拟滚动(大响应性能验收:
/api/log/token1000 条日志不卡顿) - 暗色主题落地 + 空态/错误态打磨 + 复制/下载
- 全量验证:lint + type-check + build + test,真机调通 5 接口
7. 决策记录(已闭环,不再开放)
| 决策点 | 定案 | 备注 |
|---|---|---|
| 组件库 | TDesign | Table/虚拟滚动成熟、腾讯周更维护、暗色 CSS 变量 |
| 请求层 | TanStack Query + fetch | 不引 axios,避免双层抽象 |
| JSON 展示 | 虚拟滚动文本视图 | 不解析业务结构(Postman/DevTools 先例,解析是正式项目的职责) |
| 布局 | 左 sider + 右 tabs | Postman 形态;sider 平铺 5 路径,不分组不编号,tab 标签即路径 |
| tab 内文档 | 鉴权徽标 + 注意事项 | 说明内容都在 tab 页里,sider 只管路径 |
| key 输入 | 每 tab 独立 | queryKey 天然隔离 |
| key 持久化 | 不做 | 仅 Pinia 内存(tab 组件局部 ref),刷新重置,不落盘 |
| 请求语义 | 手动 + staleTime Infinity | 调试工具的响应不该自己变 |
| 排除清单 | 不做 | 知识留在 docs/ 接口文档 |
| 主题 | 暗色默认 | — |
| 命名规范 | 文件 kebab-case 且语义化(宁长勿缩写)/ 模板引用大驼峰 | 见 §5.2 |
| 目录名 | new-api-key-bench | — |