()
| 351 | } |
| 352 | |
| 353 | function renderServersCards(){ |
| 354 | const wrap = $('serversCards'); |
| 355 | if(window.innerWidth > 700){ wrap.innerHTML = ''; return; } |
| 356 | wrap.innerHTML = visibleServers().map(s => { |
| 357 | const m = metrics(s); |
| 358 | const alertClass = m.rowLevel ? ` alert-${m.rowLevel}` : ''; |
| 359 | return `<div class="card${m.online ? '' : ' offline'}${alertClass}${osClass(s.os)}" data-key="${esc(s._key)}" data-online="${m.online ? 1 : 0}"> |
| 360 | <div class="card-header"><div class="card-title">${esc(s.name || '-')} <span class="tag">${esc(s.location || '-')}</span></div>${protoPill(s)}</div> |
| 361 | <div class="kvlist"> |
| 362 | <div><span class="key">负载</span><span>${s.load_1 === -1 ? '–' : num(s.load_1).toFixed(2)}</span></div> |
| 363 | <div><span class="key">在线</span><span>${esc(s.uptime || '-')}</span></div> |
| 364 | <div><span class="key">月流量</span><span>${trafficCaps(s, true)}</span></div> |
| 365 | <div><span class="key">网络</span><span>${humanMinKBFromB(s.network_rx)} | ${humanMinKBFromB(s.network_tx)}</span></div> |
| 366 | <div><span class="key">CPU</span><span>${num(s.cpu).toFixed(0)}%</span></div> |
| 367 | <div><span class="key">内存</span><span>${m.memPct.toFixed(0)}%</span></div> |
| 368 | </div> |
| 369 | ${buckets(s)} |
| 370 | </div>`; |
| 371 | }).join('') || '<div class="empty-state">无数据</div>'; |
| 372 | wrap.querySelectorAll('.card').forEach(card => card.addEventListener('click', () => { |
| 373 | if(card.dataset.online !== '1') return; |
| 374 | openDetail(card.dataset.key); |
| 375 | })); |
| 376 | } |
| 377 | |
| 378 | function parseCustom(str){ |
| 379 | if(typeof str !== 'string' || !str.trim()) return []; |
no test coverage detected