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>
This commit is contained in:
dekun
2026-10-14 19:17:00 +08:00
parent fc83905cf6
commit 22de1fd40c
8 changed files with 301 additions and 9 deletions
+107
View File
@@ -0,0 +1,107 @@
/**
* 顶栏右上角 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);
+123
View File
@@ -561,6 +561,129 @@ html[data-theme="light"] .theme-toggle-btn.is-active {
margin-top: 6px;
}
/* 顶栏品牌:加密货币 | OKX 对冲 + 右上角 ETH/BTC 报价 */
.header.header--brand {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 52px;
margin-bottom: 10px;
padding: 0 168px 0 12px;
box-sizing: border-box;
}
.header.header--brand .brand-title {
margin: 0;
font-size: 1.7rem;
font-weight: 700;
color: #e8ecff;
text-align: center;
line-height: 1.25;
letter-spacing: 0.02em;
}
.header.header--brand .brand-prefix {
color: #e8ecff;
}
.header.header--brand .brand-sep {
display: inline-block;
margin: 0 0.45em;
color: #9aa3bf;
font-weight: 500;
}
.header.header--brand .brand-product {
color: #e8ecff;
}
.header-live-quotes {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 3px;
z-index: 2;
pointer-events: none;
}
.header-live-quotes .hlq-row {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.72rem;
line-height: 1.2;
color: #c5cce0;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.header-live-quotes .hlq-icon {
width: 14px;
height: 14px;
border-radius: 50%;
flex: 0 0 auto;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
}
.header-live-quotes .hlq-icon--eth {
background: radial-gradient(circle at 35% 30%, #8ad0ff, #3c7cff 55%, #1a3a8a);
}
.header-live-quotes .hlq-icon--btc {
background: radial-gradient(circle at 35% 30%, #ffd27a, #f0a028 55%, #a65a00);
}
.header-live-quotes .hlq-sym {
color: #9aa3bf;
min-width: 58px;
}
.header-live-quotes .hlq-last {
color: #e8ecff;
font-weight: 600;
min-width: 64px;
text-align: right;
}
.header-live-quotes .hlq-chg,
.header-live-quotes .hlq-pct {
min-width: 48px;
text-align: right;
color: #9aa3bf;
}
.header-live-quotes .hlq-row.is-up .hlq-chg,
.header-live-quotes .hlq-row.is-up .hlq-pct {
color: #3dd68c;
}
.header-live-quotes .hlq-row.is-down .hlq-chg,
.header-live-quotes .hlq-row.is-down .hlq-pct {
color: #ff8a8a;
}
html[data-theme="light"] .header.header--brand .brand-title,
html[data-theme="light"] .header.header--brand .brand-prefix,
html[data-theme="light"] .header.header--brand .brand-product {
color: #142232;
}
html[data-theme="light"] .header.header--brand .brand-sep,
html[data-theme="light"] .header-live-quotes .hlq-sym {
color: #5a6f85;
}
html[data-theme="light"] .header-live-quotes .hlq-last {
color: #142232;
}
@media (max-width: 720px) {
.header.header--brand {
padding: 0 8px;
min-height: 0;
align-items: flex-start;
}
.header.header--brand .brand-title {
font-size: 1.05rem !important;
text-align: left;
}
.header-live-quotes {
position: static;
transform: none;
margin-top: 6px;
align-items: flex-start;
width: 100%;
}
}
/* ── 统一顶栏面板:状态/筛选 + 统计条 ── */
.instance-header-panel {
margin-top: 18px;
+2
View File
@@ -79,6 +79,8 @@ def install_instance_theme_static(app) -> None:
"account_ledger.js": "application/javascript; charset=utf-8",
"options_expiry_countdown.js": "application/javascript; charset=utf-8",
"options_panel.js": "application/javascript; charset=utf-8",
"header_live_quotes.js": "application/javascript; charset=utf-8",
"hedge_plan.js": "application/javascript; charset=utf-8",
"order_entry_model.js": "application/javascript; charset=utf-8",
"focus_chart_page.js": "application/javascript; charset=utf-8",
"focus_chart_page.css": "text/css; charset=utf-8",
+1 -1
View File
@@ -215,7 +215,7 @@ def pwa_app_name(exchange_key: str) -> str:
ex = (exchange_key or "").strip().lower()
return {
"binance": "Binance 交易系统",
"okx": "okx对冲",
"okx": "加密货币 | OKX 对冲",
"gate": "Gate 交易系统",
}.get(ex, "交易系统")
+3 -4
View File
@@ -8,7 +8,7 @@
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
<link rel="stylesheet" href="/static/instance_page.css?v=13">
<link rel="stylesheet" href="/static/instance_theme.css?v=121">
<link rel="stylesheet" href="/static/instance_theme.css?v=122">
<script src="/static/account_risk_badge.js?v=4"></script>
<script src="/static/open_submit_gate.js?v=1"></script>
<meta name="theme-color" content="#0b0d14">
@@ -27,9 +27,7 @@
data-price-refresh-ms="{{ price_refresh_seconds * 1000 }}"
>
<div class="container">
<div class="header">
<h1>okx对冲</h1>
</div>
{% include 'instance_brand_header.html' %}
<nav class="top-nav embed-top-nav" aria-label="实例导航">
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
<a href="/hedge-plan" data-embed-tab="hedge_plan" class="{% if initial_tab == 'hedge_plan' %}active{% endif %}">{% if hedge_plan_show_options_options %}期期对冲{% else %}永期对冲{% endif %}</a>
@@ -140,5 +138,6 @@ window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
<script src="/static/instance_live.js?v=6"></script>
<script src="/static/instance_embed.js?v=32"></script>
<script src="/static/instance_mobile_nav.js?v=3"></script>
<script src="/static/header_live_quotes.js?v=1"></script>
</body>
</html>
+3 -4
View File
@@ -19,7 +19,7 @@
<link rel="manifest" href="/static/icons/manifest.webmanifest">
<title>{{ pwa_app_name }}</title>
<link rel="stylesheet" href="/static/instance_page.css?v=13">
<link rel="stylesheet" href="/static/instance_theme.css?v=121">
<link rel="stylesheet" href="/static/instance_theme.css?v=122">
</head>
<body
@@ -150,9 +150,7 @@
</div>
{% endmacro %}
<div class="container">
<div class="header">
<h1>okx对冲</h1>
</div>
{% include 'instance_brand_header.html' %}
<div class="top-nav">
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
<a href="/hedge-plan" class="{% if page == 'hedge_plan' %}active{% endif %}">{% if hedge_plan_show_options_options %}期期对冲{% else %}永期对冲{% endif %}</a>
@@ -2091,5 +2089,6 @@ document.addEventListener("DOMContentLoaded", function () {
{% endif %}
</script>
<script src="/static/instance_settings_prefs.js?v=22"></script>
<script src="/static/header_live_quotes.js?v=1"></script>
</body>
</html>
@@ -0,0 +1,24 @@
{# 顶栏品牌标题 + 右上角 ETH/BTC 报价(与期权仓同款) #}
<div class="header header--brand">
<h1 class="brand-title">
<span class="brand-prefix">加密货币</span>
<span class="brand-sep" aria-hidden="true">|</span>
<span class="brand-product">OKX 对冲</span>
</h1>
<div class="header-live-quotes" id="header-live-quotes" aria-label="ETH/BTC 行情">
<div class="hlq-row" data-base="ETH">
<span class="hlq-icon hlq-icon--eth" aria-hidden="true"></span>
<span class="hlq-sym">ETHUSDT</span>
<span class="hlq-last" data-hlq="last">—</span>
<span class="hlq-chg" data-hlq="chg">—</span>
<span class="hlq-pct" data-hlq="pct">—</span>
</div>
<div class="hlq-row" data-base="BTC">
<span class="hlq-icon hlq-icon--btc" aria-hidden="true"></span>
<span class="hlq-sym">BTCUSDT</span>
<span class="hlq-last" data-hlq="last">—</span>
<span class="hlq-chg" data-hlq="chg">—</span>
<span class="hlq-pct" data-hlq="pct">—</span>
</div>
</div>
</div>