08029ccb15
Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.5 KiB
JavaScript
121 lines
3.5 KiB
JavaScript
/**
|
|
* 导航栏 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);
|