diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html
index b77cdcb..0745853 100644
--- a/internal/web/templates/index.html
+++ b/internal/web/templates/index.html
@@ -63,8 +63,25 @@
#startBtn:hover { transform: translateY(-1px); filter: brightness(1.08); }
#startBtn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
+ /* ---------- 单位切换 ---------- */
+ .toolbar { display: flex; justify-content: flex-end; margin-top: 16px; }
+ .unit-switch {
+ display: inline-flex; gap: 2px; padding: 3px;
+ background: var(--card); border: 1px solid var(--border); border-radius: 999px;
+ }
+ .unit-btn {
+ padding: 5px 16px; font-size: 13px; color: var(--muted);
+ background: transparent; border: none; border-radius: 999px;
+ cursor: pointer; transition: all .15s;
+ }
+ .unit-btn:hover { color: var(--text); }
+ .unit-btn.active {
+ color: #06121f; font-weight: 700;
+ background: linear-gradient(90deg, var(--accent1), var(--accent2));
+ }
+
/* ---------- 统计卡片 ---------- */
- .stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 18px; }
+ .stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 12px; }
.stat {
background: var(--card); border: 1px solid var(--border); border-radius: 14px;
padding: 16px 10px; text-align: center;
@@ -160,6 +177,14 @@
+
+
+
@@ -221,6 +246,44 @@ const statUpload = document.getElementById('statUpload');
let running = false;
let gaugeMode = 'speed'; // speed | latency
+/* ================= 单位切换 =================
+ * 显示单位:Mbps(兆比特/秒,默认)或 MB/s(兆字节/秒 = Mbps ÷ 8)
+ * 数据库始终以 Mbps 存储,仅影响显示。
+ */
+let unit = localStorage.getItem('speedtest_unit') === 'MB/s' ? 'MB/s' : 'Mbps';
+let lastGaugeMbps = 0; // 仪表盘当前速率(Mbps 原值)
+let lastResult = null; // 最近一次测速结果(Mbps 原值)
+
+function fmtSpeed(mbps) {
+ const v = unit === 'MB/s' ? mbps / 8 : mbps;
+ return v.toFixed(2);
+}
+
+function updateUnitButtons() {
+ document.querySelectorAll('.unit-btn').forEach(b => {
+ b.classList.toggle('active', b.dataset.unit === unit);
+ });
+}
+
+// 切换单位后刷新所有速度显示(仪表盘 / 统计卡 / 排行榜)
+function refreshSpeedDisplays() {
+ if (gaugeMode === 'speed') updateGaugeSpeed(lastGaugeMbps);
+ if (lastResult) {
+ statDownload.textContent = fmtSpeed(lastResult.download);
+ statUpload.textContent = fmtSpeed(lastResult.upload);
+ }
+ loadRankings(false);
+}
+
+document.querySelectorAll('.unit-btn').forEach(b => {
+ b.addEventListener('click', () => {
+ unit = b.dataset.unit;
+ localStorage.setItem('speedtest_unit', unit);
+ updateUnitButtons();
+ refreshSpeedDisplays();
+ });
+});
+
/* ================= 仪表盘 ================= */
function drawGauge(frac) {
const ctx = gauge.getContext('2d');
@@ -267,8 +330,10 @@ function speedFrac(mbps) {
function updateGaugeSpeed(mbps) {
gaugeMode = 'speed';
- gaugeValue.textContent = (mbps >= 100 ? mbps.toFixed(0) : mbps.toFixed(1));
- gaugeUnit.textContent = 'Mbps';
+ lastGaugeMbps = mbps;
+ const v = unit === 'MB/s' ? mbps / 8 : mbps;
+ gaugeValue.textContent = (v >= 100 ? v.toFixed(0) : v.toFixed(1));
+ gaugeUnit.textContent = unit;
drawGauge(speedFrac(mbps));
}
@@ -550,8 +615,8 @@ function renderRanking(rows) {
'
' + r.rank + ' | ' +
'' + user + (r.is_mine ? '我的成绩' : '') + ' | ' +
'' + r.latency_ms.toFixed(1) + ' ms | ' +
- '' + r.download_mbps.toFixed(2) + ' Mbps | ' +
- '' + r.upload_mbps.toFixed(2) + ' Mbps | ' +
+ '' + fmtSpeed(r.download_mbps) + ' ' + unit + ' | ' +
+ '' + fmtSpeed(r.upload_mbps) + ' ' + unit + ' | ' +
'' + r.created_at + ' | ' +
'';
}).join('');
@@ -606,14 +671,16 @@ async function runTest() {
// 2. 下载(固定 10 秒)
setPhase('下载测速准备中…');
const download = await downloadTest();
- statDownload.textContent = download.toFixed(2);
+ lastResult = { download, upload: 0 };
+ statDownload.textContent = fmtSpeed(download);
updateGaugeSpeed(download);
markDone(document.getElementById('cardDownload'));
// 3. 上传(固定 10 秒)
setPhase('上传测速准备中…');
const upload = await uploadTest();
- statUpload.textContent = upload.toFixed(2);
+ lastResult = { download, upload };
+ statUpload.textContent = fmtSpeed(upload);
updateGaugeSpeed(upload);
markDone(document.getElementById('cardUpload'));
@@ -640,6 +707,7 @@ startBtn.addEventListener('click', runTest);
/* ================= 初始化 ================= */
drawGauge(0);
+updateUnitButtons();
updateSortHeaders();
loadRankings(false);