From dd01058b3225ec4aa269a8b178d4f8aab0ee457f Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Wed, 3 Dec 2025 09:04:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9web=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E6=8C=87=E6=A0=87=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/static/index.html | 6 +- backend/static/js/app.js | 270 +++++++++++++++++++++++--------------- 2 files changed, 172 insertions(+), 104 deletions(-) diff --git a/backend/static/index.html b/backend/static/index.html index f05a67e..991f325 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -203,6 +203,7 @@

CPU 使用率

0.0%

+

0.00 GHz | 负载: 0.00

@@ -216,6 +217,7 @@

内存使用率

0.0%

+

0 MB / 0 MB

@@ -229,6 +231,7 @@

磁盘使用率

0.0%

+

0 MB / 0 MB

@@ -241,7 +244,8 @@

网络流量

-

0.0 MB/s

+

0.0

+

接收: 0 MB/s | 发送: 0 MB/s
总量: 接收 0 MB | 发送 0 MB

diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 43a2286..18b4091 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -6,7 +6,8 @@ let state = { currentTimeRange: '1h', // 与UI默认值保持一致 customStartTime: '', customEndTime: '', - currentInterval: '10m' // 固定10分钟区间 + currentInterval: '10m', // 固定10分钟区间 + historyMetrics: {} // 存储历史指标数据 } // WebSocket连接 @@ -1200,133 +1201,196 @@ function handleMetricsUpdate(message) { // 更新状态卡片 function updateStatusCards(metrics) { + // 更新历史指标数据 + updateHistoryMetrics(metrics); + + // 使用历史数据或当前数据 + const displayMetrics = { + cpu: metrics.cpu || state.historyMetrics.cpu, + memory: metrics.memory || state.historyMetrics.memory, + disk: metrics.disk || state.historyMetrics.disk, + network: metrics.network || state.historyMetrics.network + }; + // 更新CPU状态卡片 - if (metrics.cpu) { - if (Array.isArray(metrics.cpu) && metrics.cpu.length > 0) { - const latestCPU = metrics.cpu[metrics.cpu.length - 1].value; - const cpuElement = document.getElementById('cpuValue'); - if (cpuElement) { - cpuElement.textContent = `${latestCPU.toFixed(1)}%`; - } - } else if (typeof metrics.cpu === 'number') { - const cpuElement = document.getElementById('cpuValue'); - if (cpuElement) { - cpuElement.textContent = `${metrics.cpu.toFixed(1)}%`; - } + if (displayMetrics.cpu) { + let cpuUsage = 0; + let cpuGhz = 0; + let cpuLoad = 0; + + // 解析CPU数据 + if (Array.isArray(displayMetrics.cpu) && displayMetrics.cpu.length > 0) { + cpuUsage = displayMetrics.cpu[displayMetrics.cpu.length - 1].value; + } else if (typeof displayMetrics.cpu === 'number') { + cpuUsage = displayMetrics.cpu; + } else if (typeof displayMetrics.cpu === 'object' && displayMetrics.cpu.usage) { + cpuUsage = displayMetrics.cpu.usage; + cpuGhz = displayMetrics.cpu.frequency || 0; + cpuLoad = displayMetrics.cpu.load || 0; + } + + // 更新显示 + const cpuElement = document.getElementById('cpuValue'); + const cpuDetailsElement = document.getElementById('cpuDetails'); + if (cpuElement) { + cpuElement.textContent = `${cpuUsage.toFixed(1)}%`; + // 设置红色显示如果达到顶峰 + cpuElement.className = cpuUsage > 90 ? 'text-red-500' : ''; + } + if (cpuDetailsElement) { + cpuDetailsElement.textContent = `${cpuGhz.toFixed(2)} GHz | 负载: ${cpuLoad.toFixed(2)}`; } } // 更新内存状态卡片 - if (metrics.memory) { - if (Array.isArray(metrics.memory) && metrics.memory.length > 0) { - const latestMemory = metrics.memory[metrics.memory.length - 1].value; - const memoryElement = document.getElementById('memoryValue'); - if (memoryElement) { - memoryElement.textContent = `${latestMemory.toFixed(1)}%`; - } - } else if (typeof metrics.memory === 'number') { - const memoryElement = document.getElementById('memoryValue'); - if (memoryElement) { - memoryElement.textContent = `${metrics.memory.toFixed(1)}%`; - } + if (displayMetrics.memory) { + let memoryUsage = 0; + let memoryUsed = 0; + let memoryTotal = 0; + + // 解析内存数据 + if (Array.isArray(displayMetrics.memory) && displayMetrics.memory.length > 0) { + memoryUsage = displayMetrics.memory[displayMetrics.memory.length - 1].value; + } else if (typeof displayMetrics.memory === 'number') { + memoryUsage = displayMetrics.memory; + } else if (typeof displayMetrics.memory === 'object') { + memoryUsage = displayMetrics.memory.usage || 0; + memoryUsed = displayMetrics.memory.used || 0; + memoryTotal = displayMetrics.memory.total || 0; + } + + // 更新显示 + const memoryElement = document.getElementById('memoryValue'); + const memoryDetailsElement = document.getElementById('memoryDetails'); + if (memoryElement) { + memoryElement.textContent = `${memoryUsage.toFixed(1)}%`; + // 设置红色显示如果达到顶峰 + memoryElement.className = memoryUsage > 90 ? 'text-red-500' : ''; + } + if (memoryDetailsElement) { + memoryDetailsElement.textContent = `${formatBytes(memoryUsed)} / ${formatBytes(memoryTotal)}`; } } // 更新磁盘状态卡片 - if (metrics.disk) { - const diskElement = document.getElementById('diskValue'); - if (diskElement) { - if (typeof metrics.disk === 'object' && metrics.disk !== null && !Array.isArray(metrics.disk)) { - // 计算所有挂载点的平均使用率 - let totalUsage = 0; - let mountpointCount = 0; - - for (const mountpoint in metrics.disk) { - const data = metrics.disk[mountpoint]; - if (data && Array.isArray(data) && data.length > 0) { - const latestValue = data[data.length - 1].value; - totalUsage += latestValue; - mountpointCount++; - } else if (typeof data === 'number') { - totalUsage += data; - mountpointCount++; - } + if (displayMetrics.disk) { + let totalUsed = 0; + let totalSize = 0; + let usagePercent = 0; + + // 过滤掉不需要的挂载点 + const excludedMountpoints = ['/usr', '/boot', '/boot/efi']; + + // 解析磁盘数据 + if (typeof displayMetrics.disk === 'object' && displayMetrics.disk !== null && !Array.isArray(displayMetrics.disk)) { + // 按挂载点分组的数据 + for (const mountpoint in displayMetrics.disk) { + // 跳过排除的挂载点 + if (excludedMountpoints.includes(mountpoint)) { + continue; } - if (mountpointCount > 0) { - const averageUsage = totalUsage / mountpointCount; - diskElement.textContent = `${averageUsage.toFixed(1)}%`; + const data = displayMetrics.disk[mountpoint]; + if (data && typeof data === 'object' && data.used !== undefined && data.total !== undefined) { + totalUsed += data.used; + totalSize += data.total; } - } else if (Array.isArray(metrics.disk) && metrics.disk.length > 0) { - const latestDisk = metrics.disk[metrics.disk.length - 1].value; - diskElement.textContent = `${latestDisk.toFixed(1)}%`; - } else if (typeof metrics.disk === 'number') { - diskElement.textContent = `${metrics.disk.toFixed(1)}%`; } + } else if (typeof displayMetrics.disk === 'object' && displayMetrics.disk.used !== undefined && displayMetrics.disk.total !== undefined) { + // 单磁盘数据 + totalUsed = displayMetrics.disk.used; + totalSize = displayMetrics.disk.total; + } + + // 计算使用率 + if (totalSize > 0) { + usagePercent = (totalUsed / totalSize) * 100; + } + + // 更新显示 + const diskElement = document.getElementById('diskValue'); + const diskDetailsElement = document.getElementById('diskDetails'); + if (diskElement) { + diskElement.textContent = `${usagePercent.toFixed(1)}%`; + // 设置红色显示如果达到顶峰 + diskElement.className = usagePercent > 90 ? 'text-red-500' : ''; + } + if (diskDetailsElement) { + diskDetailsElement.textContent = `${formatBytes(totalUsed)} / ${formatBytes(totalSize)}`; } } // 更新网络状态卡片 - if (metrics.network) { - const networkValueElement = document.getElementById('networkValue'); - const networkSentElement = document.getElementById('networkSent'); - const networkReceivedElement = document.getElementById('networkReceived'); + if (displayMetrics.network) { + let sentRate = 0; + let receivedRate = 0; + let sentTotal = 0; + let receivedTotal = 0; - // 处理不同格式的网络数据 - if (metrics.network.sent && metrics.network.received) { + // 解析网络数据 + if (displayMetrics.network.sent && displayMetrics.network.received) { // 处理数组格式的数据 - if (Array.isArray(metrics.network.sent) && metrics.network.sent.length > 0 && - Array.isArray(metrics.network.received) && metrics.network.received.length > 0) { + if (Array.isArray(displayMetrics.network.sent) && displayMetrics.network.sent.length > 0 && + Array.isArray(displayMetrics.network.received) && displayMetrics.network.received.length > 0) { - const latestSent = metrics.network.sent[metrics.network.sent.length - 1].value; - const latestReceived = metrics.network.received[metrics.network.received.length - 1].value; - - if (networkValueElement) { - // 显示较大的值 - const maxValue = Math.max(latestSent, latestReceived); - networkValueElement.textContent = formatBytes(maxValue, 2, true); // 显示速率 - } - - if (networkSentElement) { - networkSentElement.textContent = formatBytes(latestSent, 2, true); - } - - if (networkReceivedElement) { - networkReceivedElement.textContent = formatBytes(latestReceived, 2, true); - } + sentRate = displayMetrics.network.sent[displayMetrics.network.sent.length - 1].value; + receivedRate = displayMetrics.network.received[displayMetrics.network.received.length - 1].value; + // 计算总量(假设数组中的值是累积的) + sentTotal = displayMetrics.network.sent.reduce((sum, item) => sum + item.value, 0); + receivedTotal = displayMetrics.network.received.reduce((sum, item) => sum + item.value, 0); } // 处理数值格式的数据 - else if (typeof metrics.network.sent === 'number' && typeof metrics.network.received === 'number') { - if (networkValueElement) { - const maxValue = Math.max(metrics.network.sent, metrics.network.received); - networkValueElement.textContent = formatBytes(maxValue, 2, true); - } - - if (networkSentElement) { - networkSentElement.textContent = formatBytes(metrics.network.sent, 2, true); - } - - if (networkReceivedElement) { - networkReceivedElement.textContent = formatBytes(metrics.network.received, 2, true); - } + else if (typeof displayMetrics.network.sent === 'number' && typeof displayMetrics.network.received === 'number') { + sentRate = displayMetrics.network.sent; + receivedRate = displayMetrics.network.received; + sentTotal = displayMetrics.network.sent_total || sentRate; + receivedTotal = displayMetrics.network.received_total || receivedRate; } - } else if (typeof metrics.network === 'object' && - metrics.network.bytes_sent !== undefined && - metrics.network.bytes_received !== undefined) { + } else if (typeof displayMetrics.network === 'object' && + displayMetrics.network.bytes_sent !== undefined && + displayMetrics.network.bytes_received !== undefined) { // WebSocket消息格式 - if (networkValueElement) { - const maxValue = Math.max(metrics.network.bytes_sent, metrics.network.bytes_received); - networkValueElement.textContent = formatBytes(maxValue, 2, true); - } - - if (networkSentElement) { - networkSentElement.textContent = formatBytes(metrics.network.bytes_sent, 2, true); - } - - if (networkReceivedElement) { - networkReceivedElement.textContent = formatBytes(metrics.network.bytes_received, 2, true); - } + sentRate = displayMetrics.network.bytes_sent; + receivedRate = displayMetrics.network.bytes_received; + sentTotal = displayMetrics.network.bytes_sent_total || sentRate; + receivedTotal = displayMetrics.network.bytes_received_total || receivedRate; } + + // 计算比率 + let ratioText = '0.0'; + if (sentRate > 0) { + ratioText = (receivedRate / sentRate).toFixed(1); + } + + // 更新显示 + const networkValueElement = document.getElementById('networkValue'); + const networkDetailsElement = document.getElementById('networkDetails'); + if (networkValueElement) { + networkValueElement.innerHTML = `${ratioText} / `; + } + if (networkDetailsElement) { + networkDetailsElement.innerHTML = + `接收: ${formatBytes(receivedRate, 2, true)} | ` + + `发送: ${formatBytes(sentRate, 2, true)}
` + + `总量: 接收 ${formatBytes(receivedTotal)} | 发送 ${formatBytes(sentTotal)}`; + } + } +} + +// 更新历史指标数据 +function updateHistoryMetrics(metrics) { + // 只更新有有效数据的指标 + if (metrics.cpu) { + state.historyMetrics.cpu = metrics.cpu; + } + if (metrics.memory) { + state.historyMetrics.memory = metrics.memory; + } + if (metrics.disk) { + state.historyMetrics.disk = metrics.disk; + } + if (metrics.network) { + state.historyMetrics.network = metrics.network; } }