From febeb63f9025de133d0fd3d92e10bf4f7546b576 Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Mon, 12 May 2025 10:17:53 +0800 Subject: [PATCH] Update StatusCharts.vue --- .../frontend/src/components/StatusCharts.vue | 193 ++++++++++++++---- 1 file changed, 149 insertions(+), 44 deletions(-) diff --git a/packages/frontend/src/components/StatusCharts.vue b/packages/frontend/src/components/StatusCharts.vue index b22d808..907fd4e 100644 --- a/packages/frontend/src/components/StatusCharts.vue +++ b/packages/frontend/src/components/StatusCharts.vue @@ -7,9 +7,9 @@
-
内存使用率
+
{{ memoryChartTitle }}
- +
@@ -47,19 +47,37 @@ ChartJS.register( CategoryScale ); +// Define a more specific type for serverStatus if possible, or use as is if memUsed/memTotal are reliably present. +// For now, assuming props.serverStatus will have memUsed and memTotal in MB. +interface ServerStatusData { + cpuPercent?: number; + memPercent?: number; // Will be ignored for chart data, but kept for other potential uses + memUsed?: number; // in MB + memTotal?: number; // in MB + netRxRate?: number; // in Bytes/sec + netTxRate?: number; // in Bytes/sec + // ... other properties if needed +} + const props = defineProps<{ - serverStatus: any; + serverStatus: ServerStatusData | null; }>(); const MAX_DATA_POINTS = 60; -const KB_TO_MB_THRESHOLD = 1024; +const KB_TO_MB_THRESHOLD = 1024; // For network +const MB_TO_GB_THRESHOLD = 1024; // For memory const cpuChartKey = ref(0); const memoryChartKey = ref(0); const networkChartKey = ref(0); + const networkRateUnitIsMB = ref(false); const networkChartTitle = ref('网络速度 (KB/s)'); +const memoryUnitIsGB = ref(false); +const memoryChartTitle = ref('内存使用情况 (MB)'); + + const initialLabels = Array.from({ length: MAX_DATA_POINTS }, (_, i) => `-${(MAX_DATA_POINTS - 1 - i)}s`); const cpuChartData = ref({ @@ -82,7 +100,7 @@ const memoryChartData = ref({ labels: [...initialLabels], datasets: [ { - label: '内存使用率 (%)', + label: '内存使用 (MB)', // Initial label backgroundColor: 'rgba(255, 99, 132, 0.2)', borderColor: 'rgba(255, 99, 132, 1)', borderWidth: 1, @@ -131,14 +149,56 @@ const baseChartOptions: Omit, 'scales'> = { interaction: { mode: 'index', intersect: false }, }; -const percentageChartOptions = ref>({ +const percentageChartOptions = ref>({ // For CPU ...baseChartOptions, scales: { y: { beginAtZero: true, min: 0, max: 100, - ticks: { color: '#9CA3AF' }, + ticks: { color: '#9CA3AF', callback: value => `${value}%` }, + grid: { color: 'rgba(156, 163, 175, 0.1)' }, + }, + x: { + ticks: { display: false, color: '#9CA3AF', maxRotation: 0, minRotation: 0 }, + grid: { display: false }, + }, + }, +}); + +const memoryChartOptions = ref>({ + ...baseChartOptions, + plugins: { + ...baseChartOptions.plugins, + tooltip: { + ...baseChartOptions.plugins?.tooltip, + callbacks: { + label: (context: TooltipItem<'line'>) => { + let label = context.dataset.label || ''; + if (label) { + label = label.substring(0, label.lastIndexOf('(') -1); // Remove old unit from label + label += ': '; + } + if (context.parsed.y !== null) { + const value = parseFloat(context.parsed.y.toFixed(1)); + label += `${value} ${memoryUnitIsGB.value ? 'GB' : 'MB'}`; + } + return label; + }, + }, + }, + }, + scales: { + y: { + beginAtZero: true, + min: 0, + // max will be set dynamically based on memTotal + ticks: { + color: '#9CA3AF', + callback: function(value) { + return `${parseFloat(Number(value).toFixed(1))}`; // Unit will be implicit from title or tooltip + } + }, grid: { color: 'rgba(156, 163, 175, 0.1)' }, }, x: { @@ -157,7 +217,8 @@ const networkChartOptions = ref>({ callbacks: { label: (context: TooltipItem<'line'>) => { let label = context.dataset.label || ''; - if (label) { + if (label) { + label = label.substring(0, label.lastIndexOf('(') -1); // Remove old unit from label label += ': '; } if (context.parsed.y !== null) { @@ -173,7 +234,6 @@ const networkChartOptions = ref>({ y: { beginAtZero: true, min: 0, - // max will be set dynamically ticks: { color: '#9CA3AF', callback: function(value) { @@ -190,7 +250,7 @@ const networkChartOptions = ref>({ }); -const updateCharts = (newStatus: any) => { +const updateCharts = (newStatus: ServerStatusData | null) => { if (!newStatus) return; // Update CPU Chart @@ -202,38 +262,88 @@ const updateCharts = (newStatus: any) => { } // Update Memory Chart - if (typeof newStatus.memPercent === 'number') { - const newMemData = [...memoryChartData.value.datasets[0].data]; - newMemData.shift(); - newMemData.push(parseFloat(newStatus.memPercent.toFixed(1))); - memoryChartData.value = { ...memoryChartData.value, datasets: [{ ...memoryChartData.value.datasets[0], data: newMemData }] }; + if (typeof newStatus.memUsed === 'number' && typeof newStatus.memTotal === 'number') { + let currentMemUsed = newStatus.memUsed; // MB + const memTotal = newStatus.memTotal; // MB + let currentData = [...memoryChartData.value.datasets[0].data]; + + if (!memoryUnitIsGB.value && (memTotal >= MB_TO_GB_THRESHOLD || currentMemUsed >= MB_TO_GB_THRESHOLD)) { + memoryUnitIsGB.value = true; + memoryChartTitle.value = '内存使用情况 (GB)'; + memoryChartData.value.datasets[0].label = '内存使用 (GB)'; + currentData = currentData.map(d => parseFloat((d / MB_TO_GB_THRESHOLD).toFixed(1))); + memoryChartKey.value++; // Force re-render + } else if (memoryUnitIsGB.value && memTotal < MB_TO_GB_THRESHOLD && currentMemUsed < MB_TO_GB_THRESHOLD) { + // This case is less likely if total is large, but handles potential fluctuations or incorrect initial state + memoryUnitIsGB.value = false; + memoryChartTitle.value = '内存使用情况 (MB)'; + memoryChartData.value.datasets[0].label = '内存使用 (MB)'; + currentData = currentData.map(d => parseFloat((d * MB_TO_GB_THRESHOLD).toFixed(1))); + memoryChartKey.value++; + } + + let newMemValue; + if (memoryUnitIsGB.value) { + newMemValue = parseFloat((currentMemUsed / MB_TO_GB_THRESHOLD).toFixed(1)); + } else { + newMemValue = parseFloat(currentMemUsed.toFixed(1)); + } + + currentData.shift(); + currentData.push(newMemValue); + memoryChartData.value = { ...memoryChartData.value, datasets: [{ ...memoryChartData.value.datasets[0], data: currentData }] }; + + if (memoryChartOptions.value.scales?.y && typeof memTotal === 'number') { + let yAxisTopValue = memoryUnitIsGB.value + ? parseFloat((memTotal / MB_TO_GB_THRESHOLD).toFixed(1)) + : parseFloat(memTotal.toFixed(1)); + + // Ensure the yAxisTopValue is at least slightly larger than the max data point if data exceeds total. + // Also, handle cases where memTotal might be 0 or very small. + const currentMaxDataPoint = Math.max(...currentData, 0); + // The y-axis max should primarily be driven by memTotal. + // If current data somehow exceeds memTotal (which shouldn't happen for 'used' vs 'total'), + // then the axis should expand. Otherwise, stick to memTotal. + yAxisTopValue = Math.max(yAxisTopValue, currentMaxDataPoint); + + // If memTotal is 0 (and thus yAxisTopValue is 0 after parseFloat), set a small default max. + if (yAxisTopValue === 0) { + yAxisTopValue = memoryUnitIsGB.value ? 1 : 100; // Default small max if total is 0 + } else { + // Add a very tiny buffer if yAxisTopValue is not 0, to ensure the top line is visible if data hits the max. + // This helps if Chart.js clips data exactly at the 'max'. + // For example, if max is 8.0GB, a data point of 8.0GB might be at the very edge. + // A small increment ensures it's clearly within the chart area. + const epsilon = memoryUnitIsGB.value ? 0.01 : 1; + yAxisTopValue += epsilon; + // Re-round after adding epsilon to maintain clean ticks if possible + if (memoryUnitIsGB.value) { + yAxisTopValue = Math.ceil(yAxisTopValue * 100) / 100; // Round to 2 decimal places for GB after epsilon + } else { + yAxisTopValue = Math.ceil(yAxisTopValue); // Round to next integer for MB after epsilon + } + } + memoryChartOptions.value.scales.y.max = yAxisTopValue; + } } + // Update Network Chart let currentNetRxRateKB = (newStatus.netRxRate || 0) / 1024; let currentNetTxRateKB = (newStatus.netTxRate || 0) / 1024; + let newNetRxData = [...networkChartData.value.datasets[0].data]; + let newNetTxData = [...networkChartData.value.datasets[1].data]; - const newNetRxData = [...networkChartData.value.datasets[0].data]; - const newNetTxData = [...networkChartData.value.datasets[1].data]; - - // Check if unit needs to be switched to MB/s if (!networkRateUnitIsMB.value && (currentNetRxRateKB >= KB_TO_MB_THRESHOLD || currentNetTxRateKB >= KB_TO_MB_THRESHOLD)) { networkRateUnitIsMB.value = true; networkChartTitle.value = '网络速度 (MB/s)'; - - // Convert existing data to MB/s - networkChartData.value.datasets[0].data = newNetRxData.map(d => parseFloat((d / KB_TO_MB_THRESHOLD).toFixed(1))); - networkChartData.value.datasets[1].data = newNetTxData.map(d => parseFloat((d / KB_TO_MB_THRESHOLD).toFixed(1))); - - // Update dataset labels networkChartData.value.datasets[0].label = '下载 (MB/s)'; networkChartData.value.datasets[1].label = '上传 (MB/s)'; - - // Force chart re-render for label and unit changes + newNetRxData = newNetRxData.map(d => parseFloat((d / KB_TO_MB_THRESHOLD).toFixed(1))); + newNetTxData = newNetTxData.map(d => parseFloat((d / KB_TO_MB_THRESHOLD).toFixed(1))); networkChartKey.value++; } - // Prepare new data points based on current unit let newRxValue, newTxValue; if (networkRateUnitIsMB.value) { newRxValue = parseFloat((currentNetRxRateKB / KB_TO_MB_THRESHOLD).toFixed(1)); @@ -243,30 +353,25 @@ const updateCharts = (newStatus: any) => { newTxValue = parseFloat(currentNetTxRateKB.toFixed(1)); } - const finalNewNetRxData = [...networkChartData.value.datasets[0].data]; - finalNewNetRxData.shift(); - finalNewNetRxData.push(newRxValue); - - const finalNewNetTxData = [...networkChartData.value.datasets[1].data]; - finalNewNetTxData.shift(); - finalNewNetTxData.push(newTxValue); + newNetRxData.shift(); + newNetRxData.push(newRxValue); + newNetTxData.shift(); + newNetTxData.push(newTxValue); networkChartData.value = { ...networkChartData.value, datasets: [ - { ...networkChartData.value.datasets[0], data: finalNewNetRxData }, - { ...networkChartData.value.datasets[1], data: finalNewNetTxData }, + { ...networkChartData.value.datasets[0], data: newNetRxData }, + { ...networkChartData.value.datasets[1], data: newNetTxData }, ], }; - // Dynamically adjust Y-axis for network chart - const allNetworkData = [...finalNewNetRxData, ...finalNewNetTxData]; - const minMax = networkRateUnitIsMB.value ? 1 : 10; // Min max for MB/s or KB/s - const maxNetworkRate = Math.max(...allNetworkData, minMax); - if (networkChartOptions.value.scales?.y) { - const roundingFactor = networkRateUnitIsMB.value ? 1 : 10; // Round to next 1 or 10 - const buffer = networkRateUnitIsMB.value ? 1 : 10; // Buffer for MB/s or KB/s + const allNetworkData = [...newNetRxData, ...newNetTxData]; + const minMax = networkRateUnitIsMB.value ? 1 : 10; + const maxNetworkRate = Math.max(...allNetworkData, minMax); + const roundingFactor = networkRateUnitIsMB.value ? 1 : 10; + const buffer = networkRateUnitIsMB.value ? 1 : 10; networkChartOptions.value.scales.y.max = Math.ceil(maxNetworkRate / roundingFactor) * roundingFactor + buffer; } };