Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/user-stories.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
>
> **v1.16(2026-08-17)· B 交互(UE)优化(rant `2026-08-17T15:50:05`).** **toast 分级**(成功 success / 错误 error / 信息 info,39 个调用点全部分级着色);**按钮 loading 态**(充值 / 加额 / 部门确认 / 上架提交 → 转圈 + 禁用,模拟反馈后恢复);**API Key 复制反馈**(复制后按钮短暂变「已复制 ✓」,1.2s 恢复,降级路径「请 Ctrl+C」);**键盘可达**(充值金额 / 加额金额·原因 / 部门名称·配额 Enter 提交;topup/raise/dept 行内卡片 Esc 关闭;打开时自动聚焦)。**rant 15:50:05 全部验收项满足,UI 美化与 UE 优化波收官(PR #38/#39/#40,docs v1.14→v1.16)。**
> **v1.17(2026-08-17)· 多角度 UI/UE 深化(rant `2026-08-17T16:57:17`,分批 PR).** **A. 清除原生弹窗 ✓(PR #41)**:约 5 处原生确认/输入弹窗(key 删除、共享下架、部门删除、API Key 新建/改名、运营者/成员充值)全部改为**行内二次确认**(按钮变「确认删除?」红色态,3 秒无操作或 Esc 还原,再次点击执行)与**行内编辑**(行内展开输入框,Enter 确认 / Esc 取消);抽象可复用 `confirmInline` / `inlineForm` 组件全站统一;`grep ui/` 无原生弹窗残留。**B. 相对时间显示 ✓(PR #42)**:新增 `timeAgo` 格式化(`刚刚 / N 分钟前 / N 小时前 / 昨天 / MM-DD`,hover `title` 显示完整绝对时间),交易列表、加额申请列表、共享列表(新增「上架时间」列)与 API Key 最近使用时间统一使用。**C. 数字列对齐 ✓(PR #43)**:价格 / 点数 / 已用 / 额度 / 余额等**数字列统一右对齐 + 等宽字体**(`--mono` + `tabular-nums`,新增 `.table .num` 类,覆盖市场输入/输出价与上下文、交易 Token 用量/点数、共享已用/额度·单价·收益、成员配额/剩余、部门成员数·月分配·已用·剩余、运营者余额、加额申请点数),时间列 / 状态列保持左对齐;金额统一 `D.fmt`(整数千分位、小数 2 位)全覆盖检查无遗漏。其余 D–G(`:focus-visible` 与快捷键、行内校验错误、视图过渡、细节一致性)分批落地。**D. 键盘可达性 / 无障碍 ✓(PR #44)**:全局 `:focus-visible` 焦点环(accent 色 outline,Tab 导航清晰可见;输入类控件沿用边框高亮不叠加);**全局快捷键**:`/` 聚焦市场搜索框(输入框内不触发)、数字 **1-7** 切换侧边栏视图(仪表盘/市场/共享/钱包/交易/管理/设置,带 title 与右侧键位角标提示,Cmd/Ctrl+数字 不劫持)、Esc 关闭行内新建 Key;侧边栏导航按钮补 `title`(快捷键提示)。**E. 表单行内校验错误 ✓(PR #45)**:新增 `setFieldError(input, msg)` / `clearFieldError(input)`(输入框**红边框** + 字段下方**红字小号行内文案**,输入修正自动清除,打开表单时重置);覆盖**充值金额**(自定义金额非法)、**申请加额**(点数/原因)、**部门表单**(名称/配额/重名)、**共享上架表单**(API Key/厂商·Plan·模型·额度),提交失败不再仅依赖 toast,聚焦首个错误字段。**F. 视图切换过渡 ✓(PR #46)**:页面切换加轻量动画(`.view:not(.hidden)` **fade + 轻微上移 6px,150ms** `viewIn`,替代生硬切换);数据表格更新无闪烁(`.table tbody` **fade-in** `tbodyIn`,新增 `pulseTbody` 在静态 tbody 重渲染后重启动画,动态表格整表重建自动生效)。**G. 细节一致性 ✓(PR #47)**:一致性审计确认——**所有数字/金额统一 `D.fmt`(千分位/2 位小数)**(侧边栏余额、统计卡、表格、钱包点数格式一致)、已用/额度展示统一;表格操作按钮统一小号样式(`padding:4px 10px;font-size:12px`);**行内编辑校验统一走行内错误**(inlineForm 校验失败由 toast 改为 `setFieldError` 红边框+行内文案,API Key 改名/运营者充值/成员充值 3 处对齐 item E);子文本字号统一(11px→12px);上架 toast 单价措辞统一「点/1M(自动)」;移除 3 处「(未生效)」残留文案。**rant 16:57:17 全部验收项满足(A–G 七项,PR #41–#47),UI/UE 深化波收官。**
> **v1.20(2026-08-17)· UI/UE 第五轮多角度深化(rant `2026-08-17T20:46:57`,分批 PR,进行中).** **A. 新用户首次引导 ✓(待合入)**:首次登录(localStorage `atp-tour-done` 未标记)触发**轻量引导 tour**——4 步依次高亮:① 仪表盘(余额/趋势)→ ② 模型市场(浏览/使用)→ ③ 共享管理(上架赚点数)→ ④ 钱包/设置(充值、API Key、接入端点);实现为**非 modal 浮层**(`#tour-overlay` 半透明遮罩 + `#tour-ring` 目标 **accent 描边高亮环**(`tour-pulse` 呼吸动画,`prefers-reduced-motion` 下静止)+ `#tour-pop` 气泡卡片:右上「跳过」、底部「上一步/下一步/完成」(末步变完成)、步骤计数 `n / 4`);**步骤自动切视图**(`switchView(view, {sync:false})` 不入历史)并按目标 `getBoundingClientRect` 定位高亮环与气泡(视口内钳制);**Esc / 点浮层外 / 跳过 / 完成 均可关闭**,关闭即写 `atp-tour-done=1`(之后登录不再出现);设置页加**「重新查看引导」**按钮(`#tour-replay-btn`,`startTour()` 重放)。其余 B–G(复制引导 / 密度 / 最近使用 / CSV / 键盘导航 / 品牌)分批落地。
> **v1.19(2026-08-17)· 设置页「API 接入端点」卡片(rant `2026-08-17T20:44:18`).** 设置页新增**「接入方式 / API 端点」卡片**(`#endpoint-card`,放在 API Key 卡片**上方**——先看端点再生成 key):**OpenAI 兼容** `https://gateway.aitokenpool.local/v1`(Chat Completions · Cursor / Cline / Roo Code / OpenCode / OpenAI SDK)与 **Anthropic 兼容** `https://gateway.aitokenpool.local/anthropic`(Messages API · Claude Code / Goose / OpenClaw)各一行——协议标签(accent 药丸)+ **等宽可整段选中** URL(`user-select:all`)+ **复制按钮**(复用 copyKey 降级链:clipboard API → `execCommand("copy")` → 提示 Ctrl+C,带「已复制 ✓」flash)+ 说明小字;URL 旁小注「部署后替换为你的网关域名」(原型占位);卡片内**使用步骤** ① 生成 API Key → ② 工具 Base URL 填端点 → ③ 填 key;端点为 **UI 静态常量** `API_ENDPOINTS`(注释标明真实值来自部署配置,后端实现时接入 `config.server.base_url`);窄屏端点行纵向堆叠 + URL 自动换行。
> **v1.19(2026-08-17)· UI/UE 第四轮多角度深化(rant `2026-08-17T20:39:30`,分批 PR,进行中).****A. URL hash 路由 ✓(PR #55)**:视图与地址栏 hash 联动——`#/marketplace`、`#/wallet` 等视图 hash 可**直接收藏 / 分享 / 刷新恢复**;规则:① 视图切换(导航 / 快捷键 1-7 / 登录恢复)同步 `history.pushState` 更新 hash(已相同不重复入栈,不触发 hashchange 回环);② **前进 / 后退**(hashchange)视图跟随切换;③ **非法 hash** 回退仪表盘视图但**不重写 URL**(避免 pushState 污染历史、后退需两次);④ 空 hash 不动作(无 hash 时保持默认行为);⑤ 刷新后登录自动恢复 hash 对应视图(游客浏览 / 未登录态无冲突);⑥ 游客受限视图 hash 被拦截时保持原视图、不入栈。**B. 交易记录汇总条 ✓(PR #56)**:交易页卡片顶部加**紧凑汇总条**(`.tx-summary`,三列 inline:总收入 / 总支出 / 净变化,+绿 −红,零值中性显示 `0`);**随 tab 过滤联动**——「全部 / 消费 / 收益」tab 切换即时重算,且**与表格列筛选一致**(抽公共 `filterRows(rows, columns, filters)` 供 `buildDataTable` 与汇总共用,汇总反映与表格可见行相同的过滤集,不受分页影响);窄屏自动换行收窄。**C. 原生 select 美化 ✓(待合入)**:全站 `select` 去掉浏览器原生箭头,改**自定义 SVG 下拉箭头**(`--select-arrow` CSS 变量,内联 data-URI,深/亮主题各一色),`appearance:none` + `padding-right` 预留箭头空间;**hover / focus 边框同 input**(`var(--accent)`,focus 加 1px 光圈);`disabled` 态降透明度;覆盖全部 select 来源——静态 `.input`(市场筛选 `#mk-provider`/`#mk-sort`、共享表单 `#sf-provider/plan/model`、设置语言/默认模型)、动态 `select.th-filter`(表格列筛选)、`select[data-page-size]`(分页每页条数);`option` 深色背景与 `input-error` 错误态保留。**D. toast 队列堆叠 ✓(待合入)**:单例 toast 改为**队列容器** `#toast-wrap`(固定底部居中,纵向堆叠,`gap:10px`,`pointer-events:none` 不拦截点击);`toast(msg, type)` 每次**创建独立元素**(不再覆盖旧消息),**最多同时 3 条**(超限立即移除最旧一条腾位),每条**独立生命周期**——到时(2.6s)加 `.out` 淡出(0.2s `toast-out` 动画)后移除,互不影响;**分级样式保留**(success/error/info 边框色与文字色不变,39 个调用点零改动);toast 自身 `pointer-events:auto` 为后续可交互 toast 预留。**E. 快捷键帮助面板 ✓(待合入)**:按 **`?`**(或 `Shift+/`)在右上角弹出**行内卡片**(`#help-panel`,非 modal、无遮罩、不阻塞页面)展示快捷键速查:`/` 聚焦市场搜索、`1–7` 切换视图、`Esc` 关闭/取消、`?` 开合面板;**Esc 或再按 `?` 关闭**(帮助打开时 Esc 优先于行内表单,不会误关「新建 Key」);面板底部显示**当前上下文**(当前视图 + 亮/深色主题);输入框内按 `?` 不劫持(typing 守卫,正常输入字符);关闭按钮 ×;窄屏(≤560px)全宽贴顶。**F. 市场模型行展开详情 ✓(待合入)**:市场表格每行首列加 **`+`/`−` 展开按钮**(`.row-expand`,hover accent);点击在 **tr 下追加详情行**(`.mk-detail`,`colspan=7` 浅底网格,行内展开不弹窗)展示:**Max tokens**(单次输出上限,模型未公布显示「未公布」)、**价格换算**(`1M tokens ≈ N 点` 按输出价 + 输入价/1M)、**上下文长度**、**可用性**(可用/繁忙 + 成功率)、**多 key 自动故障转移说明**(仅 `multi` 模型显示,架构 v0.2 路由策略);**仅展开当前行**(点其它行自动收起,再点收起),展开态存 `mkExpanded` 数据(搜索/筛选重渲染后保留)。**G. 登录页 polish ✓(待合入)**:登录卡片视觉微调——**logo 加大**(52px + 渐变微光 `0 0 18px rgba(78,205,196,.35)`)、**品牌标题层次**(h1 22px + 副文案行高)、**输入框聚焦光晕**(`0 0 0 3px var(--accent-soft)`,双主题适配);**空邮箱/密码提交行内错误**(复用 `field-error` 组件:红边框 + 行内文案,聚焦首个错误字段,输入修正自动清除,`novalidate` 自管校验);**「记住我」checkbox**(`#login-remember`,localStorage `atp-remember` 记忆,加载时还原)+ **演示模式说明小字**(`demo-hint`:demo@aitokenpool.local / demo1234)。**rant 20:39:30 全部验收项满足(A–G 七项,PR #55–#61),UI/UE 第四轮深化波收官,docs v1.19。**
> **v1.18(2026-08-17)· UI/UE 第三轮多角度深化(rant `2026-08-17T18:06:09`,分批 PR).** **A. 仪表盘数据可视化 ✓(PR #48)**:纯 SVG sparkline(零外部依赖,手写 path,颜色用现有 CSS 变量):「本月点数变化」卡片按天聚合近 7 日净变化画迷你折线图(渐变填充,每点 `<title>` hover 显示当天数值);「我的共享」卡显示共享收益累计趋势 sparkline(`--ok` 色),无上架 key 时保留空状态不画图。**B. 亮色主题 ✓(PR #49)**:CSS 变量重构——新增 `:root[data-theme="light"]` 全套变量重定义(--bg/--bg-card/--border/--text 等),并把原先硬编码颜色(表格行边框/斑马纹、卡片 hover 阴影、确认态、spinner、遮罩)抽为语义变量(--table-row-border/--table-stripe/--card-hover-border/--card-shadow/--danger-soft/--danger-text/--spin-track/--overlay)双主题共用;侧边栏加**日/月 SVG 切换按钮**(按当前主题显隐图标),**localStorage 记忆**(atp-theme),首次加载尊重 **`prefers-color-scheme`**;两主题对比度均达标(正文/次要文字/边框层次清晰)。**C. 移动端表格卡片化 ✓(PR #50)**:`@media (max-width: 560px)` 下关键表格(市场/共享/交易/管理/设置 API Key)**隐藏表头**,每行渲染为**卡片**(`label: value` 布局,`td::before` 取 `data-label`),不再横向滚动;卡片内主字段加粗、状态徽章保留、操作按钮整行宽(`flex: 1 1 45%` 换行);全部表格 td 补齐 `data-label`(含 buildDataTable 动态列用 col.title)。**D. 搜索增强 ✓(PR #51)**:全部搜索框(市场 `#mk-search` / API Key `#ak-search` / 部门 `#od-search` / 运营者用户 `#ops-search`)统一接入 **~150ms 输入防抖**(避免每次按键整表重绘闪烁,连续输入只渲染一次);渲染结果对匹配关键词**大小写不敏感 `<mark>` 高亮**(先 HTML 转义再包裹,`--accent-soft` 底 + `--accent-text` 字,双主题对比度达标;清空后自动清除);搜索框加**「清空 ×」小按钮**(有内容时显示,点击清空立即重绘并聚焦输入框;空状态「清除搜索/清除筛选」按钮同样同步 × 态)。**E. 动效与系统偏好 ✓(PR #52)**:按钮 **`:active` 轻微下压**(`scale(0.98)`,disabled 不触发);统计卡 **hover 微抬升**(`translateY(-2px)` + 边框/阴影提升,与卡片 hover 语言一致);**数字/点数变化轻微跳动动画**(新增 `bump(el)` 助手——remove→reflow→add `.bump` 类重放 `numJump`(`translateY(-3px) scale(1.02)`,0.35s);接入 4 处余额变化点:钱包充值(侧边栏+钱包余额)、聊天消费扣款、加额批准、运营者给自己充值);**尊重 `prefers-reduced-motion: reduce`**——全局 `animation-duration/transition-duration` 压到 0.01ms,禁用过渡/动画但保留全部功能。**F. 动态文档标题 ✓(PR #53)**:`document.title` **跟随视图切换**(`switchView` 内统一设置「视图标题 · AITokenPool」,如「模型市场 Marketplace · AITokenPool」;`VIEW_TITLE` 7 个视图全覆盖;未知视图回退「AITokenPool」);**默认「AITokenPool」**——首次加载(DOMContentLoaded)与登录页/无视图态回默认标题,HTML `<title>` 同步改为「AITokenPool」;游客受限视图被拦截时标题保持不变。**G. 其他细节 ✓(验证确认,PR #54 收尾)**:① **视图切换滚动复位**——`switchView` 内 `$("#main").scrollTop = 0`(`.main` 即 `overflow-y:auto` 滚动容器,渲染后复位,多次切换均生效,DOM 冒烟验证);② **复制按钮「已复制 ✓」态**——`copyKey` 的 flash 反馈(复制成功按钮短暂变「已复制 ✓」并禁用,1.2s 恢复;降级路径「请 Ctrl+C」;v1.16 已实现,DOM 冒烟验证)。**rant 18:06:09 全部验收项满足(A–G 七项,PR #48–#54),UI/UE 第三轮深化波收官,docs v1.18。**
Expand Down
8 changes: 8 additions & 0 deletions ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ ui/
- **复制**:`copyEndpoint(i)` 复用 copyKey 的降级链(clipboard API → textarea+execCommand → 提示 Ctrl+C)与「已复制 ✓」flash(1.2s 恢复);事件绑定 `document.querySelectorAll("[data-ep-copy]")`(bindEvents);
- 窄屏(≤560px):`.endpoint-row` 纵向堆叠,`.ep-url` `word-break:break-all` 自动换行。

## 首次引导 tour 约定(v1.20,rant 2026-08-17T20:46:57 A)

- **触发**:登录成功后 `maybeStartTour()`——`localStorage atp-tour-done === "1"` 则不触发;设置页 `#tour-replay-btn`「重新查看引导」随时重放;
- **4 步**(`TOUR_STEPS`,每步 `{view, sel, title, desc}`):仪表盘 `#dash-stats` → 模型市场 `#view-marketplace` → 共享管理 `#view-sharing` → 钱包/设置 `#endpoint-card`;
- **结构**:`#tour-overlay`(半透明遮罩,点击=关闭)+ `#tour-ring`(目标 accent 高亮环,`getBoundingClientRect` 定位,`tour-pulse` 呼吸动画,reduced-motion 静止)+ `#tour-pop` 气泡(右上「跳过」、底部「上一步/下一步/完成」、计数 `n / 4`;`data-tour-action` 委托);
- **行为**:步骤切换自动 `switchView(step.view, {sync:false})`(不入历史);气泡定位视口内钳制;**Esc / 点外 / 跳过 / 完成 均关闭并写 `atp-tour-done=1`**(关闭即标记,之后不再出现);
- 全局 keydown 优先级:**引导开时 Esc 先关引导**(再处理帮助/行内表单);冒烟测试注意 stub 需给元素 `getBoundingClientRect`,登录须预填邮箱/密码(item G 行内校验拦截空值)。

## 数据说明

- 点数规则与机制细节见 `docs/user-stories.md`(v1.8:机制说明不再进入面向用户的界面文案);UI 只呈现结果(余额数字、模型价格点数、交易金额/类型/状态、可用/繁忙)
Expand Down
37 changes: 37 additions & 0 deletions ui/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,43 @@ mark {
.help-panel { right: 12px; left: 12px; width: auto; top: 68px; }
}

/* 首次引导 tour(rant 20:46:57 A:非 modal 浮层 + 目标 accent 高亮环;reduced-motion 静止) */
#tour-overlay {
position: fixed; inset: 0; z-index: 940;
background: rgba(14, 17, 22, .45); cursor: pointer;
}
#tour-ring {
position: fixed; z-index: 941; pointer-events: none;
border: 2px solid var(--accent); border-radius: 8px;
box-shadow: 0 0 0 4px var(--accent-soft), 0 0 24px rgba(78,205,196,.35);
animation: tour-pulse 1.2s ease-in-out infinite;
}
@keyframes tour-pulse {
0%, 100% { box-shadow: 0 0 0 4px var(--accent-soft), 0 0 24px rgba(78,205,196,.35); }
50% { box-shadow: 0 0 0 8px var(--accent-soft), 0 0 32px rgba(78,205,196,.5); }
}
@media (prefers-reduced-motion: reduce) {
#tour-ring { animation: none; }
}
#tour-pop {
position: fixed; z-index: 942; width: 300px; max-width: calc(100vw - 24px);
background: var(--bg-card); border: 1px solid var(--border);
border-radius: var(--radius); box-shadow: var(--card-shadow);
padding: 16px;
}
.tour-skip {
position: absolute; top: 10px; right: 12px;
border: none; background: none; color: var(--text-mute);
font-size: 12px; cursor: pointer;
}
.tour-skip:hover { color: var(--text); }
.tour-title { font-size: 15px; font-weight: 700; padding-right: 48px; }
.tour-desc { color: var(--text-dim); font-size: 13px; line-height: 1.55; margin-top: 6px; }
.tour-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 14px; }
.tour-progress { color: var(--text-mute); font-size: 12px; }
.tour-nav { display: flex; gap: 8px; }
.tour-replay { margin-top: 10px; }

/* 按钮 loading 转圈(rant 15:50:05 B.8:提交中反馈) */
.spin {
display: inline-block;
Expand Down
Loading
Loading