From dda022d3c3db7c07eebfd1cf013aa0b155a279c1 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 7 Jul 2026 13:33:13 +0800 Subject: [PATCH] Hub monitor: OKX left column with dual inner cards when options enabled. Use a stats bar plus left OKX / right Binance+Gate split layout with equal-height inner perpetual and options cards and no in-card scrolling. Co-authored-by: Cursor --- manual_trading_hub/static/app.css | 117 ++++++++++++++++++++++++ manual_trading_hub/static/app.js | 131 ++++++++++++++++++++------- manual_trading_hub/static/index.html | 4 +- 3 files changed, 216 insertions(+), 36 deletions(-) diff --git a/manual_trading_hub/static/app.css b/manual_trading_hub/static/app.css index 93045ed..8b52050 100644 --- a/manual_trading_hub/static/app.css +++ b/manual_trading_hub/static/app.css @@ -1164,6 +1164,123 @@ html[data-theme="light"] .host-metric-bar { grid-template-columns: 1fr; } +.grid-monitor.grid-monitor-options-split { + display: flex; + flex-direction: column; + gap: 16px; +} + +.grid-monitor.grid-monitor-options-split .monitor-stats-card { + width: 100%; +} + +.grid-monitor.grid-monitor-options-split .monitor-stats-grid { + grid-template-columns: repeat(6, minmax(0, 1fr)); +} + +.monitor-split-body { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 16px; + align-items: stretch; + min-height: 0; +} + +.monitor-split-left, +.monitor-split-right { + min-width: 0; + min-height: 0; +} + +.monitor-split-left > .card, +.monitor-split-right > .card { + height: 100%; +} + +.monitor-split-right { + display: grid; + grid-template-rows: 1fr 1fr; + gap: 16px; +} + +.card-monitor-okx-split, +.card-monitor-split-side { + display: flex; + flex-direction: column; + overflow: visible; +} + +.card-monitor-okx-split .card-body, +.card-monitor-split-side .card-body { + flex: 1; + display: flex; + flex-direction: column; + overflow: visible; + min-height: 0; +} + +.hub-inner-cards { + flex: 1; + display: grid; + grid-template-rows: 1fr 1fr; + gap: 12px; + min-height: 0; +} + +.hub-inner-card { + display: flex; + flex-direction: column; + min-height: 0; + border: 1px solid var(--border-soft); + border-radius: 10px; + background: color-mix(in srgb, var(--inset-surface) 92%, transparent); + overflow: visible; +} + +.hub-inner-card-head { + flex: 0 0 auto; + padding: 8px 12px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.05em; + color: var(--text); + border-bottom: 1px solid var(--border-soft); +} + +.hub-inner-card-options .hub-inner-card-head { + color: var(--accent); +} + +.hub-inner-card-body { + flex: 1; + padding: 10px 12px; + overflow: visible; + min-height: 0; +} + +.grid-monitor-options-split .hub-options-table-wrap, +.grid-monitor-options-split .pos-table-wrap, +.grid-monitor-options-split .table-scroll { + overflow: visible; + max-height: none; +} + +@media (max-width: 1100px) { + .grid-monitor.grid-monitor-options-split .monitor-stats-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 960px) { + .monitor-split-body { + grid-template-columns: 1fr; + } + + .monitor-split-right { + grid-template-rows: auto auto; + } +} + .monitor-stats-card .card-head { padding-bottom: 0; } diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js index ec785ce..e54d496 100644 --- a/manual_trading_hub/static/app.js +++ b/manual_trading_hub/static/app.js @@ -120,6 +120,7 @@ } let tpslPending = null; let lastMonitorRows = []; + let monitorGridOptionsSplit = false; let lastMonitorTotals = null; let expandedExchangeId = sessionStorage.getItem("hub_expanded_ex") || ""; const HUB_MONITOR_BOARD_CACHE_KEY = "hub_monitor_board_v1"; @@ -1632,10 +1633,14 @@ el.innerHTML = `正常 ${ok}·关注 ${warn}·异常 ${err}`; } - /** 监控卡片列数:桌面 2×2(统计+三所);手机 2 列瓦片 */ + /** 监控卡片列数:桌面 2×2(统计+三所);期权分栏时由 CSS 控制 */ function syncMonitorGridColumns(gridEl, itemCount, opts) { if (!gridEl) return; const options = opts || {}; + if (options.optionsSplit) { + gridEl.style.gridTemplateColumns = ""; + return; + } if (isMobileLayout()) { gridEl.style.gridTemplateColumns = options.statsFirst ? "1fr" @@ -1837,8 +1842,10 @@ wasMobile = nowMobile; const box = document.getElementById("monitor-grid"); if (box && lastMonitorRows.length) { + const split = monitorOptionsSplitActive(lastMonitorRows); syncMonitorGridColumns(box, lastMonitorRows.length + (lastMonitorTotals ? 1 : 0), { - statsFirst: !!lastMonitorTotals, + statsFirst: !!lastMonitorTotals && !split, + optionsSplit: split, }); updateMonitorAlertSummary(lastMonitorRows); } @@ -2200,16 +2207,32 @@ } const mobileTiles = isMobileLayout() && !expandedExchangeId; const displayRows = mobileTiles ? sortRowsForMobileDashboard(rows) : rows; + const optionsSplit = monitorOptionsSplitActive(displayRows); + monitorGridOptionsSplit = optionsSplit; const showStatsCard = !expandedExchangeId; box.classList.toggle("grid-monitor-tiles", mobileTiles); - box.classList.toggle("grid-monitor-2x2", showStatsCard && !mobileTiles); + box.classList.toggle("grid-monitor-2x2", showStatsCard && !mobileTiles && !optionsSplit); box.classList.toggle("grid-monitor-with-stats", showStatsCard && mobileTiles); + box.classList.toggle("grid-monitor-options-split", optionsSplit && showStatsCard); try { const statsHtml = showStatsCard ? renderMonitorStatsCard(lastMonitorTotals) : ""; - const cardsHtml = - displayRows - .map((r) => (mobileTiles ? renderMonitorTile(r) : renderMonitorCard(r))) - .join("") || (showStatsCard ? "" : '
无已启用账户
'); + let cardsHtml = ""; + if (optionsSplit) { + const okxRow = displayRows.find((r) => rowHasOptionsLayout(r)); + const otherRows = displayRows.filter((r) => !rowHasOptionsLayout(r)); + const leftHtml = okxRow ? renderMonitorCard(okxRow, { okxSplit: true }) : ""; + const rightHtml = otherRows.map((r) => renderMonitorCard(r, { splitSide: true })).join(""); + cardsHtml = + `
` + + `
${leftHtml}
` + + `
${rightHtml}
` + + `
`; + } else { + cardsHtml = + displayRows + .map((r) => (mobileTiles ? renderMonitorTile(r) : renderMonitorCard(r))) + .join("") || (showStatsCard ? "" : '
无已启用账户
'); + } box.innerHTML = statsHtml + cardsHtml; if (showStatsCard && !cardsHtml && !statsHtml) { box.innerHTML = '
无已启用账户
'; @@ -2219,7 +2242,8 @@ box.innerHTML = `
监控区渲染失败:${esc(String(err && err.message ? err.message : err))}
`; } syncMonitorGridColumns(box, displayRows.length + (showStatsCard ? 1 : 0), { - statsFirst: showStatsCard, + statsFirst: showStatsCard && !optionsSplit, + optionsSplit: optionsSplit && showStatsCard, }); bindMonitorInteractions(box); if (window.TimeCloseUI && TimeCloseUI.tickLocalCountdowns) { @@ -3468,33 +3492,59 @@ return html; } - function renderOptionsMonitorSection(row) { - if (!rowHasOptionsLayout(row)) return ""; + function monitorOptionsSplitActive(rows) { + if (isMobileLayout() || expandedExchangeId) return false; + return (rows || []).some((r) => rowHasOptionsLayout(r)); + } + + function renderPerpetualInnerCard(row, ag, pos, orders, trends, tickMap, intraday) { + let html = '
'; + html += '
永续账户
'; + html += '
'; + html += renderAccountStatRow(row, ag); + html += renderPerpetualPositionsSection(row, ag, pos, orders, trends, tickMap, intraday); + html += "
"; + return html; + } + + function renderOptionsSectionBody(row) { const opt = row.options || {}; - let html = '
'; - html += '
期权账户
'; + let html = ""; if (opt.enabled === false) { html += renderStatRow(null, null, null); html += '
期权持仓
'; html += '
期权未启用(OKX_OPTIONS_ENABLED)
'; - html += "
"; - return html; - } - if (opt.ok === false) { + } else if (opt.ok === false) { html += renderStatRow(null, null, null); html += '
期权持仓
'; html += `
${esc(opt.msg || "期权数据不可用")}
`; - html += ""; - return html; - } - const pos = Array.isArray(opt.positions) ? opt.positions : []; - const bal = optionsBalanceFields(opt); - html += renderStatRow(bal.funding, bal.trading, bal.upl); - html += `
期权持仓 · ${pos.length} 仓
`; - html += renderOptionsPositionsTable(pos); - if (row.flask_url_browser || row.flask_url) { - html += ``; + } else { + const pos = Array.isArray(opt.positions) ? opt.positions : []; + const bal = optionsBalanceFields(opt); + html += renderStatRow(bal.funding, bal.trading, bal.upl); + html += `
期权持仓 · ${pos.length} 仓
`; + html += renderOptionsPositionsTable(pos); + if (row.flask_url_browser || row.flask_url) { + html += ``; + } } + return html; + } + + function renderOptionsInnerCard(row) { + let html = '
'; + html += '
期权账户
'; + html += '
'; + html += renderOptionsSectionBody(row); + html += "
"; + return html; + } + + function renderOptionsMonitorSection(row) { + if (!rowHasOptionsLayout(row)) return ""; + let html = '
'; + html += '
期权账户
'; + html += renderOptionsSectionBody(row); html += "
"; return html; } @@ -3523,12 +3573,19 @@ const intraday = isIntradayDisciplineRow(row); let inner = ""; if (rowHasOptionsLayout(row)) { - inner += '
'; - inner += '
永续账户
'; - inner += renderAccountStatRow(row, ag); - inner += renderPerpetualPositionsSection(row, ag, pos, orders, trends, tickMap, intraday); - inner += "
"; - inner += renderOptionsMonitorSection(row); + if (monitorGridOptionsSplit) { + inner += '
'; + inner += renderPerpetualInnerCard(row, ag, pos, orders, trends, tickMap, intraday); + inner += renderOptionsInnerCard(row); + inner += "
"; + } else { + inner += '
'; + inner += '
永续账户
'; + inner += renderAccountStatRow(row, ag); + inner += renderPerpetualPositionsSection(row, ag, pos, orders, trends, tickMap, intraday); + inner += "
"; + inner += renderOptionsMonitorSection(row); + } } else { inner += renderAccountStatRow(row, ag); inner += renderPerpetualPositionsSection(row, ag, pos, orders, trends, tickMap, intraday); @@ -3878,7 +3935,8 @@ `; } - function renderMonitorCard(row) { + function renderMonitorCard(row, layoutOpts) { + const opts = layoutOpts || {}; const ag = row.agent || {}; const pos = Array.isArray(ag.positions) ? ag.positions : []; const hm = row.hub_monitor || {}; @@ -3924,7 +3982,12 @@ : ""; const intraday = isIntradayDisciplineRow(row); const fcHeadBadge = intraday ? forceCloseHeadBadgeHtml(row.force_close) : ""; - return `
+ const layoutCls = opts.okxSplit + ? " card-monitor-okx-split" + : opts.splitSide + ? " card-monitor-split-side" + : ""; + return `
diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index 56307fa..d470d2c 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -15,7 +15,7 @@ - + @@ -1151,6 +1151,6 @@ - +