From ec3156021a7993ed611c917d7fe958add8ddfeb8 Mon Sep 17 00:00:00 2001 From: dekun Date: Sun, 2 Aug 2026 12:07:42 +0800 Subject: [PATCH] Add status column with blinking dots, widen shell, risk/open format. Co-authored-by: Cursor --- control/frontend/src/pages/Monitor.tsx | 27 ++++++--- control/frontend/src/styles.css | 81 ++++++++++++++++++++------ 2 files changed, 80 insertions(+), 28 deletions(-) diff --git a/control/frontend/src/pages/Monitor.tsx b/control/frontend/src/pages/Monitor.tsx index 8e03898..f9cde56 100644 --- a/control/frontend/src/pages/Monitor.tsx +++ b/control/frontend/src/pages/Monitor.tsx @@ -106,7 +106,7 @@ type RiskLines = { lossPct: string | null; exit: string; openRatio: string | null; - /** 表格「风险/开仓」列:以损显示风险%;手动显示名义比例 */ + /** 表格「风险/开仓」列:如 5%/1:2;手动为 —/名义比 */ riskOrOpen: string; leverage: string; }; @@ -144,7 +144,7 @@ function riskLines(strat: Record): RiskLines { const openRatio = riskBased ? `${unitLabel(strat.risk_perp_unit)}:${unitLabel(strat.risk_option_unit)}` : `${unitLabel(strat.perp_qty_eth)}:${unitLabel(strat.option_qty_eth)}`; - const riskOrOpen = riskBased ? lossPct || "—" : openRatio; + const riskOrOpen = `${lossPct || "—"}/${openRatio}`; return { riskBased, sizing: riskBased ? "以损定仓" : "手动仓位", @@ -516,6 +516,7 @@ export default function MonitorPage() { 选择 + 状态 机器名字 模式 交易所 @@ -550,30 +551,38 @@ export default function MonitorPage() { aria-label={`选择 ${n.name}`} /> - -
+ +
- {n.name}
+ + {n.name} + {modeLabel(s.mode, s.strat)} {s.exchange} {inPos ? "持仓中" : "无持仓"} {s.rounds ?? "—"} - + {risk.riskOrOpen} {risk.exit} diff --git a/control/frontend/src/styles.css b/control/frontend/src/styles.css index 8de8685..0364f45 100644 --- a/control/frontend/src/styles.css +++ b/control/frontend/src/styles.css @@ -25,9 +25,11 @@ body { } .shell { - max-width: 1200px; + width: 100%; + max-width: min(1920px, 100%); margin: 0 auto; - padding: 16px 20px 40px; + padding: 16px 24px 40px; + box-sizing: border-box; } .header { @@ -211,17 +213,20 @@ input { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); + width: 100%; } .monitor-table { width: 100%; + min-width: 1100px; border-collapse: collapse; - font-size: 0.88rem; + font-size: 0.9rem; + table-layout: auto; } .monitor-table th, .monitor-table td { - padding: 10px 12px; + padding: 12px 14px; border-bottom: 1px solid var(--line); text-align: left; white-space: nowrap; @@ -254,53 +259,91 @@ input { } .monitor-table .col-check { - width: 36px; + width: 40px; text-align: center; } +.monitor-table .col-status { + width: 88px; +} + .monitor-table .col-actions { white-space: normal; } -.machine-cell { +.status-dots { display: inline-flex; align-items: center; - gap: 6px; - min-width: 0; + gap: 8px; } .machine-name { font-weight: 650; + font-size: 0.95rem; } -.dot, -.run-dot { - width: 8px; - height: 8px; +.dot { + width: 9px; + height: 9px; border-radius: 50%; flex: 0 0 auto; background: var(--muted); } -.dot.ok, -.run-dot.ok { +.dot.ok { background: var(--ok, #3cb371); - box-shadow: 0 0 0 2px rgba(60, 179, 113, 0.2); + box-shadow: 0 0 0 2px rgba(60, 179, 113, 0.25); } -.dot.bad, -.run-dot.bad { +.dot.bad { background: var(--bad, #e35d5d); - box-shadow: 0 0 0 2px rgba(227, 93, 93, 0.2); + box-shadow: 0 0 0 2px rgba(227, 93, 93, 0.25); } .dot.warn { background: #e6a23c; - box-shadow: 0 0 0 2px rgba(230, 162, 60, 0.25); + box-shadow: 0 0 0 2px rgba(230, 162, 60, 0.3); } .dot.muted { background: #5a6570; + box-shadow: 0 0 0 2px rgba(90, 101, 112, 0.25); +} + +.dot.pulse { + animation: status-blink 1.4s ease-in-out infinite; +} + +.dot.pulse.d1 { + animation-delay: 0s; +} + +.dot.pulse.d2 { + animation-delay: 0.25s; +} + +.dot.pulse.d3 { + animation-delay: 0.5s; +} + +@keyframes status-blink { + 0%, + 100% { + opacity: 1; + transform: scale(1); + filter: brightness(1.15); + } + 50% { + opacity: 0.35; + transform: scale(0.85); + filter: brightness(0.85); + } +} + +@media (prefers-reduced-motion: reduce) { + .dot.pulse { + animation: none; + } } .row-actions {