中控顶栏对齐实例:标题改为加密货币|OKX 期权中控,导航用卡片,报价放右侧。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+116
-65
@@ -52,47 +52,129 @@ a:hover { color: #7dd3fc; }
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
min-height: 118px;
|
min-height: 0;
|
||||||
padding: 36px 88px 20px;
|
padding: 18px 28px 14px;
|
||||||
border-bottom: 1px solid var(--line);
|
border-bottom: 1px solid var(--line);
|
||||||
background: rgba(8, 14, 26, 0.75);
|
background: rgba(8, 14, 26, 0.75);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
}
|
}
|
||||||
.topbar-brand {
|
.topbar-brand {
|
||||||
font-family: var(--font-display);
|
align-self: center;
|
||||||
font-size: clamp(1.55rem, 2.1vw, 2.05rem);
|
font-family: var(--font);
|
||||||
|
font-size: 1.75rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.38em;
|
letter-spacing: 0;
|
||||||
text-indent: 0.38em;
|
text-indent: 0;
|
||||||
color: #f4f8ff;
|
color: #dbe4ff;
|
||||||
line-height: 1.15;
|
line-height: 1.25;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.topbar-brand:hover {
|
.topbar-brand:hover {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
.topbar-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px 12px;
|
||||||
|
}
|
||||||
.topbar-nav {
|
.topbar-nav {
|
||||||
|
grid-column: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 6px 28px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
.topbar-nav a {
|
.topbar-nav a {
|
||||||
color: var(--muted);
|
color: #8fc8ff;
|
||||||
font-size: 1.08rem;
|
font-size: 0.92rem;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0;
|
||||||
padding: 4px 0;
|
padding: 6px 10px;
|
||||||
|
border: 1px solid #304164;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #151a2a;
|
||||||
|
}
|
||||||
|
.topbar-nav a:hover {
|
||||||
|
color: #dbe4ff;
|
||||||
|
border-color: #3d5278;
|
||||||
}
|
}
|
||||||
.topbar-nav a.active {
|
.topbar-nav a.active {
|
||||||
color: var(--accent);
|
color: #dbe4ff;
|
||||||
text-shadow: 0 0 18px rgba(34, 211, 238, 0.35);
|
background: #2a3f6c;
|
||||||
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
.top-nav-spot-tickers {
|
||||||
|
grid-column: 3;
|
||||||
|
justify-self: end;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
min-width: 0;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.nav-spot-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 3px 18px minmax(4.6em, auto) minmax(5em, auto) minmax(3.6em, auto) minmax(3.8em, auto);
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 7px;
|
||||||
|
padding: 3px 9px 3px 5px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(16, 22, 36, 0.72);
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
.nav-spot-row:hover {
|
||||||
|
border-color: #3a4568;
|
||||||
|
}
|
||||||
|
.nav-spot-ribbon {
|
||||||
|
width: 3px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 1px 2px 2px 1px;
|
||||||
|
background: #e24b6a;
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
.nav-spot-icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.nav-spot-icon--eth { background: #627eea; }
|
||||||
|
.nav-spot-icon--btc { background: #f2a900; color: #1a1200; }
|
||||||
|
.nav-spot-sym {
|
||||||
|
color: #e8ecff;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
.nav-spot-last,
|
||||||
|
.nav-spot-chg,
|
||||||
|
.nav-spot-pct {
|
||||||
|
text-align: right;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.nav-spot--up .nav-spot-last,
|
||||||
|
.nav-spot--up .nav-spot-chg,
|
||||||
|
.nav-spot--up .nav-spot-pct { color: #2dd4a8; }
|
||||||
|
.nav-spot--down .nav-spot-last,
|
||||||
|
.nav-spot--down .nav-spot-chg,
|
||||||
|
.nav-spot--down .nav-spot-pct { color: #ff6b7a; }
|
||||||
|
.nav-spot--flat .nav-spot-last,
|
||||||
|
.nav-spot--flat .nav-spot-chg,
|
||||||
|
.nav-spot--flat .nav-spot-pct { color: #9aa3bf; }
|
||||||
.topbar-logout {
|
.topbar-logout {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 22px;
|
top: 18px;
|
||||||
right: 22px;
|
right: 22px;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
@@ -101,18 +183,23 @@ a:hover { color: #7dd3fc; }
|
|||||||
.topbar-logout:hover {
|
.topbar-logout:hover {
|
||||||
color: #e8f1ff;
|
color: #e8f1ff;
|
||||||
}
|
}
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.topbar-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.topbar-nav,
|
||||||
|
.top-nav-spot-tickers {
|
||||||
|
grid-column: 1;
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.topbar {
|
.topbar {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 24px 16px 14px;
|
padding: 16px 16px 12px;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
}
|
|
||||||
.topbar-nav a {
|
|
||||||
font-size: 0.98rem;
|
|
||||||
}
|
}
|
||||||
.topbar-brand {
|
.topbar-brand {
|
||||||
letter-spacing: 0.22em;
|
|
||||||
text-indent: 0.22em;
|
|
||||||
font-size: 1.35rem;
|
font-size: 1.35rem;
|
||||||
}
|
}
|
||||||
.topbar-logout {
|
.topbar-logout {
|
||||||
@@ -168,39 +255,8 @@ body.page-login .main {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
.overview-quotes {
|
|
||||||
margin-left: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
min-width: 0;
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
.overview-quote-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 2.4em minmax(4.6em, auto) minmax(3.4em, auto) minmax(3.6em, auto);
|
|
||||||
align-items: center;
|
|
||||||
column-gap: 8px;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1.2;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.overview-quote-sym { color: var(--muted); }
|
|
||||||
.overview-quote-last,
|
|
||||||
.overview-quote-chg,
|
|
||||||
.overview-quote-pct { text-align: right; }
|
|
||||||
.overview-quote-row.is-up .overview-quote-last,
|
|
||||||
.overview-quote-row.is-up .overview-quote-chg,
|
|
||||||
.overview-quote-row.is-up .overview-quote-pct { color: var(--ok); }
|
|
||||||
.overview-quote-row.is-down .overview-quote-last,
|
|
||||||
.overview-quote-row.is-down .overview-quote-chg,
|
|
||||||
.overview-quote-row.is-down .overview-quote-pct { color: var(--bad); }
|
|
||||||
.overview-quote-row.is-flat .overview-quote-last,
|
|
||||||
.overview-quote-row.is-flat .overview-quote-chg,
|
|
||||||
.overview-quote-row.is-flat .overview-quote-pct { color: var(--text); }
|
|
||||||
.overview-bj-clock {
|
.overview-bj-clock {
|
||||||
margin-left: 0;
|
margin-left: auto;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -267,14 +323,9 @@ body.page-login .main {
|
|||||||
}
|
}
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.page-head { flex-wrap: wrap; }
|
.page-head { flex-wrap: wrap; }
|
||||||
.overview-quotes {
|
|
||||||
margin-left: 0;
|
|
||||||
order: 3;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.overview-bj-clock {
|
.overview-bj-clock {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
order: 4;
|
order: 3;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
|
|||||||
+2
-123
@@ -566,127 +566,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fmtQuotePx(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 >= 1) digits = 2;
|
|
||||||
else digits = 4;
|
|
||||||
try {
|
|
||||||
return v.toLocaleString("en-US", {
|
|
||||||
minimumFractionDigits: digits,
|
|
||||||
maximumFractionDigits: digits,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
return v.toFixed(digits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fmtQuoteSigned(n, isPct) {
|
|
||||||
const v = Number(n);
|
|
||||||
if (!Number.isFinite(v)) return "—";
|
|
||||||
const abs = Math.abs(v);
|
|
||||||
const digits = isPct ? 2 : abs >= 100 ? 1 : 2;
|
|
||||||
let body;
|
|
||||||
try {
|
|
||||||
body = abs.toLocaleString("en-US", {
|
|
||||||
minimumFractionDigits: digits,
|
|
||||||
maximumFractionDigits: digits,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
body = abs.toFixed(digits);
|
|
||||||
}
|
|
||||||
const sign = v > 0 ? "+" : v < 0 ? "-" : "";
|
|
||||||
return sign + body + (isPct ? "%" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function quoteDirClass(n) {
|
|
||||||
const v = Number(n);
|
|
||||||
if (!Number.isFinite(v) || v === 0) return "is-flat";
|
|
||||||
return v > 0 ? "is-up" : "is-down";
|
|
||||||
}
|
|
||||||
|
|
||||||
function snapshotTickers(row) {
|
|
||||||
const snap = (row && row.snapshot) || {};
|
|
||||||
const quotes = snap.quotes && typeof snap.quotes === "object" ? snap.quotes : {};
|
|
||||||
const list = quotes.tickers;
|
|
||||||
return Array.isArray(list) ? list : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function tickersHaveLast(tickers) {
|
|
||||||
return (tickers || []).some(function (t) {
|
|
||||||
return t && t.last != null && t.last !== "" && Number.isFinite(Number(t.last));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickQuoteSource(list) {
|
|
||||||
let best = null;
|
|
||||||
(list || []).forEach(function (row) {
|
|
||||||
if (!row || !row.online) return;
|
|
||||||
const tickers = snapshotTickers(row);
|
|
||||||
if (!tickersHaveLast(tickers)) return;
|
|
||||||
const mode = String((((row.snapshot || {}).instance || {}).mode) || "").toLowerCase();
|
|
||||||
const live = mode === "live" ? 1 : 0;
|
|
||||||
const pulled = String(row.pulled_at || ((row.snapshot || {}).quotes || {}).updated_at || "");
|
|
||||||
const cand = { tickers: tickers, name: row.name || "", live: live, pulled: pulled };
|
|
||||||
if (!best) {
|
|
||||||
best = cand;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cand.live !== best.live) {
|
|
||||||
if (cand.live > best.live) best = cand;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cand.pulled > best.pulled) best = cand;
|
|
||||||
});
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
|
|
||||||
function paintOverviewQuotes(list) {
|
|
||||||
const root = document.getElementById("overview-quotes");
|
|
||||||
if (!root) return;
|
|
||||||
const src = pickQuoteSource(list);
|
|
||||||
const tickers = src ? src.tickers : [];
|
|
||||||
["ETH", "BTC"].forEach(function (base) {
|
|
||||||
const row = root.querySelector('.overview-quote-row[data-base="' + base + '"]');
|
|
||||||
if (!row) return;
|
|
||||||
let t = null;
|
|
||||||
for (let i = 0; i < tickers.length; i++) {
|
|
||||||
if (String((tickers[i] && tickers[i].base) || "").toUpperCase() === base) {
|
|
||||||
t = tickers[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
row.classList.remove("is-up", "is-down", "is-flat");
|
|
||||||
const lastEl = row.querySelector('[data-field="last"]');
|
|
||||||
const chgEl = row.querySelector('[data-field="change"]');
|
|
||||||
const pctEl = row.querySelector('[data-field="change_pct"]');
|
|
||||||
if (!t || t.last == null) {
|
|
||||||
row.classList.add("is-flat");
|
|
||||||
if (lastEl) lastEl.textContent = "—";
|
|
||||||
if (chgEl) chgEl.textContent = "—";
|
|
||||||
if (pctEl) pctEl.textContent = "—";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
row.classList.add(quoteDirClass(t.change));
|
|
||||||
if (lastEl) lastEl.textContent = fmtQuotePx(t.last);
|
|
||||||
if (chgEl) chgEl.textContent = fmtQuoteSigned(t.change, false);
|
|
||||||
if (pctEl) pctEl.textContent = fmtQuoteSigned(t.change_pct, true);
|
|
||||||
});
|
|
||||||
if (src && src.name) {
|
|
||||||
root.title =
|
|
||||||
"来自 " +
|
|
||||||
src.name +
|
|
||||||
(src.live ? " · live" : " · sim") +
|
|
||||||
" · 相对北京 08:00";
|
|
||||||
} else {
|
|
||||||
root.title = "暂无在线客户端报价 · 相对北京 08:00";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
const status = document.getElementById("overview-status");
|
const status = document.getElementById("overview-status");
|
||||||
const grid = document.getElementById("overview-grid");
|
const grid = document.getElementById("overview-grid");
|
||||||
@@ -698,7 +577,7 @@
|
|||||||
if (!d.ok) throw new Error(d.msg || "失败");
|
if (!d.ok) throw new Error(d.msg || "失败");
|
||||||
const list = sortInstancesByMode(d.instances || []);
|
const list = sortInstancesByMode(d.instances || []);
|
||||||
renderOverviewSummary(list);
|
renderOverviewSummary(list);
|
||||||
paintOverviewQuotes(list);
|
if (global.HubQuotes && HubQuotes.paint) HubQuotes.paint(list);
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
grid.innerHTML =
|
grid.innerHTML =
|
||||||
'<p class="muted">暂无启用实例,请先到「实例」添加.</p>';
|
'<p class="muted">暂无启用实例,请先到「实例」添加.</p>';
|
||||||
@@ -733,7 +612,7 @@
|
|||||||
box.hidden = true;
|
box.hidden = true;
|
||||||
box.innerHTML = "";
|
box.innerHTML = "";
|
||||||
}
|
}
|
||||||
paintOverviewQuotes([]);
|
if (global.HubQuotes && HubQuotes.paint) HubQuotes.paint([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
(function (global) {
|
||||||
|
function fmtQuotePx(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 >= 1) digits = 2;
|
||||||
|
else digits = 4;
|
||||||
|
try {
|
||||||
|
return v.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: digits,
|
||||||
|
maximumFractionDigits: digits,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return v.toFixed(digits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtQuoteSigned(n, isPct) {
|
||||||
|
const v = Number(n);
|
||||||
|
if (!Number.isFinite(v)) return "—";
|
||||||
|
const abs = Math.abs(v);
|
||||||
|
const digits = isPct ? 2 : abs >= 100 ? 1 : 2;
|
||||||
|
let body;
|
||||||
|
try {
|
||||||
|
body = abs.toLocaleString("en-US", {
|
||||||
|
minimumFractionDigits: digits,
|
||||||
|
maximumFractionDigits: digits,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
body = abs.toFixed(digits);
|
||||||
|
}
|
||||||
|
const sign = v > 0 ? "" : v < 0 ? "-" : "";
|
||||||
|
return sign + body + (isPct ? "%" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function quoteDirClass(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 snapshotTickers(row) {
|
||||||
|
const snap = (row && row.snapshot) || {};
|
||||||
|
const quotes = snap.quotes && typeof snap.quotes === "object" ? snap.quotes : {};
|
||||||
|
const list = quotes.tickers;
|
||||||
|
return Array.isArray(list) ? list : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function tickersHaveLast(tickers) {
|
||||||
|
return (tickers || []).some(function (t) {
|
||||||
|
return t && t.last != null && t.last !== "" && Number.isFinite(Number(t.last));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickQuoteSource(list) {
|
||||||
|
let best = null;
|
||||||
|
(list || []).forEach(function (row) {
|
||||||
|
if (!row || !row.online) return;
|
||||||
|
const tickers = snapshotTickers(row);
|
||||||
|
if (!tickersHaveLast(tickers)) return;
|
||||||
|
const mode = String((((row.snapshot || {}).instance || {}).mode) || "").toLowerCase();
|
||||||
|
const live = mode === "live" ? 1 : 0;
|
||||||
|
const pulled = String(row.pulled_at || ((row.snapshot || {}).quotes || {}).updated_at || "");
|
||||||
|
const cand = { tickers: tickers, name: row.name || "", live: live, pulled: pulled };
|
||||||
|
if (!best) {
|
||||||
|
best = cand;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cand.live !== best.live) {
|
||||||
|
if (cand.live > best.live) best = cand;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cand.pulled > best.pulled) best = cand;
|
||||||
|
});
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
function paint(list) {
|
||||||
|
const root = document.getElementById("nav-spot-tickers");
|
||||||
|
if (!root) return;
|
||||||
|
const src = pickQuoteSource(list);
|
||||||
|
const tickers = src ? src.tickers : [];
|
||||||
|
["ETH", "BTC"].forEach(function (base) {
|
||||||
|
const row = root.querySelector('.nav-spot-row[data-base="' + base + '"]');
|
||||||
|
if (!row) return;
|
||||||
|
let t = null;
|
||||||
|
for (let i = 0; i < tickers.length; i++) {
|
||||||
|
if (String((tickers[i] && tickers[i].base) || "").toUpperCase() === base) {
|
||||||
|
t = tickers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.classList.remove("nav-spot--up", "nav-spot--down", "nav-spot--flat");
|
||||||
|
const lastEl = row.querySelector('[data-field="last"]');
|
||||||
|
const chgEl = row.querySelector('[data-field="change"]');
|
||||||
|
const pctEl = row.querySelector('[data-field="change_pct"]');
|
||||||
|
if (!t || t.last == null) {
|
||||||
|
row.classList.add("nav-spot--flat");
|
||||||
|
if (lastEl) lastEl.textContent = "—";
|
||||||
|
if (chgEl) chgEl.textContent = "—";
|
||||||
|
if (pctEl) pctEl.textContent = "—";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
row.classList.add(quoteDirClass(t.change));
|
||||||
|
if (lastEl) lastEl.textContent = fmtQuotePx(t.last);
|
||||||
|
if (chgEl) chgEl.textContent = fmtQuoteSigned(t.change, false);
|
||||||
|
if (pctEl) pctEl.textContent = fmtQuoteSigned(t.change_pct, true);
|
||||||
|
});
|
||||||
|
if (src && src.name) {
|
||||||
|
root.title =
|
||||||
|
"来自 " +
|
||||||
|
src.name +
|
||||||
|
(src.live ? " · live" : " · sim") +
|
||||||
|
" · 相对北京 08:00";
|
||||||
|
} else {
|
||||||
|
root.title = "暂无在线客户端报价 · 相对北京 08:00";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = null;
|
||||||
|
let inflight = false;
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
if (inflight) return;
|
||||||
|
inflight = true;
|
||||||
|
try {
|
||||||
|
const r = await fetch("/api/overview");
|
||||||
|
const d = await r.json();
|
||||||
|
paint((d && d.instances) || []);
|
||||||
|
} catch (e) {
|
||||||
|
paint([]);
|
||||||
|
} finally {
|
||||||
|
inflight = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function boot() {
|
||||||
|
if (!document.getElementById("nav-spot-tickers")) return;
|
||||||
|
if (global.HubOverview) return;
|
||||||
|
refresh();
|
||||||
|
if (timer) clearInterval(timer);
|
||||||
|
timer = setInterval(refresh, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.HubQuotes = { boot: boot, paint: paint, refresh: refresh };
|
||||||
|
})(window);
|
||||||
+37
-11
@@ -3,21 +3,41 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% block title %}期权中控{% endblock %}</title>
|
<title>{% block title %}加密货币|OKX 期权中控{% endblock %}</title>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='hub.css') }}?v=22">
|
<link rel="stylesheet" href="{{ url_for('static', filename='hub.css') }}?v=23">
|
||||||
</head>
|
</head>
|
||||||
<body class="{% if request.path.startswith('/login') %}page-login{% elif request.path.startswith('/desk') %}page-desk{% elif request.path.startswith('/stats') %}page-stats{% elif request.path.startswith('/instances') %}page-instances{% elif request.path == '/' %}page-dashboard{% endif %}">
|
<body class="{% if request.path.startswith('/login') %}page-login{% elif request.path.startswith('/desk') %}page-desk{% elif request.path.startswith('/stats') %}page-stats{% elif request.path.startswith('/instances') %}page-instances{% elif request.path == '/' %}page-dashboard{% endif %}">
|
||||||
{% if session.get('logged_in') %}
|
{% if session.get('logged_in') %}
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<a href="/" class="topbar-brand">期权中控</a>
|
<a href="/" class="topbar-brand">加密货币|OKX 期权中控</a>
|
||||||
<nav class="topbar-nav">
|
<div class="topbar-row">
|
||||||
<a href="/" class="{% if request.path == '/' %}active{% endif %}">总览</a>
|
<nav class="topbar-nav">
|
||||||
<a href="/cond_orders" class="{% if request.path.startswith('/cond_orders') %}active{% endif %}">条件单</a>
|
<a href="/" class="{% if request.path == '/' %}active{% endif %}">总览</a>
|
||||||
<a href="/instances" class="{% if request.path.startswith('/instances') %}active{% endif %}">实例</a>
|
<a href="/cond_orders" class="{% if request.path.startswith('/cond_orders') %}active{% endif %}">条件单</a>
|
||||||
<a href="/desk" class="{% if request.path.startswith('/desk') %}active{% endif %}">工作台</a>
|
<a href="/instances" class="{% if request.path.startswith('/instances') %}active{% endif %}">实例</a>
|
||||||
<a href="/stats" class="{% if request.path.startswith('/stats') %}active{% endif %}">数据统计</a>
|
<a href="/desk" class="{% if request.path.startswith('/desk') %}active{% endif %}">工作台</a>
|
||||||
<a href="/settings" class="{% if request.path.startswith('/settings') %}active{% endif %}">系统设置</a>
|
<a href="/stats" class="{% if request.path.startswith('/stats') %}active{% endif %}">数据统计</a>
|
||||||
</nav>
|
<a href="/settings" class="{% if request.path.startswith('/settings') %}active{% endif %}">系统设置</a>
|
||||||
|
</nav>
|
||||||
|
<div class="top-nav-spot-tickers" id="nav-spot-tickers" aria-label="BTC ETH 报价" title="来自在线客户端 · 相对北京 08:00">
|
||||||
|
<div class="nav-spot-row" data-base="ETH">
|
||||||
|
<span class="nav-spot-ribbon" aria-hidden="true"></span>
|
||||||
|
<span class="nav-spot-icon nav-spot-icon--eth" aria-hidden="true">Ξ</span>
|
||||||
|
<span class="nav-spot-sym">ETHUSDT</span>
|
||||||
|
<span class="nav-spot-last" data-field="last">—</span>
|
||||||
|
<span class="nav-spot-chg" data-field="change">—</span>
|
||||||
|
<span class="nav-spot-pct" data-field="change_pct">—</span>
|
||||||
|
</div>
|
||||||
|
<div class="nav-spot-row" data-base="BTC">
|
||||||
|
<span class="nav-spot-ribbon" aria-hidden="true"></span>
|
||||||
|
<span class="nav-spot-icon nav-spot-icon--btc" aria-hidden="true">₿</span>
|
||||||
|
<span class="nav-spot-sym">BTCUSDT</span>
|
||||||
|
<span class="nav-spot-last" data-field="last">—</span>
|
||||||
|
<span class="nav-spot-chg" data-field="change">—</span>
|
||||||
|
<span class="nav-spot-pct" data-field="change_pct">—</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<a href="/logout" class="topbar-logout">退出</a>
|
<a href="/logout" class="topbar-logout">退出</a>
|
||||||
</header>
|
</header>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -27,6 +47,12 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
{% if session.get('logged_in') %}
|
||||||
|
<script src="{{ url_for('static', filename='hub_quotes.js') }}?v=1"></script>
|
||||||
|
{% endif %}
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
|
{% if session.get('logged_in') %}
|
||||||
|
<script>if (window.HubQuotes) HubQuotes.boot();</script>
|
||||||
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,22 +1,8 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}总览 · 期权中控{% endblock %}
|
{% block title %}总览 · 加密货币|OKX 期权中控{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<section class="page-head">
|
<section class="page-head">
|
||||||
<h1>期权持仓总览</h1>
|
<h1>期权持仓总览</h1>
|
||||||
<div class="overview-quotes" id="overview-quotes" title="来自在线客户端 · 相对北京 08:00">
|
|
||||||
<div class="overview-quote-row" data-base="ETH">
|
|
||||||
<span class="overview-quote-sym">ETH</span>
|
|
||||||
<span class="overview-quote-last" data-field="last">—</span>
|
|
||||||
<span class="overview-quote-chg" data-field="change">—</span>
|
|
||||||
<span class="overview-quote-pct" data-field="change_pct">—</span>
|
|
||||||
</div>
|
|
||||||
<div class="overview-quote-row" data-base="BTC">
|
|
||||||
<span class="overview-quote-sym">BTC</span>
|
|
||||||
<span class="overview-quote-last" data-field="last">—</span>
|
|
||||||
<span class="overview-quote-chg" data-field="change">—</span>
|
|
||||||
<span class="overview-quote-pct" data-field="change_pct">—</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="overview-bj-clock" id="overview-bj-clock" title="Asia/Shanghai">
|
<div class="overview-bj-clock" id="overview-bj-clock" title="Asia/Shanghai">
|
||||||
<span class="overview-bj-label">北京时间</span>
|
<span class="overview-bj-label">北京时间</span>
|
||||||
<time class="overview-bj-time" id="overview-bj-time" datetime="">--</time>
|
<time class="overview-bj-time" id="overview-bj-time" datetime="">--</time>
|
||||||
@@ -29,6 +15,6 @@
|
|||||||
<div id="overview-grid"></div>
|
<div id="overview-grid"></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="{{ url_for('static', filename='hub.js') }}?v=21"></script>
|
<script src="{{ url_for('static', filename='hub.js') }}?v=22"></script>
|
||||||
<script>HubOverview.boot();</script>
|
<script>HubOverview.boot();</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="login-wrap">
|
<div class="login-wrap">
|
||||||
<section class="login-card">
|
<section class="login-card">
|
||||||
<h1>期权中控</h1>
|
<h1>加密货币|OKX 期权中控</h1>
|
||||||
<p class="login-sub">多机期权持仓总览 · 内嵌打开客户端 · 本仓不下单</p>
|
<p class="login-sub">多机期权持仓总览 · 内嵌打开客户端 · 本仓不下单</p>
|
||||||
{# 诱饵字段 + readonly/聚焦解锁,避免浏览器自动填入用户名密码 #}
|
{# 诱饵字段 + readonly/聚焦解锁,避免浏览器自动填入用户名密码 #}
|
||||||
<form method="post" class="stack login-form" autocomplete="off" data-lpignore="true" data-1p-ignore="true">
|
<form method="post" class="stack login-form" autocomplete="off" data-lpignore="true" data-1p-ignore="true">
|
||||||
|
|||||||
Reference in New Issue
Block a user