Files
CoinOX/lib/common/static/header_live_quotes.js
T
dekun 22de1fd40c Match options branding: title prefix and ETH/BTC header quotes.
Use 「加密货币 | OKX 对冲」 and show live top-right ETHUSDT/BTCUSDT quotes like the options instance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-14 19:17:00 +08:00

108 lines
3.2 KiB
JavaScript

/**
* 顶栏右上角 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);