Files
cpa-plugin/internal/web/ui.html
T
2026-08-19 16:44:23 +08:00

112 lines
12 KiB
HTML
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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Billing 管理台</title>
<link rel="stylesheet" href="./styles/base.css">
<link rel="stylesheet" href="./styles/layout.css">
<link rel="stylesheet" href="./styles/keys.css">
<link rel="stylesheet" href="./styles/usage.css">
<link rel="stylesheet" href="./styles/pricing.css">
<link rel="stylesheet" href="./styles/responsive.css">
</head>
<body class="read-only">
<div class="app-shell">
<header class="topbar">
<div class="brand"><div class="brand-mark">B</div><div><strong>Billing</strong><span> · 管理控制台</span></div></div>
<div class="auth-box"><input id="key" type="password" autocomplete="off" aria-label="管理密钥"></div>
</header>
<nav class="workspace-nav" aria-label="工作区">
<button class="nav-button active" type="button" data-view="keys">用户 Key</button>
<button class="nav-button" type="button" data-view="usage">请求明细</button>
<button class="nav-button" type="button" data-view="pricing">价格配置</button>
</nav>
<main>
<section id="view-keys" class="view active">
<div class="summary-grid">
<article class="summary-card"><span>今日请求</span><strong id="metric-requests"></strong><small>自然日内实际请求</small></article>
<article class="summary-card"><span>今日 Token</span><strong id="metric-tokens"></strong><small>输入与输出合计</small></article>
<article class="summary-card"><span>今日成本</span><strong id="metric-cost"></strong><small>按已配置价格计算</small></article>
</div>
<div class="chart-grid"><article class="chart-card"><header><div><h2>今日用户用量</h2></div></header><div id="user-usage" class="user-usage"></div></article><article class="chart-card"><header><div><h2>近 7 日 Token</h2></div></header><div id="daily-chart" class="bar-chart"></div></article></div>
<div id="key-management" class="surface admin-only">
<div class="surface-toolbar"><span id="key-status" class="status"></span><button id="create-key" class="primary" type="button">创建 Key</button></div>
<div class="table-wrap"><table class="key-table"><thead><tr><th class="left">用户</th><th class="left">Key</th><th class="left">状态</th><th class="left">余额 / 额度</th><th class="left">并发</th><th class="left">路由</th><th class="left">允许模型</th><th class="left">查看</th></tr></thead><tbody id="key-rows"></tbody></table></div>
</div>
</section>
<section id="view-usage" class="view">
<div class="section-heading"><div><div id="page-buttons" class="page-buttons"></div><span id="status" class="status"></span></div><div class="tools">
<details id="usage-filter-panel" class="usage-filter-panel"><summary>查询过滤</summary><div class="surface usage-filters">
<label class="usage-filter">开始时间<input id="usage-from" type="datetime-local" step="1"></label>
<label class="usage-filter">结束时间<input id="usage-to" type="datetime-local" step="1"></label>
<label class="usage-filter">用户<select id="usage-key"><option value="">全部用户</option></select></label>
<label class="usage-filter">模型<input id="usage-model" list="usage-model-options" placeholder="全部模型"><datalist id="usage-model-options"></datalist></label>
<label class="usage-filter">结果<select id="usage-result"><option value="">全部结果</option><option value="succeeded">成功</option><option value="failed">失败</option><option value="rejected">已拒绝</option><option value="canceled">已取消</option></select></label>
<label class="usage-filter">上游<select id="usage-auth"><option value="">全部上游</option></select></label>
<label class="usage-filter">端点<select id="usage-endpoint"><option value="">全部端点</option><option value="responses">responses</option><option value="compact">compact</option><option value="chat">chat</option></select></label>
<label class="usage-filter request-filter">请求 ID<input id="usage-request-id" placeholder="粘贴完整 Request ID"></label>
<div class="usage-filter-actions"><button id="clear-usage-filters" type="button">清空</button><button id="apply-usage-filters" class="primary" type="button">查询</button></div>
</div></details>
<details><summary></summary><div id="column-options" class="column-options"></div></details><button id="refresh" type="button">刷新</button>
</div></div>
<div class="surface"><div class="table-wrap"><table><thead><tr id="headers"></tr></thead><tbody id="rows"></tbody></table></div></div>
</section>
<section id="view-pricing" class="view">
<div class="section-heading"><div><h1>价格配置</h1></div><div id="price-status" class="status"></div></div>
<div class="surface price-panel price-layout">
<aside class="price-sidebar"><div class="price-sidebar-header"><h2>已配置模型</h2><button id="new-price" class="small admin-only" type="button">新增</button></div><div id="price-list" class="price-list"></div></aside>
<section class="price-editor">
<div id="price-editor-content" class="hidden">
<div class="price-editor-header"><h2>模型价格</h2></div>
<div class="price-section"><label class="field price-model-field">模型名称<input id="price-model" placeholder="gpt-5.6-sol"></label><div id="price-source" class="price-source">手动配置</div></div>
<div class="price-section admin-only"><div class="price-section-header"><h3>models.dev 参考价格</h3><button id="refresh-catalog" class="small" type="button">更新目录</button></div><div id="catalog-summary" class="catalog-summary"></div><div class="catalog-toolbar"><input id="catalog-query" placeholder="搜索模型或供应商"><button id="search-catalog" class="small" type="button">搜索</button></div><div id="catalog-results" class="catalog-results"></div></div>
<div class="price-section"><div class="price-section-header"><h3>基础价格 · $ / 1M Token</h3></div><div class="price-grid">
<label>输入<input id="price-input" inputmode="decimal"></label><label>缓存读取<input id="price-cache-read" inputmode="decimal"></label><label>缓存写入<input id="price-cache-write" inputmode="decimal"></label><label>输出<input id="price-output" inputmode="decimal"></label>
</div></div>
<div id="long-price-section" class="price-section hidden"><div class="price-section-header"><h3>长上下文价格</h3><label class="inline-check"><input id="long-enabled" type="checkbox">启用</label></div><div class="price-grid long-grid">
<label class="long-field hidden">输入门槛<input id="long-threshold" type="number" min="0" step="1"></label><label class="long-field hidden">门槛比较<select id="long-comparison"><option value="gt">大于</option><option value="gte">大于等于</option></select></label><label class="long-field hidden">输入<input id="long-input" inputmode="decimal"></label><label class="long-field hidden">缓存读取<input id="long-cache-read" inputmode="decimal"></label><label class="long-field hidden">缓存写入<input id="long-cache-write" inputmode="decimal"></label><label class="long-field hidden">输出<input id="long-output" inputmode="decimal"></label>
</div></div>
<div class="price-section"><div class="price-section-header"><h3>Fast 价格</h3></div><div class="fast-row"><label class="inline-check"><input id="fast-pricing-enabled" type="checkbox">使用倍率计价</label><label class="field">倍率<input id="fast-multiplier" inputmode="decimal" value="2.5"></label></div></div>
<div class="price-actions admin-only"><button id="delete-price" class="danger hidden" type="button">删除</button><button id="clear-price" type="button">清空</button><button id="save-price" class="primary" type="button">保存价格</button></div>
</div>
</section>
</div>
</section>
</main>
</div>
<div id="drawer-overlay" class="drawer-overlay hidden"></div>
<aside id="key-drawer" class="drawer hidden" aria-hidden="true">
<div class="drawer-header"><div><h2 id="drawer-title">管理 Key</h2><p id="drawer-subtitle">修改用户状态、路由和模型权限</p></div><button id="close-drawer" class="icon-button" type="button" aria-label="关闭">×</button></div>
<div class="drawer-body">
<div id="key-editor" class="drawer-form">
<div class="drawer-section"><h3>用户</h3><label class="field">名称<input id="editor-name" placeholder="例如 alice"></label>
<label class="field">完整 Key<input id="editor-secret" class="secret" minlength="6" maxlength="256" placeholder="留空自动生成"></label></div>
<div id="managed-fields" class="drawer-form">
<div class="drawer-section"><h3>访问与路由</h3>
<div class="field-row"><label class="field">状态<select id="editor-status"><option value="active">启用</option><option value="disabled">禁用</option><option value="archived">已归档</option></select></label><label class="field">路由<select id="editor-route"><option value="auto">自由选择</option><option value="strict">指定账号</option></select></label></div>
<label class="field">上游账号<select id="editor-upstream"></select></label>
<label class="inline-check"><input id="editor-all-models" type="checkbox">允许全部模型</label>
<label class="field">允许模型规则<input id="editor-models" placeholder="deepseek-*gpt-5.6-sol"></label><p class="field-hint">多个规则用逗号分隔;* 匹配任意字符,未匹配的模型会被拒绝。</p></div>
<div class="drawer-section"><h3>额度与周期</h3>
<div id="billing-preview" class="billing-preview"><div><span>额度</span><strong id="billing-quota-view">$0.0000</strong></div><div><span>已用</span><strong id="billing-spent-view">$0.0000</strong></div><div><span>余额</span><strong id="billing-balance-view">$0.0000</strong></div></div>
<div class="field-row"><label class="field">本周期额度(USD<input id="editor-quota" inputmode="decimal" value="0"></label><label class="field">并发上限<input id="editor-concurrency" type="number" min="1" max="64" step="1" value="4"></label></div>
<div class="field-row"><label class="field">自动重置<select id="editor-reset-period"><option value="none">不重置</option><option value="daily">每天</option><option value="weekly">每周</option><option value="monthly">每月</option></select></label><label class="field">下次重置时间<input id="editor-next-reset" type="datetime-local" step="1"></label></div>
<p class="field-hint">请求结束后按实际费用扣款;最后一个并发请求可能产生负余额。</p>
<div class="billing-tools"><span id="billing-status" class="status"></span><button id="reset-billing" class="small" type="button">立即重置额度</button></div>
</div>
<div id="ledger-section" class="drawer-section"><h3>最近账目</h3><div id="billing-ledger" class="ledger-list"></div></div>
</div>
</div>
<div id="key-stats" class="hidden"><div id="stats-grid" class="stats-grid"></div><h3>最近请求</h3><div id="stats-recent" class="recent-list"></div></div>
</div>
<div id="editor-footer" class="drawer-footer"><button id="archive-key" class="danger hidden" type="button">永久归档</button><div><button id="cancel-editor" type="button">取消</button><button id="save-key" class="primary" type="button">保存</button></div></div>
</aside>
<script src="./ui-config.js"></script>
<script type="module" src="./app/main.js"></script>
</body>
</html>