/** * 顶栏右上角 ETH/BTC 实时报价(公共 ticker). */ (function (global) { const BASES = ["ETH", "BTC"]; function $(id) { return document.getElementById(id); } function refreshMs() { const raw = document.body && document.body.getAttribute("data-price-refresh-ms"); const n = Number(raw); return n > 2000 ? n : 5000; } function fmtLast(base, v) { if (v == null || Number.isNaN(Number(v))) return "—"; const n = Number(v); if (base === "BTC") { return n.toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 }); } return n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function fmtChg(base, v) { if (v == null || Number.isNaN(Number(v))) return "—"; const n = Number(v); const abs = Math.abs(n); const body = base === "BTC" ? abs.toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 }) : abs.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); if (n > 0) return "+" + body; if (n < 0) return "-" + body; return body; } function fmtPct(v) { if (v == null || Number.isNaN(Number(v))) return "—"; const n = Number(v); const body = Math.abs(n).toFixed(2) + "%"; if (n > 0) return "+" + body; if (n < 0) return "-" + body; return body; } function paintRow(row, q) { if (!row || !q) return; const last = row.querySelector('[data-hlq="last"]'); const chg = row.querySelector('[data-hlq="chg"]'); const pct = row.querySelector('[data-hlq="pct"]'); const base = row.getAttribute("data-base") || ""; if (last) last.textContent = fmtLast(base, q.last); if (chg) chg.textContent = fmtChg(base, q.change); if (pct) pct.textContent = fmtPct(q.pct); const up = Number(q.change) > 0 || Number(q.pct) > 0; const down = Number(q.change) < 0 || Number(q.pct) < 0; row.classList.toggle("is-up", up); row.classList.toggle("is-down", down); } async function fetchQuotes() { const r = await fetch("/api/header-quotes", { credentials: "same-origin", cache: "no-store", }); const d = await r.json(); if (!d || !d.ok) throw new Error((d && d.msg) || "行情失败"); return d.quotes || []; } async function tick() { const host = $("header-live-quotes"); if (!host) return; try { const quotes = await fetchQuotes(); BASES.forEach(function (base) { const row = host.querySelector('.hlq-row[data-base="' + base + '"]'); const q = quotes.find(function (x) { return String((x && x.base) || "").toUpperCase() === base; }); if (row && q && !q.error) paintRow(row, q); }); } catch (_) { /* 静默:保持上次有效行情 */ } } let timer = null; function boot() { if (!$("header-live-quotes")) return; void tick(); if (timer) clearInterval(timer); timer = setInterval(function () { void tick(); }, refreshMs()); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } global.HeaderLiveQuotes = { refresh: tick, boot: boot }; })(window);