Skip to content

Commit 0d185f6

Browse files
committed
update appjs
1 parent 83977b5 commit 0d185f6

File tree

1 file changed

+86
-22
lines changed

1 file changed

+86
-22
lines changed

web/js/app.js

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ const els = {
99
};
1010

1111
function bytes(v){ if(v===0) return '0B'; if(!v) return '-'; const k=1000; const u=['B','KB','MB','GB','TB','PB']; const i=Math.floor(Math.log(v)/Math.log(k)); return (v/Math.pow(k,i)).toFixed(i?1:0)+u[i]; }
12+
// 通用进位:从 KB/MB 起始单位自动进位到 KB/MB/GB/TB,与 bytes() 风格一致 (1000 进位)
13+
function humanAuto(v,startIdx=0){ if(v==null||isNaN(v)) return '-'; const units=['KB','MB','GB','TB','PB']; let val=v; let i=startIdx; while(val>=1000 && i<units.length-1){ val/=1000; i++; } return (i>startIdx? val.toFixed(1): val.toFixed(0))+units[i]; }
14+
// 最小单位 MB:
15+
function humanMinMBFromKB(kb){ if(kb==null||isNaN(kb)) return '-'; // 输入单位: KB
16+
let mb = kb/1000; const units=['MB','GB','TB','PB']; let i=0; while(mb>=1000 && i<units.length-1){ mb/=1000;i++; }
17+
const out = mb>=100? mb.toFixed(0): mb.toFixed(1); return out+units[i]; }
18+
function humanMinMBFromMB(mbVal){ if(mbVal==null||isNaN(mbVal)) return '-'; // 输入单位: MB
19+
let v=mbVal; const units=['MB','GB','TB','PB']; let i=0; while(v>=1000 && i<units.length-1){ v/=1000;i++; }
20+
const out = v>=100? v.toFixed(0): v.toFixed(1); return out+units[i]; }
21+
function humanMinMBFromB(bytes){ if(bytes==null||isNaN(bytes)) return '-'; // 输入单位: B
22+
let mb = bytes/1000/1000; const units=['MB','GB','TB','PB']; let i=0; while(mb>=1000 && i<units.length-1){ mb/=1000;i++; }
23+
const out = mb>=100? mb.toFixed(0): mb.toFixed(1); return out+units[i]; }
24+
function humanRateMinMBFromB(bytes){ if(bytes==null||isNaN(bytes)) return '-'; if(bytes<=0) return '0.0MB'; return humanMinMBFromB(bytes); }
25+
function humanMinKBFromB(bytes){ if(bytes==null||isNaN(bytes)) return '-'; // 输入单位: B; 最小单位 KB
26+
let kb = bytes/1000; const units=['KB','MB','GB','TB','PB']; let i=0; while(kb>=1000 && i<units.length-1){ kb/=1000; i++; }
27+
const out = kb>=100? kb.toFixed(0): kb.toFixed(1); return out+units[i]; }
1228
function pct(v){ return (v||0).toFixed(0)+'%'; }
1329
function clsBy(v){ return v>=90?'danger':v>=80?'warn':'ok'; }
1430
function humanAgo(ts){ if(!ts) return '-'; const s=Math.floor((Date.now()/1000 - ts)); const m=Math.floor(s/60); return m>0? m+' 分钟前':'几秒前'; }
@@ -75,15 +91,15 @@ function renderServers(){
7591
const cpuCls = clsBy(s.cpu);
7692
const memPct = s.memory_total? (s.memory_used/s.memory_total*100):0; const memCls = clsBy(memPct);
7793
const hddPct = s.hdd_total? (s.hdd_used/s.hdd_total*100):0; const hddCls = clsBy(hddPct);
78-
const monthInBytes = (s.network_in - s.last_network_in) || 0;
94+
const monthInBytes = (s.network_in - s.last_network_in) || 0; // 原始: B
7995
const monthOutBytes = (s.network_out - s.last_network_out) || 0;
80-
const monthIn = bytes(monthInBytes);
81-
const monthOut = bytes(monthOutBytes);
96+
const monthIn = humanMinMBFromB(monthInBytes); // 最小单位 MB
97+
const monthOut = humanMinMBFromB(monthOutBytes);
8298
const HEAVY_THRESHOLD = 500 * 1000 * 1000 * 1000; // 500GB
8399
const heavy = monthInBytes >= HEAVY_THRESHOLD || monthOutBytes >= HEAVY_THRESHOLD;
84100
const trafficCls = heavy ? 'caps-traffic duo heavy' : 'caps-traffic duo normal';
85-
const netNow = bytes(s.network_rx) + ' | ' + bytes(s.network_tx);
86-
const netTotal = bytes(s.network_in)+' | '+bytes(s.network_out);
101+
const netNow = humanMinKBFromB(s.network_rx) + ' | ' + humanMinKBFromB(s.network_tx); // 最小单位 KB
102+
const netTotal = humanMinMBFromB(s.network_in)+' | '+humanMinMBFromB(s.network_out); // 最小单位 MB
87103
const p1 = (s.ping_10010||0); const p2 = (s.ping_189||0); const p3 = (s.ping_10086||0);
88104
function bucket(p){ const v = Math.max(0, Math.min(100, p)); const level = v>=20?'bad':(v>=10?'warn':'ok'); return `<div class=\"bucket\" data-lv=\"${level}\"><span style=\"--h:${v}%\"></span><label>${v.toFixed(0)}%</label></div>`; }
89105
const pingBuckets = `<div class=\"buckets\" title=\"CU/CT/CM\">${bucket(p1)}${bucket(p2)}${bucket(p3)}</div>`;
@@ -146,15 +162,15 @@ function renderServersCards(){
146162
const memPct = s.memory_total? (s.memory_used/s.memory_total*100):0;
147163
const hddPct = s.hdd_total? (s.hdd_used/s.hdd_total*100):0;
148164
// 月流量(移动端)并应用 500GB 阈值配色逻辑
149-
const monthInBytes = (s.network_in - s.last_network_in) || 0;
165+
const monthInBytes = (s.network_in - s.last_network_in) || 0; // B
150166
const monthOutBytes = (s.network_out - s.last_network_out) || 0;
151-
const monthIn = bytes(monthInBytes);
152-
const monthOut = bytes(monthOutBytes);
167+
const monthIn = humanMinMBFromB(monthInBytes);
168+
const monthOut = humanMinMBFromB(monthOutBytes);
153169
const HEAVY_THRESHOLD = 500 * 1000 * 1000 * 1000; // 500GB
154170
const heavy = monthInBytes >= HEAVY_THRESHOLD || monthOutBytes >= HEAVY_THRESHOLD;
155171
const trafficCls = heavy ? 'caps-traffic duo heavy sm' : 'caps-traffic duo normal sm';
156-
const netNow = bytes(s.network_rx)+' | '+bytes(s.network_tx);
157-
const netTotal = bytes(s.network_in)+' | '+bytes(s.network_out);
172+
const netNow = humanMinKBFromB(s.network_rx)+' | '+humanMinKBFromB(s.network_tx);
173+
const netTotal = humanMinMBFromB(s.network_in)+' | '+humanMinMBFromB(s.network_out);
158174
const p1 = (s.ping_10010||0); const p2=(s.ping_189||0); const p3=(s.ping_10086||0);
159175
function bucket(p){ const v=Math.max(0,Math.min(100,p)); const level = v>=20?'bad':(v>=10?'warn':'ok'); return `<div class=\"bucket\" data-lv=\"${level}\"><span style=\"--h:${v}%\"></span><label>${v.toFixed(0)}%</label></div>`; }
160176
const buckets = `<div class=\"buckets\">${bucket(p1)}${bucket(p2)}${bucket(p3)}</div>`;
@@ -328,21 +344,32 @@ function openDetail(i){
328344

329345
// 旧进度条函数 barHTML/ioBar 已弃用
330346
// 资源行(移除百分比显示,仅显示 已用 / 总量)
331-
const memLine = s.memory_total? bytes(s.memory_used)+' / '+bytes(s.memory_total):'-';
332-
const swapLine = s.swap_total? bytes(s.swap_used)+' / '+bytes(s.swap_total):'-';
333-
const diskLine = s.hdd_total? bytes(s.hdd_used)+' / '+bytes(s.hdd_total):'-';
334-
const ioReadLine = ioRead? bytes(ioRead)+'/s':'-';
335-
const ioWriteLine = ioWrite? bytes(ioWrite)+'/s':'-';
336-
// 阈值着色 ( >80% / >100MB/s )
337-
const memColor = memPct>80? ' style="color:var(--danger)"':'';
347+
// 资源行(单独拆分 span 便于后续动态刷新)
348+
// 单位来源:memory/swap: KB; hdd: MB; io: B (速率) -> 统一最小单位显示为 MB,并向上进位 (MB/GB/TB)
349+
const memUsed = s.memory_total!=null? humanMinMBFromKB(s.memory_used||0):'-';
350+
const memTotal = s.memory_total!=null? humanMinMBFromKB(s.memory_total):'-';
351+
const swapUsed = s.swap_total!=null? humanMinMBFromKB(s.swap_used||0):'-';
352+
const swapTotal = s.swap_total!=null? humanMinMBFromKB(s.swap_total):'-';
353+
const hddUsed = s.hdd_total!=null? humanMinMBFromMB(s.hdd_used||0):'-';
354+
const hddTotal = s.hdd_total!=null? humanMinMBFromMB(s.hdd_total):'-';
355+
const ioReadLine = (ioRead!=null)? humanRateMinMBFromB(ioRead):'-';
356+
const ioWriteLine = (ioWrite!=null)? humanRateMinMBFromB(ioWrite):'-';
357+
const memColor = memPct>80? ' style="color:var(--danger)"':''; // 已用/总量显示为单一块,所以对已用着色
338358
const swapColor = swapPct>80? ' style="color:var(--danger)"':'';
339359
const hddColor = hddPct>80? ' style="color:var(--danger)"':'';
360+
// IO 阈值:>100MB (原始单位 B) -> >100*1000*1000 B
340361
const readColor = ioRead>100*1000*1000? ' style="color:var(--danger)"':'';
341362
const writeColor = ioWrite>100*1000*1000? ' style="color:var(--danger)"':'';
342363
box.innerHTML = `
343364
<div class="kv"><span>TCP/UDP/进/线</span><span class="mono" id="detail-proc">${procLine}</span></div>
344-
<div class="kv"><span>内存 / 虚存</span><span class="mono"><span${memColor}>${memLine}</span> | <span${swapColor}>${swapLine}</span></span></div>
345-
<div class="kv"><span>硬盘 / 读写</span><span class="mono"><span${hddColor}>${diskLine}</span> | 读 <span${readColor}>${ioReadLine}</span> / 写 <span${writeColor}>${ioWriteLine}</span></span></div>
365+
<div class="kv"><span>内存 / 虚存</span><span class="mono">
366+
<span id="mem-line"${memColor}><span id="mem-used">${memUsed}</span> / <span id="mem-total">${memTotal}</span></span>
367+
| <span id="swap-line"${swapColor}><span id="swap-used">${swapUsed}</span> / <span id="swap-total">${swapTotal}</span></span>
368+
</span></div>
369+
<div class="kv"><span>硬盘 / 读写</span><span class="mono">
370+
<span id="disk-line"${hddColor}><span id="hdd-used">${hddUsed}</span> / <span id="hdd-total">${hddTotal}</span></span>
371+
| <span id="io-read"${readColor}>${ioReadLine}</span> / <span id="io-write"${writeColor}>${ioWriteLine}</span>
372+
</span></div>
346373
<div style="display:flex;flex-direction:column;gap:.35rem;">
347374
<canvas id="loadChart" height="120" style="width:100%;border:1px solid var(--border);border-radius:10px;background:linear-gradient(145deg,var(--bg),var(--bg-alt));"></canvas>
348375
<div class="mono" style="font-size:11px;display:flex;gap:.9rem;flex-wrap:wrap;align-items:center;opacity:.8;">
@@ -456,7 +483,14 @@ function drawLoadChart(key){
456483
if(l1.length<2){ ctx.fillStyle='var(--text-dim)'; ctx.font='12px system-ui'; ctx.fillText('暂无负载数据', W/2-42, H/2); return; }
457484
const all = [...l1,...l5,...l15];
458485
const padL=38,padR=8,padT=8,padB=16;
459-
const max=Math.max(...all); const min=Math.min(...all); const range=Math.max(0.5,max-min);
486+
// 修正:纵轴下限不小于 0,且当真实 range <0.5 时向上扩展 max 而不是向下产生负刻度
487+
const rawMax = all.length? Math.max(...all):0;
488+
const rawMin = all.length? Math.min(...all):0;
489+
const min = 0; // 我们只显示 >=0
490+
let max = Math.max(rawMax,0);
491+
let range = max - min;
492+
const MIN_RANGE = 0.5;
493+
if(range < MIN_RANGE){ max = MIN_RANGE; range = MIN_RANGE; }
460494
const n = Math.max(l1.length,l5.length,l15.length); const xStep=(W-padL-padR)/Math.max(1,n-1);
461495
const isLight = document.body.classList.contains('light');
462496
const axisColor = isLight? 'rgba(0,0,0,0.22)' : 'rgba(255,255,255,0.18)';
@@ -465,13 +499,19 @@ function drawLoadChart(key){
465499
// 轴 & 网格 (增强暗色对比)
466500
ctx.strokeStyle=axisColor; ctx.lineWidth=1.1; ctx.beginPath(); ctx.moveTo(padL,padT); ctx.lineTo(padL,H-padB); ctx.lineTo(W-padR,H-padB); ctx.stroke();
467501
ctx.fillStyle=textColor; ctx.font='10px system-ui';
468-
const yMarks=4; for(let i=0;i<=yMarks;i++){ const y=padT+(H-padT-padB)*i/yMarks; const val=(max - range*i/yMarks).toFixed(2); ctx.fillText(val,4,y+3); ctx.strokeStyle=gridColor; ctx.beginPath(); ctx.moveTo(padL,y); ctx.lineTo(W-padR,y); ctx.stroke(); }
502+
const yMarks=4; for(let i=0;i<=yMarks;i++){
503+
const y=padT+(H-padT-padB)*i/yMarks;
504+
const val=(max - range*i/yMarks); // top -> bottom
505+
const labelVal = (Math.abs(val) < 0.005 ? 0 : val).toFixed(2);
506+
ctx.fillText(labelVal,4,y+3);
507+
ctx.strokeStyle=gridColor; ctx.beginPath(); ctx.moveTo(padL,y); ctx.lineTo(W-padR,y); ctx.stroke();
508+
}
469509
const series=[{arr:l1,color:'#8b5cf6',fill:true},{arr:l5,color:'#10b981'},{arr:l15,color:'#f59e0b'}];
470510
// 面积先画 load1
471511
series.forEach(s=>{
472512
if(s.arr.length<2) return;
473513
ctx.beginPath(); ctx.lineWidth=1.5; ctx.strokeStyle=s.color;
474-
s.arr.forEach((v,i)=>{ const x=padL+xStep*i; const y=padT+(H-padT-padB)*(1-(v-min)/range); if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); });
514+
s.arr.forEach((v,i)=>{ const vClamped = Math.max(0, v); const x=padL+xStep*i; const y=padT+(H-padT-padB)*(1-(vClamped-min)/range); if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); });
475515
ctx.stroke();
476516
if(s.fill){
477517
const lastX = padL + xStep*(s.arr.length-1);
@@ -497,6 +537,30 @@ function updateDetailMetrics(key){
497537
const cuE1=document.getElementById('lat-cu'); if(cuE1) cuE1.textContent = num(s.time_10010)+'ms';
498538
const ctE1=document.getElementById('lat-ct'); if(ctE1) ctE1.textContent = num(s.time_189)+'ms';
499539
const cmE1=document.getElementById('lat-cm'); if(cmE1) cmE1.textContent = num(s.time_10086)+'ms';
540+
// 资源动态刷新
541+
const memLineEl = document.getElementById('mem-line');
542+
if(memLineEl){
543+
const pct = s.memory_total? (s.memory_used/s.memory_total*100):0;
544+
document.getElementById('mem-used').textContent = s.memory_total!=null? humanMinMBFromKB(s.memory_used||0):'-';
545+
document.getElementById('mem-total').textContent = s.memory_total!=null? humanMinMBFromKB(s.memory_total):'-';
546+
if(pct>80) memLineEl.style.color='var(--danger)'; else memLineEl.style.color='';
547+
}
548+
const swapLineEl = document.getElementById('swap-line');
549+
if(swapLineEl){
550+
const pct = s.swap_total? (s.swap_used/s.swap_total*100):0;
551+
document.getElementById('swap-used').textContent = s.swap_total!=null? humanMinMBFromKB(s.swap_used||0):'-';
552+
document.getElementById('swap-total').textContent = s.swap_total!=null? humanMinMBFromKB(s.swap_total):'-';
553+
if(pct>80) swapLineEl.style.color='var(--danger)'; else swapLineEl.style.color='';
554+
}
555+
const diskLineEl = document.getElementById('disk-line');
556+
if(diskLineEl){
557+
const pct = s.hdd_total? (s.hdd_used/s.hdd_total*100):0;
558+
document.getElementById('hdd-used').textContent = s.hdd_total!=null? humanMinMBFromMB(s.hdd_used||0):'-';
559+
document.getElementById('hdd-total').textContent = s.hdd_total!=null? humanMinMBFromMB(s.hdd_total):'-';
560+
if(pct>80) diskLineEl.style.color='var(--danger)'; else diskLineEl.style.color='';
561+
}
562+
const ioReadEl = document.getElementById('io-read'); if(ioReadEl){ const v = (typeof s.io_read==='number')? s.io_read:0; ioReadEl.textContent = humanRateMinMBFromB(v); ioReadEl.style.color = v>100*1000*1000? 'var(--danger)':''; }
563+
const ioWriteEl = document.getElementById('io-write'); if(ioWriteEl){ const v = (typeof s.io_write==='number')? s.io_write:0; ioWriteEl.textContent = humanRateMinMBFromB(v); ioWriteEl.style.color = v>100*1000*1000? 'var(--danger)':''; }
500564
}
501565
function startDetailAutoUpdate(){ stopDetailAutoUpdate(); S._detailTimer = setInterval(()=>{ if(S._openDetailKey) updateDetailMetrics(S._openDetailKey); }, 1000); }
502566
function stopDetailAutoUpdate(){ if(S._detailTimer){ clearInterval(S._detailTimer); S._detailTimer=null; } }

0 commit comments

Comments
 (0)