From 2e7dbd705fc82e8db74b073e55af938d67d8c19f Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 17 三月 2026 09:05:49 +0800
Subject: [PATCH] #
---
src/main/webapp/views/dashboard/dashboard.html | 308 ++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 252 insertions(+), 56 deletions(-)
diff --git a/src/main/webapp/views/dashboard/dashboard.html b/src/main/webapp/views/dashboard/dashboard.html
index 857578d..3c2b87e 100644
--- a/src/main/webapp/views/dashboard/dashboard.html
+++ b/src/main/webapp/views/dashboard/dashboard.html
@@ -271,6 +271,14 @@
font-weight: 700;
}
+ .panel-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
.mini-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
@@ -324,6 +332,26 @@
border-color: rgba(151, 110, 204, 0.18);
}
+ .network-mini-ok {
+ background: linear-gradient(180deg, rgba(47, 163, 142, 0.11) 0%, rgba(47, 163, 142, 0.03) 100%);
+ border-color: rgba(47, 163, 142, 0.18);
+ }
+
+ .network-mini-warning {
+ background: linear-gradient(180deg, rgba(245, 154, 74, 0.12) 0%, rgba(245, 154, 74, 0.03) 100%);
+ border-color: rgba(245, 154, 74, 0.18);
+ }
+
+ .network-mini-offline {
+ background: linear-gradient(180deg, rgba(222, 92, 92, 0.10) 0%, rgba(222, 92, 92, 0.03) 100%);
+ border-color: rgba(222, 92, 92, 0.18);
+ }
+
+ .network-mini-latency {
+ background: linear-gradient(180deg, rgba(31, 111, 178, 0.09) 0%, rgba(31, 111, 178, 0.02) 100%);
+ border-color: rgba(31, 111, 178, 0.16);
+ }
+
.chart-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -344,12 +372,20 @@
margin-bottom: 8px;
}
+ .chart-subtitle {
+ margin-bottom: 10px;
+ font-size: 12px;
+ color: #7d90a4;
+ line-height: 1.6;
+ }
+
.chart-box {
width: 100%;
height: 280px;
}
.panel-device .mini-grid,
+ .panel-network .mini-grid,
.panel-ai .mini-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@@ -387,6 +423,7 @@
}
.device-chart-box,
+ .network-chart-box,
.ai-chart-box {
width: 100%;
height: 250px;
@@ -466,8 +503,80 @@
padding: 8px 10px;
}
+ .network-note {
+ margin-top: 6px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 12px;
+ padding: 8px 10px;
+ }
+
+ .network-note-danger {
+ color: #c15b5b;
+ background: rgba(222, 92, 92, 0.08);
+ }
+
+ .network-note-warning {
+ color: #b67632;
+ background: rgba(245, 154, 74, 0.12);
+ }
+
+ .network-note-info {
+ color: #657d95;
+ background: rgba(125, 144, 164, 0.10);
+ }
+
+ .network-healthy-state {
+ margin-top: 14px;
+ padding: 14px 16px;
+ border-radius: 16px;
+ border: 1px solid rgba(87, 186, 128, 0.20);
+ background: linear-gradient(135deg, rgba(87, 186, 128, 0.10) 0%, rgba(87, 186, 128, 0.03) 100%);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ }
+
+ .network-healthy-main {
+ min-width: 0;
+ flex: 1;
+ }
+
+ .network-healthy-title {
+ font-size: 14px;
+ font-weight: 700;
+ color: #2d7f56;
+ line-height: 1.5;
+ }
+
+ .network-healthy-desc {
+ margin-top: 4px;
+ font-size: 12px;
+ color: #698399;
+ line-height: 1.6;
+ }
+
+ .network-healthy-tags {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
+ .network-healthy-tag {
+ padding: 6px 10px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.72);
+ border: 1px solid rgba(87, 186, 128, 0.18);
+ font-size: 12px;
+ color: #557160;
+ white-space: nowrap;
+ }
+
.recent-panel {
- min-height: 100%;
+ min-height: 0;
}
.recent-table {
@@ -553,6 +662,7 @@
}
.panel-device .mini-grid,
+ .panel-network .mini-grid,
.panel-ai .mini-grid {
grid-template-columns: 1fr;
}
@@ -579,6 +689,20 @@
.route-row-side {
align-items: flex-start;
text-align: left;
+ }
+
+ .network-healthy-state {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .network-healthy-tags {
+ justify-content: flex-start;
+ }
+
+ .panel-actions {
+ width: 100%;
+ justify-content: flex-start;
}
}
</style>
@@ -732,60 +856,6 @@
</el-table>
</div>
</section>
- </div>
-
- <div class="dashboard-column">
- <section class="panel panel-device">
- <div class="panel-header">
- <div>
- <div class="panel-kicker">Devices</div>
- <h2 class="panel-title">璁惧鎬佸娍</h2>
- <div class="panel-desc">姹囨�昏緭閫佺珯鐐广�佸爢鍨涙満銆佸弻宸ヤ綅鍫嗗灈鏈轰笌 RGV 鐨勫湪绾裤�佸繖纰屽拰鍛婅鎯呭喌銆�</div>
- </div>
- <el-tag size="small" type="info">鍦ㄧ嚎鐜� {{ devices.overview.onlineRate || 0 }}%</el-tag>
- </div>
-
- <div class="mini-grid">
- <div class="mini-card">
- <div class="mini-label">璁惧鎬绘暟</div>
- <div class="mini-value">{{ formatNumber(devices.overview.total) }}</div>
- <div class="mini-hint">宸插惎鐢ㄩ厤缃澶�</div>
- </div>
- <div class="mini-card">
- <div class="mini-label">鍦ㄧ嚎璁惧</div>
- <div class="mini-value">{{ formatNumber(devices.overview.online) }}</div>
- <div class="mini-hint">瀹炴椂杩為�氳澶囨暟閲�</div>
- </div>
- <div class="mini-card">
- <div class="mini-label">蹇欑璁惧</div>
- <div class="mini-value">{{ formatNumber(devices.overview.busy) }}</div>
- <div class="mini-hint">褰撳墠鎵胯浇浠诲姟鐨勮澶�</div>
- </div>
- <div class="mini-card">
- <div class="mini-label">鍛婅璁惧</div>
- <div class="mini-value">{{ formatNumber(devices.overview.alarm) }}</div>
- <div class="mini-hint">鍚樆濉炴垨鎶ヨ鐘舵��</div>
- </div>
- </div>
-
- <div class="chart-card">
- <div class="chart-title">璁惧鍦ㄧ嚎鍒嗗竷</div>
- <div ref="deviceTypeChart" class="device-chart-box"></div>
- </div>
-
- <div class="type-list">
- <div v-for="item in devices.typeStats" :key="item.name" class="type-row">
- <div class="type-row-main">
- <div class="type-row-name">{{ item.name }}</div>
- <div class="type-row-desc">鍦ㄧ嚎 {{ formatNumber(item.online) }} / 鎬绘暟 {{ formatNumber(item.total) }}锛岀绾� {{ formatNumber(item.offline) }}</div>
- </div>
- <div class="type-row-side">
- <el-tag size="mini" type="success">蹇欑 {{ formatNumber(item.busy) }}</el-tag>
- <el-tag size="mini" :type="item.alarm > 0 ? 'danger' : 'info'">鍛婅 {{ formatNumber(item.alarm) }}</el-tag>
- </div>
- </div>
- </div>
- </section>
<section class="panel panel-ai">
<div class="panel-header">
@@ -842,6 +912,132 @@
<el-empty v-else description="鏆傛棤 AI 璺敱鏁版嵁"></el-empty>
</section>
</div>
+
+ <div class="dashboard-column">
+ <section class="panel panel-device">
+ <div class="panel-header">
+ <div>
+ <div class="panel-kicker">Devices</div>
+ <h2 class="panel-title">璁惧鎬佸娍</h2>
+ <div class="panel-desc">姹囨�昏緭閫佺珯鐐广�佸爢鍨涙満銆佸弻宸ヤ綅鍫嗗灈鏈轰笌 RGV 鐨勫湪绾裤�佸繖纰屽拰鍛婅鎯呭喌銆�</div>
+ </div>
+ <el-tag size="small" type="info">鍦ㄧ嚎鐜� {{ devices.overview.onlineRate || 0 }}%</el-tag>
+ </div>
+
+ <div class="mini-grid">
+ <div class="mini-card">
+ <div class="mini-label">璁惧鎬绘暟</div>
+ <div class="mini-value">{{ formatNumber(devices.overview.total) }}</div>
+ <div class="mini-hint">宸插惎鐢ㄩ厤缃澶�</div>
+ </div>
+ <div class="mini-card">
+ <div class="mini-label">鍦ㄧ嚎璁惧</div>
+ <div class="mini-value">{{ formatNumber(devices.overview.online) }}</div>
+ <div class="mini-hint">瀹炴椂杩為�氳澶囨暟閲�</div>
+ </div>
+ <div class="mini-card">
+ <div class="mini-label">蹇欑璁惧</div>
+ <div class="mini-value">{{ formatNumber(devices.overview.busy) }}</div>
+ <div class="mini-hint">褰撳墠鎵胯浇浠诲姟鐨勮澶�</div>
+ </div>
+ <div class="mini-card">
+ <div class="mini-label">鍛婅璁惧</div>
+ <div class="mini-value">{{ formatNumber(devices.overview.alarm) }}</div>
+ <div class="mini-hint">鍚樆濉炴垨鎶ヨ鐘舵��</div>
+ </div>
+ </div>
+
+ <div class="chart-card">
+ <div class="chart-title">璁惧鍦ㄧ嚎鍒嗗竷</div>
+ <div ref="deviceTypeChart" class="device-chart-box"></div>
+ </div>
+
+ <div class="type-list">
+ <div v-for="item in devices.typeStats" :key="item.name" class="type-row">
+ <div class="type-row-main">
+ <div class="type-row-name">{{ item.name }}</div>
+ <div class="type-row-desc">鍦ㄧ嚎 {{ formatNumber(item.online) }} / 鎬绘暟 {{ formatNumber(item.total) }}锛岀绾� {{ formatNumber(item.offline) }}</div>
+ </div>
+ <div class="type-row-side">
+ <el-tag size="mini" type="success">蹇欑 {{ formatNumber(item.busy) }}</el-tag>
+ <el-tag size="mini" :type="item.alarm > 0 ? 'danger' : 'info'">鍛婅 {{ formatNumber(item.alarm) }}</el-tag>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <section class="panel panel-network">
+ <div class="panel-header">
+ <div>
+ <div class="panel-kicker">Network</div>
+ <h2 class="panel-title">璁惧缃戠粶鍒嗘瀽</h2>
+ <div class="panel-desc">姹囨�绘渶鏂� Ping 鏍锋湰鐨勮繛閫氭�с�佸欢杩熶笌寮傚父璁惧锛屽府鍔╁揩閫熷彂鐜扮綉缁滄尝鍔ㄣ��</div>
+ </div>
+ <div class="panel-actions">
+ <el-tag size="small" :type="network.overview.attentionDevices > 0 ? 'warning' : 'success'">
+ 闇�鍏虫敞 {{ formatNumber(network.overview.attentionDevices) }}
+ </el-tag>
+ <el-button size="mini" plain @click="openDevicePingAnalysis">鏌ョ湅鏄庣粏</el-button>
+ </div>
+ </div>
+
+ <div class="mini-grid">
+ <div class="mini-card network-mini-ok">
+ <div class="mini-label">姝e父</div>
+ <div class="mini-value">{{ formatNumber(network.overview.okDevices) }}</div>
+ <div class="mini-hint">鏈�鏂版牱鏈姸鎬� OK</div>
+ </div>
+ <div class="mini-card network-mini-warning">
+ <div class="mini-label">娉㈠姩</div>
+ <div class="mini-value">{{ formatNumber(network.overview.unstableDevices) }}</div>
+ <div class="mini-hint">閮ㄥ垎鎺㈡祴鎴愬姛</div>
+ </div>
+ <div class="mini-card network-mini-offline">
+ <div class="mini-label">瓒呮椂/寮傚父</div>
+ <div class="mini-value">{{ formatNumber(network.overview.offlineDevices) }}</div>
+ <div class="mini-hint">鏆傛棤鏁版嵁 {{ formatNumber(network.overview.noDataDevices) }}</div>
+ </div>
+ <div class="mini-card network-mini-latency">
+ <div class="mini-label">骞冲潎寤惰繜</div>
+ <div class="mini-value">{{ formatLatency(network.overview.avgLatencyMs) }}</div>
+ <div class="mini-hint">宄板�� {{ formatLatency(network.overview.maxLatencyMs) }}</div>
+ </div>
+ </div>
+
+ <div class="chart-card">
+ <div class="chart-title">杩為�氱姸鎬佸垎甯�</div>
+ <div class="chart-subtitle">{{ networkSamplingText() }}</div>
+ <div ref="networkStatusChart" class="network-chart-box"></div>
+ </div>
+
+ <div class="route-list" v-if="network.focusDevices.length">
+ <div v-for="item in network.focusDevices" :key="item.name + '-' + item.ip" class="route-row">
+ <div class="route-row-main">
+ <div class="route-row-name">{{ item.name }}</div>
+ <div class="route-row-desc">{{ displayText(item.ip, '-') }}</div>
+ <div v-if="item.message" :class="['network-note', 'network-note-' + (item.statusType || 'info')]">{{ item.message }}</div>
+ </div>
+ <div class="route-row-side">
+ <el-tag size="mini" :type="item.statusType">{{ item.statusText }}</el-tag>
+ <div class="route-extra">骞冲潎 {{ formatLatency(item.avgLatencyMs) }}</div>
+ <div class="route-extra">鏈�杩戞牱鏈� {{ displayText(item.latestTimeLabel, '-') }}</div>
+ </div>
+ </div>
+ </div>
+ <div v-else-if="network.overview.totalDevices > 0" class="network-healthy-state">
+ <div class="network-healthy-main">
+ <div class="network-healthy-title">褰撳墠缃戠粶鎺㈡祴绋冲畾</div>
+ <div class="network-healthy-desc">宸茬撼鍏� {{ formatNumber(network.overview.totalDevices) }} 鍙拌澶囷紝鏈�杩戜竴杞湭鍙戠幇瓒呮椂鎴栨尝鍔ㄣ��</div>
+ </div>
+ <div class="network-healthy-tags">
+ <div class="network-healthy-tag">姝e父 {{ formatNumber(network.overview.okDevices) }}</div>
+ <div class="network-healthy-tag">骞冲潎 {{ formatLatency(network.overview.avgLatencyMs) }}</div>
+ <div class="network-healthy-tag">宄板�� {{ formatLatency(network.overview.maxLatencyMs) }}</div>
+ </div>
+ </div>
+ <el-empty v-else description="鏆傛棤璁惧缃戠粶鏍锋湰"></el-empty>
+ </section>
+ </div>
</div>
<div v-if="loading" class="loading-mask">
@@ -858,6 +1054,6 @@
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/echarts/echarts.min.js"></script>
-<script type="text/javascript" src="../../static/js/dashboard/dashboard.js"></script>
+<script type="text/javascript" src="../../static/js/dashboard/dashboard.js?v=20260317-dashboard-network-focus"></script>
</body>
</html>
--
Gitblit v1.9.1