From 1f136387326f2299e7e24098d39434d228a0ccd7 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 2 Jun 2026 12:29:08 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=91=E6=8E=A7=E5=8C=BA=E6=8C=81=E4=BB=93?= =?UTF-8?q?=E5=90=88=E7=BA=A6=E7=82=B9=E5=87=BB=E8=B7=B3=E8=BD=AC=E8=A1=8C?= =?UTF-8?q?=E6=83=85=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- manual_trading_hub/static/app.css | 25 +++++++++++++ manual_trading_hub/static/app.js | 54 ++++++++++++++++++++++++---- manual_trading_hub/static/chart.js | 15 ++++++++ manual_trading_hub/static/index.html | 6 ++-- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/manual_trading_hub/static/app.css b/manual_trading_hub/static/app.css index 1773796..801e2d0 100644 --- a/manual_trading_hub/static/app.css +++ b/manual_trading_hub/static/app.css @@ -2060,6 +2060,31 @@ body.login-page { margin-right: 4px; } +.sym-link { + background: none; + border: none; + padding: 0; + margin: 0; + font: inherit; + color: var(--accent); + cursor: pointer; + text-align: left; + text-decoration: underline; + text-underline-offset: 2px; +} + +.sym-link:hover { + color: #00ff9d; +} + +.pos-symbol-link { + display: inline; +} + +.pos-symbol-link strong { + font-weight: inherit; +} + .market-price-tag { position: absolute; right: 0; diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js index 488a79f..dfcc1ca 100644 --- a/manual_trading_hub/static/app.js +++ b/manual_trading_hub/static/app.js @@ -491,7 +491,47 @@ } } + function normalizeMarketSymbol(raw) { + let s = (raw || "").trim().toUpperCase(); + if (!s) return ""; + if (s.includes(":")) { + const base = s.split(":")[0]; + if (base.includes("/")) return base; + } + return s; + } + + function resolveExchangeKey(exchangeId) { + const row = (lastMonitorRows || []).find((r) => String(r.id) === String(exchangeId)); + return (row && (row.key || row.id)) || exchangeId; + } + + function openMarketForPosition(exchangeId, symbol, exchangeKey) { + const exKey = exchangeKey || resolveExchangeKey(exchangeId); + const sym = normalizeMarketSymbol(symbol); + if (!exKey || !sym) { + showToast("无法打开行情:缺少交易所或合约", true); + return; + } + if (expandedExchangeId) { + closeExchangeFullscreen(); + } + const qs = new URLSearchParams({ exchange_key: exKey, symbol: sym }); + history.pushState({}, "", "/market?" + qs.toString()); + setActiveNav(); + if (window.hubMarketChart && window.hubMarketChart.openWith) { + window.hubMarketChart.openWith(exKey, sym); + } + } + function bindMonitorInteractions(box) { + box.querySelectorAll(".btn-open-market").forEach((btn) => { + btn.onclick = (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + openMarketForPosition(btn.dataset.exId, btn.dataset.symbol, btn.dataset.exKey); + }; + }); box.querySelectorAll(".btn-open-instance").forEach((btn) => { btn.onclick = (ev) => { ev.preventDefault(); @@ -662,8 +702,9 @@ return row("止损", sl) + row("止盈", tp); } - function renderLivePositionCard(exchangeId, pos, monitorOrder) { + function renderLivePositionCard(exchangeId, exchangeKey, pos, monitorOrder) { const symbol = pos.symbol || ""; + const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, """); const side = (pos.side || "long").toLowerCase(); const sideCn = sideDirLabel(side); const sideCls = sideDirCls(side) || "side-long"; @@ -706,7 +747,7 @@ return `
- ${esc(symbol)} + ${sideCn}
@@ -807,8 +848,9 @@ .join(""); } - function renderPositionBlock(exchangeId, x) { + function renderPositionBlock(exchangeId, exchangeKey, x) { const symAttr = esc(x.symbol || "").replace(/"/g, """); + const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, """); const sideAttr = esc((x.side || "").toLowerCase()).replace(/"/g, """); const contractsAttr = esc(String(x.contracts != null ? x.contracts : "")).replace(/"/g, """); const cond = condOrdersFromPosition(x); @@ -820,7 +862,7 @@
- + @@ -844,7 +886,7 @@ `; inner += `
交易所持仓 · ${pos.length} 仓
`; if (pos.length) { - inner += pos.map((p) => renderPositionBlock(row.id, p)).join(""); + inner += pos.map((p) => renderPositionBlock(row.id, row.key || row.id, p)).join(""); } else { inner += '
无持仓
'; } @@ -937,7 +979,7 @@ html += `
`; if (posCount) { pos.forEach((p) => { - html += renderLivePositionCard(row.id, p, findMonitorOrder(orders, p.symbol, p.side)); + html += renderLivePositionCard(row.id, row.key || row.id, p, findMonitorOrder(orders, p.symbol, p.side)); }); } else { html += '
暂无持仓
'; diff --git a/manual_trading_hub/static/chart.js b/manual_trading_hub/static/chart.js index 8a01b9a..b43ffcf 100644 --- a/manual_trading_hub/static/chart.js +++ b/manual_trading_hub/static/chart.js @@ -628,11 +628,26 @@ marketInited = true; await loadMeta(); bind(); + } else { + readQuery(); } startAutoRefresh(); await loadChart(false); startPriceTagTimer(); }, + openWith: async function (exKey, sym, tf) { + if (!marketInited) { + await this.init(); + } + if (elExchange && exKey) elExchange.value = exKey; + if (elSymbol && sym) elSymbol.value = String(sym).trim().toUpperCase(); + if (tf && elTf) elTf.value = tf; + lastViewKey = ""; + updateExchangeDisplay(); + startAutoRefresh(); + await loadChart(false); + startPriceTagTimer(); + }, reload: function (force) { loadChart(!!force); }, diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index 719442e..085f3a6 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -8,7 +8,7 @@ - + @@ -193,7 +193,7 @@
- - + +
合约方向张数浮盈操作
${esc(x.symbol)} ${renderDirectionHtml(x.side)} ${fmt(x.contracts, 4)} ${fmt(x.unrealized_pnl, 2)}