From de5cd95742871691c24c23a6f79e9499281ac289 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Wed, 22 Apr 2026 11:39:52 +0800 Subject: [PATCH] 1 --- frontend/console.html | 408 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 329 insertions(+), 79 deletions(-) diff --git a/frontend/console.html b/frontend/console.html index b9b44db..939070f 100644 --- a/frontend/console.html +++ b/frontend/console.html @@ -131,7 +131,7 @@ .hero-subtitle { margin: 14px 0 24px; - max-width: 780px; + max-width: 860px; color: var(--muted); font-size: 15px; line-height: 1.7; @@ -148,6 +148,19 @@ border-radius: 18px; background: var(--panel-soft); border: 1px solid rgba(126, 200, 255, 0.08); + position: relative; + overflow: hidden; + } + + .hero-metric::after { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 3px; + background: linear-gradient(180deg, var(--cold), transparent); + opacity: 0.9; } .metric-label { @@ -172,6 +185,13 @@ color: var(--danger); } + .metric-note { + margin-top: 6px; + color: var(--muted); + font-size: 11px; + font-family: "IBM Plex Mono", monospace; + } + .hero-side { padding: 22px; display: flex; @@ -247,7 +267,7 @@ .layout { display: grid; - grid-template-columns: 1.2fr 0.8fr; + grid-template-columns: minmax(0, 1.35fr) minmax(360px, 0.78fr); gap: 18px; } @@ -257,6 +277,30 @@ gap: 18px; } + .section-label { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + border-radius: 999px; + background: rgba(126, 200, 255, 0.08); + border: 1px solid rgba(126, 200, 255, 0.16); + color: var(--cold); + font-size: 11px; + font-family: "IBM Plex Mono", monospace; + text-transform: uppercase; + letter-spacing: 0.08em; + } + + .section-label::before { + content: ""; + width: 8px; + height: 8px; + border-radius: 999px; + background: currentColor; + box-shadow: 0 0 10px currentColor; + } + .panel { padding: 22px; } @@ -282,12 +326,107 @@ font-family: "IBM Plex Mono", monospace; } + .priority-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(320px, 0.84fr); + gap: 18px; + margin-top: 18px; + } + + .priority-main, + .priority-side { + display: grid; + gap: 18px; + } + + .health-ribbon { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + margin-top: 12px; + } + + .health-card { + padding: 14px 16px; + border-radius: 16px; + background: rgba(255,255,255,0.03); + border: 1px solid rgba(255,255,255,0.05); + } + + .health-card .kicker { + color: var(--muted); + font-size: 11px; + margin-bottom: 6px; + text-transform: uppercase; + letter-spacing: 0.08em; + } + + .health-card .headline { + font-family: "IBM Plex Mono", monospace; + font-size: 14px; + color: var(--text); + } + + .health-card .headline.good { + color: var(--good); + } + + .health-card .headline.warn { + color: var(--warn); + } + + .health-card .headline.danger { + color: var(--danger); + } + + .health-card .detail { + margin-top: 6px; + color: var(--muted); + font-size: 11px; + line-height: 1.55; + font-family: "IBM Plex Mono", monospace; + } + .signal-grid, .platform-grid { display: grid; gap: 14px; } + .coord-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; + } + + .coord-block { + padding: 14px; + border-radius: 18px; + background: rgba(255,255,255,0.025); + border: 1px solid rgba(255,255,255,0.06); + } + + .coord-block .signal-grid { + grid-template-columns: 1fr; + } + + .block-head { + margin-bottom: 12px; + } + + .block-title { + margin: 10px 0 4px; + font-size: 16px; + font-weight: 600; + letter-spacing: -0.02em; + } + + .block-sub { + color: var(--muted); + font-size: 12px; + font-family: "IBM Plex Mono", monospace; + } + .signal-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } @@ -401,6 +540,10 @@ grid-template-columns: repeat(3, minmax(0, 1fr)); } + .platform-grid.compact { + grid-template-columns: 1fr; + } + .platform-card { padding: 18px; } @@ -467,6 +610,10 @@ padding: 18px; } + .dense-panel .panel-header { + margin-bottom: 12px; + } + .stream-list { display: grid; gap: 10px; @@ -805,7 +952,7 @@ .ops-grid { display: grid; gap: 18px; - grid-template-columns: 0.9fr 1.1fr; + grid-template-columns: 1fr; margin-top: 18px; } @@ -967,13 +1114,15 @@ @media (max-width: 1240px) { .hero, - .layout { + .layout, + .priority-layout { grid-template-columns: 1fr; } .platform-grid, .signal-grid, - .ops-grid { + .ops-grid, + .coord-grid { grid-template-columns: 1fr; } @@ -1000,7 +1149,8 @@ .hero-metrics, .platform-stats, .signal-stats, - .heartbeat-grid { + .heartbeat-grid, + .health-ribbon { grid-template-columns: 1fr; } @@ -1056,31 +1206,126 @@
-

平台执行概览

-
资金、杠杆、持仓、挂单、回撤阈值
+ +

系统健康总览

+
先判断系统活着没有,再判断风险和堵点
-
-
正在加载平台状态...
+
+
分析状态
-
+
最近轮次
-
+
人工处理
-
-
+
+
+
+
+
+ +

管理待处理事项

+
把需要你判断和干预的事情放到最前面
+
+
+
+
正在汇总待处理事项...
+
+
+ +
+
+
+ +

平台执行概览

+
资金、杠杆、持仓、挂单、回撤阈值
+
+
+
+
正在加载平台状态...
+
+
+
+ +
+
+
+
+ +

分析心跳与日志

+
没有信号时,也能确认系统仍在正常扫盘
+
+
+
+
最近心跳-
+
最近轮次-
+
当前进度-
+
下一次运行-
+
+
+
正在读取分析日志...
+
+
+
+
+ + +
+
-

管理待处理事项

-
风险、停机、异常、待成交提醒
+ +

信号与执行协同

+
把 Agent 状态和最近决策合并观察,减少来回切换
-
-
正在汇总待处理事项...
+
+
+
+ +

Crypto Agent 状态

+
最近信号、平台停机、执行层状态
+
+
+
正在读取 Agent 状态...
+
+
+
+
+ +

最近决策预览

+
最近一轮各平台准备执行的动作
+
+
+
正在读取决策预览...
+
+
-
+
-

最近信号流

+ +

平台停机 / 熔断

+
风险触发后,这里应当最先看到
+
+
+
+
正在读取平台停机状态...
+
+ +
+
+
+ +
+
+
+
+
+ +

最近信号流

数据库最新信号与等级/置信度
@@ -1091,52 +1336,11 @@
-
+
-

Crypto Agent 状态

-
最近信号、平台停机、执行层状态
-
-
-
-
正在读取 Agent 状态...
-
-
- -
-
-
-

分析心跳

-
没有信号时,用它判断系统是否仍在正常扫盘
-
-
-
-
最近心跳-
-
最近轮次-
-
当前进度-
-
下一次运行-
-
-
-
正在读取分析日志...
-
-
- -
-
-
-

最近决策预览

-
最近一轮各平台准备执行的动作
-
-
-
-
正在读取决策预览...
-
-
- -
-
-
-

执行事件流

+ +

执行事件流

最近执行结果、未执行原因、平台异常
@@ -1151,19 +1355,6 @@
正在读取执行事件...
- -
-
-
-

平台停机 / 熔断

-
出现风险时,这里是你第一眼要看的位置
-
-
-
-
正在读取平台停机状态...
-
- -
@@ -1171,7 +1362,8 @@
-

统一持仓视图

+ +

统一持仓视图

三端持仓合并,优先看风险与盈亏
@@ -1184,7 +1376,8 @@
-

统一挂单视图

+ +

统一挂单视图

入场单、保护单、资金占用一屏观察
@@ -1200,6 +1393,7 @@ let autoRefresh = true; let timer = null; let currentEventFilter = 'all'; + let cachedExecutionEvents = []; function formatNumber(value, digits = 2) { const num = Number(value || 0); @@ -1270,6 +1464,56 @@ el.innerHTML = `
${message}
`; } + function toneClassForHealth(status) { + if (['error', 'failed', 'stopped'].includes(String(status || '').toLowerCase())) return 'danger'; + if (['warning', 'halted', 'idle'].includes(String(status || '').toLowerCase())) return 'warn'; + return 'good'; + } + + function renderHealthRibbon(data) { + const container = document.getElementById('healthRibbon'); + const cryptoAgent = data.crypto_agent || {}; + const monitor = cryptoAgent.analysis_monitor || {}; + const attentionItems = data.management?.attention_items || []; + const dangerCount = attentionItems.filter((item) => item.severity === 'danger').length; + const warningCount = attentionItems.filter((item) => item.severity === 'warning').length; + const haltedCount = countHalted(cryptoAgent.platform_halts || {}); + const cycleStatus = cryptoAgent.running + ? String(monitor.last_cycle_status || 'waiting').toUpperCase() + : 'STOPPED'; + const cycleTone = toneClassForHealth(cryptoAgent.running ? monitor.last_cycle_status : 'stopped'); + const attentionTone = attentionItems.length === 0 + ? 'good' + : dangerCount > 0 || haltedCount > 0 + ? 'danger' + : 'warn'; + const analysisTone = toneClassForHealth(monitor.last_analysis_status || 'idle'); + const lastCycleText = monitor.last_cycle_completed_at + ? `${relativeTime(monitor.last_cycle_completed_at)}` + : '尚无完成轮次'; + const nextRunText = monitor.next_scheduled_run_at + ? formatTime(monitor.next_scheduled_run_at) + : (monitor.current_cycle_total ? '轮次进行中' : '-'); + + container.innerHTML = ` +
+
分析状态
+
${cycleStatus}
+
最近分析: ${(monitor.last_analysis_symbol || '-')} / ${(monitor.last_analysis_status || '-')}
+
+
+
最近轮次
+
${lastCycleText}
+
下一次运行: ${nextRunText}
+
+
+
人工处理
+
${attentionItems.length} 项
+
danger ${dangerCount} / warning ${warningCount} / halted ${haltedCount}
+
+ `; + } + function renderHero(data) { const heroMetrics = document.getElementById('heroMetrics'); const system = data.system || {}; @@ -1282,18 +1526,22 @@
运行 Agent
${system.running_agents || 0}/${system.total_agents || 0}
+
异常 ${system.error_agents || 0} 个
30 分钟信号
${signals.recent_30m_count || 0}
+
最新分析窗口
活跃持仓
${sumPlatformPositions(platforms)}
+
三端持仓合计
停机平台
${haltedCount}
+
熔断或人工停机
`; @@ -1599,9 +1847,10 @@ }); } - function renderExecutionEvents(events) { + function renderExecutionEvents(events = cachedExecutionEvents) { const container = document.getElementById('eventStream'); - const filtered = (events || []).filter((event) => currentEventFilter === 'all' || event.status === currentEventFilter); + cachedExecutionEvents = Array.isArray(events) ? events : []; + const filtered = cachedExecutionEvents.filter((event) => currentEventFilter === 'all' || event.status === currentEventFilter); if (!filtered || filtered.length === 0) { container.innerHTML = '
最近还没有执行事件
'; return; @@ -1790,6 +2039,7 @@ const data = result.data || {}; renderHero(data); + renderHealthRibbon(data); renderPlatforms(data.platforms, data.crypto_agent?.platform_halts); renderSignalStream(data.signals?.latest || []); renderAgentSignals(data.crypto_agent, data.signals); @@ -1834,7 +2084,7 @@ document.getElementById('eventFilters').querySelectorAll('[data-filter]').forEach((item) => { item.classList.toggle('active', item === button); }); - loadConsole(); + renderExecutionEvents(); }); });