fix: 清理残留变量赋值 + 重写 renderNodes (行/网格统一逻辑)
This commit is contained in:
@@ -95,7 +95,6 @@
|
||||
var btn = document.getElementById("btn-layout");
|
||||
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 = {};
|
||||
_firstRender = true;
|
||||
@@ -123,8 +122,7 @@
|
||||
}
|
||||
|
||||
// ═══ 仪表盘 ═══
|
||||
Object.keys(_chartInstances).forEach(function(k){ if(_chartInstances[k]) _chartInstances[k].forEach(function(c){ if(c.destroy)c.destroy(); }); });
|
||||
_chartInstances = {};
|
||||
|
||||
var _cardCache = {};
|
||||
|
||||
function animateValue(el, oldVal, newVal, decimals, suffix) {
|
||||
@@ -148,15 +146,14 @@
|
||||
var grid = document.getElementById("sentinel-grid");
|
||||
if (!nodes.length) {
|
||||
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 = {};
|
||||
_firstRender = true;
|
||||
return;
|
||||
}
|
||||
// 首次渲染清除占位
|
||||
if (_firstRender) { grid.innerHTML = ''; _firstRender = false; }
|
||||
// 清理已删除的节点
|
||||
var isRow = _layoutMode === 'row';
|
||||
|
||||
// 清理删除的节点
|
||||
var activeIds = {};
|
||||
nodes.forEach(function(n){ activeIds[n.id||n.name] = true; });
|
||||
Object.keys(_cardCache).forEach(function(id){
|
||||
@@ -165,81 +162,115 @@
|
||||
|
||||
nodes.forEach(function(n){
|
||||
var sys = n.system || {};
|
||||
var cpu = sys.cpu || {};
|
||||
var mem = sys.memory || {};
|
||||
var cpu = sys.cpu || {}; var cpuVal = cpu.percent || 0;
|
||||
var mem = sys.memory || {}; var memVal = mem.percent || 0;
|
||||
var net = sys.net_speed || {};
|
||||
var id = n.id || n.name;
|
||||
var cpuVal = cpu.percent || 0;
|
||||
var memVal = mem.percent || 0;
|
||||
var cpuBarCls = bar(cpuVal);
|
||||
var memBarCls = bar(memVal);
|
||||
|
||||
var card = _cardCache[id];
|
||||
if (!card) {
|
||||
// 首次创建卡片
|
||||
card = document.createElement('div');
|
||||
card.className = 'node-card ' + (n.online ? 'online' : 'offline');
|
||||
card.setAttribute('data-id', id);
|
||||
var plat = n.platform || {};
|
||||
var sysInfo = (plat.system||'') + (plat.release ? ' ' + plat.release : '') + (plat.machine ? ' | ' + plat.machine : '');
|
||||
var isRow = _layoutMode === 'row';
|
||||
if (isRow) {
|
||||
card.innerHTML =
|
||||
'<div class="card-left">'+
|
||||
'<div class="node-name">'+(n.online?'🟢':'🔴')+' '+esc(n.name)+'</div>'+
|
||||
(sysInfo.trim() ? '<div class="node-sys">'+esc(sysInfo)+'</div>' : '')+
|
||||
'<div class="node-url">'+esc(n.url||'')+'</div>'+
|
||||
(n.notes ? '<div class="node-notes">📝 '+esc(n.notes)+'</div>' : '')+
|
||||
'</div>'+
|
||||
'<div class="card-mid">'+
|
||||
'<div class="metrics-body">'+
|
||||
'<div style="display:flex;flex-wrap:wrap;gap:12px">'+
|
||||
'<div class="chart-box"><canvas class="chart-cpu" width="200" height="60"></canvas></div>'+
|
||||
'<div class="chart-box"><canvas class="chart-mem" width="200" height="60"></canvas></div>'+
|
||||
'<div class="chart-box"><canvas class="chart-net" width="200" height="60"></canvas></div>'+
|
||||
'</div>'+
|
||||
'<div class="metric-row" style="margin-top:4px">'+
|
||||
'<div class="metric"><span class="metric-label">CPU</span> <span class="metric-value cpu-val" style="font-size:.9rem">0%</span></div>'+
|
||||
'<div class="metric"><span class="metric-label">MEM</span> <span class="metric-value mem-val" style="font-size:.9rem">0%</span></div>'+
|
||||
'<div class="metric"><span class="mem-detail-val" style="font-size:.65rem;color:var(--text-dim)"></span></div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="card-right">'+
|
||||
'<div class="node-time"></div>'+
|
||||
'</div>';
|
||||
if (typeof MiniChart !== 'undefined') {
|
||||
var body = card.querySelector('.metrics-body');
|
||||
_chartInstances[id] = [
|
||||
new MiniChart(body.querySelector('.chart-cpu'), '#7aa2f7'),
|
||||
new MiniChart(body.querySelector('.chart-mem'), '#9ece6a'),
|
||||
new MiniChart(body.querySelector('.chart-net'), '#e0af68'),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
card.innerHTML =
|
||||
(isRow ? '<div class="card-left">' : '') +
|
||||
'<div class="node-name">'+(n.online?'🟢':'🔴')+' '+esc(n.name)+'</div>'+
|
||||
(sysInfo.trim() ? '<div class="node-sys">'+esc(sysInfo)+'</div>' : '')+
|
||||
'<div class="node-url">'+esc(n.url||'')+'</div>'+
|
||||
(n.notes ? '<div class="node-notes">📝 '+esc(n.notes)+'</div>' : '')+
|
||||
(isRow ? '</div><div class="card-mid">' : '') +
|
||||
'<div class="metrics-body"></div>'+
|
||||
(isRow ? '</div><div class="card-right"><div class="node-time"></div></div>' : '<div class="node-time"></div>');
|
||||
'<div class="node-time"></div>';
|
||||
}
|
||||
grid.appendChild(card);
|
||||
_cardCache[id] = card;
|
||||
}
|
||||
|
||||
// 更新状态样式
|
||||
// 更新通用信息
|
||||
card.className = 'node-card ' + (n.online ? 'online' : 'offline');
|
||||
card.querySelector('.node-name').innerHTML = (n.online?'🟢':'🔴')+' '+esc(n.name);
|
||||
var nameEl = card.querySelector('.node-name');
|
||||
if (nameEl) nameEl.innerHTML = (n.online?'🟢':'🔴')+' '+esc(n.name);
|
||||
var urlEl = card.querySelector('.node-url');
|
||||
if (urlEl) urlEl.textContent = n.url||'';
|
||||
var sysEl = card.querySelector('.node-sys');
|
||||
var plat = n.platform || {};
|
||||
var sysInfo = (plat.system||'') + (plat.release ? ' ' + plat.release : '') + (plat.machine ? ' | ' + plat.machine : '');
|
||||
if (sysInfo.trim()) {
|
||||
if (!sysEl) { sysEl = document.createElement('div'); sysEl.className = 'node-sys'; card.insertBefore(sysEl, card.querySelector('.node-url')); }
|
||||
sysEl.textContent = sysInfo;
|
||||
var plat2 = n.platform || {};
|
||||
var si2 = (plat2.system||'') + (plat2.release ? ' ' + plat2.release : '') + (plat2.machine ? ' | ' + plat2.machine : '');
|
||||
if (si2.trim()) {
|
||||
if (!sysEl) { sysEl = document.createElement('div'); sysEl.className = 'node-sys'; card.insertBefore(sysEl, card.querySelector('.metrics-body')||card.querySelector('.card-mid')); }
|
||||
sysEl.textContent = si2;
|
||||
} else if (sysEl) { sysEl.remove(); }
|
||||
card.querySelector('.node-url').textContent = n.url||'';
|
||||
var notesEl = card.querySelector('.node-notes');
|
||||
if (n.notes) {
|
||||
if (!notesEl) {
|
||||
notesEl = document.createElement('div'); notesEl.className = 'node-notes';
|
||||
card.insertBefore(notesEl, card.querySelector('.metrics-body'));
|
||||
}
|
||||
notesEl.innerHTML = '📝 '+esc(n.notes);
|
||||
} else if (notesEl) { notesEl.remove(); }
|
||||
|
||||
var body = card.querySelector('.metrics-body');
|
||||
if (n.online) {
|
||||
var oldCpu = parseFloat((body.getAttribute('data-cpu')||cpuVal));
|
||||
var oldMem = parseFloat((body.getAttribute('data-mem')||memVal));
|
||||
if (!body) return;
|
||||
|
||||
if (isRow) {
|
||||
// 更新图表
|
||||
var charts = _chartInstances[id];
|
||||
if (charts && charts[0]) {
|
||||
charts[0].update(cpuVal);
|
||||
charts[1].update(memVal);
|
||||
charts[2].update(net.rx_bytes_sec||0);
|
||||
}
|
||||
// 更新数值
|
||||
var ce = body.querySelector('.cpu-val');
|
||||
var me = body.querySelector('.mem-val');
|
||||
var md = body.querySelector('.mem-detail-val');
|
||||
if (ce) ce.textContent = cpuVal.toFixed(1) + '%';
|
||||
if (me) me.textContent = memVal.toFixed(1) + '%';
|
||||
if (md) md.textContent = 'NET ↓'+fmtSpeed(net.rx_bytes_sec||0)+' ↑'+fmtSpeed(net.tx_bytes_sec||0);
|
||||
// 右边数值
|
||||
var right = card.querySelector('.card-right');
|
||||
if (right) right.innerHTML =
|
||||
'<div style="font-size:1.1rem;font-weight:600">CPU '+(cpuVal).toFixed(1)+'%</div>'+
|
||||
'<div style="font-size:1.1rem;font-weight:600;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>';
|
||||
// 时间
|
||||
var timeEl = card.querySelector('.node-time');
|
||||
if (timeEl) timeEl.innerHTML = '<div style="font-size:.7rem;color:var(--text-dim);margin-top:8px">'+(n.last_seen ? new Date(n.last_seen*1000).toLocaleTimeString() : '—')+'</div>';
|
||||
} else {
|
||||
// 网格模式
|
||||
var oldCpu = parseFloat(body.getAttribute('data-cpu')||cpuVal);
|
||||
var oldMem = parseFloat(body.getAttribute('data-mem')||memVal);
|
||||
body.setAttribute('data-cpu', cpuVal);
|
||||
body.setAttribute('data-mem', memVal);
|
||||
var isNew = !body.querySelector('.cpu-val');
|
||||
var isRow = _layoutMode === 'row';
|
||||
if (isNew) {
|
||||
if (isRow) {
|
||||
body.innerHTML =
|
||||
'<div style="display:flex;flex-wrap:wrap;gap:12px">'+
|
||||
'<div class="chart-box"><canvas class="chart-cpu"></canvas></div>'+
|
||||
'<div class="chart-box"><canvas class="chart-mem"></canvas></div>'+
|
||||
'<div class="chart-box"><canvas class="chart-net"></canvas></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"),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
body.innerHTML =
|
||||
'<div class="metric-row">'+
|
||||
'<div class="metric"><div class="metric-label">CPU</div><div class="metric-value cpu-val">0%</div><div class="metric-bar"><div class="metric-bar-fill" style="width:0%"></div></div></div>'+
|
||||
@@ -250,43 +281,25 @@
|
||||
'<div class="metric"><div class="metric-label">NET ↑</div><div class="metric-value net-up" style="font-size:.9rem"></div></div>'+
|
||||
'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
if (isRow) {
|
||||
var charts = _chartInstances[id];
|
||||
if (charts && charts[0]) {
|
||||
charts[0].update(cpuVal);
|
||||
charts[1].update(memVal);
|
||||
charts[2].update(net.rx_bytes_sec||0);
|
||||
}
|
||||
var right = card.querySelector('.card-right');
|
||||
if (right) right.innerHTML =
|
||||
'<div class="metric-value" style="font-size:1.2rem">CPU '+(cpuVal).toFixed(1)+'%</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 {
|
||||
body.querySelector('.net-down').textContent = fmtSpeed(net.rx_bytes_sec||0);
|
||||
body.querySelector('.net-up').textContent = fmtSpeed(net.tx_bytes_sec||0);
|
||||
var md = body.querySelector('.mem-detail-val');
|
||||
if (md) md.textContent = (mem.used_gb||0).toFixed(1) + '/' + (mem.total_gb||0).toFixed(1) + ' GB';
|
||||
|
||||
var barCpu = body.querySelectorAll('.metric-bar-fill')[0];
|
||||
var barMem = body.querySelectorAll('.metric-bar-fill')[1];
|
||||
if (barCpu && barMem) {
|
||||
var cpuBarCls = bar(cpuVal), memBarCls = bar(memVal);
|
||||
barCpu.style.transition = 'none'; barMem.style.transition = 'none';
|
||||
barCpu.style.width = oldCpu+'%'; barMem.style.width = oldMem+'%';
|
||||
barCpu.className = 'metric-bar-fill '+cpuBarCls; barMem.className = 'metric-bar-fill '+memBarCls;
|
||||
barCpu.offsetWidth; barMem.offsetWidth;
|
||||
barCpu.style.transition = ''; barMem.style.transition = '';
|
||||
barCpu.style.width = cpuVal+'%'; barMem.style.width = memVal+'%';
|
||||
|
||||
}
|
||||
animateValue(body.querySelector('.cpu-val'), oldCpu, cpuVal, 1, '%');
|
||||
animateValue(body.querySelector('.mem-val'), oldMem, memVal, 1, '%');
|
||||
}
|
||||
} // close if (n.online)
|
||||
card.querySelector('.node-time').innerHTML = '<div style="font-size:.7rem;color:var(--text-dim);margin-top:8px">'+(n.last_seen ? new Date(n.last_seen*1000).toLocaleTimeString() : '—')+'</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user