feat: Sentinel 横版折线图 — MiniChart 复用主页 chart.js (SPA 全局可用)

This commit is contained in:
qinglong
2026-06-13 22:10:02 +08:00
parent 31f1a2e177
commit 765387981e
+31 -15
View File
@@ -86,6 +86,7 @@
// 确保 chart.js 已加载 (SPA 导航时可能未加载) // 确保 chart.js 已加载 (SPA 导航时可能未加载)
var pollTimer = null; var pollTimer = null;
var _chartInstances = {};
var editingId = null; var editingId = null;
var _layoutMode = 'grid'; // 'grid' | 'row' var _layoutMode = 'grid'; // 'grid' | 'row'
@@ -94,6 +95,8 @@
var btn = document.getElementById("btn-layout"); var btn = document.getElementById("btn-layout");
var grid = document.getElementById("sentinel-grid"); var grid = document.getElementById("sentinel-grid");
// 销毁旧图表 // 销毁旧图表
Object.keys(_chartInstances).forEach(function(k){ if(_chartInstances[k]) _chartInstances[k].forEach(function(c){ if(c.destroy)c.destroy(); }); });
_chartInstances = {};
_cardCache = {}; _cardCache = {};
_firstRender = true; _firstRender = true;
grid.innerHTML = '<div class="card" style="text-align:center;padding:24px;color:var(--text-dim)">加载中...</div>'; grid.innerHTML = '<div class="card" style="text-align:center;padding:24px;color:var(--text-dim)">加载中...</div>';
@@ -120,6 +123,8 @@
} }
// ═══ 仪表盘 ═══ // ═══ 仪表盘 ═══
Object.keys(_chartInstances).forEach(function(k){ if(_chartInstances[k]) _chartInstances[k].forEach(function(c){ if(c.destroy)c.destroy(); }); });
_chartInstances = {};
var _cardCache = {}; var _cardCache = {};
function animateValue(el, oldVal, newVal, decimals, suffix) { function animateValue(el, oldVal, newVal, decimals, suffix) {
@@ -143,6 +148,8 @@
var grid = document.getElementById("sentinel-grid"); var grid = document.getElementById("sentinel-grid");
if (!nodes.length) { if (!nodes.length) {
grid.innerHTML = '<div class="card" style="text-align:center;padding:24px;color:var(--text-dim)">暂无节点 — 切换到节点管理添加</div>'; grid.innerHTML = '<div class="card" style="text-align:center;padding:24px;color:var(--text-dim)">暂无节点 — 切换到节点管理添加</div>';
Object.keys(_chartInstances).forEach(function(k){ if(_chartInstances[k]) _chartInstances[k].forEach(function(c){ if(c.destroy)c.destroy(); }); });
_chartInstances = {};
_cardCache = {}; _cardCache = {};
_firstRender = true; _firstRender = true;
return; return;
@@ -220,11 +227,18 @@
if (isNew) { if (isNew) {
if (isRow) { if (isRow) {
body.innerHTML = body.innerHTML =
'<div class="metric-row" style="flex-wrap:wrap">'+ '<div style="display:flex;flex-wrap:wrap;gap:12px">'+
'<div class="metric" style="min-width:100px"><div class="metric-label">CPU</div><div class="metric-value cpu-val" style="font-size:1.4rem">0%</div></div>'+ '<div class="chart-box"><canvas class="chart-cpu"></canvas></div>'+
'<div class="metric" style="min-width:100px"><div class="metric-label">MEM</div><div class="metric-value mem-val" style="font-size:1.4rem">0%</div><div class="mem-detail-val" style="font-size:.65rem;color:var(--text-dim)"></div></div>'+ '<div class="chart-box"><canvas class="chart-mem"></canvas></div>'+
'<div class="metric" style="min-width:100px"><div class="metric-label">NET ↓</div><div class="metric-value net-down" style="font-size:1rem"></div></div>'+ '<div class="chart-box"><canvas class="chart-net"></canvas></div>'+
'<div class="metric" style="min-width:100px"><div class="metric-label">NET ↑</div><div class="metric-value net-up" style="font-size:1rem"></div></div>'+ if (typeof MiniChart !== "undefined") {
_chartInstances[id] = [
new MiniChart(body.querySelector(".chart-cpu"), "#7aa2f7"),
new MiniChart(body.querySelector(".chart-mem"), "#9ece6a"),
new MiniChart(body.querySelector(".chart-net"), "#e0af68"),
];
}
''+
'</div>'; '</div>';
} else { } else {
body.innerHTML = body.innerHTML =
@@ -240,18 +254,20 @@
} }
if (isRow) { if (isRow) {
// 横版: 更新数值 + 内存详情 var charts = _chartInstances[id];
body.querySelector('.net-down').textContent = fmtSpeed(net.rx_bytes_sec||0); if (charts && charts[0]) {
body.querySelector('.net-up').textContent = fmtSpeed(net.tx_bytes_sec||0); charts[0].update(cpuVal);
var md = body.querySelector('.mem-detail-val'); charts[1].update(memVal);
if (md) md.textContent = (mem.used_gb||0).toFixed(1) + '/' + (mem.total_gb||0).toFixed(1) + ' GB'; charts[2].update(net.rx_bytes_sec||0);
animateValue(body.querySelector('.cpu-val'), oldCpu, cpuVal, 1, '%'); }
animateValue(body.querySelector('.mem-val'), oldMem, memVal, 1, '%');
// 右边摘要
var right = card.querySelector('.card-right'); var right = card.querySelector('.card-right');
if (right) right.innerHTML = if (right) right.innerHTML =
'<div style="font-size:.75rem;color:var(--text-dim)">'+ '<div class="metric-value" style="font-size:1.2rem">CPU '+(cpuVal).toFixed(1)+'%</div>'+
(n.last_seen ? new Date(n.last_seen*1000).toLocaleTimeString() : '—')+'</div>'; '<div class="metric-value" style="font-size:1.2rem;margin-top:4px">MEM '+(memVal).toFixed(1)+'%</div>'+
'<div style="font-size:.7rem;color:var(--text-dim);margin-top:4px">'+(mem.used_gb||0).toFixed(1)+'/'+(mem.total_gb||0).toFixed(1)+' GB</div>'+
'<div style="font-size:.75rem;color:var(--text-dim);margin-top:8px">↓ '+fmtSpeed(net.rx_bytes_sec||0)+'</div>'+
'<div style="font-size:.75rem;color:var(--text-dim)">↑ '+fmtSpeed(net.tx_bytes_sec||0)+'</div>'+
'<div style="font-size:.7rem;color:var(--text-dim);margin-top:8px">'+(n.last_seen ? new Date(n.last_seen*1000).toLocaleTimeString() : '—')+'</div>';
} else { } else {
body.querySelector('.net-down').textContent = fmtSpeed(net.rx_bytes_sec||0); body.querySelector('.net-down').textContent = fmtSpeed(net.rx_bytes_sec||0);
body.querySelector('.net-up').textContent = fmtSpeed(net.tx_bytes_sec||0); body.querySelector('.net-up').textContent = fmtSpeed(net.tx_bytes_sec||0);