fix(metrics): refine chart handling and data validation in metrics dashboard

- Updated chart management logic to ensure proper destruction of existing charts before creating new ones, preventing memory leaks.
- Enhanced data validation by adding a check for the data format returned from the API, ensuring it is an array before processing.
- Consolidated chart options into a common configuration for improved consistency across different charts.
- Improved the updateChart function to handle canvas element existence checks and streamline chart creation.

These changes enhance the performance and reliability of the metrics dashboard, providing clearer visualizations and better data handling.
This commit is contained in:
wood chen 2024-12-05 01:28:17 +08:00
parent b5a3cfeee4
commit 68a0ea234b

View File

@ -629,7 +629,7 @@ var metricsTemplate = `
// 每5秒自动刷新 // 每5秒自动刷新
setInterval(refreshMetrics, 5000); setInterval(refreshMetrics, 5000);
// 修改图表加载相关代码 // 修改图表相关代码
let currentCharts = { let currentCharts = {
requests: null, requests: null,
errorRate: null, errorRate: null,
@ -644,6 +644,11 @@ var metricsTemplate = `
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (!Array.isArray(data)) {
console.error('Invalid data format');
return;
}
// 反转数据顺序,使时间从左到右 // 反转数据顺序,使时间从左到右
data.reverse(); data.reverse();
@ -658,27 +663,64 @@ var metricsTemplate = `
} }
}); });
// 更新图表配置,添加时间轴方向设置 const commonOptions = {
const chartOptions = { responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: { scales: {
x: { x: {
reverse: false // 确保时间轴从左到右 display: true,
grid: {
display: false
}
},
y: {
beginAtZero: true,
grid: {
drawBorder: false
}
}
},
elements: {
line: {
tension: 0.4
},
point: {
radius: 2
} }
} }
}; };
// 更新或创建图表 // 更新或创建图表
updateChart('requestsChart', currentCharts.requests, labels, data, '请求数', updateChart('requestsChart', 'requests', labels, data, '请求数',
m => m.total_requests, '#007bff', chartOptions); m => m.total_requests, '#007bff', commonOptions);
updateChart('errorRateChart', currentCharts.errorRate, labels, data, '错误率 (%)', updateChart('errorRateChart', 'errorRate', labels, data, '错误率 (%)',
m => m.error_rate * 100, '#dc3545', chartOptions); m => m.error_rate * 100, '#dc3545', commonOptions);
updateChart('bytesChart', currentCharts.bytes, labels, data, '流量 (MB)', updateChart('bytesChart', 'bytes', labels, data, '流量 (MB)',
m => m.total_bytes / (1024 * 1024), '#28a745', chartOptions); m => m.total_bytes / (1024 * 1024), '#28a745', commonOptions);
}); })
.catch(error => console.error('Error:', error));
} }
function updateChart(canvasId, chartInstance, labels, data, label, valueGetter, color, options = {}) { function updateChart(canvasId, chartKey, labels, data, label, valueGetter, color, options) {
const ctx = document.getElementById(canvasId).getContext('2d'); // 确保 canvas 元素存在
const canvas = document.getElementById(canvasId);
if (!canvas) {
console.error('Canvas element ' + canvasId + ' not found');
return;
}
// 如果存在旧图表,销毁它
if (currentCharts[chartKey]) {
currentCharts[chartKey].destroy();
currentCharts[chartKey] = null;
}
const ctx = canvas.getContext('2d');
const chartData = { const chartData = {
labels: labels, labels: labels,
datasets: [{ datasets: [{
@ -686,52 +728,16 @@ var metricsTemplate = `
data: data.map(valueGetter), data: data.map(valueGetter),
borderColor: color, borderColor: color,
backgroundColor: color + '20', backgroundColor: color + '20',
fill: true, fill: true
tension: 0.4
}] }]
}; };
if (chartInstance) { // 创建新图表
chartInstance.data = chartData; currentCharts[chartKey] = new Chart(ctx, {
chartInstance.options = { type: 'line',
...chartInstance.options, data: chartData,
...options, options: options
animation: false });
};
chartInstance.update();
} else {
// 创建新图表前先销毁旧的
if (currentCharts[canvasId.replace('Chart', '')]) {
currentCharts[canvasId.replace('Chart', '')].destroy();
}
// 创建新图表
const newChart = new Chart(ctx, {
type: 'line',
data: chartData,
options: {
...options,
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true
},
x: {
reverse: false
}
}
}
});
// 更新 currentCharts 对象
currentCharts[canvasId.replace('Chart', '')] = newChart;
}
} }
// 时间范围按钮处理 // 时间范围按钮处理
@ -739,16 +745,19 @@ var metricsTemplate = `
btn.addEventListener('click', function() { btn.addEventListener('click', function() {
document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active'); this.classList.add('active');
loadHistoryData(this.dataset.hours); loadHistoryData(parseInt(this.dataset.hours));
}); });
}); });
// 初始加载历史数据 // 初始加载历史数据
loadHistoryData(24); document.addEventListener('DOMContentLoaded', function() {
loadHistoryData(24);
});
</script> </script>
<!-- 添加 Chart.js --> <!-- 添加 Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <!-- body 结束标签前添加 -->
<script src="https://i-aws.czl.net/jsdelivr/npm/chart.js@3.7.0/dist/chart.min.js"></script>
</body> </body>
</html> </html>
` `