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
31 changes: 13 additions & 18 deletions ui/css/style.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -344,29 +344,24 @@ select.input option { background: var(--bg-card); }
.tsw-btn.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-text); }
.tx-trend-chart { position: relative; }
.tx-trend svg { width: 100%; height: auto; display: block; }
.tx-trend-grid { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 4; }
.tx-trend-axis { fill: var(--text-mute); font-size: 10px; }
/* 平滑折线 + 渐变面积(指标色随切换,rant 2026-08-23T16:17:18 需求 1) */
.tx-trend svg .trend-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.tx-trend-axis { fill: var(--text-mute); font-size: 9px; }
/* 平滑折线 + 渐变面积(sparkline 化,rant 2026-08-24T10:51:57.821209:
无网格线、无数据点圆、渐变 0.35→0、teal 主题色系——消费用主强调色区分) */
.tx-trend svg .trend-line { fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.tx-trend svg .trend-area { stroke: none; }
.tx-trend svg .trend-dot { fill: var(--bg-card); stroke-width: 1.5; }
.tx-trend svg .tg-0 { stop-opacity: 0; }
.tx-trend svg .tg-1 { stop-opacity: 0; }
.tx-trend svg.m-exp .trend-line { stroke: var(--danger-text); }
.tx-trend svg.m-exp .trend-dot { stroke: var(--danger-text); }
.tx-trend svg.m-exp .tg-0 { stop-color: var(--danger-text); stop-opacity: 0.22; }
.tx-trend svg.m-exp .tg-1 { stop-color: var(--danger-text); }
.tx-trend svg.m-exp .trend-line { stroke: var(--accent); }
.tx-trend svg.m-exp .tg-0 { stop-color: var(--accent); stop-opacity: 0.35; }
.tx-trend svg.m-exp .tg-1 { stop-color: var(--accent); }
.tx-trend svg.m-inc .trend-line { stroke: var(--ok); }
.tx-trend svg.m-inc .trend-dot { stroke: var(--ok); }
.tx-trend svg.m-inc .tg-0 { stop-color: var(--ok); stop-opacity: 0.22; }
.tx-trend svg.m-inc .tg-0 { stop-color: var(--ok); stop-opacity: 0.35; }
.tx-trend svg.m-inc .tg-1 { stop-color: var(--ok); }
.tx-trend svg.m-net .trend-line { stroke: var(--accent-text); }
.tx-trend svg.m-net .trend-dot { stroke: var(--accent-text); }
.tx-trend svg.m-net .tg-0 { stop-color: var(--accent-text); stop-opacity: 0.22; }
.tx-trend svg.m-net .tg-0 { stop-color: var(--accent-text); stop-opacity: 0.35; }
.tx-trend svg.m-net .tg-1 { stop-color: var(--accent-text); }
.tx-trend svg.m-tok .trend-line { stroke: var(--warn); }
.tx-trend svg.m-tok .trend-dot { stroke: var(--warn); }
.tx-trend svg.m-tok .tg-0 { stop-color: var(--warn); stop-opacity: 0.22; }
.tx-trend svg.m-tok .tg-0 { stop-color: var(--warn); stop-opacity: 0.35; }
.tx-trend svg.m-tok .tg-1 { stop-color: var(--warn); }
/* 悬停引导线 + tooltip */
.tx-trend-guide { position: absolute; top: 2px; bottom: 6px; width: 1px; background: var(--text-dim); opacity: 0.45; display: none; pointer-events: none; }
Expand All@@ -377,9 +372,9 @@ select.input option { background: var(--bg-card); }
box-shadow: var(--shadow); z-index: 5; white-space: nowrap; line-height: 1.5;
}
.tx-trend-tip b { color: var(--accent-text); font-weight: 600; }
.tx-trend-legend { display: flex; gap: 18px; font-size: 12px; color: var(--text-mute); padding-top: 4px; align-items: center; }
.tx-trend-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; vertical-align: middle; }
.tx-trend-dot.exp { background: var(--danger-text); }
.tx-trend-legend { display: flex; gap: 12px; font-size: 11px; color: var(--text-mute); padding-top: 4px; align-items: center; }
.tx-trend-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 4px; vertical-align: middle; }
.tx-trend-dot.exp { background: var(--accent); }
.tx-trend-dot.inc { background: var(--ok); }
.tx-trend-dot.net { background: var(--accent-text); }
.tx-trend-dot.tok { background: var(--warn); }
Expand Down
10 changes: 5 additions & 5 deletions ui/index.html
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITokenPool</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%234ecdc4'/%3E%3Cstop offset='1' stop-color='%232a9d8f'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='14' fill='url(%23g)'/%3E%3Ctext x='32' y='43' font-family='-apple-system,Segoe UI,Arial,sans-serif' font-size='30' font-weight='800' text-anchor='middle' fill='%23062021'%3EAT%3C/text%3E%3C/svg%3E">
<link rel="stylesheet" href="css/style.css?v=20260824-3">
<link rel="stylesheet" href="css/style.css?v=20260824-4">
</head>
<body>

Expand DownExpand Up@@ -683,9 +683,9 @@ <h3 id="chat-title">使用模型</h3>
</div>
</div>

<script src="js/api.js?v=20260824-3"></script>
<script src="js/data.js?v=20260824-3"></script>
<script src="js/i18n.js?v=20260824-3"></script>
<script src="js/app.js?v=20260824-3"></script>
<script src="js/api.js?v=20260824-4"></script>
<script src="js/data.js?v=20260824-4"></script>
<script src="js/i18n.js?v=20260824-4"></script>
<script src="js/app.js?v=20260824-4"></script>
</body>
</html>
41 changes: 17 additions & 24 deletions ui/js/app.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1241,9 +1241,10 @@
$("#tx-summary").innerHTML = html;
}

// 交易趋势图(rant 2026-08-23T16:01:07 需求 2 + 2026-08-23T16:17:18 优化):手写 SVG(无外部依赖),
// 数据源 = /api/transactions/trend(跟随 tab + 外部时间段筛选,与 summary 同口径);
// 指标可切换(消费/收入/净变化/Token,默认消费点数);平滑折线 + 渐变面积 + 自适应刻度 + 悬停 tooltip。
// 交易趋势图(rant 2026-08-23T16:01:07 需求 2 + 16:17:18 优化 + 2026-08-24T10:51:57 重做):
// 参考仪表盘 sparkline():渐变面积(0.35→0)、连续平滑曲线(round 连接)、无网格极简坐标、
// teal 主题配色、紧凑高度(viewBox 85,原 170 减半,rant 10:51:57.821928);
// 保留指标切换(消费/收入/净变化/Token,默认消费)与悬停 tooltip。
let _trendCtx = null; // 悬停 tooltip 上下文(renderTxTrend 写入,事件委托读取)
function renderTxTrend() {
const el = $("#tx-trend");
Expand DownExpand Up@@ -1279,7 +1280,8 @@
const nm = Math.max(absMax, 1e-9) / np;
const maxV = Math.max((nm <= 1 ? 1 : nm <= 2 ? 2 : nm <= 5 ? 5 : 10) * np, 4);
const minV = neg ? -maxV : 0;
const W = 640, H = 190, PL = 46, PR = 14, PT = 14, PB = 26;
// 紧凑几何(rant 10:51:57.821928:高 170 → 85 减半,无网格预留边距)
const W = 640, H = 85, PL = 32, PR = 8, PT = 6, PB = 14;
const iw = W - PL - PR, ih = H - PT - PB;
const X = (i) => PL + (buckets.length === 1 ? iw / 2 : iw * i / (buckets.length - 1));
const Y = (v) => PT + ih * (maxV - v) / (maxV - minV || 1);
Expand All@@ -1297,33 +1299,22 @@
}
return d;
};
// 网格 + 纵轴刻度(4 等分,5 条线,负值指标含负刻度)
let grid = "";
for (let g = 0; g <= 4; g++) {
const gy = PT + ih * g / 4;
const gv = minV + (maxV - minV) * (4 - g) / 4;
grid += '<line class="tx-trend-grid" x1="' + PL + '" y1="' + gy.toFixed(1) + '" x2="' + (W - PR) + '" y2="' + gy.toFixed(1) + '"/>' +
'<text class="tx-trend-axis" x="' + (PL - 6) + '" y="' + (gy + 4).toFixed(1) + '" text-anchor="end">' + fmtAxis(gv) + "</text>";
}
// 横轴标签:首 / 中 / 尾
// 极简坐标(rant 10:51:57.821209:去生硬网格):仅顶/底 2 个纵轴刻度标签,无网格线
let axis =
'<text class="tx-trend-axis" x="' + (PL - 4) + '" y="' + (PT + 3).toFixed(1) + '" text-anchor="end">' + fmtAxis(maxV) + "</text>" +
'<text class="tx-trend-axis" x="' + (PL - 4) + '" y="' + (H - PB + 3).toFixed(1) + '" text-anchor="end">' + fmtAxis(minV) + "</text>";
// 横轴标签:首 / 中 / 尾(紧凑小字号)
const idxs = [0, Math.floor((buckets.length - 1) / 2), buckets.length - 1].filter((v, i, a) => a.indexOf(v) === i);
let xlabels = "";
idxs.forEach((i) => {
xlabels += '<text class="tx-trend-axis" x="' + X(i).toFixed(1) + '" y="' + (H - 8) + '" text-anchor="middle">' + esc(lbl(buckets[i])) + "</text>";
xlabels += '<text class="tx-trend-axis" x="' + X(i).toFixed(1) + '" y="' + (H - 4) + '" text-anchor="middle">' + esc(lbl(buckets[i])) + "</text>";
});
// 折线 + 渐变面积 + 数据点(面积基线:负值指标取零轴,正值指标取图表底)
// 渐变面积(基线:负值指标取零轴,正值指标取图表底)+ 平滑曲线;
// 不画数据点圆(rant 10:51:57.821209:空白圆点造成折线断点/锯齿,sparkline 化优雅降级)
const yBase = Y(neg ? 0 : minV);
const areaPath = pts.length
? smoothPath(pts) + " L" + pts[pts.length - 1][0].toFixed(1) + "," + yBase.toFixed(1) + " L" + pts[0][0].toFixed(1) + "," + yBase.toFixed(1) + " Z"
: "";
const lineHtml = () => {
let dots = "";
pts.forEach((p) => {
dots += '<circle class="trend-dot" cx="' + p[0].toFixed(1) + '" cy="' + p[1].toFixed(1) + '" r="2.5"/>';
});
return '<path class="trend-area" d="' + areaPath + '"/>' +
'<path class="trend-line" d="' + smoothPath(pts) + '"/>' + dots;
};
const switchHtml =
'<div class="tx-trend-switch" role="tablist" aria-label="' + esc(T("tx.trend.metricLabel")) + '">' +
METRICS.map((x) =>
Expand All@@ -1337,7 +1328,9 @@
'<div class="tx-trend-chart">' +
'<svg class="m-' + m.cls + '" viewBox="0 0 ' + W + " " + H + '" role="img" aria-label="' + esc(T("tx.trend.title")) + '">' +
'<defs><linearGradient id="tx-trend-grad" x1="0" y1="0" x2="0" y2="1"><stop class="tg-0" offset="0%"/><stop class="tg-1" offset="100%"/></linearGradient></defs>' +
grid + xlabels + lineHtml() +
axis + xlabels +
'<path class="trend-area" d="' + areaPath + '"/>' +
'<path class="trend-line" d="' + smoothPath(pts) + '"/>' +
"</svg>" +
'<div class="tx-trend-guide" id="tx-trend-guide"></div>' +
'<div class="tx-trend-tip" id="tx-trend-tip"></div>' +
Expand Down
Loading