1
This commit is contained in:
parent
80f5e65d1d
commit
ef0d40dbd1
@ -350,6 +350,133 @@
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.focus-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.focus-summary-card {
|
||||
padding: 14px 16px;
|
||||
border-radius: 16px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.focus-summary-card .kicker {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
}
|
||||
|
||||
.focus-summary-card .headline {
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
font-size: 18px;
|
||||
color: var(--text);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.focus-summary-card .headline.good {
|
||||
color: var(--good);
|
||||
}
|
||||
|
||||
.focus-summary-card .headline.warn {
|
||||
color: var(--warn);
|
||||
}
|
||||
|
||||
.focus-summary-card .headline.danger {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.focus-summary-card .detail {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.alert-strip {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.alert-strip:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert-banner {
|
||||
display: grid;
|
||||
grid-template-columns: 140px 1fr auto;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
background: rgba(255,255,255,0.03);
|
||||
}
|
||||
|
||||
.alert-banner.danger {
|
||||
border-color: rgba(255, 111, 97, 0.26);
|
||||
background: rgba(255, 111, 97, 0.10);
|
||||
}
|
||||
|
||||
.alert-banner.warn {
|
||||
border-color: rgba(255, 184, 77, 0.24);
|
||||
background: rgba(255, 184, 77, 0.10);
|
||||
}
|
||||
|
||||
.alert-banner.good {
|
||||
border-color: rgba(48, 209, 88, 0.22);
|
||||
background: rgba(48, 209, 88, 0.08);
|
||||
}
|
||||
|
||||
.alert-kicker {
|
||||
color: var(--text);
|
||||
font-size: 11px;
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.alert-detail {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.alert-count {
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
font-size: 18px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.focus-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(320px, 0.82fr) minmax(0, 1.18fr);
|
||||
gap: 18px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.focus-panel {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.overview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.18fr) minmax(320px, 0.82fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.overview-main,
|
||||
.overview-side {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.ops-panel-body {
|
||||
min-height: 320px;
|
||||
}
|
||||
@ -636,7 +763,7 @@
|
||||
}
|
||||
|
||||
.platform-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.platform-grid.compact {
|
||||
@ -647,12 +774,22 @@
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.platform-card.danger {
|
||||
border-color: rgba(255, 111, 97, 0.22);
|
||||
background: rgba(255, 111, 97, 0.06);
|
||||
}
|
||||
|
||||
.platform-card.warn {
|
||||
border-color: rgba(255, 184, 77, 0.18);
|
||||
background: rgba(255, 184, 77, 0.05);
|
||||
}
|
||||
|
||||
.platform-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.platform-title {
|
||||
@ -667,8 +804,38 @@
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.platform-overview {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.platform-pill-stat {
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(255,255,255,0.035);
|
||||
border: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
.platform-pill-stat .label {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: 10px;
|
||||
margin-bottom: 4px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
}
|
||||
|
||||
.platform-pill-stat .value {
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
font-size: 14px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.risk-band {
|
||||
margin: 12px 0 14px;
|
||||
margin: 8px 0 12px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
@ -683,7 +850,7 @@
|
||||
|
||||
.platform-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@ -705,6 +872,29 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.platform-detail-toggle {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.platform-detail-toggle summary {
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--cold);
|
||||
}
|
||||
|
||||
.platform-detail-toggle summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stream-card {
|
||||
padding: 18px;
|
||||
}
|
||||
@ -789,6 +979,21 @@
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.scroll-panel {
|
||||
max-height: 560px;
|
||||
overflow: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.log-shell {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.log-head {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.event-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@ -1557,7 +1762,9 @@
|
||||
@media (max-width: 1240px) {
|
||||
.hero,
|
||||
.layout,
|
||||
.priority-layout {
|
||||
.priority-layout,
|
||||
.focus-grid,
|
||||
.overview-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@ -1568,9 +1775,18 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.platform-overview,
|
||||
.platform-stats {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.hero-metrics {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.focus-summary {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
@ -1590,10 +1806,16 @@
|
||||
|
||||
.hero-metrics,
|
||||
.platform-stats,
|
||||
.platform-overview,
|
||||
.signal-stats,
|
||||
.heartbeat-grid,
|
||||
.health-ribbon,
|
||||
.ops-summary {
|
||||
.ops-summary,
|
||||
.focus-summary {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.alert-banner {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@ -1659,62 +1881,47 @@
|
||||
<div class="health-card"><div class="kicker">最近轮次</div><div class="headline">-</div></div>
|
||||
<div class="health-card"><div class="kicker">人工处理</div><div class="headline">-</div></div>
|
||||
</div>
|
||||
<div class="focus-summary" id="focusSummary">
|
||||
<div class="focus-summary-card"><div class="kicker">运行心跳</div><div class="headline">-</div></div>
|
||||
<div class="focus-summary-card"><div class="kicker">待执行机会</div><div class="headline">-</div></div>
|
||||
<div class="focus-summary-card"><div class="kicker">风险暴露</div><div class="headline">-</div></div>
|
||||
<div class="focus-summary-card"><div class="kicker">阻塞状态</div><div class="headline">-</div></div>
|
||||
</div>
|
||||
<div class="alert-strip" id="alertStrip"></div>
|
||||
</section>
|
||||
|
||||
<section class="priority-layout">
|
||||
<div class="priority-main">
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<div>
|
||||
<div class="section-label">Risk Layer</div>
|
||||
<h2 class="panel-title" style="margin-top: 12px;">管理待处理事项</h2>
|
||||
<div class="panel-sub">把需要你判断和干预的事情放到最前面</div>
|
||||
</div>
|
||||
<section class="panel workspace-panel">
|
||||
<div class="workspace-head">
|
||||
<div>
|
||||
<div class="section-label">Command Layer</div>
|
||||
<h2 class="panel-title" style="margin-top: 12px;">指挥台视图</h2>
|
||||
<div class="panel-sub">先看系统机会与风险,再下钻到运行细节和日志</div>
|
||||
</div>
|
||||
<div class="panel-actions">
|
||||
<button class="ghost-btn" id="toggleSensitiveBtn">敏感数据: 隐藏</button>
|
||||
<div class="workspace-tabs" data-tab-group="command">
|
||||
<button class="workspace-tab active" data-tab="command" data-target="commandOverview">总览</button>
|
||||
<button class="workspace-tab" data-tab="command" data-target="commandRisk">风险</button>
|
||||
<button class="workspace-tab" data-tab="command" data-target="commandRuntime">运行</button>
|
||||
<button class="workspace-tab" data-tab="command" data-target="commandLogs">日志</button>
|
||||
</div>
|
||||
<div class="attention-list" id="attentionList">
|
||||
<div class="loading">正在汇总待处理事项...</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="priority-side">
|
||||
<section class="panel dense-panel">
|
||||
<div class="workspace-head">
|
||||
<div>
|
||||
<div class="section-label">Operations Layer</div>
|
||||
<h2 class="panel-title" style="margin-top: 12px;">运行监控面板</h2>
|
||||
<div class="panel-sub">把平台概览、分析心跳和停机熔断收进一个运行面板,避免首屏纵向堆叠</div>
|
||||
</div>
|
||||
<div class="panel-actions">
|
||||
<button class="ghost-btn" id="toggleSensitiveBtn">敏感数据: 隐藏</button>
|
||||
<div class="workspace-tabs" data-tab-group="ops">
|
||||
<button class="workspace-tab active" data-tab="ops" data-target="opsPlatform">平台概览</button>
|
||||
<button class="workspace-tab" data-tab="ops" data-target="opsRuntime">心跳日志</button>
|
||||
<button class="workspace-tab" data-tab="ops" data-target="opsRisk">停机熔断</button>
|
||||
<div class="tab-pane active" data-tab-pane="command" id="commandOverview">
|
||||
<div class="overview-grid">
|
||||
<div class="overview-main">
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Decision Layer</div>
|
||||
<h3 class="block-title">最近决策预览</h3>
|
||||
<div class="block-sub">优先看系统准备做什么,而不是先看历史事件</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ops-summary" id="opsSummary">
|
||||
<button class="ops-summary-card active" data-ops-target="opsPlatform">
|
||||
<div class="ops-summary-kicker">平台概览</div>
|
||||
<div class="ops-summary-headline">-</div>
|
||||
<div class="ops-summary-detail">正在汇总平台状态...</div>
|
||||
</button>
|
||||
<button class="ops-summary-card" data-ops-target="opsRuntime">
|
||||
<div class="ops-summary-kicker">心跳日志</div>
|
||||
<div class="ops-summary-headline">-</div>
|
||||
<div class="ops-summary-detail">正在汇总分析状态...</div>
|
||||
</button>
|
||||
<button class="ops-summary-card" data-ops-target="opsRisk">
|
||||
<div class="ops-summary-kicker">停机熔断</div>
|
||||
<div class="ops-summary-headline">-</div>
|
||||
<div class="ops-summary-detail">正在汇总风险状态...</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="ops-panel-body">
|
||||
<div class="tab-pane active ops-pane" data-tab-pane="ops" id="opsPlatform">
|
||||
<div class="signal-grid" id="decisionPreview">
|
||||
<div class="loading">正在读取决策预览...</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Platform Layer</div>
|
||||
<h3 class="block-title">平台执行概览</h3>
|
||||
@ -1723,12 +1930,65 @@
|
||||
<div class="platform-grid" id="platformGrid">
|
||||
<div class="loading">正在加载平台状态...</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="overview-side">
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Risk Layer</div>
|
||||
<h3 class="block-title">管理待处理事项</h3>
|
||||
<div class="block-sub">把需要你判断和干预的事情放到最前面</div>
|
||||
</div>
|
||||
<div class="attention-list" id="attentionList">
|
||||
<div class="loading">正在汇总待处理事项...</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Market Layer</div>
|
||||
<h3 class="block-title">最近信号流</h3>
|
||||
<div class="block-sub">只保留紧凑摘要,不让历史列表抢占主视图</div>
|
||||
</div>
|
||||
<div class="stream-list workspace-stream scroll-panel" id="signalStream">
|
||||
<div class="loading">正在读取信号...</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane ops-pane" data-tab-pane="ops" id="opsRuntime">
|
||||
<div class="tab-pane" data-tab-pane="command" id="commandRisk">
|
||||
<div class="focus-grid">
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Risk Layer</div>
|
||||
<h3 class="block-title">平台停机 / 熔断</h3>
|
||||
<div class="block-sub">风险触发后,这里应当最先看到</div>
|
||||
</div>
|
||||
<div class="halt-list" id="haltList">
|
||||
<div class="loading">正在读取平台停机状态...</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Blocked Layer</div>
|
||||
<h3 class="block-title">最近阻塞原因</h3>
|
||||
<div class="block-sub">未落单、风险过滤、执行限制统一归因</div>
|
||||
</div>
|
||||
<div class="blocked-list" id="blockedSummaryList">
|
||||
<div class="loading">正在读取未落单汇总...</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" data-tab-pane="command" id="commandRuntime">
|
||||
<div class="focus-grid">
|
||||
<div class="overview-main">
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Runtime Layer</div>
|
||||
<h3 class="block-title">分析心跳与日志</h3>
|
||||
<h3 class="block-title">分析心跳</h3>
|
||||
<div class="block-sub">没有信号时,也能确认系统仍在正常扫盘</div>
|
||||
</div>
|
||||
<div class="heartbeat-grid" id="analysisHeartbeat">
|
||||
@ -1746,34 +2006,53 @@
|
||||
<div class="runtime-summary-title">执行监管器</div>
|
||||
<div class="runtime-summary-main">正在整理执行监管状态...</div>
|
||||
</div>
|
||||
<div class="runtime-summary-card">
|
||||
<div class="runtime-summary-title">最近阻塞原因</div>
|
||||
<div class="blocked-list" id="blockedSummaryList">
|
||||
<div class="loading">正在读取未落单汇总...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="overview-side">
|
||||
<section class="coord-block focus-panel">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Guardian Layer</div>
|
||||
<h3 class="block-title">执行监管目标</h3>
|
||||
<div class="block-sub">监管 target、保护单补救、持仓管理能力</div>
|
||||
</div>
|
||||
<div class="analysis-log-list" id="guardianTargetList">
|
||||
<div class="loading">正在读取执行监管目标...</div>
|
||||
</div>
|
||||
<div class="analysis-log-list" id="analysisLogList">
|
||||
<div class="loading">正在读取分析日志...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane ops-pane" data-tab-pane="ops" id="opsRisk">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Risk Layer</div>
|
||||
<h3 class="block-title">平台停机 / 熔断</h3>
|
||||
<div class="block-sub">风险触发后,这里应当最先看到</div>
|
||||
</div>
|
||||
<div class="halt-list" id="haltList">
|
||||
<div class="loading">正在读取平台停机状态...</div>
|
||||
</div>
|
||||
<div class="footer-note">建议挂在大屏或副屏,默认每 15 秒刷新。</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" data-tab-pane="command" id="commandLogs">
|
||||
<div class="workspace-head log-head">
|
||||
<div>
|
||||
<div class="section-label">Log Layer</div>
|
||||
<h3 class="block-title">运行与执行日志</h3>
|
||||
<div class="block-sub">日志放到最后一级,按需查看,不再占用首屏决策空间</div>
|
||||
</div>
|
||||
<div class="workspace-tabs" data-tab-group="logs">
|
||||
<button class="workspace-tab active" data-tab="logs" data-target="logsExecution">执行事件</button>
|
||||
<button class="workspace-tab" data-tab="logs" data-target="logsAnalysis">分析日志</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane active" data-tab-pane="logs" id="logsExecution">
|
||||
<div class="filter-row" id="eventFilters">
|
||||
<button class="filter-chip active" data-filter="all">All</button>
|
||||
<button class="filter-chip" data-filter="error">Error</button>
|
||||
<button class="filter-chip" data-filter="warning">Warning</button>
|
||||
<button class="filter-chip" data-filter="hold">Hold</button>
|
||||
<button class="filter-chip" data-filter="success">Success</button>
|
||||
</div>
|
||||
<div class="event-list workspace-stream scroll-panel" id="eventStream">
|
||||
<div class="loading">正在读取执行事件...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" data-tab-pane="logs" id="logsAnalysis">
|
||||
<div class="analysis-log-list scroll-panel" id="analysisLogList">
|
||||
<div class="loading">正在读取分析日志...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@ -1782,14 +2061,13 @@
|
||||
<section class="panel workspace-panel">
|
||||
<div class="workspace-head">
|
||||
<div>
|
||||
<div class="section-label">Workspace Layer</div>
|
||||
<h2 class="panel-title" style="margin-top: 12px;">交易工作区</h2>
|
||||
<div class="panel-sub">次级明细放进 tab,保留一个干净的管理视角</div>
|
||||
<div class="section-label">Drilldown Layer</div>
|
||||
<h2 class="panel-title" style="margin-top: 12px;">补充工作区</h2>
|
||||
<div class="panel-sub">首屏用于判断,补充区用于核对账户、信号缓存和明细状态</div>
|
||||
</div>
|
||||
<div class="workspace-tabs" data-tab-group="workspace">
|
||||
<button class="workspace-tab active" data-tab="workspace" data-target="workspaceCoordination">协同</button>
|
||||
<button class="workspace-tab" data-tab="workspace" data-target="workspaceSignals">信号流</button>
|
||||
<button class="workspace-tab" data-tab="workspace" data-target="workspaceExecution">执行流</button>
|
||||
<button class="workspace-tab active" data-tab="workspace" data-target="workspaceCoordination">账户协同</button>
|
||||
<button class="workspace-tab" data-tab="workspace" data-target="workspaceSignals">信号缓存</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1808,11 +2086,14 @@
|
||||
<div class="coord-block">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Decision Layer</div>
|
||||
<h3 class="block-title">最近决策预览</h3>
|
||||
<div class="block-sub">最近一轮各平台准备执行的动作</div>
|
||||
<h3 class="block-title">Agent / 账户协同</h3>
|
||||
<div class="block-sub">保留辅助视角,用于对照信号缓存与系统工作状态</div>
|
||||
</div>
|
||||
<div class="signal-grid" id="decisionPreview">
|
||||
<div class="loading">正在读取决策预览...</div>
|
||||
<div class="signal-grid" id="coordinationMirror">
|
||||
<div class="empty-box compact">
|
||||
<strong>主视图已迁移到指挥台</strong>
|
||||
<div class="empty-detail">最近决策预览已放到上方“总览”主标签,这里仅保留补充信息,避免重复占屏。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1821,29 +2102,14 @@
|
||||
<div class="tab-pane" data-tab-pane="workspace" id="workspaceSignals">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Market Layer</div>
|
||||
<h3 class="block-title">最近信号流</h3>
|
||||
<div class="block-sub">数据库最新信号与等级、置信度、时间分布</div>
|
||||
<h3 class="block-title">最近信号缓存</h3>
|
||||
<div class="block-sub">用于核对 Agent 最近缓存的信号,不再重复展示完整主信号流</div>
|
||||
</div>
|
||||
<div class="stream-list workspace-stream" id="signalStream">
|
||||
<div class="loading">正在读取信号...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" data-tab-pane="workspace" id="workspaceExecution">
|
||||
<div class="block-head">
|
||||
<div class="section-label">Execution Layer</div>
|
||||
<h3 class="block-title">执行事件流</h3>
|
||||
<div class="block-sub">最近执行结果、未执行原因、平台异常</div>
|
||||
</div>
|
||||
<div class="filter-row" id="eventFilters">
|
||||
<button class="filter-chip active" data-filter="all">All</button>
|
||||
<button class="filter-chip" data-filter="error">Error</button>
|
||||
<button class="filter-chip" data-filter="warning">Warning</button>
|
||||
<button class="filter-chip" data-filter="hold">Hold</button>
|
||||
<button class="filter-chip" data-filter="success">Success</button>
|
||||
</div>
|
||||
<div class="event-list workspace-stream" id="eventStream">
|
||||
<div class="loading">正在读取执行事件...</div>
|
||||
<div class="signal-grid" id="signalMirror">
|
||||
<div class="empty-box compact">
|
||||
<strong>主信号流已迁移到指挥台</strong>
|
||||
<div class="empty-detail">最近信号流已放到上方“总览”主标签,这里保留为信号缓存核对区。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -2111,6 +2377,118 @@
|
||||
setActiveTab('ops', getActiveTabTarget('ops') || 'opsPlatform');
|
||||
}
|
||||
|
||||
function renderFocusSummary(data) {
|
||||
const container = document.getElementById('focusSummary');
|
||||
if (!container) return;
|
||||
|
||||
const cryptoAgent = data.crypto_agent || {};
|
||||
const monitor = cryptoAgent.analysis_monitor || {};
|
||||
const previews = Object.values(cryptoAgent.last_execution_preview || {});
|
||||
const management = data.management || {};
|
||||
const positions = management.positions || [];
|
||||
const attentionItems = management.attention_items || [];
|
||||
const haltedCount = countHalted(cryptoAgent.platform_halts || {});
|
||||
const blockedEvents = (data.execution_events || []).filter((event) => event.event_type === 'execution_blocked_summary');
|
||||
const actionablePreviewCount = previews.filter((preview) => {
|
||||
const paperDecision = preview.paper?.decision;
|
||||
const bitgetDecision = preview.bitget?.decision;
|
||||
return ['OPEN', 'ADD', 'CLOSE', 'CANCEL_PENDING'].includes(paperDecision) ||
|
||||
['OPEN', 'ADD', 'CLOSE', 'CANCEL_PENDING'].includes(bitgetDecision);
|
||||
}).length;
|
||||
const heartbeatHeadline = monitor.last_heartbeat_at ? relativeTime(monitor.last_heartbeat_at) : '无心跳';
|
||||
const heartbeatTone = toneClassForHealth(cryptoAgent.running ? (monitor.last_cycle_status || monitor.last_analysis_status) : 'stopped');
|
||||
const exposureNotional = positions.reduce((sum, item) => sum + Number(item.notional || item.size || 0), 0);
|
||||
const riskTone = haltedCount > 0 ? 'danger' : (positions.length > 0 ? 'warn' : 'good');
|
||||
const blockedTone = blockedEvents.length > 0 || attentionItems.length > 0 ? 'warn' : 'good';
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="focus-summary-card">
|
||||
<div class="kicker">运行心跳</div>
|
||||
<div class="headline ${heartbeatTone}">${heartbeatHeadline}</div>
|
||||
<div class="detail">状态 ${String(monitor.last_cycle_status || monitor.last_analysis_status || 'idle').toUpperCase()} / 下次 ${monitor.next_scheduled_run_at ? formatTime(monitor.next_scheduled_run_at) : '-'}</div>
|
||||
</div>
|
||||
<div class="focus-summary-card">
|
||||
<div class="kicker">待执行机会</div>
|
||||
<div class="headline ${actionablePreviewCount > 0 ? 'good' : 'warn'}">${actionablePreviewCount}</div>
|
||||
<div class="detail">最近预览中可执行动作 ${actionablePreviewCount} 个</div>
|
||||
</div>
|
||||
<div class="focus-summary-card">
|
||||
<div class="kicker">风险暴露</div>
|
||||
<div class="headline ${riskTone}">${positions.length} 仓</div>
|
||||
<div class="detail">名义暴露 ${formatSensitiveMoney(exposureNotional)} / 停机 ${haltedCount}</div>
|
||||
</div>
|
||||
<div class="focus-summary-card">
|
||||
<div class="kicker">阻塞状态</div>
|
||||
<div class="headline ${blockedTone}">${blockedEvents.length}</div>
|
||||
<div class="detail">阻塞事件 ${blockedEvents.length} / 待处理 ${attentionItems.length}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderAlertStrip(data) {
|
||||
const container = document.getElementById('alertStrip');
|
||||
if (!container) return;
|
||||
|
||||
const cryptoAgent = data.crypto_agent || {};
|
||||
const monitor = cryptoAgent.analysis_monitor || {};
|
||||
const attentionItems = data.management?.attention_items || [];
|
||||
const haltedCount = countHalted(cryptoAgent.platform_halts || {});
|
||||
const disabledCount = Object.values(cryptoAgent.target_execution_controls || {}).filter((item) => item && item.enabled === false).length;
|
||||
const blockedEvents = (data.execution_events || []).filter((event) => event.event_type === 'execution_blocked_summary');
|
||||
const banners = [];
|
||||
|
||||
if (!cryptoAgent.running || !monitor.last_heartbeat_at) {
|
||||
banners.push({
|
||||
tone: 'danger',
|
||||
kicker: 'System Halt',
|
||||
detail: !cryptoAgent.running ? 'Crypto Agent 当前未运行。' : '尚未收到最近心跳,请优先确认调度与任务循环。',
|
||||
count: !cryptoAgent.running ? 'STOP' : 'NO HB',
|
||||
});
|
||||
}
|
||||
|
||||
if (haltedCount > 0) {
|
||||
banners.push({
|
||||
tone: 'danger',
|
||||
kicker: 'Platform Halt',
|
||||
detail: `当前有 ${haltedCount} 个执行目标处于停机/熔断状态,需要优先恢复或核查风控触发原因。`,
|
||||
count: `${haltedCount} HALT`,
|
||||
});
|
||||
} else if (disabledCount > 0) {
|
||||
banners.push({
|
||||
tone: 'warn',
|
||||
kicker: 'Manual Off',
|
||||
detail: `当前有 ${disabledCount} 个执行目标被人工关闭自动交易。`,
|
||||
count: `${disabledCount} OFF`,
|
||||
});
|
||||
}
|
||||
|
||||
if (blockedEvents.length > 0) {
|
||||
banners.push({
|
||||
tone: 'warn',
|
||||
kicker: 'Execution Blocked',
|
||||
detail: `最近存在 ${blockedEvents.length} 条执行阻塞汇总,建议切到“风险”标签查看平台级归因。`,
|
||||
count: `${blockedEvents.length} BLOCK`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!banners.length && attentionItems.length === 0) {
|
||||
banners.push({
|
||||
tone: 'good',
|
||||
kicker: 'System Clear',
|
||||
detail: '当前没有停机、阻塞或明显告警,系统处于可管理状态。',
|
||||
count: 'CLEAR',
|
||||
});
|
||||
}
|
||||
|
||||
container.innerHTML = banners.slice(0, 3).map((banner) => `
|
||||
<div class="alert-banner ${banner.tone}">
|
||||
<div class="alert-kicker">${banner.kicker}</div>
|
||||
<div class="alert-detail">${banner.detail}</div>
|
||||
<div class="alert-count">${banner.count}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function syncTabState(data) {
|
||||
const recentSignals = data.signals?.latest || [];
|
||||
const executionEvents = data.execution_events || [];
|
||||
@ -2126,30 +2504,42 @@
|
||||
const haltedCount = countHalted(data.crypto_agent?.platform_halts || {});
|
||||
const disabledCount = Object.values(data.crypto_agent?.target_execution_controls || {}).filter((item) => item && item.enabled === false).length;
|
||||
const runtimeCount = (data.crypto_agent?.recent_analysis_events || []).length + (data.crypto_agent?.analysis_monitor?.last_heartbeat_at ? 1 : 0);
|
||||
const blockedCount = (data.execution_events || []).filter((event) => event.event_type === 'execution_blocked_summary').length;
|
||||
|
||||
updateTabButton('ops', 'opsPlatform', '平台概览', platformCount, platformCount > 0);
|
||||
updateTabButton('ops', 'opsRuntime', '心跳日志', runtimeCount, runtimeCount > 0);
|
||||
updateTabButton('ops', 'opsRisk', '停机熔断', haltedCount + disabledCount, haltedCount + disabledCount > 0);
|
||||
updateTabButton('workspace', 'workspaceCoordination', '协同', coordinationCount, coordinationCount > 0);
|
||||
updateTabButton('workspace', 'workspaceSignals', '信号流', recentSignals.length, recentSignals.length > 0);
|
||||
updateTabButton('workspace', 'workspaceExecution', '执行流', executionEvents.length, executionEvents.length > 0);
|
||||
updateTabButton('command', 'commandOverview', '总览', coordinationCount + platformCount, coordinationCount + platformCount > 0);
|
||||
updateTabButton('command', 'commandRisk', '风险', haltedCount + disabledCount + blockedCount, haltedCount + disabledCount + blockedCount > 0);
|
||||
updateTabButton('command', 'commandRuntime', '运行', runtimeCount, runtimeCount > 0);
|
||||
updateTabButton('command', 'commandLogs', '日志', executionEvents.length + runtimeCount, executionEvents.length + runtimeCount > 0);
|
||||
updateTabButton('logs', 'logsExecution', '执行事件', executionEvents.length, executionEvents.length > 0);
|
||||
updateTabButton('logs', 'logsAnalysis', '分析日志', runtimeCount, runtimeCount > 0);
|
||||
updateTabButton('workspace', 'workspaceCoordination', '账户协同', coordinationCount, coordinationCount > 0);
|
||||
updateTabButton('workspace', 'workspaceSignals', '信号缓存', recentSignals.length, recentSignals.length > 0);
|
||||
updateTabButton('asset', 'assetPositions', '持仓', positions.length, positions.length > 0);
|
||||
updateTabButton('asset', 'assetOrders', '挂单', orders.length, orders.length > 0);
|
||||
|
||||
const opsCurrent = getActiveTabTarget('ops');
|
||||
const opsChoices = [
|
||||
{ target: 'opsRisk', hasData: haltedCount + disabledCount > 0 },
|
||||
{ target: 'opsRuntime', hasData: runtimeCount > 0 },
|
||||
{ target: 'opsPlatform', hasData: platformCount > 0 },
|
||||
const commandCurrent = getActiveTabTarget('command');
|
||||
const commandChoices = [
|
||||
{ target: 'commandRisk', hasData: haltedCount + disabledCount + blockedCount > 0 },
|
||||
{ target: 'commandOverview', hasData: coordinationCount + platformCount > 0 },
|
||||
{ target: 'commandRuntime', hasData: runtimeCount > 0 },
|
||||
{ target: 'commandLogs', hasData: executionEvents.length + runtimeCount > 0 },
|
||||
];
|
||||
if (!opsChoices.find((item) => item.target === opsCurrent && item.hasData)) {
|
||||
setActiveTab('ops', opsChoices.find((item) => item.hasData)?.target || 'opsPlatform');
|
||||
if (!commandChoices.find((item) => item.target === commandCurrent && item.hasData)) {
|
||||
setActiveTab('command', commandChoices.find((item) => item.hasData)?.target || 'commandOverview');
|
||||
}
|
||||
|
||||
const logsCurrent = getActiveTabTarget('logs');
|
||||
const logsChoices = [
|
||||
{ target: 'logsExecution', hasData: executionEvents.length > 0 },
|
||||
{ target: 'logsAnalysis', hasData: runtimeCount > 0 },
|
||||
];
|
||||
if (!logsChoices.find((item) => item.target === logsCurrent && item.hasData)) {
|
||||
setActiveTab('logs', logsChoices.find((item) => item.hasData)?.target || 'logsExecution');
|
||||
}
|
||||
|
||||
const workspaceCurrent = getActiveTabTarget('workspace');
|
||||
const workspaceChoices = [
|
||||
{ target: 'workspaceCoordination', hasData: coordinationCount > 0 },
|
||||
{ target: 'workspaceExecution', hasData: executionEvents.length > 0 },
|
||||
{ target: 'workspaceSignals', hasData: recentSignals.length > 0 },
|
||||
];
|
||||
if (!workspaceChoices.find((item) => item.target === workspaceCurrent && item.hasData)) {
|
||||
@ -2304,9 +2694,18 @@
|
||||
const disabledBitgetCount = key === 'bitget'
|
||||
? accounts.filter((accountItem) => executionControls?.[`Bitget:${accountItem.account_id}`]?.enabled === false).length
|
||||
: 0;
|
||||
const drawdownValue = risk.drawdown_percent || risk.drawdown || 0;
|
||||
const thresholdValue = risk.circuit_breaker_threshold || 25;
|
||||
const platformTone = (!enabled || halt.halted || bitgetHaltActive || drawdownValue >= thresholdValue)
|
||||
? 'danger'
|
||||
: ((key === 'paper' && platformExecutionControl?.enabled === false) || disabledBitgetCount > 0 || currentLeverage >= maxLeverage * 0.75)
|
||||
? 'warn'
|
||||
: '';
|
||||
const accountRows = key === 'bitget' && accounts.length
|
||||
? `
|
||||
<div class="target-grid">
|
||||
<details class="platform-detail-toggle">
|
||||
<summary>查看账号明细</summary>
|
||||
<div class="target-grid">
|
||||
${accounts.slice(0, 4).map((accountItem) => {
|
||||
const accountTargetKey = `Bitget:${accountItem.account_id}`;
|
||||
const accountHalt = platformHalts?.[accountTargetKey] || {};
|
||||
@ -2333,12 +2732,13 @@
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
`
|
||||
: '';
|
||||
|
||||
return `
|
||||
<article class="platform-card">
|
||||
<article class="platform-card ${platformTone}">
|
||||
<div class="platform-top">
|
||||
<div>
|
||||
<div class="panel-sub">${subtitle}</div>
|
||||
@ -2346,24 +2746,26 @@
|
||||
</div>
|
||||
<div class="platform-state">${!enabled ? 'DISABLED' : (halt.halted || bitgetHaltActive) ? 'HALTED' : ((key === 'paper' && platformExecutionControl?.enabled === false) ? 'MANUAL OFF' : 'ONLINE')}</div>
|
||||
</div>
|
||||
<div class="risk-band"><div class="risk-fill" style="width:${fill}%"></div></div>
|
||||
<div class="platform-stats">
|
||||
<div class="platform-stat">
|
||||
<div class="platform-overview">
|
||||
<div class="platform-pill-stat">
|
||||
<span class="label">权益</span>
|
||||
<span class="value">${formatSensitiveMoney(account.current_balance || account.account_value)}</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="label">可用</span>
|
||||
<span class="value">${formatSensitiveMoney(account.available || account.available_balance)}</span>
|
||||
<div class="platform-pill-stat">
|
||||
<span class="label">可用 / 保证金</span>
|
||||
<span class="value">${formatSensitiveMoney(account.available || account.available_balance)} / ${formatSensitiveMoney(account.used_margin || account.total_margin_used)}</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="label">持仓</span>
|
||||
<span class="value">${positions.count || 0}</span>
|
||||
<div class="platform-pill-stat">
|
||||
<span class="label">持仓 / 挂单</span>
|
||||
<span class="value">${positions.count || 0} / ${orders.count || 0}</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="label">挂单</span>
|
||||
<span class="value">${orders.count || 0}</span>
|
||||
<div class="platform-pill-stat">
|
||||
<span class="label">自动交易</span>
|
||||
<span class="value">${key === 'paper' ? (platformExecutionControl?.enabled === false ? 'OFF' : 'ON') : `${accounts.length - disabledBitgetCount}/${accounts.length} ON`}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="risk-band"><div class="risk-fill" style="width:${fill}%"></div></div>
|
||||
<div class="platform-stats">
|
||||
<div class="platform-stat">
|
||||
<span class="label">开仓单 / TP-SL</span>
|
||||
<span class="value">${orders.entry_orders || orders.pending_count || 0} / ${orders.tp_sl_orders || 0}</span>
|
||||
@ -2374,13 +2776,17 @@
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="label">回撤</span>
|
||||
<span class="value" style="color:${(risk.drawdown_percent || risk.drawdown || 0) >= (risk.circuit_breaker_threshold || 25) ? 'var(--danger)' : 'var(--text)'};">
|
||||
${formatPercent(risk.drawdown_percent || risk.drawdown || 0)}
|
||||
<span class="value" style="color:${drawdownValue >= thresholdValue ? 'var(--danger)' : 'var(--text)'};">
|
||||
${formatPercent(drawdownValue)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="label">自动交易</span>
|
||||
<span class="value">${key === 'paper' ? (platformExecutionControl?.enabled === false ? 'OFF' : 'ON') : `${accounts.length - disabledBitgetCount}/${accounts.length} ON`}</span>
|
||||
<span class="label">风险阈值</span>
|
||||
<span class="value">${formatPercent(thresholdValue, 1)}</span>
|
||||
</div>
|
||||
<div class="platform-stat">
|
||||
<span class="label">账号数量</span>
|
||||
<span class="value">${key === 'bitget' ? accounts.length : 1}</span>
|
||||
</div>
|
||||
</div>
|
||||
${key === 'paper' ? `
|
||||
@ -3041,6 +3447,8 @@
|
||||
syncTabState(data);
|
||||
renderHero(data);
|
||||
renderHealthRibbon(data);
|
||||
renderFocusSummary(data);
|
||||
renderAlertStrip(data);
|
||||
renderOpsSummary(data);
|
||||
renderPlatforms(data.platforms, data.crypto_agent?.platform_halts, data.crypto_agent?.target_execution_controls || {});
|
||||
renderSignalStream(data.signals?.latest || []);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user