Update StatusCharts.vue
This commit is contained in:
@@ -7,9 +7,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-container bg-header rounded p-3">
|
<div class="chart-container bg-header rounded p-3">
|
||||||
<h5 class="text-sm font-medium mb-2 text-text-secondary">内存使用率</h5>
|
<h5 class="text-sm font-medium mb-2 text-text-secondary">{{ memoryChartTitle }}</h5>
|
||||||
<div class="chart-wrapper h-40">
|
<div class="chart-wrapper h-40">
|
||||||
<Line :data="memoryChartData" :options="percentageChartOptions" :key="memoryChartKey" />
|
<Line :data="memoryChartData" :options="memoryChartOptions" :key="memoryChartKey" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-container bg-header rounded p-3">
|
<div class="chart-container bg-header rounded p-3">
|
||||||
@@ -47,19 +47,37 @@ ChartJS.register(
|
|||||||
CategoryScale
|
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<{
|
const props = defineProps<{
|
||||||
serverStatus: any;
|
serverStatus: ServerStatusData | null;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const MAX_DATA_POINTS = 60;
|
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 cpuChartKey = ref(0);
|
||||||
const memoryChartKey = ref(0);
|
const memoryChartKey = ref(0);
|
||||||
const networkChartKey = ref(0);
|
const networkChartKey = ref(0);
|
||||||
|
|
||||||
const networkRateUnitIsMB = ref(false);
|
const networkRateUnitIsMB = ref(false);
|
||||||
const networkChartTitle = ref('网络速度 (KB/s)');
|
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 initialLabels = Array.from({ length: MAX_DATA_POINTS }, (_, i) => `-${(MAX_DATA_POINTS - 1 - i)}s`);
|
||||||
|
|
||||||
const cpuChartData = ref({
|
const cpuChartData = ref({
|
||||||
@@ -82,7 +100,7 @@ const memoryChartData = ref({
|
|||||||
labels: [...initialLabels],
|
labels: [...initialLabels],
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: '内存使用率 (%)',
|
label: '内存使用 (MB)', // Initial label
|
||||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
borderColor: 'rgba(255, 99, 132, 1)',
|
borderColor: 'rgba(255, 99, 132, 1)',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
@@ -131,14 +149,56 @@ const baseChartOptions: Omit<ChartOptions<'line'>, 'scales'> = {
|
|||||||
interaction: { mode: 'index', intersect: false },
|
interaction: { mode: 'index', intersect: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
const percentageChartOptions = ref<ChartOptions<'line'>>({
|
const percentageChartOptions = ref<ChartOptions<'line'>>({ // For CPU
|
||||||
...baseChartOptions,
|
...baseChartOptions,
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
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<ChartOptions<'line'>>({
|
||||||
|
...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)' },
|
grid: { color: 'rgba(156, 163, 175, 0.1)' },
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
@@ -157,7 +217,8 @@ const networkChartOptions = ref<ChartOptions<'line'>>({
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
label: (context: TooltipItem<'line'>) => {
|
label: (context: TooltipItem<'line'>) => {
|
||||||
let label = context.dataset.label || '';
|
let label = context.dataset.label || '';
|
||||||
if (label) {
|
if (label) {
|
||||||
|
label = label.substring(0, label.lastIndexOf('(') -1); // Remove old unit from label
|
||||||
label += ': ';
|
label += ': ';
|
||||||
}
|
}
|
||||||
if (context.parsed.y !== null) {
|
if (context.parsed.y !== null) {
|
||||||
@@ -173,7 +234,6 @@ const networkChartOptions = ref<ChartOptions<'line'>>({
|
|||||||
y: {
|
y: {
|
||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
min: 0,
|
min: 0,
|
||||||
// max will be set dynamically
|
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#9CA3AF',
|
color: '#9CA3AF',
|
||||||
callback: function(value) {
|
callback: function(value) {
|
||||||
@@ -190,7 +250,7 @@ const networkChartOptions = ref<ChartOptions<'line'>>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const updateCharts = (newStatus: any) => {
|
const updateCharts = (newStatus: ServerStatusData | null) => {
|
||||||
if (!newStatus) return;
|
if (!newStatus) return;
|
||||||
|
|
||||||
// Update CPU Chart
|
// Update CPU Chart
|
||||||
@@ -202,38 +262,88 @@ const updateCharts = (newStatus: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update Memory Chart
|
// Update Memory Chart
|
||||||
if (typeof newStatus.memPercent === 'number') {
|
if (typeof newStatus.memUsed === 'number' && typeof newStatus.memTotal === 'number') {
|
||||||
const newMemData = [...memoryChartData.value.datasets[0].data];
|
let currentMemUsed = newStatus.memUsed; // MB
|
||||||
newMemData.shift();
|
const memTotal = newStatus.memTotal; // MB
|
||||||
newMemData.push(parseFloat(newStatus.memPercent.toFixed(1)));
|
let currentData = [...memoryChartData.value.datasets[0].data];
|
||||||
memoryChartData.value = { ...memoryChartData.value, datasets: [{ ...memoryChartData.value.datasets[0], data: newMemData }] };
|
|
||||||
|
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
|
// Update Network Chart
|
||||||
let currentNetRxRateKB = (newStatus.netRxRate || 0) / 1024;
|
let currentNetRxRateKB = (newStatus.netRxRate || 0) / 1024;
|
||||||
let currentNetTxRateKB = (newStatus.netTxRate || 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)) {
|
if (!networkRateUnitIsMB.value && (currentNetRxRateKB >= KB_TO_MB_THRESHOLD || currentNetTxRateKB >= KB_TO_MB_THRESHOLD)) {
|
||||||
networkRateUnitIsMB.value = true;
|
networkRateUnitIsMB.value = true;
|
||||||
networkChartTitle.value = '网络速度 (MB/s)';
|
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[0].label = '下载 (MB/s)';
|
||||||
networkChartData.value.datasets[1].label = '上传 (MB/s)';
|
networkChartData.value.datasets[1].label = '上传 (MB/s)';
|
||||||
|
newNetRxData = newNetRxData.map(d => parseFloat((d / KB_TO_MB_THRESHOLD).toFixed(1)));
|
||||||
// Force chart re-render for label and unit changes
|
newNetTxData = newNetTxData.map(d => parseFloat((d / KB_TO_MB_THRESHOLD).toFixed(1)));
|
||||||
networkChartKey.value++;
|
networkChartKey.value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare new data points based on current unit
|
|
||||||
let newRxValue, newTxValue;
|
let newRxValue, newTxValue;
|
||||||
if (networkRateUnitIsMB.value) {
|
if (networkRateUnitIsMB.value) {
|
||||||
newRxValue = parseFloat((currentNetRxRateKB / KB_TO_MB_THRESHOLD).toFixed(1));
|
newRxValue = parseFloat((currentNetRxRateKB / KB_TO_MB_THRESHOLD).toFixed(1));
|
||||||
@@ -243,30 +353,25 @@ const updateCharts = (newStatus: any) => {
|
|||||||
newTxValue = parseFloat(currentNetTxRateKB.toFixed(1));
|
newTxValue = parseFloat(currentNetTxRateKB.toFixed(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalNewNetRxData = [...networkChartData.value.datasets[0].data];
|
newNetRxData.shift();
|
||||||
finalNewNetRxData.shift();
|
newNetRxData.push(newRxValue);
|
||||||
finalNewNetRxData.push(newRxValue);
|
newNetTxData.shift();
|
||||||
|
newNetTxData.push(newTxValue);
|
||||||
const finalNewNetTxData = [...networkChartData.value.datasets[1].data];
|
|
||||||
finalNewNetTxData.shift();
|
|
||||||
finalNewNetTxData.push(newTxValue);
|
|
||||||
|
|
||||||
networkChartData.value = {
|
networkChartData.value = {
|
||||||
...networkChartData.value,
|
...networkChartData.value,
|
||||||
datasets: [
|
datasets: [
|
||||||
{ ...networkChartData.value.datasets[0], data: finalNewNetRxData },
|
{ ...networkChartData.value.datasets[0], data: newNetRxData },
|
||||||
{ ...networkChartData.value.datasets[1], data: finalNewNetTxData },
|
{ ...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) {
|
if (networkChartOptions.value.scales?.y) {
|
||||||
const roundingFactor = networkRateUnitIsMB.value ? 1 : 10; // Round to next 1 or 10
|
const allNetworkData = [...newNetRxData, ...newNetTxData];
|
||||||
const buffer = networkRateUnitIsMB.value ? 1 : 10; // Buffer for MB/s or KB/s
|
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;
|
networkChartOptions.value.scales.y.max = Math.ceil(maxNetworkRate / roundingFactor) * roundingFactor + buffer;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user