From e89708726fe5bed66f2f2a54e36d7917031ac300 Mon Sep 17 00:00:00 2001 From: dekun Date: Mon, 25 May 2026 07:46:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=8B=E6=9C=BA=E7=AB=AF?= =?UTF-8?q?=E8=87=AA=E9=80=82=E5=BA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manual_trading_hub/hub.py | 2 +- manual_trading_hub/static/app.css | 331 ++++++++++++++++++++++++++- manual_trading_hub/static/app.js | 41 +++- manual_trading_hub/static/index.html | 6 +- manual_trading_hub/static/login.html | 4 +- 5 files changed, 367 insertions(+), 17 deletions(-) diff --git a/manual_trading_hub/hub.py b/manual_trading_hub/hub.py index b0c5a89..a127366 100644 --- a/manual_trading_hub/hub.py +++ b/manual_trading_hub/hub.py @@ -43,7 +43,7 @@ HUB_BRIDGE_TOKEN = (os.getenv("HUB_BRIDGE_TOKEN") or os.getenv("CONTROL_TOKEN") _trust_raw = (os.getenv("HUB_TRUST_LAN", "true") or "").strip().lower() HUB_TRUST_LAN = _trust_raw not in ("0", "false", "no", "off") DIR = Path(__file__).resolve().parent -HUB_BUILD = "20260525-fs-pos-row" +HUB_BUILD = "20260525-mobile" def _is_local(host: str | None) -> bool: diff --git a/manual_trading_hub/static/app.css b/manual_trading_hub/static/app.css index 065d804..e7718e1 100644 --- a/manual_trading_hub/static/app.css +++ b/manual_trading_hub/static/app.css @@ -604,16 +604,73 @@ body.hub-fullscreen-open { margin-bottom: 14px; } -/* 全屏放大:持仓卡片横向排列 */ +/* 全屏放大:持仓卡片横向排列,列数随仓位数量自适应 */ .exchange-fullscreen .hub-pos-list { display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; - align-items: start; + align-items: stretch; + width: 100%; +} + +.exchange-fullscreen .hub-pos-list.count-1 { + grid-template-columns: minmax(0, 1fr); +} + +.exchange-fullscreen .hub-pos-list.count-1 .hub-pos-card.pos-card { + max-width: min(960px, 100%); + margin-inline: auto; + width: 100%; +} + +.exchange-fullscreen .hub-pos-list.count-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.exchange-fullscreen .hub-pos-list.count-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.exchange-fullscreen .hub-pos-list.count-4 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.exchange-fullscreen .hub-pos-list.count-5, +.exchange-fullscreen .hub-pos-list.count-6 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.exchange-fullscreen .hub-pos-list.count-many { + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); } .exchange-fullscreen .hub-pos-card.pos-card { min-width: 0; + height: 100%; +} + +@media (max-width: 1100px) { + .exchange-fullscreen .hub-pos-list.count-3, + .exchange-fullscreen .hub-pos-list.count-5, + .exchange-fullscreen .hub-pos-list.count-6 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .exchange-fullscreen .hub-pos-list.count-many { + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + } +} + +@media (max-width: 640px) { + .exchange-fullscreen .hub-pos-list.count-2, + .exchange-fullscreen .hub-pos-list.count-3, + .exchange-fullscreen .hub-pos-list.count-4, + .exchange-fullscreen .hub-pos-list.count-5, + .exchange-fullscreen .hub-pos-list.count-6, + .exchange-fullscreen .hub-pos-list.count-many { + grid-template-columns: minmax(0, 1fr); + } + .exchange-fullscreen .hub-pos-list.count-1 .hub-pos-card.pos-card { + max-width: 100%; + } } /* 对齐实盘「实时持仓」pos-card */ @@ -1132,8 +1189,15 @@ button.btn-sm { gap: 8px; } +.table-scroll { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + max-width: 100%; +} + .data-table { width: 100%; + min-width: 300px; border-collapse: collapse; font-size: 11px; } @@ -1436,16 +1500,267 @@ body.login-page { font-size: 10px; } +/* —— 手机 / 窄屏自适应 —— */ @media (max-width: 720px) { .app-shell { - padding: 0 12px 32px; + padding: 0 max(12px, env(safe-area-inset-right)) max(28px, env(safe-area-inset-bottom)) + max(12px, env(safe-area-inset-left)); } - .grid-monitor, - .settings-grid-wrap { - grid-template-columns: 1fr; + + .app-header { + flex-direction: column; + align-items: stretch; + gap: 12px; + padding: 14px 0; } + .header-right { width: 100%; - justify-content: space-between; + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .sys-pill { + align-self: flex-start; + } + + .top-nav { + width: 100%; + justify-content: stretch; + } + + .top-nav a { + flex: 1; + text-align: center; + padding: 10px 12px; + min-height: 44px; + display: flex; + align-items: center; + justify-content: center; + } + + button.ghost#btn-logout { + width: 100%; + min-height: 44px; + } + + .page-head { + margin: 16px 0 12px; + } + + .page-head h1 { + font-size: 17px; + flex-wrap: wrap; + } + + .toolbar { + flex-direction: column; + align-items: stretch; + gap: 8px; + } + + .toolbar-spacer { + display: none; + } + + .toolbar-meta { + text-align: center; + order: 10; + } + + .toolbar button, + .toolbar .chk-label { + width: 100%; + justify-content: center; + min-height: 44px; + } + + .grid-monitor, + .settings-grid-wrap { + grid-template-columns: minmax(0, 1fr) !important; + gap: 12px; + } + + .card-head { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .card-actions { + flex-wrap: wrap; + width: 100%; + gap: 8px; + } + + .card-actions .btn-link, + .card-actions button { + flex: 1 1 calc(50% - 4px); + min-height: 40px; + text-align: center; + justify-content: center; + } + + .card-body { + padding: 12px; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + + .stat-row { + grid-template-columns: 1fr 1fr; + gap: 8px; + } + + .stat-value { + font-size: 15px; + } + + .exchange-fullscreen { + padding: max(10px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right)) + max(16px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left)); + } + + .exchange-fullscreen-panel { + max-width: 100%; + } + + .fs-head { + flex-direction: column; + align-items: stretch; + gap: 12px; + } + + .fs-head-actions { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + width: 100%; + } + + .fs-head-actions .btn-expand-back { + grid-column: 1 / -1; + } + + .fs-head-actions .btn-link, + .fs-head-actions button { + min-height: 44px; + text-align: center; + justify-content: center; + } + + .hub-pos-card .pos-card-head { + flex-direction: column; + align-items: stretch; + } + + .hub-pos-card .pos-head-actions { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + width: 100%; + } + + .hub-pos-card .pos-entrust-btn, + .hub-pos-card .pos-close-btn { + width: 100%; + min-height: 44px; + text-align: center; + } + + .hub-pos-card .pos-ex-order-row { + flex-direction: column; + align-items: stretch; + gap: 6px; + } + + .settings-grid { + grid-template-columns: 1fr; + } + + .settings-card-foot { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .settings-card-foot .field { + max-width: none; + } + + .modal { + padding: max(12px, env(safe-area-inset-top)) 12px max(12px, env(safe-area-inset-bottom)); + align-items: flex-end; + } + + .modal-panel { + max-width: none; + width: 100%; + border-radius: 12px 12px 0 0; + max-height: 90vh; + overflow-y: auto; + } + + .modal-actions { + flex-direction: column-reverse; + } + + .modal-actions button { + width: 100%; + min-height: 44px; + } + + #toast { + left: 12px; + right: 12px; + bottom: max(12px, env(safe-area-inset-bottom)); + max-width: none; + } + + body.login-page { + padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom)); + } + + .login-panel { + padding: 22px 18px; + } +} + +@media (max-width: 480px) { + body { + font-size: 12px; + } + + .brand-title { + font-size: 13px; + } + + .stat-row { + grid-template-columns: 1fr; + } + + .card-actions .btn-link, + .card-actions button { + flex: 1 1 100%; + } + + .fs-head-actions { + grid-template-columns: 1fr; + } + + .pos-action-group { + flex-direction: column; + align-items: stretch; + width: 100%; + } + + .pos-action-group .btn-sm { + width: 100%; + min-height: 44px; + } + + .data-table .td-actions { + white-space: normal; } } diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js index 2252e95..a717b13 100644 --- a/manual_trading_hub/static/app.js +++ b/manual_trading_hub/static/app.js @@ -61,6 +61,14 @@ return n > 0 ? "pnl-pos" : "pnl-neg"; } + /** 全屏持仓区:按仓位数量附加布局 class(1~6 固定列数,7+ 自动填充) */ + function hubPosListCountClass(n) { + const c = Math.max(0, parseInt(n, 10) || 0); + if (c <= 0) return "count-0"; + if (c <= 6) return `count-${c}`; + return "count-many"; + } + function currentPage() { const p = window.location.pathname.replace(/\/$/, "") || "/monitor"; if (p.includes("settings")) return "settings"; @@ -103,9 +111,17 @@ return (settingsCache?.exchanges || []).filter((x) => x.enabled); } - /** 监控卡片列数:3 个一行;4 个 2×2;5/6 个两行(每行最多 3) */ + function isMobileLayout() { + return window.matchMedia("(max-width: 720px)").matches; + } + + /** 监控卡片列数:桌面 3/2 列;手机端固定单列 */ function syncMonitorGridColumns(gridEl, count) { if (!gridEl) return; + if (isMobileLayout()) { + gridEl.style.gridTemplateColumns = "minmax(0, 1fr)"; + return; + } let cols = 3; if (count <= 1) cols = 1; else if (count === 2) cols = 2; @@ -115,6 +131,19 @@ gridEl.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`; } + function initMobileLayout() { + let resizeTimer = null; + window.addEventListener("resize", () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { + const box = document.getElementById("monitor-grid"); + if (box && lastMonitorRows.length) { + syncMonitorGridColumns(box, lastMonitorRows.length); + } + }, 120); + }); + } + function normSym(s) { return String(s || "") .toUpperCase() @@ -538,6 +567,7 @@ const slAttr = esc(String(guess.sl)).replace(/"/g, """); const tpAttr = esc(String(guess.tp)).replace(/"/g, """); return `
+
@@ -552,6 +582,7 @@
合约方向张数浮盈操作
${esc(x.symbol)}
+
${renderOrdersCollapse(exchangeId, x.symbol, cond, reg)}
`; } @@ -644,8 +675,11 @@
余额
${fmt(ag.balance_usdt, 2)} U
浮盈合计
${fmt(ag.total_unrealized_pnl, 4)}
`; - html += '
持仓(每币种一卡)
'; - if (pos.length) { + const posCount = pos.length; + const posListCls = hubPosListCountClass(posCount); + html += `
持仓(${posCount} 仓 · 每币种一卡)
`; + html += `
`; + if (posCount) { pos.forEach((p) => { html += renderLivePositionCard(row.id, p, findMonitorOrder(orders, p.symbol, p.side)); }); @@ -1087,6 +1121,7 @@ initTpslModal(); initFullscreen(); + initMobileLayout(); initAuth().then((ok) => { if (!ok) return; diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index cdbfd24..d26b655 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -2,12 +2,12 @@ - + 复盘系统中控 - + @@ -108,6 +108,6 @@
- + diff --git a/manual_trading_hub/static/login.html b/manual_trading_hub/static/login.html index 6f3f28f..a3dde65 100644 --- a/manual_trading_hub/static/login.html +++ b/manual_trading_hub/static/login.html @@ -2,12 +2,12 @@ - + 登录 · 复盘系统中控 - +