${n.role}OC ${n.oc_version}${n.os}
${hbBars}
供应商
${provs.sort((a,b)=>b.default-a.default||(a.name>b.name?1:-1)).map(p=>`
${p.default?'':''}${p.name} ${p.model}
${p.status==='ok'?'✓ '+p.ms+'ms':'✗ '+(p.err||'离线')+''}
`).join('')}
-${[['cpu',n.cpu],['mem',n.mem],['disk',n.disk],['swap',n.swap]].map(([l,v])=>`
`).join('')}
+${[['cpu',n.cpu],['mem',n.mem],['disk',n.disk],['swap',n.swap]].map(([l,v])=>`
`).join('')}
⏱ ${fmtAge(now-n.uptime)}📡 ${n.sessions} 会话⚡ 网关 ${n.gw_ok?'✓':'✗'}🐾 守护 ${n.daemon_ok?'✓':'✗'}
@@ -178,6 +178,29 @@ function renderLogs(){
$('#logTable').innerHTML=h+'';
}
+function updateNodeCard(n){
+ const el=document.querySelector(`.nd[data-id="${n.id}"]`);
+ if(!el)return false;
+ const on=Date.now()/1000-n.last_seen<120;
+ el.className='nd'+(on?'':' offline');
+ // gauges
+ [['cpu',n.cpu],['mem',n.mem],['disk',n.disk],['swap',n.swap]].forEach(([k,v])=>{
+ const g=el.querySelector(`[data-g="${k}"]`);
+ if(g){g.querySelector('.g-f').style.width=v+'%';g.querySelector('.g-f').className='g-f '+gaugeColor(v);g.querySelector('.g-n').textContent=on?v+'%':'—';}
+ });
+ // tokens
+ const tv=el.querySelectorAll('.tk-v');
+ if(tv[0])tv[0].textContent=fmtTok(n.tok_today);
+ if(tv[1])tv[1].textContent=fmtTok(n.tok_week);
+ if(tv[2])tv[2].textContent=fmtTok(n.tok_month);
+ // footer
+ const fs=el.querySelectorAll('.nd-f span');
+ if(fs[0])fs[0].textContent='⏱ '+fmtAge(Date.now()/1000-n.uptime);
+ if(fs[1])fs[1].textContent='📡 '+n.sessions+' 会话';
+ if(fs[2])fs[2].textContent='⚡ 网关 '+(n.gw_ok?'✓':'✗');
+ if(fs[3])fs[3].textContent='🐾 守护 '+(n.daemon_ok?'✓':'✗');
+ return true;
+}
function render(){renderStats();renderNodes();renderMatrix();renderLogs();
$('#subtitle').textContent=DATA.nodes.length+' 个节点 · 更新于 '+new Date().toLocaleString('zh-CN');
}
@@ -194,7 +217,8 @@ function connectWS(){
if(d.type==='heartbeat'){
const i=DATA.nodes.findIndex(function(n){return n.id===d.node.id});
if(i>=0)Object.assign(DATA.nodes[i],d.node);else DATA.nodes.push(d.node);
- render();
+ if(!updateNodeCard(DATA.nodes[i>=0?i:DATA.nodes.length-1]))renderNodes();
+ renderStats();
}
if(d.type==='request'){DATA.requests.unshift(d.request);if(DATA.requests.length>100)DATA.requests.pop();render();}
if(d.type==='rename'){var n=DATA.nodes.find(function(x){return x.id===d.id});if(n)n.name=d.name;render();}