diff --git a/ui/README.md b/ui/README.md
new file mode 100644
index 0000000..9cad8a5
--- /dev/null
+++ b/ui/README.md
@@ -0,0 +1,60 @@
+# AITokenPool UI 原型(Static HTML Prototype)
+
+纯静态 HTML + CSS + JS 原型,无框架、无构建工具、无外部 CDN 依赖。
+浏览器直接打开即可浏览与演示。
+
+## 产品模型(重要)
+
+**一套产品,两种部署场景,角色区分**:
+
+- **公共版**:部署在公网,任何人都能注册、分享 key、赚点数、消费别人共享的 key;
+- **企业版**:部署在企业内网,管理员(IT)把采购的 key 放进池子、给成员分配点数;
+- 两者的**功能集合完全相同**(市场 / 共享 / 钱包 / 交易 / 设置),不存在"企业版专属功能";
+- 差异只在**谁会主动分享**(公共版人人可分享;企业版只有管理员有动力采购/分享 key);
+- **角色是权限差异,不是产品差异**:管理员额外拥有"管理视图"(key 池 / 成员点数 / 用量报表 / 组织设置),普通用户没有;员工就是普通用户,用同一套界面。
+
+本原型据此实现:单一登录入口(角色由账号决定),一套导航(仪表盘 / 模型市场 / 共享管理 / 钱包 / 交易记录 / 设置 + 管理员角色视图)。
+
+## 如何浏览
+
+**方式一(推荐)**:双击 `index.html`,用浏览器打开即可。
+
+**方式二**:本地起一个静态服务器(可选):
+
+```bash
+python3 -m http.server 8000 --directory ui
+# 然后访问 http://localhost:8000
+```
+
+## 页面清单
+
+登录页:单一入口(邮箱登录占位),不做公共版/企业版二选一。
+
+同一套界面(角色视图):
+
+1. 仪表盘 Dashboard — 点数余额、本月用量、共享收益、最近交易
+2. 模型市场 Marketplace — 模型浏览、搜索、厂商筛选、排序(按价格/上下文)
+3. 共享管理 Sharing — 默认只显示统计 + 我的共享列表(key 脱敏展示,可暂停/恢复/重新上架/彻底删除);点击"+ 添加 / 上架新 key"展开上架表单(须填 API Key,平台加密托管;分享者只填声明额度,单价由平台按模型定价自动计算并展示参考价),提交成功或取消后自动收起
+4. 钱包 Wallet — 点数余额、充值/提现占位入口、收支明细(MRT 风格表格:列排序/列筛选/分页/每页行数切换)
+5. 交易记录 Transactions — 消费/收益/充值/提现,Tab 筛选 + MRT 风格表格(列排序/列筛选/分页,与 Tab 叠加生效)
+6. 设置 Settings — 账户、API Key 管理(可生成)、通知、偏好
+7. 管理视图 Admin(管理员角色专属)— Key 池管理(添加/撤销)、成员管理(配额调整)、用量报表(按模型/成员)、组织设置(含"关闭外部注册"企业部署开关)
+
+## 文件结构
+
+```
+ui/
+├── index.html # 入口(登录页 + 应用外壳 + 全部视图)
+├── css/style.css # 设计系统(深色主题 · 强调色 #4ecdc4 · 响应式预留)
+├── js/data.js # 内嵌 mock 数据(模型价格、交易、共享、成员等)
+├── js/app.js # 交互逻辑(导航、筛选、表单、分页、Toast)
+└── README.md # 本文件
+```
+
+## 数据说明
+
+- 点数规则:1 USD = 1000 点;模型价格折算自 `data/models.example.json`(CNY 按 ~7.2 汇率示例折算)
+- 上架单价不由分享者手填:单价是模型×厂商的客观属性,平台按模型价格表自动计算(参考单价 = 该模型输出价 点数/1M);模型无定价数据时给出"按默认价"兜底,不报错
+- 上架需提交 API Key(password 输入):平台加密托管、仅用于代理调用;共享列表只展示脱敏值(如 sk-****1234,前 3 后 4),不展示明文
+- 删除 = 彻底下架(key 从平台移除,不可恢复);暂停 = 临时不接单,可恢复
+- 全部数据为前端常量(mock),无后端、无真实调用
diff --git a/ui/css/style.css b/ui/css/style.css
new file mode 100644
index 0000000..a114fa1
--- /dev/null
+++ b/ui/css/style.css
@@ -0,0 +1,399 @@
+/* ============================================================
+ AITokenPool UI Prototype — 设计系统
+ 深色主基调 · 克制配色 · 强调色 #4ecdc4(青)
+ ============================================================ */
+
+:root {
+ --bg: #0e1116;
+ --bg-soft: #141922;
+ --bg-card: #1a2029;
+ --bg-hover: #222a36;
+ --border: #2a3342;
+ --text: #e6ebf2;
+ --text-dim: #9aa7b8;
+ --text-mute: #67748a;
+ --accent: #4ecdc4;
+ --accent-soft: rgba(78, 205, 196, 0.12);
+ --accent-text: #6fe0d8;
+ --ok: #3ecf8e;
+ --warn: #e5b45b;
+ --danger: #e5666a;
+ --radius: 12px;
+ --radius-sm: 8px;
+ --sidebar-w: 240px;
+ --shadow: 0 8px 24px rgba(0,0,0,.35);
+ --font: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+}
+
+* { box-sizing: border-box; margin: 0; padding: 0; }
+
+html, body { height: 100%; }
+
+body {
+ font-family: var(--font);
+ background: var(--bg);
+ color: var(--text);
+ font-size: 14px;
+ line-height: 1.6;
+}
+
+.hidden { display: none !important; }
+.muted { color: var(--text-mute); }
+.en { color: var(--text-mute); font-size: 12px; font-weight: 400; }
+
+/* ---------- 通用组件 ---------- */
+
+.btn {
+ border: 1px solid var(--border);
+ background: var(--bg-card);
+ color: var(--text);
+ padding: 8px 16px;
+ border-radius: var(--radius-sm);
+ cursor: pointer;
+ font-size: 14px;
+ transition: all .15s;
+}
+.btn:hover { background: var(--bg-hover); border-color: #3a4658; }
+.btn-primary {
+ background: var(--accent);
+ border-color: var(--accent);
+ color: #062021;
+ font-weight: 600;
+}
+.btn-primary:hover { background: #63ded6; }
+.btn-ghost { background: transparent; }
+.btn-danger {
+ background: transparent;
+ border-color: transparent;
+ color: var(--danger);
+}
+.btn-danger:hover { background: rgba(229, 102, 106, 0.12); border-color: var(--danger); }
+.btn-block { width: 100%; }
+
+.input, select.input {
+ width: 100%;
+ background: var(--bg-soft);
+ border: 1px solid var(--border);
+ color: var(--text);
+ border-radius: var(--radius-sm);
+ padding: 9px 12px;
+ font-size: 14px;
+ outline: none;
+ transition: border-color .15s;
+}
+.input:focus { border-color: var(--accent); }
+
+/* 自动参考单价(只读展示,由平台按模型定价计算) */
+.price-view {
+ display: flex;
+ align-items: center;
+ min-height: 38px;
+ padding: 9px 12px;
+ border: 1px dashed var(--border);
+ border-radius: var(--radius-sm);
+ background: var(--bg-soft);
+ color: var(--text-dim);
+ font-size: 13px;
+}
+
+/* 表单辅助说明 */
+.form-hint {
+ color: var(--text-mute);
+ font-size: 12px;
+ line-height: 1.5;
+}
+select.input option { background: var(--bg-card); }
+
+.card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 20px;
+}
+.card h3 { font-size: 15px; margin-bottom: 14px; color: var(--text); }
+
+.card-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+ gap: 16px;
+ margin-top: 16px;
+}
+.settings-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
+
+.stat-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+ gap: 14px;
+ margin-bottom: 4px;
+}
+.stat {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 16px 18px;
+}
+.stat .label { color: var(--text-mute); font-size: 12px; }
+.stat .value { font-size: 24px; font-weight: 700; margin-top: 4px; }
+.stat .sub { color: var(--text-dim); font-size: 12px; }
+.stat.accent .value { color: var(--accent-text); }
+
+.table-wrap { overflow-x: auto; }
+.table-wrap.compact { margin-top: 4px; }
+.table { width: 100%; border-collapse: collapse; font-size: 13px; }
+.table th {
+ text-align: left;
+ color: var(--text-mute);
+ font-weight: 500;
+ font-size: 12px;
+ padding: 8px 12px;
+ border-bottom: 1px solid var(--border);
+ white-space: nowrap;
+}
+.table td { padding: 10px 12px; border-bottom: 1px solid rgba(42,51,66,.5); white-space: nowrap; }
+.table tbody tr:hover { background: var(--bg-hover); }
+
+.badge {
+ display: inline-block;
+ padding: 2px 10px;
+ border-radius: 999px;
+ font-size: 12px;
+ border: 1px solid var(--border);
+ color: var(--text-dim);
+}
+.badge.ok { color: var(--ok); border-color: rgba(62,207,142,.4); background: rgba(62,207,142,.08); }
+.badge.warn { color: var(--warn); border-color: rgba(229,180,91,.4); background: rgba(229,180,91,.08); }
+.badge.danger { color: var(--danger); border-color: rgba(229,102,106,.4); background: rgba(229,102,106,.08); }
+.badge.accent { color: var(--accent-text); border-color: rgba(78,205,196,.4); background: var(--accent-soft); }
+.badge.dim { color: var(--text-mute); }
+
+.toolbar { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
+.toolbar .input { max-width: 240px; }
+.toolbar-count { align-self: center; color: var(--text-mute); font-size: 12px; margin-left: auto; }
+
+.tabs { display: flex; gap: 6px; margin-bottom: 14px; flex-wrap: wrap; }
+.tab {
+ border: 1px solid var(--border);
+ background: transparent;
+ color: var(--text-dim);
+ padding: 7px 16px;
+ border-radius: 999px;
+ cursor: pointer;
+ font-size: 13px;
+}
+.tab:hover { color: var(--text); }
+.tab.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-text); }
+
+.pager { display: flex; gap: 6px; align-items: center; justify-content: flex-end; margin-top: 12px; }
+.pager button {
+ min-width: 30px;
+ height: 30px;
+ border: 1px solid var(--border);
+ background: var(--bg-card);
+ color: var(--text-dim);
+ border-radius: var(--radius-sm);
+ cursor: pointer;
+}
+.pager button.active { border-color: var(--accent); color: var(--accent-text); }
+.pager span { color: var(--text-mute); font-size: 12px; }
+
+/* MRT 风格表格:表头排序 + 列筛选 + 每页行数 */
+.th-sort {
+ background: transparent;
+ border: none;
+ color: inherit;
+ font: inherit;
+ cursor: pointer;
+ padding: 0;
+ text-align: left;
+ white-space: nowrap;
+}
+.th-sort:hover { color: var(--accent-text); }
+.th-filter {
+ width: 100%;
+ min-width: 72px;
+ background: var(--bg-soft);
+ border: 1px solid var(--border);
+ color: var(--text);
+ border-radius: var(--radius-sm);
+ padding: 5px 8px;
+ font-size: 12px;
+ outline: none;
+}
+.th-filter:focus { border-color: var(--accent); }
+.range-filter { display: flex; gap: 4px; }
+.range-filter .th-filter { min-width: 0; width: 100%; }
+.pager-size {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 6px;
+ margin-top: 8px;
+ color: var(--text-mute);
+ font-size: 12px;
+}
+.pager-size select {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ color: var(--text);
+ border-radius: var(--radius-sm);
+ padding: 3px 6px;
+ font-size: 12px;
+}
+
+.form { display: flex; flex-direction: column; gap: 12px; }
+.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
+.form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--text-dim); }
+.form label.check { flex-direction: row; align-items: center; gap: 8px; }
+.form label.check input { accent-color: var(--accent); }
+
+.mini-list { display: flex; flex-direction: column; gap: 8px; }
+.mini-item {
+ display: flex; justify-content: space-between; align-items: center;
+ gap: 10px; padding: 10px 12px;
+ background: var(--bg-soft); border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+}
+.mini-item .t { font-weight: 600; font-size: 13px; }
+.mini-item .d { color: var(--text-mute); font-size: 12px; }
+.mini-item .r { text-align: right; }
+.mini-item .r .pts { color: var(--accent-text); font-weight: 700; }
+
+.bar-list { display: flex; flex-direction: column; gap: 12px; }
+.bar-row .bar-label { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 4px; }
+.bar-row .bar-label .n { color: var(--text-mute); }
+.bar {
+ height: 8px; border-radius: 999px;
+ background: var(--bg-hover); overflow: hidden;
+}
+.bar i { display: block; height: 100%; background: linear-gradient(90deg, var(--accent), #7ee8b8); border-radius: 999px; }
+
+/* ---------- 登录页 ---------- */
+
+.login-view {
+ min-height: 100vh;
+ display: flex; align-items: center; justify-content: center;
+ padding: 24px;
+ background:
+ radial-gradient(600px 300px at 20% 0%, rgba(78,205,196,.08), transparent 60%),
+ radial-gradient(600px 400px at 90% 100%, rgba(78,205,196,.06), transparent 60%),
+ var(--bg);
+}
+.login-card {
+ width: 100%; max-width: 400px;
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 32px;
+ box-shadow: var(--shadow);
+}
+.login-brand { display: flex; align-items: center; gap: 14px; margin-bottom: 22px; }
+.login-brand h1 { font-size: 20px; }
+.login-brand p { color: var(--text-mute); font-size: 12px; }
+
+.logo {
+ width: 40px; height: 40px; border-radius: 10px;
+ background: linear-gradient(135deg, #4ecdc4, #2a9d8f);
+ color: #062021; font-weight: 800;
+ display: flex; align-items: center; justify-content: center;
+ font-size: 16px; flex-shrink: 0;
+}
+
+.mode-switch { display: none; }
+
+.login-form { display: flex; flex-direction: column; gap: 12px; }
+.login-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--text-dim); }
+.login-divider { display: flex; align-items: center; gap: 12px; margin: 16px 0; color: var(--text-mute); font-size: 12px; }
+.login-divider::before, .login-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
+.login-foot { margin-top: 16px; text-align: center; font-size: 13px; color: var(--text-dim); }
+.login-foot a { color: var(--accent-text); text-decoration: none; }
+.login-tip { margin-top: 14px; text-align: center; color: var(--text-mute); font-size: 11px; }
+
+/* ---------- 应用布局 ---------- */
+
+.app { display: flex; height: 100vh; overflow: hidden; }
+
+.sidebar {
+ width: var(--sidebar-w);
+ flex-shrink: 0;
+ background: var(--bg-soft);
+ border-right: 1px solid var(--border);
+ display: flex; flex-direction: column;
+ padding: 14px 12px;
+}
+.sidebar-top { display: flex; align-items: center; gap: 10px; padding: 2px 6px 14px; border-bottom: 1px solid var(--border); }
+.brand-text { display: flex; flex-direction: column; line-height: 1.3; }
+.brand-text strong { font-size: 14px; }
+.brand-text span { color: var(--text-mute); font-size: 11px; }
+
+.nav { flex: 1; padding: 14px 0; display: flex; flex-direction: column; gap: 2px; overflow-y: auto; }
+.nav-group { color: var(--text-mute); font-size: 11px; padding: 10px 8px 4px; }
+.nav-item {
+ display: flex; align-items: center; gap: 10px;
+ padding: 9px 10px; border-radius: var(--radius-sm);
+ color: var(--text-dim); cursor: pointer; font-size: 13.5px;
+ border: none; background: transparent; text-align: left; width: 100%;
+}
+.nav-item .ico { width: 18px; text-align: center; font-size: 15px; }
+.nav-tag {
+ margin-left: auto;
+ font-size: 10px;
+ padding: 1px 7px;
+ border-radius: 999px;
+ border: 1px solid rgba(78,205,196,.4);
+ color: var(--accent-text);
+ background: var(--accent-soft);
+}
+.nav-item:hover { background: var(--bg-hover); color: var(--text); }
+.nav-item.active { background: var(--accent-soft); color: var(--accent-text); font-weight: 600; }
+
+.sidebar-bottom { border-top: 1px solid var(--border); padding-top: 12px; display: flex; flex-direction: column; gap: 8px; }
+.user-chip {
+ display: flex; align-items: center; gap: 10px;
+ background: var(--bg-card); border: 1px solid var(--border);
+ border-radius: var(--radius-sm); padding: 10px;
+}
+.user-chip .avatar {
+ width: 32px; height: 32px; border-radius: 50%;
+ background: linear-gradient(135deg, #4ecdc4, #2a9d8f);
+ color: #062021; font-weight: 700;
+ display: flex; align-items: center; justify-content: center;
+}
+.user-chip strong { font-size: 13px; display: block; }
+.user-chip span { color: var(--text-mute); font-size: 11px; }
+.user-chip b { color: var(--accent-text); }
+
+.main { flex: 1; overflow-y: auto; padding: 28px 32px 48px; }
+
+.page-head { margin-bottom: 18px; }
+.page-head h2 { font-size: 22px; }
+.page-head p { color: var(--text-mute); margin-top: 2px; }
+
+.wallet-hero { display: flex; justify-content: space-between; align-items: center; gap: 20px; flex-wrap: wrap; }
+.balance-big { font-size: 42px; font-weight: 800; color: var(--accent-text); line-height: 1.2; }
+.wallet-actions { display: flex; gap: 10px; }
+
+.toast {
+ position: fixed; bottom: 28px; left: 50%; transform: translateX(-50%);
+ background: var(--bg-card); border: 1px solid var(--accent);
+ color: var(--text); padding: 12px 22px; border-radius: 999px;
+ box-shadow: var(--shadow); z-index: 999; font-size: 14px;
+ animation: toast-in .2s ease;
+}
+@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }
+
+/* ---------- 响应式预留 ---------- */
+
+@media (max-width: 900px) {
+ .sidebar { width: 64px; padding: 12px 8px; }
+ .sidebar .brand-text, .sidebar .nav-item .label, .sidebar .user-chip > div:last-child,
+ .sidebar #toggle-mode-btn, .sidebar #logout-btn { display: none; }
+ .nav-item { justify-content: center; }
+ .main { padding: 20px 16px 40px; }
+ .form-row { grid-template-columns: 1fr; }
+}
+
+@media (max-width: 560px) {
+ .stat-grid { grid-template-columns: 1fr 1fr; }
+ .card-grid { grid-template-columns: 1fr; }
+}
diff --git a/ui/index.html b/ui/index.html
new file mode 100644
index 0000000..465331e
--- /dev/null
+++ b/ui/index.html
@@ -0,0 +1,323 @@
+
+
+
+
+
+AITokenPool — AI Token 共享平台
+
+
+
+
+
+
+
+
+
AT
+
+
AITokenPool
+
AI Token 共享平台 · 一套产品,两种部署场景
+
+
+
+
+
静态演示原型 · 数据为内嵌 mock,无真实后端 · 管理员与普通用户由账号角色决定登录后看到的界面
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 仪表盘 Dashboard
+ 总览你的点数、用量与收益
+
+
+
+
+
最近交易 Recent transactions
+
+
+
+
+
+
+
+
+
+ 模型市场 Marketplace
+ 浏览 / 搜索各家模型,按点数计价
+
+
+
+
+
+
+
+
+
+
+ | 厂商 Provider | 模型 Model | 输入价 /1M | 输出价 /1M | 上下文 | 可用性 | |
+
+
+
+
+
+
+
+
+
+ 共享管理 Sharing
+ 上架闲置 key 赚点数 · 管理共享中的 key
+
+
+
+
+
+
+
+
+
+
+
+ 钱包 Wallet
+ 点数余额 · 充值 / 提现 · 收支明细
+
+
+
+
点数余额 Points balance
+
0
+
1 USD = 1000 点 (1 USD = 1,000 points)
+
+
+
+
+
+
+
+
+
+
+
+
+ 交易记录 Transactions
+ 消费记录(用谁的 key)与收益记录(谁用了我的 key)
+
+
+
+
+
+
+
+
+
+
+
+
+ 设置 Settings
+ 账户 · API Key · 通知 · 偏好(原型展示)
+
+
+
+
+
API Key API keys
+
用于本地工具 / 代码接入平台的密钥。
+
+
+
+
+
+
偏好 Preferences
+
+
+
+
+
+
+
+
+
+
+
+ 管理视图 Admin
+ 管理员角色专属:Key 池 · 成员/员工点数 · 用量报表 · 组织设置(普通用户无此视图)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/js/app.js b/ui/js/app.js
new file mode 100644
index 0000000..fdbe9f7
--- /dev/null
+++ b/ui/js/app.js
@@ -0,0 +1,641 @@
+/* ============================================================
+ AITokenPool UI Prototype — 交互逻辑(纯原生 JS)
+ 无框架 / 无构建 / 无外部依赖
+ ============================================================ */
+
+(function () {
+ "use strict";
+
+ const D = window.ATDATA;
+ const $ = (sel) => document.querySelector(sel);
+ const $$ = (sel) => Array.from(document.querySelectorAll(sel));
+ const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
+
+ let activeView = "dashboard";
+ let txTab = "all";
+
+ // MRT 风格表格状态(页面级变量:切换页面不丢失排序/筛选/分页)
+ const walletTable = { sort: [], filters: {}, page: 1, pageSize: 10 };
+ const txTable = { sort: [], filters: {}, page: 1, pageSize: 10 };
+
+ /* ---------------- 工具 ---------------- */
+
+ function toast(msg) {
+ const el = $("#toast");
+ el.textContent = msg;
+ el.classList.remove("hidden");
+ clearTimeout(toast._t);
+ toast._t = setTimeout(() => el.classList.add("hidden"), 2400);
+ }
+
+ function badge(status, labels) {
+ return '' +
+ esc(labels[status] ? labels[status].text : status) + "";
+ }
+
+ // 自动单价:单价是模型×厂商的客观属性,由平台按模型定价自动计算(输出 1M tokens 折算点数)
+ function autoPrice(model) {
+ const m = D.MODELS.find((x) => x.model === model);
+ if (m && typeof m.out === "number") return m.out;
+ // 兜底:取同厂商相近模型的输出价;仍无则用固定默认价
+ const same = m ? D.MODELS.find((x) => x.provider === m.provider && typeof x.out === "number") : null;
+ return same ? same.out : 300;
+ }
+
+ // key 脱敏展示:仅显示前 3 后 4(如 sk-****1234)
+ function maskKey(key) {
+ if (!key) return "—";
+ if (key.length <= 8) return key.slice(0, 3) + "****" + key.slice(-4);
+ return key.slice(0, 3) + "****" + key.slice(-4);
+ }
+
+ function showPriceHint(model) {
+ const el = $("#sf-price-view");
+ if (!el) return;
+ const m = D.MODELS.find((x) => x.model === model);
+ if (!model) { el.textContent = "选择模型后自动计算"; return; }
+ if (m && typeof m.out === "number") {
+ el.textContent = D.fmt(m.out) + " 点 / 1M 输出(自动)";
+ } else {
+ el.textContent = "按默认价:" + D.fmt(autoPrice(model)) + " 点 / 1M 输出(自动)";
+ }
+ }
+
+ /* ---------------- 导航 ---------------- */
+
+ const NAV = [
+ { g: "主导航", items: [
+ { id: "dashboard", ico: "📊", label: "仪表盘 Dashboard" },
+ { id: "marketplace", ico: "🛒", label: "模型市场 Marketplace" },
+ { id: "sharing", ico: "🔗", label: "共享管理 Sharing" },
+ { id: "wallet", ico: "👛", label: "钱包 Wallet" },
+ { id: "transactions", ico: "🧾", label: "交易记录 Transactions" },
+ ]},
+ { g: "角色视图", items: [
+ { id: "admin", ico: "🛠️", label: "管理视图 Admin", role: "admin" },
+ { id: "settings", ico: "⚙️", label: "设置 Settings" },
+ ]},
+ ];
+
+ const VIEW_TITLE = {
+ dashboard: "仪表盘 Dashboard", marketplace: "模型市场 Marketplace", sharing: "共享管理 Sharing",
+ wallet: "钱包 Wallet", transactions: "交易记录 Transactions", settings: "设置 Settings",
+ admin: "管理视图 Admin",
+ };
+
+ function renderNav() {
+ const nav = $("#nav");
+ nav.innerHTML = "";
+ NAV.forEach((group) => {
+ const g = document.createElement("div");
+ g.className = "nav-group";
+ g.textContent = group.g;
+ nav.appendChild(g);
+ group.items.forEach((item) => {
+ const b = document.createElement("button");
+ b.className = "nav-item" + (item.id === activeView ? " active" : "");
+ b.dataset.view = item.id;
+ b.innerHTML = '' + item.ico + '' + esc(item.label) + "";
+ if (item.role) {
+ const tag = document.createElement("span");
+ tag.className = "nav-tag";
+ tag.textContent = "管理员";
+ b.appendChild(tag);
+ }
+ b.addEventListener("click", () => switchView(item.id));
+ nav.appendChild(b);
+ });
+ });
+ $("#mode-label").textContent = "共享市场 · 角色视图";
+ }
+
+ function switchView(id) {
+ activeView = id;
+ $$(".view").forEach((v) => v.classList.add("hidden"));
+ $("#view-" + id).classList.remove("hidden");
+ renderNav();
+ renderView(id);
+ $("#main").scrollTop = 0;
+ }
+
+ /* ---------------- 视图渲染 ---------------- */
+
+ function renderView(id) {
+ if (id === "dashboard") renderDashboard();
+ else if (id === "marketplace") renderMarketplace();
+ else if (id === "sharing") renderSharing();
+ else if (id === "wallet") renderWallet();
+ else if (id === "transactions") renderTransactions();
+ else if (id === "settings") renderSettings();
+ else if (id === "admin") renderAdmin();
+ }
+
+ /* --- 仪表盘 --- */
+
+ function renderDashboard() {
+ const txs = D.TRANSACTIONS;
+ const monthUse = txs.filter((t) => t.type === "consume").reduce((a, t) => a + Math.abs(t.pts), 0);
+ const monthEarn = txs.filter((t) => t.type === "earn").reduce((a, t) => a + t.pts, 0);
+ const recent = txs.slice(0, 5);
+
+ $("#dash-stats").innerHTML = [
+ stat("点数余额 Points", D.fmt(D.USER.balance), "1 USD = 1000 点", "accent"),
+ stat("本月用量 Usage", D.fmt(monthUse) + " 点", "共 " + txs.filter((t) => t.type === "consume").length + " 笔消费"),
+ stat("共享收益 Earnings", "+" + D.fmt(monthEarn) + " 点", D.SHARINGS.filter((s) => s.status === "on").length + " 个 key 上架中"),
+ stat("交易笔数 Trades", txs.length + " 笔", "含充值 / 提现 / 消费 / 收益"),
+ ].join("");
+
+ $("#dash-recent").innerHTML = recent.map((t) =>
+ '' + esc(t.detail) + "
" +
+ '
' + esc(t.time) + " · " + esc(t.partner) + "
" +
+ '
' + (t.pts > 0 ? "+" : "") + D.fmt(t.pts) + "
"
+ ).join("") + (recent.length ? "" : '暂无交易
');
+
+ const on = D.SHARINGS.filter((s) => s.status === "on");
+ $("#dash-sharings").innerHTML = on.map((s) =>
+ '' + esc(s.model) + "
" +
+ '
已用 ' + D.fmt(s.used) + " / " + D.fmt(s.quota) + " 点 · 单价 " + D.fmt(s.price) + " 点/1M
" +
+ '
+' + D.fmt(s.earned) + "累计收益
"
+ ).join("") + (on.length ? "" : '还没有上架的 key
');
+ }
+
+ function stat(label, value, sub, cls) {
+ return '' + esc(label) +
+ '
' + value + "
" + esc(sub) + "
";
+ }
+
+ /* --- 模型市场 --- */
+
+ function renderMarketplace() {
+ const q = ($("#mk-search").value || "").toLowerCase();
+ const prov = $("#mk-provider").value;
+ const sort = $("#mk-sort").value;
+
+ let list = D.MARKET.filter((m) =>
+ (!q || m.model.toLowerCase().includes(q) || m.provider.toLowerCase().includes(q)) &&
+ (!prov || m.provider === prov)
+ );
+ if (sort === "price-asc") list = [...list].sort((a, b) => a.in - b.in);
+ else if (sort === "price-desc") list = [...list].sort((a, b) => b.in - a.in);
+ else if (sort === "ctx-desc") list = [...list].sort((a, b) => b.ctx - a.ctx);
+
+ $("#mk-count").textContent = list.length + " 个在售 key";
+ $("#mk-body").innerHTML = list.length ? list.map((m) =>
+ "| " + esc(m.provider) + " | " + esc(m.model) + " | " +
+ "" + D.fmt(m.in) + " 点 | " + D.fmt(m.out) + " 点 | " +
+ "" + D.ctxFmt(m.ctx) + " | " +
+ "" + (m.avail ? '可用' : '繁忙') + " | " +
+ "成功率 " + m.success + "% |
"
+ ).join("") : '| 没有匹配的模型 |
';
+ }
+
+ /* --- 共享管理 --- */
+
+ const SHARE_STATUS = {
+ on: { text: "上架中", cls: "ok" },
+ paused: { text: "已暂停", cls: "warn" },
+ off: { text: "已下线", cls: "dim" },
+ };
+
+ function renderSharing() {
+ const on = D.SHARINGS.filter((s) => s.status === "on");
+ const totalEarned = D.SHARINGS.reduce((a, s) => a + s.earned, 0);
+ const totalUsed = D.SHARINGS.reduce((a, s) => a + s.used, 0);
+
+ $("#share-stats").innerHTML = [
+ stat("上架中 Listings", on.length + " 个", D.SHARINGS.length + " 个历史"),
+ stat("累计收益 Earnings", "+" + D.fmt(totalEarned) + " 点", "全部 key"),
+ stat("已用量 Used", D.fmt(totalUsed) + " 点", D.SHARINGS.reduce((a, s) => a + s.quota, 0) + " 点总额度"),
+ ].join("");
+
+ // 表单下拉
+ const selP = $("#sf-provider");
+ if (!selP.dataset.init) {
+ selP.innerHTML = '' + D.PROVIDERS.map((p) => '").join("");
+ const selM = $("#sf-model");
+ const fillModels = () => {
+ const p = selP.value;
+ selM.innerHTML = '' + D.MODELS.filter((m) => !p || m.provider === p)
+ .map((m) => '").join("");
+ showPriceHint(selM.value);
+ };
+ selP.addEventListener("change", fillModels);
+ selM.addEventListener("change", () => showPriceHint(selM.value));
+ selP.dataset.init = "1";
+ fillModels();
+ }
+
+ $("#share-body").innerHTML = D.SHARINGS.map((s, i) =>
+ "| " + esc(s.model) + " | " +
+ "" + esc(maskKey(s.key)) + " | " +
+ "" + D.fmt(s.used) + " / " + D.fmt(s.quota) + " | " +
+ "" + D.fmt(s.price) + " 点/1M | " +
+ "+" + D.fmt(s.earned) + " 点 | " +
+ "" + badge(s.status, SHARE_STATUS) + " | " +
+ " " +
+ " |
"
+ ).join("");
+ }
+
+ function deleteSharing(i) {
+ const s = D.SHARINGS[i];
+ if (!s) return;
+ if (!window.confirm("确认彻底下架 " + s.model + " 的 key?删除后不再共享,不可恢复。")) return;
+ D.SHARINGS.splice(i, 1);
+ renderSharing();
+ if (activeView === "dashboard") renderDashboard();
+ toast("已删除 " + s.model + " 的 key(彻底下架)");
+ }
+
+ function toggleSharing(i) {
+ const s = D.SHARINGS[i];
+ if (s.status === "on") { s.status = "paused"; toast("已暂停 " + s.model + " 的共享"); }
+ else if (s.status === "paused") { s.status = "on"; toast("已恢复 " + s.model + " 的共享"); }
+ else { s.status = "on"; s.quota = 50000; s.used = 0; toast("已重新上架 " + s.model); }
+ renderSharing();
+ if (activeView === "dashboard") renderDashboard();
+ }
+
+ /* --- 钱包 --- */
+
+ const WALLET_COLUMNS = [
+ { key: "time", title: "时间", sort: "string", filter: "text" },
+ { key: "type", title: "类型", sort: "string", filter: "select", options: ["消费", "收益", "充值", "提现"], filterVal: (t) => TX_TYPE[t.type] || t.type,
+ render: (t) => esc(TX_TYPE[t.type] || t.type) },
+ { key: "partner", title: "对象", sort: "string", filter: "text" },
+ { key: "detail", title: "说明", sort: "string", filter: "text" },
+ { key: "pts", title: "点数", sort: "number", filter: "number-range",
+ render: (t) => '' + (t.pts > 0 ? "+" : "") + D.fmt(t.pts) + "" },
+ ];
+
+ function renderWallet() {
+ $("#side-balance").textContent = D.fmt(D.USER.balance);
+ $("#wallet-balance").textContent = D.fmt(D.USER.balance);
+ buildDataTable({
+ container: $("#wallet-table"),
+ columns: WALLET_COLUMNS,
+ rows: D.TRANSACTIONS,
+ state: walletTable,
+ onState: renderWallet,
+ });
+ }
+
+ /* --- 交易记录 --- */
+
+ const TX_TYPE = {
+ consume: "消费", earn: "收益", topup: "充值", withdraw: "提现",
+ };
+
+ const TX_COLUMNS = [
+ { key: "time", title: "时间", sort: "string", filter: "text" },
+ { key: "type", title: "类型", sort: "string", filter: "select", options: ["消费", "收益", "充值", "提现"], filterVal: (t) => TX_TYPE[t.type] || t.type,
+ render: (t) => t.type === "earn" ? '收益' : t.type === "consume" ? '消费' : '' + esc(TX_TYPE[t.type] || t.type) + "" },
+ { key: "partner", title: "模型 / Key", sort: "string", filter: "text" },
+ { key: "tokens", title: "Token 用量", sort: "string", filter: "text" },
+ { key: "pts", title: "点数", sort: "number", filter: "number-range",
+ render: (t) => '' + (t.pts > 0 ? "+" : "") + D.fmt(t.pts) + "" },
+ { key: "status", title: "状态", sort: "string", filter: "select", options: ["成功", "入账", "处理中"],
+ render: (t) => t.status === "处理中" ? '' + esc(t.status) + "" : esc(t.status) },
+ ];
+
+ function renderTransactions() {
+ $$("#tx-tabs .tab").forEach((b) => b.classList.toggle("active", b.dataset.txTab === txTab));
+ let list = D.TRANSACTIONS;
+ if (txTab === "consume") list = list.filter((t) => t.type === "consume");
+ else if (txTab === "earn") list = list.filter((t) => t.type === "earn");
+ buildDataTable({
+ container: $("#tx-table"),
+ columns: TX_COLUMNS,
+ rows: list,
+ state: txTable,
+ onState: renderTransactions,
+ });
+ }
+
+ /* --- 通用 MRT 风格数据表格渲染器 ---
+ cfg: { container, columns, rows, state, onState }
+ columns: [{ key, title, sort?: "string"|"number", filter?: "text"|"select"|"number-range", options?, render? }]
+ state: { sort: [{key,dir}], filters: {key:val}, page, pageSize }(原地更新,跨页保留) */
+ function buildDataTable(cfg) {
+ const { container, columns, rows, state, onState } = cfg;
+
+ // 1) 筛选
+ let data = rows.filter((row) => {
+ for (const key of Object.keys(state.filters)) {
+ const fv = state.filters[key];
+ if (fv == null || fv === "") continue;
+ const col = columns.find((c) => c.key === key);
+ if (!col || !col.filter) continue;
+ const v = col.filterVal ? col.filterVal(row) : row[key];
+ if (col.filter === "select") { if (String(v) !== String(fv)) return false; }
+ else if (col.filter === "number-range") {
+ const parts = String(fv).split(":");
+ const min = parts[0] === "" ? NaN : Number(parts[0]);
+ const max = parts[1] === "" || parts[1] == null ? NaN : Number(parts[1]);
+ if (!isNaN(min) && Number(v) < min) return false;
+ if (!isNaN(max) && Number(v) > max) return false;
+ } else {
+ if (!String(v).toLowerCase().includes(String(fv).toLowerCase())) return false;
+ }
+ }
+ return true;
+ });
+
+ // 2) 排序(多列:Shift 点击叠加)
+ if (state.sort.length) {
+ data = data.slice().sort((a, b) => {
+ for (const sk of state.sort) {
+ const col = columns.find((c) => c.key === sk.key);
+ const av = a[sk.key], bv = b[sk.key];
+ let cmp;
+ if (col && col.sort === "number") cmp = Number(av) - Number(bv);
+ else cmp = String(av).localeCompare(String(bv), "zh-CN");
+ if (cmp !== 0) return sk.dir === "asc" ? cmp : -cmp;
+ }
+ return 0;
+ });
+ }
+
+ // 3) 分页
+ const pages = Math.max(1, Math.ceil(data.length / state.pageSize));
+ if (state.page > pages) state.page = pages;
+ const pageRows = data.slice((state.page - 1) * state.pageSize, state.page * state.pageSize);
+
+ // 4) 渲染表头(排序按钮)+ 筛选行
+ let html = '";
+
+ // 5) 分页器 + 每页行数
+ if (pages > 1) {
+ html += '";
+ }
+ html += '';
+
+ container.innerHTML = html;
+
+ // 6) 事件绑定
+ container.querySelectorAll("[data-sort-key]").forEach((b) => {
+ b.addEventListener("click", (e) => {
+ const key = b.dataset.sortKey;
+ const ex = state.sort.find((s) => s.key === key);
+ if (ex) {
+ if (ex.dir === "asc") ex.dir = "desc";
+ else state.sort = state.sort.filter((s) => s.key !== key);
+ } else {
+ if (!e.shiftKey) state.sort = [];
+ state.sort.push({ key, dir: "asc" });
+ }
+ state.page = 1;
+ onState();
+ });
+ });
+ container.querySelectorAll(".th-filter").forEach((el) => {
+ el.addEventListener("input", () => {
+ const key = el.dataset.filterKey;
+ const range = el.dataset.range;
+ if (range) {
+ const other = container.querySelector('[data-filter-key="' + key + '"][data-range="' + (range === "min" ? "max" : "min") + '"]');
+ const min = range === "min" ? el.value : (other ? other.value : "");
+ const max = range === "max" ? el.value : (other ? other.value : "");
+ state.filters[key] = min + ":" + max;
+ } else {
+ state.filters[key] = el.value;
+ }
+ state.page = 1;
+ const focusSel = range ? '[data-filter-key="' + key + '"][data-range="' + range + '"]' : '[data-filter-key="' + key + '"]';
+ onState();
+ const n = container.querySelector(focusSel);
+ if (n) n.focus();
+ });
+ });
+ container.querySelectorAll("[data-p]").forEach((b) => {
+ b.addEventListener("click", () => { state.page = Number(b.dataset.p); onState(); });
+ });
+ const ps = container.querySelector("[data-page-size]");
+ if (ps) {
+ ps.value = state.pageSize;
+ ps.addEventListener("change", () => { state.pageSize = Number(ps.value); state.page = 1; onState(); });
+ }
+ }
+
+ /* --- 设置 --- */
+
+ function renderSettings() {
+ $("#api-keys").innerHTML = D.API_KEYS.map((k) =>
+ '' + esc(k.name) + "
" +
+ '
' + esc(k.id) + " · 创建于 " + esc(k.created) + "
" +
+ '
最近使用 ' + esc(k.last) + "
"
+ ).join("");
+ }
+
+ /* --- 管理员角色视图 --- */
+
+ const KEY_STATUS = {
+ ok: { text: "可用", cls: "ok" },
+ limit: { text: "达限额", cls: "warn" },
+ exhausted: { text: "已用尽", cls: "danger" },
+ revoked: { text: "已撤销", cls: "dim" },
+ };
+
+ function renderAdmin() {
+ const tab = $("#admin-tabs .tab.active").dataset.adminTab;
+ $$(".admin-pane").forEach((p) => p.classList.toggle("hidden", p.dataset.adminPane !== tab));
+
+ if (tab === "keys") {
+ const q = ($("#ak-search").value || "").toLowerCase();
+ const rows = D.KEYS.filter((k) => !q || k.model.includes(q) || k.provider.includes(q) || k.key.includes(q));
+ $("#keys-body").innerHTML = rows.map((k) =>
+ "| " + esc(k.provider) + " | " + esc(k.model) + " | " +
+ "" + esc(k.key) + " | " +
+ "" + D.fmt(k.used) + " / " + D.fmt(k.quota) + " | " +
+ "" + badge(k.status, KEY_STATUS) + " | " +
+ " |
"
+ ).join("");
+ } else if (tab === "employees") {
+ const total = D.EMPLOYEES.reduce((a, e) => a + e.quota, 0);
+ const used = D.EMPLOYEES.reduce((a, e) => a + e.used, 0);
+ $("#emp-stats").innerHTML = [
+ stat("成员数 Members", D.EMPLOYEES.length + " 人", "2 个部门"),
+ stat("总配额 Quota", D.fmt(total) + " 点", "月"),
+ stat("已用 Usage", D.fmt(used) + " 点", Math.round((used / total) * 100) + "% 消耗率"),
+ stat("剩余 Remain", D.fmt(total - used) + " 点", "按成员分配"),
+ ].join("");
+ $("#emp-body").innerHTML = D.EMPLOYEES.map((e, i) =>
+ "| " + esc(e.name) + " | " + esc(e.dept) + " | " +
+ "" + D.fmt(e.used) + " / " + D.fmt(e.quota) + " | " +
+ "" + D.fmt(e.quota - e.used) + " | " +
+ "" + (e.used / e.quota > 0.9 ? '接近限额' : '正常') + " | " +
+ " |
"
+ ).join("");
+ } else if (tab === "usage") {
+ const maxM = Math.max(...D.USAGE_MODEL.map((u) => u.pts));
+ const maxE = Math.max(...D.USAGE_EMP.map((u) => u.pts));
+ $("#usage-model").innerHTML = D.USAGE_MODEL.map((u) => barRow(u.name, u.pts, maxM, "点")).join("");
+ $("#usage-emp").innerHTML = D.USAGE_EMP.map((u) => barRow(u.name, u.pts, maxE, "点")).join("");
+ }
+ }
+
+ function barRow(name, pts, max, unit) {
+ const pct = Math.round((pts / max) * 100);
+ return '' + esc(name) + '' + D.fmt(pts) + " " + unit + "
" +
+ '
';
+ }
+
+ /* ---------------- 事件 ---------------- */
+
+ function bindEvents() {
+ // 登录(单一入口,角色由账号决定)
+ $("#login-form").addEventListener("submit", (e) => {
+ e.preventDefault();
+ $("#login-view").classList.add("hidden");
+ $("#app").classList.remove("hidden");
+ switchView("dashboard");
+ toast("欢迎回来,阿零(演示账号)");
+ });
+
+ $("#logout-btn").addEventListener("click", () => {
+ $("#app").classList.add("hidden");
+ $("#login-view").classList.remove("hidden");
+ toast("已退出(静态演示)");
+ });
+
+ // 市场筛选
+ $("#mk-search").addEventListener("input", renderMarketplace);
+ $("#mk-provider").addEventListener("change", renderMarketplace);
+ $("#mk-sort").addEventListener("change", renderMarketplace);
+ if (!$("#mk-provider").dataset.init) {
+ $("#mk-provider").innerHTML = '' + D.PROVIDERS.map((p) => '").join("");
+ $("#mk-provider").dataset.init = "1";
+ }
+
+ // 共享上架表单(默认收起;点添加展开,提交成功或取消后收起)
+ const shareFormCard = () => $("#share-form-card");
+ const showShareForm = () => { shareFormCard().hidden = false; $("#sf-key").focus(); };
+ const hideShareForm = () => { shareFormCard().hidden = true; };
+
+ $("#share-add-btn").addEventListener("click", showShareForm);
+ $("#sf-cancel").addEventListener("click", hideShareForm);
+
+ // 共享上架表单(须填 API Key;单价由平台按模型定价自动计算)
+ $("#share-form").addEventListener("submit", (e) => {
+ e.preventDefault();
+ const model = $("#sf-model").value;
+ const quota = Number($("#sf-quota").value || 0);
+ const key = $("#sf-key").value.trim();
+ if (!key) { toast("请填写 API Key(上架 key 必须提供真实密钥)"); return; }
+ if (!model || quota <= 0) { toast("请选择模型并填写有效额度"); return; }
+ const price = autoPrice(model);
+ D.SHARINGS.unshift({ id: Date.now(), model, quota, used: 0, price, earned: 0, status: "on", key });
+ renderSharing();
+ if (activeView === "dashboard") renderDashboard();
+ toast("已上架 " + model + "(key 已加密托管,单价 " + D.fmt(price) + " 点/1M 自动)");
+ e.target.reset();
+ const p = $("#sf-provider"); p.value = ""; p.dispatchEvent(new Event("change"));
+ $("#sf-quota").value = 5000;
+ hideShareForm();
+ });
+
+ // 共享列表操作(事件委托:暂停/恢复/重新上架 + 删除)
+ $("#share-body").addEventListener("click", (e) => {
+ const b = e.target.closest("[data-share-toggle]");
+ if (b) { toggleSharing(Number(b.dataset.shareToggle)); return; }
+ const d = e.target.closest("[data-share-delete]");
+ if (d) deleteSharing(Number(d.dataset.shareDelete));
+ });
+
+ // 钱包按钮
+ $("#topup-btn").addEventListener("click", () => toast("充值入口为占位(静态原型)"));
+ $("#withdraw-btn").addEventListener("click", () => toast("提现入口为占位(静态原型)"));
+
+ // 交易 Tab
+ $$("#tx-tabs .tab").forEach((b) => b.addEventListener("click", () => { txTab = b.dataset.txTab; txTable.page = 1; renderTransactions(); }));
+
+ // API Key 生成
+ $("#new-api-key-btn").addEventListener("click", () => {
+ const id = "atk_live_" + Math.random().toString(16).slice(2, 10) + "…" + Math.random().toString(16).slice(2, 6);
+ D.API_KEYS.unshift({ id, name: "新 Key(未命名)", created: "2026-08-13", last: "从未" });
+ renderSettings();
+ toast("已生成新 API Key(演示)");
+ });
+
+ // 管理台 Tabs
+ $$("#admin-tabs .tab").forEach((b) => b.addEventListener("click", () => {
+ $$("#admin-tabs .tab").forEach((x) => x.classList.remove("active"));
+ b.classList.add("active");
+ renderAdmin();
+ }));
+
+ $("#add-key-btn").addEventListener("click", () => {
+ const m = D.MODELS[Math.floor(Math.random() * D.MODELS.length)];
+ D.KEYS.unshift({ id: Date.now(), provider: m.provider, model: m.model, key: "sk-****-new1", quota: 100000, used: 0, status: "ok" });
+ renderAdmin();
+ toast("已添加上游 Key(演示:" + m.model + ")");
+ });
+
+ $("#ak-search").addEventListener("input", renderAdmin);
+
+ // 管理台事件委托(撤销 key / 员工加额)
+ $("#keys-body").addEventListener("click", (e) => {
+ const b = e.target.closest("[data-key-revoke]");
+ if (!b) return;
+ const k = D.KEYS.find((x) => x.id === Number(b.dataset.keyRevoke));
+ if (!k) return;
+ k.status = k.status === "revoked" ? "ok" : "revoked";
+ renderAdmin();
+ toast(k.status === "revoked" ? "已撤销 " + k.model + " 的 key" : "已恢复 " + k.model + " 的 key");
+ });
+
+ $("#emp-body").addEventListener("click", (e) => {
+ const b = e.target.closest("[data-emp-add]");
+ if (!b) return;
+ D.EMPLOYEES[Number(b.dataset.empAdd)].quota += 5000;
+ renderAdmin();
+ toast("已为成员 +5000 点配额");
+ });
+ }
+
+ /* ---------------- 初始化 ---------------- */
+
+ document.addEventListener("DOMContentLoaded", () => {
+ renderNav();
+ bindEvents();
+ renderView("dashboard");
+ $("#side-balance").textContent = D.fmt(D.USER.balance);
+ });
+})();
diff --git a/ui/js/data.js b/ui/js/data.js
new file mode 100644
index 0000000..fe9f03d
--- /dev/null
+++ b/ui/js/data.js
@@ -0,0 +1,124 @@
+/* ============================================================
+ AITokenPool UI Prototype — 内嵌 mock 数据
+ 点数规则:1 USD = 1000 点;CNY 按 ~7.2 CNY/USD 折算(示例)
+ ============================================================ */
+
+(function () {
+ "use strict";
+
+ const USD = (usd) => Math.round(usd * 1000);
+ const CNY = (cny) => Math.round((cny / 7.2) * 1000);
+
+ // 模型价格(来自 data/models.example.json,折算为点数 / 1M tokens)
+ const MODELS = [
+ { provider: "deepseek", model: "deepseek-v4-pro", in: USD(0.435), out: USD(0.87), ctx: 1048576, max: 384000, tag: "推理" },
+ { provider: "deepseek", model: "deepseek-v4-flash", in: USD(0.14), out: USD(0.28), ctx: 1048576, max: 384000, tag: "通用" },
+ { provider: "zhipu", model: "glm-5.2", in: CNY(8.0), out: CNY(28.0), ctx: 1048576, max: null, tag: "通用" },
+ { provider: "openai", model: "gpt-5.5-pro", in: USD(30.0), out: USD(180.0), ctx: 1050000, max: null, tag: "旗舰" },
+ { provider: "anthropic",model: "claude-opus-4.7", in: USD(5.0), out: USD(25.0), ctx: 1000000, max: null, tag: "旗舰" },
+ { provider: "google", model: "gemini-3.1-pro", in: USD(2.0), out: USD(12.0), ctx: 1048576, max: null, tag: "多模态" },
+ { provider: "xai", model: "grok-4.6", in: USD(2.0), out: USD(6.0), ctx: 500000, max: null, tag: "通用" },
+ { provider: "moonshot", model: "kimi-k3", in: CNY(20.0), out: CNY(100.0), ctx: 1048576, max: null, tag: "长文" },
+ { provider: "bytedance-ark", model: "doubao-seed-2.1-pro", in: CNY(6.0), out: CNY(30.0), ctx: 262144, max: null, tag: "通用" },
+ { provider: "minimax", model: "minimax-m3", in: USD(0.3), out: USD(1.2), ctx: 1048576, max: null, tag: "轻量" },
+ ];
+
+ // 可用性(市场展示用)
+ MODELS.forEach((m) => {
+ m.availability = Math.random() < 0.85 ? "available" : "busy";
+ });
+ MODELS[2].availability = "available"; // glm
+ MODELS[4].availability = "busy"; // claude 演示"繁忙"
+
+ const fmt = (n) => n.toLocaleString("zh-CN");
+ const ctxFmt = (n) => (n >= 1000000 ? (n / 1000000).toFixed(1) + "M" : fmt(n));
+
+ window.ATDATA = {
+ fmt, ctxFmt,
+
+ MODELS,
+
+ PROVIDERS: [...new Set(MODELS.map((m) => m.provider))],
+
+ // 当前用户(公共版)
+ USER: { name: "阿零", email: "demo@aitokenpool.local", balance: 12480 },
+
+ // 最近交易 / 收支明细(wallet 与 transactions 共用)
+ TRANSACTIONS: [
+ { id: 1, time: "08-13 20:15", type: "consume", partner: "deepseek-v4-flash", detail: "消费 · 用 shared key", tokens: "1.2M", pts: -320, status: "成功" },
+ { id: 2, time: "08-13 19:02", type: "earn", partner: "glm-5.2", detail: "收益 · 我的 key 被消费", tokens: "860K", pts: +420, status: "入账" },
+ { id: 3, time: "08-13 17:44", type: "consume", partner: "gpt-5.5-pro", detail: "消费 · 用 shared key", tokens: "45K", pts: -860, status: "成功" },
+ { id: 4, time: "08-13 15:10", type: "earn", partner: "kimi-k3", detail: "收益 · 我的 key 被消费", tokens: "1.5M", pts: +980, status: "入账" },
+ { id: 5, time: "08-13 11:36", type: "topup", partner: "—", detail: "充值", tokens: "—", pts: +5000, status: "成功" },
+ { id: 6, time: "08-12 21:08", type: "consume", partner: "gemini-3.1-pro", detail: "消费 · 用 shared key", tokens: "2.1M", pts: -310, status: "成功" },
+ { id: 7, time: "08-12 14:22", type: "earn", partner: "deepseek-v4-flash", detail: "收益 · 我的 key 被消费", tokens: "3.0M", pts: +210, status: "入账" },
+ { id: 8, time: "08-12 09:41", type: "consume", partner: "claude-opus-4.7", detail: "消费 · 用 shared key", tokens: "22K", pts: -640, status: "成功" },
+ { id: 9, time: "08-11 18:30", type: "withdraw", partner: "—", detail: "提现(占位)", tokens: "—", pts: -2000, status: "处理中" },
+ { id: 10, time: "08-11 10:12", type: "earn", partner: "glm-5.2", detail: "收益 · 我的 key 被消费", tokens: "700K", pts: +390, status: "入账" },
+ { id: 11, time: "08-10 16:05", type: "consume", partner: "doubao-seed-2.1-pro", detail: "消费 · 用 shared key", tokens: "540K", pts: -260, status: "成功" },
+ { id: 12, time: "08-10 08:50", type: "earn", partner: "minimax-m3", detail: "收益 · 我的 key 被消费", tokens: "2.4M", pts: +180, status: "入账" },
+ ],
+
+ // 我的共享(price = 按模型定价自动计算的输出单价 点数/1M;key 仅脱敏展示)
+ SHARINGS: [
+ { id: 1, model: "glm-5.2", quota: 100000, used: 32800, price: 3889, earned: 1790, status: "on", key: "sk-zhipu-9f2c41ab7d22" },
+ { id: 2, model: "deepseek-v4-flash", quota: 80000, used: 51200, price: 280, earned: 1240, status: "on", key: "sk-ds-3b88d077aa91" },
+ { id: 3, model: "kimi-k3", quota: 50000, used: 800, price: 13889, earned: 60, status: "paused", key: "sk-ms-c31f2e8b4405" },
+ { id: 4, model: "minimax-m3", quota: 20000, used: 20000, price: 1200, earned: 480, status: "off", key: "sk-mx-77d2a1c9e356" },
+ ],
+
+ // 市场在售 key(公共版浏览)
+ MARKET: [
+ { id: 1, provider: "deepseek", model: "deepseek-v4-flash", in: USD(0.14), out: USD(0.28), ctx: 1048576, avail: true, success: 99.2 },
+ { id: 2, provider: "zhipu", model: "glm-5.2", in: CNY(8.0), out: CNY(28.0), ctx: 1048576, avail: true, success: 98.6 },
+ { id: 3, provider: "openai", model: "gpt-5.5-pro", in: USD(30.0), out: USD(180.0), ctx: 1050000, avail: true, success: 97.1 },
+ { id: 4, provider: "anthropic",model: "claude-opus-4.7", in: USD(5.0), out: USD(25.0), ctx: 1000000, avail: false, success: 95.4 },
+ { id: 5, provider: "google", model: "gemini-3.1-pro", in: USD(2.0), out: USD(12.0), ctx: 1048576, avail: true, success: 98.9 },
+ { id: 6, provider: "moonshot", model: "kimi-k3", in: CNY(20.0), out: CNY(100.0), ctx: 1048576, avail: true, success: 96.8 },
+ { id: 7, provider: "xai", model: "grok-4.6", in: USD(2.0), out: USD(6.0), ctx: 500000, avail: true, success: 93.7 },
+ ],
+
+ // 我的 API Keys(设置页)
+ API_KEYS: [
+ { id: "atk_live_9f2c1a…e41b", name: "本地 CLI 工具", created: "2026-08-01", last: "08-13 20:11" },
+ { id: "atk_live_3b88d0…77aa", name: "CI / 自动化", created: "2026-07-22", last: "08-12 15:40" },
+ ],
+
+ // —— 企业版 ——
+
+ // Key 池
+ KEYS: [
+ { id: 1, provider: "openai", model: "gpt-5.5-pro", key: "sk-****-8f2a", quota: 300000, used: 214000, status: "ok" },
+ { id: 2, provider: "anthropic",model: "claude-opus-4.7", key: "sk-****-1c77", quota: 200000, used: 62000, status: "ok" },
+ { id: 3, provider: "deepseek", model: "deepseek-v4-flash", key: "sk-****-90bb", quota: 500000, used: 461000, status: "limit" },
+ { id: 4, provider: "zhipu", model: "glm-5.2", key: "sk-****-44e9", quota: 150000, used: 150000, status: "exhausted" },
+ { id: 5, provider: "google", model: "gemini-3.1-pro", key: "sk-****-77d2", quota: 180000, used: 15000, status: "ok" },
+ { id: 6, provider: "moonshot", model: "kimi-k3", key: "sk-****-ab31", quota: 100000, used: 0, status: "revoked" },
+ ],
+
+ // 员工
+ EMPLOYEES: [
+ { id: 1, name: "陈默", dept: "研发", quota: 20000, used: 12400 },
+ { id: 2, name: "林小满", dept: "研发", quota: 20000, used: 19850 },
+ { id: 3, name: "苏航", dept: "产品", quota: 15000, used: 3200 },
+ { id: 4, name: "周雨", dept: "市场", quota: 10000, used: 9800 },
+ { id: 5, name: "何明", dept: "设计", quota: 8000, used: 2100 },
+ ],
+
+ // 用量报表
+ USAGE_MODEL: [
+ { name: "gpt-5.5-pro", pts: 18600 },
+ { name: "deepseek-v4-flash", pts: 13200 },
+ { name: "glm-5.2", pts: 9800 },
+ { name: "claude-opus-4.7", pts: 7400 },
+ { name: "gemini-3.1-pro", pts: 3100 },
+ ],
+ USAGE_EMP: [
+ { name: "林小满", pts: 19850 },
+ { name: "陈默", pts: 12400 },
+ { name: "周雨", pts: 9800 },
+ { name: "苏航", pts: 3200 },
+ { name: "何明", pts: 2100 },
+ ],
+ };
+})();