2e14873f2d
Track application source and tests; exclude local env, user workspaces, and runtime data via .gitignore. Co-authored-by: Cursor <cursoragent@cursor.com>
507 lines
22 KiB
HTML
507 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>余额展示 · 圆环灯管方案预览</title>
|
||
<style>
|
||
:root {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||
color-scheme: dark;
|
||
--bg: #0f1419;
|
||
--surface: #121820;
|
||
--elevated: #1c2735;
|
||
--border: #243041;
|
||
--text: #e7ecf3;
|
||
--muted: #8fa3bf;
|
||
--faint: #5f738d;
|
||
--accent: #3d8bfd;
|
||
--remain: #3dd6c5;
|
||
--remain-glow: rgba(61, 214, 197, 0.55);
|
||
--spent: #e8a44a;
|
||
--spent-glow: rgba(232, 164, 74, 0.45);
|
||
--warn: #ddb945;
|
||
--danger: #ef7b7b;
|
||
--danger-glow: rgba(239, 123, 123, 0.5);
|
||
}
|
||
* { box-sizing: border-box; }
|
||
html, body { margin: 0; background: var(--bg); color: var(--text); }
|
||
body { padding: 24px; line-height: 1.5; }
|
||
|
||
h1 { font-size: 20px; margin: 0 0 6px; }
|
||
.lead { color: var(--muted); font-size: 14px; margin: 0 0 24px; max-width: 720px; }
|
||
|
||
.panel {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
.panel h2 { font-size: 15px; margin: 0 0 4px; }
|
||
.panel p.desc { font-size: 13px; color: var(--muted); margin: 0 0 16px; }
|
||
|
||
.controls {
|
||
display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
|
||
margin-bottom: 20px; padding: 14px 16px;
|
||
background: var(--elevated); border-radius: 10px;
|
||
}
|
||
.controls label { font-size: 13px; color: var(--muted); display: flex; align-items: center; gap: 8px; }
|
||
.controls input[type="range"] { width: 160px; }
|
||
.controls output { font-size: 13px; min-width: 72px; }
|
||
.state-btns { display: flex; gap: 8px; flex-wrap: wrap; }
|
||
.state-btns button {
|
||
padding: 6px 12px; border-radius: 8px; border: 1px solid var(--border);
|
||
background: var(--bg); color: var(--muted); font-size: 12px; cursor: pointer;
|
||
}
|
||
.state-btns button.active { border-color: var(--accent); color: var(--text); background: #152033; }
|
||
|
||
/* ===== 模拟 Chat Header ===== */
|
||
.mock-header {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 12px 16px; border-bottom: 1px solid var(--border);
|
||
background: var(--bg); border-radius: 10px 10px 0 0;
|
||
}
|
||
.mock-header-left { display: flex; align-items: center; gap: 10px; }
|
||
.avatar { width: 32px; height: 32px; border-radius: 8px; background: linear-gradient(135deg, #2458a6, #3d8bfd); }
|
||
.mock-title { font-weight: 700; font-size: 18px; }
|
||
.mock-sub { font-size: 12px; color: var(--muted); }
|
||
.mock-actions { display: flex; align-items: center; gap: 8px; }
|
||
.ghost-btn {
|
||
border: none; border-radius: 10px; padding: 10px 14px; font-size: 14px;
|
||
background: var(--elevated); color: #c9d7ea; cursor: pointer;
|
||
}
|
||
|
||
/* 当前方案 */
|
||
.balance-pill-current {
|
||
padding: 6px 10px; border-radius: 999px;
|
||
background: var(--elevated); color: #9ec5ff; font-size: 12px;
|
||
border: 1px solid transparent; cursor: pointer;
|
||
}
|
||
|
||
/* ===== 方案 A:纯圆环灯管(推荐) ===== */
|
||
.balance-ring-btn {
|
||
position: relative;
|
||
width: 40px; height: 40px;
|
||
border: none; background: transparent; cursor: pointer;
|
||
padding: 0; border-radius: 50%;
|
||
flex-shrink: 0;
|
||
}
|
||
.balance-ring-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||
|
||
.balance-ring-svg {
|
||
width: 40px; height: 40px;
|
||
transform: rotate(-90deg);
|
||
filter: drop-shadow(0 0 4px var(--ring-glow, var(--remain-glow)));
|
||
}
|
||
.balance-ring-track {
|
||
fill: none;
|
||
stroke: #1a2838;
|
||
stroke-width: 4;
|
||
}
|
||
.balance-ring-spent {
|
||
fill: none;
|
||
stroke: var(--spent);
|
||
stroke-width: 4;
|
||
stroke-linecap: round;
|
||
transition: stroke-dashoffset 0.35s ease;
|
||
filter: drop-shadow(0 0 3px var(--spent-glow));
|
||
}
|
||
.balance-ring-remain {
|
||
fill: none;
|
||
stroke: var(--ring-color, var(--remain));
|
||
stroke-width: 4;
|
||
stroke-linecap: round;
|
||
transition: stroke-dashoffset 0.35s ease, stroke 0.2s;
|
||
filter: drop-shadow(0 0 4px var(--ring-glow, var(--remain-glow)));
|
||
}
|
||
.balance-ring-btn.low .balance-ring-remain {
|
||
--ring-color: var(--warn);
|
||
--ring-glow: rgba(221, 185, 69, 0.5);
|
||
}
|
||
.balance-ring-btn.empty .balance-ring-remain {
|
||
--ring-color: var(--danger);
|
||
--ring-glow: var(--danger-glow);
|
||
animation: pulse-empty 1.8s ease-in-out infinite;
|
||
}
|
||
@keyframes pulse-empty {
|
||
0%, 100% { opacity: 1; filter: drop-shadow(0 0 4px var(--danger-glow)); }
|
||
50% { opacity: 0.55; filter: drop-shadow(0 0 8px var(--danger-glow)); }
|
||
}
|
||
|
||
.balance-ring-center {
|
||
position: absolute; inset: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 10px; font-weight: 600; color: var(--muted);
|
||
pointer-events: none;
|
||
}
|
||
.balance-ring-btn.low .balance-ring-center { color: var(--warn); }
|
||
.balance-ring-btn.empty .balance-ring-center { color: var(--danger); font-size: 9px; }
|
||
|
||
/* ===== 方案 B:圆环 + 悬停/点击浮层 ===== */
|
||
.balance-popover-wrap { position: relative; }
|
||
.balance-popover {
|
||
display: none;
|
||
position: absolute; top: calc(100% + 10px); right: 0;
|
||
width: 220px; padding: 14px;
|
||
background: #171f2b; border: 1px solid var(--border);
|
||
border-radius: 12px; box-shadow: 0 12px 32px rgba(0,0,0,0.45);
|
||
z-index: 10;
|
||
}
|
||
.balance-popover-wrap:hover .balance-popover,
|
||
.balance-popover-wrap.open .balance-popover { display: block; }
|
||
.balance-popover h4 { margin: 0 0 10px; font-size: 13px; font-weight: 600; }
|
||
.balance-popover-row {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
font-size: 12px; margin-bottom: 6px;
|
||
}
|
||
.balance-popover-row .label { color: var(--muted); display: flex; align-items: center; gap: 6px; }
|
||
.dot { width: 8px; height: 8px; border-radius: 50%; }
|
||
.dot.remain { background: var(--remain); box-shadow: 0 0 6px var(--remain-glow); }
|
||
.dot.spent { background: var(--spent); box-shadow: 0 0 6px var(--spent-glow); }
|
||
.balance-popover-bar {
|
||
height: 6px; border-radius: 999px; background: #1a2838;
|
||
overflow: hidden; display: flex; margin: 10px 0 12px;
|
||
}
|
||
.balance-popover-bar .spent-part { background: var(--spent); }
|
||
.balance-popover-bar .remain-part { background: var(--remain); }
|
||
.balance-popover-cta {
|
||
width: 100%; padding: 8px; border: none; border-radius: 8px;
|
||
background: var(--accent); color: #fff; font-size: 13px; cursor: pointer;
|
||
}
|
||
|
||
/* ===== 方案 C:胶囊微环 ===== */
|
||
.balance-capsule {
|
||
display: flex; align-items: center; gap: 8px;
|
||
padding: 4px 10px 4px 4px;
|
||
border-radius: 999px; border: 1px solid var(--border);
|
||
background: var(--elevated); cursor: pointer;
|
||
transition: border-color 0.2s;
|
||
}
|
||
.balance-capsule:hover { border-color: rgba(61, 214, 197, 0.35); }
|
||
.balance-capsule.low:hover { border-color: rgba(221, 185, 69, 0.4); }
|
||
.balance-capsule.empty:hover { border-color: rgba(239, 123, 123, 0.4); }
|
||
.balance-capsule-ring { width: 28px; height: 28px; flex-shrink: 0; }
|
||
.balance-capsule-ring svg { width: 28px; height: 28px; transform: rotate(-90deg); }
|
||
.balance-capsule-ring .balance-ring-track { stroke-width: 3; }
|
||
.balance-capsule-ring .balance-ring-spent,
|
||
.balance-capsule-ring .balance-ring-remain { stroke-width: 3; }
|
||
.balance-capsule-text { font-size: 12px; color: var(--muted); white-space: nowrap; }
|
||
.balance-capsule-text strong { color: var(--text); font-weight: 600; }
|
||
|
||
.compare-row {
|
||
display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
|
||
}
|
||
@media (max-width: 800px) { .compare-row { grid-template-columns: 1fr; } }
|
||
|
||
.legend {
|
||
display: flex; gap: 16px; flex-wrap: wrap;
|
||
font-size: 12px; color: var(--muted); margin-top: 12px;
|
||
}
|
||
.legend span { display: flex; align-items: center; gap: 6px; }
|
||
|
||
.note {
|
||
font-size: 12px; color: var(--faint);
|
||
border-left: 3px solid var(--border);
|
||
padding-left: 12px; margin-top: 16px;
|
||
}
|
||
.tag {
|
||
display: inline-block; padding: 2px 8px; border-radius: 6px;
|
||
font-size: 11px; font-weight: 600; margin-left: 8px;
|
||
background: rgba(61, 214, 197, 0.12); color: var(--remain);
|
||
}
|
||
.tag.alt { background: rgba(61, 139, 253, 0.12); color: #9ec5ff; }
|
||
|
||
.recharge-preview {
|
||
margin-top: 12px; padding: 14px; border-radius: 10px;
|
||
background: var(--elevated); border: 1px solid var(--border);
|
||
}
|
||
.recharge-preview h4 { margin: 0 0 10px; font-size: 13px; }
|
||
.recharge-ring-large {
|
||
display: flex; align-items: center; gap: 20px;
|
||
}
|
||
.recharge-ring-large svg { width: 88px; height: 88px; transform: rotate(-90deg); }
|
||
.recharge-ring-large .balance-ring-track { stroke-width: 6; }
|
||
.recharge-ring-large .balance-ring-spent,
|
||
.recharge-ring-large .balance-ring-remain { stroke-width: 6; }
|
||
.recharge-stats { font-size: 13px; }
|
||
.recharge-stats div { margin-bottom: 4px; color: var(--muted); }
|
||
.recharge-stats strong { color: var(--text); font-size: 15px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>余额展示 · 圆环灯管方案</h1>
|
||
<p class="lead">
|
||
替代 header 里直白的「余额 ¥12.80」。用圆环灯管表达<strong>累计额度</strong>与<strong>已消耗</strong>比例,剩余部分用另一色发光段表示。点击/悬停可看具体金额。
|
||
</p>
|
||
|
||
<div class="controls">
|
||
<label>
|
||
累计充值(总额)
|
||
<input type="range" id="totalSlider" min="500" max="20000" step="100" value="5000" />
|
||
<output id="totalOut">¥50.00</output>
|
||
</label>
|
||
<label>
|
||
当前余额(剩余)
|
||
<input type="range" id="remainSlider" min="0" max="5000" step="10" value="1280" />
|
||
<output id="remainOut">¥12.80</output>
|
||
</label>
|
||
<div class="state-btns">
|
||
<button type="button" data-preset="healthy" class="active">健康</button>
|
||
<button type="button" data-preset="low">余额低</button>
|
||
<button type="button" data-preset="empty">已耗尽</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 当前 vs 推荐 -->
|
||
<div class="panel">
|
||
<h2>对比:当前 vs 方案 A(推荐)</h2>
|
||
<p class="desc">方案 A 仅显示圆环 + 剩余百分比,不直接暴露金额,适合 header 紧凑场景。</p>
|
||
<div class="compare-row">
|
||
<div>
|
||
<div class="mock-header" style="border-radius:10px">
|
||
<div class="mock-header-left">
|
||
<div class="avatar"></div>
|
||
<div>
|
||
<div class="mock-title">John</div>
|
||
<div class="mock-sub">新对话</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-actions">
|
||
<span class="balance-pill-current" id="currentPill">余额 ¥12.80</span>
|
||
<button class="ghost-btn">我的空间</button>
|
||
</div>
|
||
</div>
|
||
<p class="note">当前:金额完全外露,像账单条目。</p>
|
||
</div>
|
||
<div>
|
||
<div class="mock-header" style="border-radius:10px">
|
||
<div class="mock-header-left">
|
||
<div class="avatar"></div>
|
||
<div>
|
||
<div class="mock-title">John</div>
|
||
<div class="mock-sub">新对话</div>
|
||
</div>
|
||
</div>
|
||
<div class="mock-actions">
|
||
<div class="balance-popover-wrap" id="variantA">
|
||
<button type="button" class="balance-ring-btn" aria-label="账户额度">
|
||
<svg class="balance-ring-svg" viewBox="0 0 40 40" aria-hidden="true">
|
||
<circle class="balance-ring-track" cx="20" cy="20" r="16" />
|
||
<circle class="balance-ring-spent" cx="20" cy="20" r="16"
|
||
stroke-dasharray="0 100.53" stroke-dashoffset="0" data-role="spent" />
|
||
<circle class="balance-ring-remain" cx="20" cy="20" r="16"
|
||
stroke-dasharray="0 100.53" stroke-dashoffset="0" data-role="remain" />
|
||
</svg>
|
||
<span class="balance-ring-center" data-role="pct">26%</span>
|
||
</button>
|
||
<div class="balance-popover">
|
||
<h4>账户额度</h4>
|
||
<div class="balance-popover-row">
|
||
<span class="label"><span class="dot remain"></span>剩余</span>
|
||
<strong data-role="pop-remain">¥12.80</strong>
|
||
</div>
|
||
<div class="balance-popover-row">
|
||
<span class="label"><span class="dot spent"></span>已消耗</span>
|
||
<strong data-role="pop-spent">¥37.20</strong>
|
||
</div>
|
||
<div class="balance-popover-row">
|
||
<span class="label" style="color:var(--faint)">累计充值</span>
|
||
<span data-role="pop-total">¥50.00</span>
|
||
</div>
|
||
<div class="balance-popover-bar" data-role="bar"></div>
|
||
<button type="button" class="balance-popover-cta">充值</button>
|
||
</div>
|
||
</div>
|
||
<button class="ghost-btn">我的空间</button>
|
||
</div>
|
||
</div>
|
||
<div class="legend">
|
||
<span><span class="dot remain"></span>青绿 = 剩余可用</span>
|
||
<span><span class="dot spent"></span>琥珀 = 已消耗</span>
|
||
</div>
|
||
<p class="note">方案 A:圆环总长 = 累计充值;两段颜色从 12 点顺时针排列。中心显示剩余占比。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 方案 B -->
|
||
<div class="panel">
|
||
<h2>方案 B:圆环 + 点击固定浮层<span class="tag alt">移动端友好</span></h2>
|
||
<p class="desc">与 A 相同圆环,但浮层通过点击打开(避免手机无 hover)。适合需要明确金额时再展开。</p>
|
||
<div class="mock-header" style="border-radius:10px; max-width:480px">
|
||
<div class="mock-header-left">
|
||
<div class="avatar"></div>
|
||
<div><div class="mock-title">John</div><div class="mock-sub">新对话</div></div>
|
||
</div>
|
||
<div class="mock-actions">
|
||
<div class="balance-popover-wrap" id="variantB">
|
||
<button type="button" class="balance-ring-btn" aria-label="账户额度" data-click-toggle>
|
||
<svg class="balance-ring-svg" viewBox="0 0 40 40"><circle class="balance-ring-track" cx="20" cy="20" r="16" /><circle class="balance-ring-spent" cx="20" cy="20" r="16" data-role="spent" /><circle class="balance-ring-remain" cx="20" cy="20" r="16" data-role="remain" /></svg>
|
||
<span class="balance-ring-center" data-role="pct">26%</span>
|
||
</button>
|
||
<div class="balance-popover">
|
||
<h4>账户额度</h4>
|
||
<div class="balance-popover-row"><span class="label"><span class="dot remain"></span>剩余</span><strong data-role="pop-remain">¥12.80</strong></div>
|
||
<div class="balance-popover-row"><span class="label"><span class="dot spent"></span>已消耗</span><strong data-role="pop-spent">¥37.20</strong></div>
|
||
<div class="balance-popover-bar" data-role="bar"></div>
|
||
<button type="button" class="balance-popover-cta">去充值</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="note">点击圆环切换浮层;低余额时环色变黄,耗尽时红色脉冲。</p>
|
||
</div>
|
||
|
||
<!-- 方案 C -->
|
||
<div class="panel">
|
||
<h2>方案 C:胶囊微环<span class="tag">折中</span></h2>
|
||
<p class="desc">保留简短文案「可用 26%」,圆环缩小嵌在胶囊左侧,比纯数字含蓄但仍有一行提示。</p>
|
||
<div class="mock-header" style="border-radius:10px; max-width:520px">
|
||
<div class="mock-header-left">
|
||
<div class="avatar"></div>
|
||
<div><div class="mock-title">John</div><div class="mock-sub">新对话</div></div>
|
||
</div>
|
||
<div class="mock-actions">
|
||
<div class="balance-capsule" id="variantC">
|
||
<div class="balance-capsule-ring">
|
||
<svg viewBox="0 0 40 40"><circle class="balance-ring-track" cx="20" cy="20" r="16" /><circle class="balance-ring-spent" cx="20" cy="20" r="16" data-role="spent" /><circle class="balance-ring-remain" cx="20" cy="20" r="16" data-role="remain" /></svg>
|
||
</div>
|
||
<span class="balance-capsule-text">可用 <strong data-role="pct">26%</strong></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 充值弹窗里的放大版 -->
|
||
<div class="panel">
|
||
<h2>充值弹窗 · 大号圆环(配套)</h2>
|
||
<p class="desc">弹窗内可用 88px 大圆环 + 三项明细,与 header 小环视觉语言一致。</p>
|
||
<div class="recharge-preview">
|
||
<h4>账户充值</h4>
|
||
<div class="recharge-ring-large" id="rechargePreview">
|
||
<svg viewBox="0 0 40 40">
|
||
<circle class="balance-ring-track" cx="20" cy="20" r="16" />
|
||
<circle class="balance-ring-spent" cx="20" cy="20" r="16" data-role="spent" />
|
||
<circle class="balance-ring-remain" cx="20" cy="20" r="16" data-role="remain" />
|
||
</svg>
|
||
<div class="recharge-stats">
|
||
<div>剩余 <strong data-role="pop-remain">¥12.80</strong></div>
|
||
<div>已消耗 <strong data-role="pop-spent">¥37.20</strong></div>
|
||
<div>累计充值 <strong data-role="pop-total">¥50.00</strong></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 数据与实现说明 -->
|
||
<div class="panel">
|
||
<h2>数据口径 & 实现要点</h2>
|
||
<p class="desc" style="margin-bottom:8px">
|
||
<strong>圆环语义</strong>:外圈总长 = 累计充值总额;琥珀段 = 已消耗;青绿段 = 当前余额。<br>
|
||
<strong>计算公式</strong>:已消耗 = 累计充值 − 当前余额(需后端返回 <code>totalRechargeCents</code>,或从流水汇总)。<br>
|
||
<strong>状态色</strong>:剩余 > ¥1 青绿;≤ ¥1 琥珀警告;≤ ¥0 红色脉冲 + 点击直达充值。<br>
|
||
<strong>无障碍</strong>:<code>aria-label="账户额度,剩余 26%"</code>,浮层内展示具体金额。<br>
|
||
<strong>推荐</strong>:Header 用 <strong>方案 A</strong>(纯圆环 + hover 浮层);移动端同一组件点击展开(方案 B 行为)。
|
||
</p>
|
||
</div>
|
||
|
||
<script>
|
||
const CIRC = 2 * Math.PI * 16; // r=16
|
||
const fmt = (cents) => '¥' + (cents / 100).toFixed(2);
|
||
|
||
const totalSlider = document.getElementById('totalSlider');
|
||
const remainSlider = document.getElementById('remainSlider');
|
||
const totalOut = document.getElementById('totalOut');
|
||
const remainOut = document.getElementById('remainOut');
|
||
const currentPill = document.getElementById('currentPill');
|
||
|
||
const variants = [
|
||
document.getElementById('variantA'),
|
||
document.getElementById('variantB'),
|
||
document.getElementById('variantC'),
|
||
document.getElementById('rechargePreview'),
|
||
].filter(Boolean);
|
||
|
||
function updateRing(root, total, remain) {
|
||
if (!root) return;
|
||
const spent = Math.max(0, total - remain);
|
||
const pct = total > 0 ? Math.round((remain / total) * 100) : 0;
|
||
const spentLen = total > 0 ? (spent / total) * CIRC : 0;
|
||
const remainLen = total > 0 ? (remain / total) * CIRC : 0;
|
||
|
||
root.querySelectorAll('[data-role="spent"]').forEach((el) => {
|
||
el.setAttribute('stroke-dasharray', `${spentLen} ${CIRC}`);
|
||
el.setAttribute('stroke-dashoffset', '0');
|
||
});
|
||
root.querySelectorAll('[data-role="remain"]').forEach((el) => {
|
||
el.setAttribute('stroke-dasharray', `${remainLen} ${CIRC}`);
|
||
el.setAttribute('stroke-dashoffset', `${-spentLen}`);
|
||
});
|
||
root.querySelectorAll('[data-role="pct"]').forEach((el) => {
|
||
el.textContent = total <= 0 ? '—' : (remain <= 0 ? '0%' : pct + '%');
|
||
});
|
||
root.querySelectorAll('[data-role="pop-remain"]').forEach((el) => { el.textContent = fmt(remain); });
|
||
root.querySelectorAll('[data-role="pop-spent"]').forEach((el) => { el.textContent = fmt(spent); });
|
||
root.querySelectorAll('[data-role="pop-total"]').forEach((el) => { el.textContent = fmt(total); });
|
||
|
||
const bar = root.querySelector('[data-role="bar"]');
|
||
if (bar && total > 0) {
|
||
const spentPct = (spent / total) * 100;
|
||
const remainPct = (remain / total) * 100;
|
||
bar.innerHTML = `<div class="spent-part" style="width:${spentPct}%"></div><div class="remain-part" style="width:${remainPct}%"></div>`;
|
||
}
|
||
|
||
const btn = root.querySelector('.balance-ring-btn') || root.querySelector('.balance-capsule');
|
||
if (btn) {
|
||
btn.classList.remove('low', 'empty');
|
||
if (remain <= 0) btn.classList.add('empty');
|
||
else if (remain <= 100) btn.classList.add('low');
|
||
}
|
||
}
|
||
|
||
function render() {
|
||
let total = Number(totalSlider.value);
|
||
let remain = Number(remainSlider.value);
|
||
if (remain > total) {
|
||
remain = total;
|
||
remainSlider.value = String(total);
|
||
}
|
||
remainSlider.max = String(total);
|
||
totalOut.textContent = fmt(total);
|
||
remainOut.textContent = fmt(remain);
|
||
currentPill.textContent = '余额 ' + fmt(remain);
|
||
variants.forEach((v) => updateRing(v, total, remain));
|
||
}
|
||
|
||
totalSlider.addEventListener('input', render);
|
||
remainSlider.addEventListener('input', render);
|
||
|
||
document.querySelectorAll('[data-preset]').forEach((btn) => {
|
||
btn.addEventListener('click', () => {
|
||
document.querySelectorAll('[data-preset]').forEach((b) => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
const p = btn.dataset.preset;
|
||
if (p === 'healthy') { totalSlider.value = '5000'; remainSlider.value = '1280'; }
|
||
if (p === 'low') { totalSlider.value = '5000'; remainSlider.value = '80'; }
|
||
if (p === 'empty') { totalSlider.value = '5000'; remainSlider.value = '0'; }
|
||
render();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('[data-click-toggle]').forEach((btn) => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
const wrap = btn.closest('.balance-popover-wrap');
|
||
wrap.classList.toggle('open');
|
||
});
|
||
});
|
||
document.addEventListener('click', () => {
|
||
document.querySelectorAll('.balance-popover-wrap.open').forEach((w) => w.classList.remove('open'));
|
||
});
|
||
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html>
|