diff --git a/onchain_scout_gate/static/app.js b/onchain_scout_gate/static/app.js index aad6d41..0a5b12c 100644 --- a/onchain_scout_gate/static/app.js +++ b/onchain_scout_gate/static/app.js @@ -721,28 +721,59 @@ function syncKeySlTpFields() { if (tpEl) tpEl.style.display = mode === "trend_manual" ? "" : "none"; } +function wireKeyMonitorSegGroup(groupId, hiddenInputId, onChange) { + const group = document.getElementById(groupId); + const hidden = document.getElementById(hiddenInputId); + if (!group || !hidden) return; + group.addEventListener("click", (ev) => { + const btn = ev.target.closest && ev.target.closest(".matrix-seg-btn"); + if (!btn) return; + group.querySelectorAll(".matrix-seg-btn").forEach((b) => b.classList.remove("is-active")); + btn.classList.add("is-active"); + hidden.value = btn.getAttribute("data-value") || ""; + if (onChange) onChange(); + }); +} + +function initKeyMonitorSegGroups() { + wireKeyMonitorSegGroup("keyMonitorTypeSeg", "keyMonitorTypeInput"); + wireKeyMonitorSegGroup("keyDirectionSeg", "keyDirectionInput"); + wireKeyMonitorSegGroup("keySlTpModeSeg", "keySlTpModeInput", syncKeySlTpFields); +} + +function renderKeyMonitorActiveList(rows) { + const target = document.getElementById("keyMonitorActive"); + if (!target) return; + target.innerHTML = ""; + if (!rows.length) { + target.innerHTML = '
暂无监控中的关键位
'; + return; + } + rows.forEach((row) => { + const prev = row.preview || {}; + const checks = prev.checks || {}; + const gateOk = prev.gate_ok ? "门控通过" : "门控未过"; + const gateClass = prev.gate_ok ? "key-gate-ok" : "key-gate-pending"; + const dir = row.direction === "long" ? "多" : "空"; + const modeLabel = row.sl_tp_mode === "trend_manual" ? "趋势" : "标准"; + const el = document.createElement("div"); + el.className = "item matrix-list-item key-monitor-row"; + el.innerHTML = ` +
+
${row.symbol} ${dir} · ${row.monitor_type} · ${modeLabel}
+
上 ${row.upper} / 下 ${row.lower} · ${gateOk} · 确认 ${checks.confirm_close != null ? checks.confirm_close : "—"}
+
+ + `; + target.appendChild(el); + }); +} + function renderKeyMonitors(data) { const rule = document.getElementById("keyMonitorRule"); if (rule && data.rule_text) rule.textContent = `// ${data.rule_text}`; - const active = data.active || []; - renderItems("keyMonitorActive", active, (row) => { - const prev = row.preview || {}; - const checks = prev.checks || {}; - const gateOk = prev.gate_ok ? '门控通过' : '门控未过'; - const dir = row.direction === "long" ? "做多" : "做空"; - const modeLabel = row.sl_tp_mode === "trend_manual" ? "趋势突破" : "标准突破"; - return ` -
${row.symbol} ${dir} · ${row.monitor_type} · ${modeLabel}
-
上 ${row.upper} / 下 ${row.lower} · 保本 ${row.breakeven_enabled ? "开" : "关"}
-
${gateOk} · 确认收盘 ${checks.confirm_close != null ? checks.confirm_close : "—"}
- - `; - }); - if (!active.length) { - const t = document.getElementById("keyMonitorActive"); - if (t) t.innerHTML = '
暂无监控中的关键位
'; - } + renderKeyMonitorActiveList(data.active || []); const hist = data.history || []; renderItems("keyMonitorHistory", hist.slice(0, 80), (h) => { @@ -804,8 +835,7 @@ async function addKeyMonitor() { } function wireKeyMonitorPanel() { - const modeSel = document.getElementById("keySlTpModeInput"); - if (modeSel) modeSel.addEventListener("change", syncKeySlTpFields); + initKeyMonitorSegGroups(); syncKeySlTpFields(); const addBtn = document.getElementById("keyAddBtn"); if (addBtn) addBtn.addEventListener("click", addKeyMonitor); diff --git a/onchain_scout_gate/static/style.css b/onchain_scout_gate/static/style.css index bc6123b..4ab3ed3 100644 --- a/onchain_scout_gate/static/style.css +++ b/onchain_scout_gate/static/style.css @@ -1249,6 +1249,87 @@ pre { box-shadow: -6px 0 20px rgba(255, 0, 170, 0.12); } +/* Key monitor — segmented toggles + compact active rows */ +.matrix-seg-group { + display: inline-flex; + flex-shrink: 0; + border: 1px solid rgba(0, 255, 213, 0.28); + border-radius: 2px; + overflow: hidden; +} + +.matrix-seg-btn { + border: none; + background: rgba(0, 12, 10, 0.75); + color: #3d8f7a; + font-family: inherit; + font-size: 10px; + letter-spacing: 0.06em; + padding: 8px 10px; + cursor: pointer; + transition: background 0.15s ease, color 0.15s ease; +} + +.matrix-seg-btn:not(:last-child) { + border-right: 1px solid rgba(0, 255, 213, 0.2); +} + +.matrix-seg-btn:hover { + color: #00ffd5; + background: rgba(0, 255, 213, 0.08); +} + +.matrix-seg-btn.is-active { + color: #0a1512; + background: rgba(0, 255, 213, 0.85); + font-weight: 600; +} + +.key-monitor-form .key-monitor-field { + min-width: 5.5rem; + flex: 0 1 auto; +} + +.key-monitor-form .key-monitor-tp { + min-width: 5rem; +} + +.key-monitor-form .key-monitor-check { + flex-shrink: 0; +} + +.key-monitor-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 8px 10px !important; +} + +.key-monitor-row-body { + flex: 1; + min-width: 0; + line-height: 1.35; +} + +.key-monitor-row .key-monitor-del { + flex-shrink: 0; + margin: 0; + padding: 5px 8px; + font-size: 9px; + letter-spacing: 0.04em; + white-space: nowrap; + min-height: auto; +} + +.key-gate-ok { + color: #4cd97f; +} + +.key-gate-pending { + color: #8892b0; +} + .matrix-card { transition: transform 0.2s ease, box-shadow 0.2s ease; } @@ -1681,6 +1762,25 @@ body.matrix-theme { min-height: 44px; } + .key-monitor-form .matrix-seg-group { + width: fit-content; + align-self: flex-start; + } + + .key-monitor-form .matrix-seg-btn { + min-height: 36px; + padding: 8px 12px; + } + + .key-monitor-form .key-monitor-add { + width: 100%; + } + + .key-monitor-row .key-monitor-del { + padding: 4px 6px; + font-size: 8px; + } + .matrix-grid { grid-template-columns: 1fr; } diff --git a/onchain_scout_gate/templates/dashboard.html b/onchain_scout_gate/templates/dashboard.html index a03efd1..aa60e94 100644 --- a/onchain_scout_gate/templates/dashboard.html +++ b/onchain_scout_gate/templates/dashboard.html @@ -134,28 +134,30 @@

GEMMA 漏斗仅供参考;在此录入上/下沿。箱体突破与收敛突破均支持「标准突破」或「趋势突破」(无 1.5H 方案)。

// 规则加载中…

-
- - - - - - - -