()
| 429 | } |
| 430 | function findServer(key){ return S.servers.find(s => s._key === key); } |
| 431 | function refreshDetail(){ |
| 432 | const s = findServer(S.openDetailKey); |
| 433 | if(!s){ closeDetail(); return; } |
| 434 | const m = metrics(s); |
| 435 | const title = $('detailTitle'); |
| 436 | title.innerHTML = `${esc(s.name || '-')} 详情${s.os ? `<span class="os-chip${osClass(s.os)}">${esc(osLabel(s.os))}</span>` : ''}`; |
| 437 | const modalBox = document.querySelector('#detailModal .modal-box'); |
| 438 | if(modalBox){ |
| 439 | modalBox.classList.remove('high-load'); |
| 440 | modalBox.classList.toggle('alert-critical', m.rowLevel === 'critical'); |
| 441 | modalBox.classList.toggle('alert-warning', m.rowLevel === 'warning'); |
| 442 | } |
| 443 | const loadSeries = [ |
| 444 | { data: S.loadHist[s._key]?.l1 || [], color:'#8b5cf6', label:'load1' }, |
| 445 | { data: S.loadHist[s._key]?.l5 || [], color:'#10b981', label:'load5' }, |
| 446 | { data: S.loadHist[s._key]?.l15 || [], color:'#f59e0b', label:'load15' } |
| 447 | ]; |
| 448 | const latencySeries = [ |
| 449 | { data: S.hist[s._key]?.cu || [], color:'#3b82f6', label:'联通' }, |
| 450 | { data: S.hist[s._key]?.ct || [], color:'#10b981', label:'电信' }, |
| 451 | { data: S.hist[s._key]?.cm || [], color:'#f59e0b', label:'移动' } |
| 452 | ]; |
| 453 | $('detailContent').innerHTML = ` |
| 454 | <div class="detail-grid"> |
| 455 | <section class="detail-section"><h4>身份</h4> |
| 456 | <div class="kv"><span>节点 / 位置</span><span class="mono">${esc(s.name || '-')} / ${esc(s.location || '-')}</span></div> |
| 457 | <div class="kv"><span>虚拟化 / 主机</span><span class="mono">${esc(s.type || '-')} / ${esc(s.host || '-')}</span></div> |
| 458 | <div class="kv"><span>协议 / 在线</span><span class="detail-inline">${protoPill(s)}<span class="mono">${esc(s.uptime || '-')}</span></span></div> |
| 459 | </section> |
| 460 | <section class="detail-section resource-section"><h4>资源</h4> |
| 461 | ${detailResourceHTML(s, m)} |
| 462 | </section> |
| 463 | <section class="detail-section"><h4>网络</h4> |
| 464 | <div class="kv"><span>当前 ↓|↑</span><span class="mono">${humanMinKBFromB(s.network_rx)} | ${humanMinKBFromB(s.network_tx)}</span></div> |
| 465 | <div class="kv"><span>总流量 ↓|↑</span><span class="mono">${humanMinMBFromB(s.network_in)} | ${humanMinMBFromB(s.network_out)}</span></div> |
| 466 | <div class="kv"><span>本月 ↓|↑</span><span>${trafficCaps(s, true)}</span></div> |
| 467 | </section> |
| 468 | <section class="detail-section"><h4>连接</h4> |
| 469 | <div class="kv"><span>TCP / UDP</span><span class="mono">${num(s.tcp_count)} / ${num(s.udp_count)}</span></div> |
| 470 | <div class="kv"><span>进程 / 线程</span><span class="mono">${num(s.process_count)} / ${num(s.thread_count)}</span></div> |
| 471 | <div class="kv"><span>联通|电信|移动</span><span class="mono">${packetLossLine(s)}</span></div> |
| 472 | </section> |
| 473 | </div> |
| 474 | <section class="detail-section chart-section"><div class="chart-head"><h4>负载趋势</h4>${chartLegend(loadSeries)}</div><canvas id="loadChart" class="detail-chart" height="130"></canvas></section> |
| 475 | <section class="detail-section chart-section"><div class="chart-head"><h4>三网延迟</h4>${chartLegend(latencySeries)}</div><canvas id="latChart" class="detail-chart" height="150"></canvas></section>`; |
| 476 | drawLineChart('loadChart', loadSeries, '暂无负载数据'); |
| 477 | drawLineChart('latChart', latencySeries, '暂无延迟数据', 'ms'); |
| 478 | } |
| 479 | function closeDetail(){ |
| 480 | $('detailModal').style.display = 'none'; |
| 481 | S.openDetailKey = null; |
no test coverage detected