Add BTC/ETH spot tickers on nav, anchored to Beijing 08:00 open.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 导航栏 BTC/ETH 现货报价:相对北京 08:00 开盘涨跌.
|
||||
*/
|
||||
(function (global) {
|
||||
"use strict";
|
||||
|
||||
const DEFAULT_POLL_MS = 5000;
|
||||
let timer = null;
|
||||
let inflight = false;
|
||||
|
||||
function pollMs() {
|
||||
const raw =
|
||||
(document.body && document.body.getAttribute("data-price-refresh-ms")) || "";
|
||||
const n = Number(raw);
|
||||
return Number.isFinite(n) && n >= 2000 ? n : DEFAULT_POLL_MS;
|
||||
}
|
||||
|
||||
function fmtPrice(n) {
|
||||
const v = Number(n);
|
||||
if (!Number.isFinite(v)) return "—";
|
||||
const abs = Math.abs(v);
|
||||
let digits = 2;
|
||||
if (abs >= 10000) digits = 1;
|
||||
else if (abs >= 1000) digits = 2;
|
||||
else if (abs >= 100) digits = 2;
|
||||
else if (abs >= 1) digits = 3;
|
||||
else digits = 4;
|
||||
try {
|
||||
return v.toLocaleString("en-US", {
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
});
|
||||
} catch (_) {
|
||||
return v.toFixed(digits);
|
||||
}
|
||||
}
|
||||
|
||||
function fmtSigned(n, isPct) {
|
||||
const v = Number(n);
|
||||
if (!Number.isFinite(v)) return "—";
|
||||
const abs = Math.abs(v);
|
||||
let digits = isPct ? 2 : abs >= 100 ? 1 : 2;
|
||||
if (!isPct && abs < 1) digits = 3;
|
||||
const body = abs.toLocaleString("en-US", {
|
||||
minimumFractionDigits: digits,
|
||||
maximumFractionDigits: digits,
|
||||
});
|
||||
const sign = v > 0 ? "" : v < 0 ? "-" : "";
|
||||
return sign + body + (isPct ? "%" : "");
|
||||
}
|
||||
|
||||
function dirClass(n) {
|
||||
const v = Number(n);
|
||||
if (!Number.isFinite(v) || v === 0) return "nav-spot--flat";
|
||||
return v > 0 ? "nav-spot--up" : "nav-spot--down";
|
||||
}
|
||||
|
||||
function paintRow(row, t) {
|
||||
if (!row || !t) return;
|
||||
const lastEl = row.querySelector('[data-field="last"]');
|
||||
const chgEl = row.querySelector('[data-field="change"]');
|
||||
const pctEl = row.querySelector('[data-field="change_pct"]');
|
||||
const dir = dirClass(t.change);
|
||||
row.classList.remove("nav-spot--up", "nav-spot--down", "nav-spot--flat");
|
||||
row.classList.add(dir);
|
||||
if (lastEl) lastEl.textContent = fmtPrice(t.last);
|
||||
if (chgEl) chgEl.textContent = fmtSigned(t.change, false);
|
||||
if (pctEl) pctEl.textContent = fmtSigned(t.change_pct, true);
|
||||
const openHint =
|
||||
t.open != null
|
||||
? "开盘 " + fmtPrice(t.open) + " (" + (t.anchor || "BJ 08:00") + ")"
|
||||
: t.anchor || "BJ 08:00";
|
||||
row.title = (t.symbol || "") + " " + openHint;
|
||||
}
|
||||
|
||||
function apply(data) {
|
||||
const root = document.getElementById("nav-spot-tickers");
|
||||
if (!root) return;
|
||||
const list = (data && data.tickers) || [];
|
||||
list.forEach(function (t) {
|
||||
const base = (t.base || "").toUpperCase();
|
||||
if (!base) return;
|
||||
const row = root.querySelector('.nav-spot-row[data-base="' + base + '"]');
|
||||
paintRow(row, t);
|
||||
});
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
const root = document.getElementById("nav-spot-tickers");
|
||||
if (!root || inflight) return;
|
||||
inflight = true;
|
||||
fetch("/api/nav_spot_tickers")
|
||||
.then(function (r) {
|
||||
return r.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
if (data && data.ok !== false) apply(data);
|
||||
})
|
||||
.catch(function () {})
|
||||
.finally(function () {
|
||||
inflight = false;
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (!document.getElementById("nav-spot-tickers")) return;
|
||||
refresh();
|
||||
if (timer) clearInterval(timer);
|
||||
timer = setInterval(refresh, pollMs());
|
||||
}
|
||||
|
||||
global.refreshNavSpotTickers = refresh;
|
||||
global.startNavSpotTickers = start;
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", start);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
})(window);
|
||||
Reference in New Issue
Block a user