美化CPU显示

This commit is contained in:
CN-JS-HuiBai
2026-04-05 01:06:08 +08:00
parent b3580c15cc
commit e50f95c325

View File

@@ -161,7 +161,7 @@
networkChart.draw(); networkChart.draw();
}); });
} }
// Source filter listener // Source filter listener
if (dom.sourceFilter) { if (dom.sourceFilter) {
dom.sourceFilter.addEventListener('change', () => { dom.sourceFilter.addEventListener('change', () => {
@@ -346,22 +346,22 @@
// ---- Update Dashboard ---- // ---- Update Dashboard ----
function updateDashboard(data) { function updateDashboard(data) {
// Server count // Server count
dom.totalServers.textContent = `${data.activeServers} / ${data.totalServers}`; dom.totalServers.textContent = `${data.activeServers}/${data.totalServers}`;
// CPU // CPU
const cpuPct = data.cpu.percent; const cpuPct = data.cpu.percent;
dom.cpuPercent.textContent = formatPercent(cpuPct); dom.cpuPercent.textContent = formatPercent(cpuPct);
dom.cpuDetail.textContent = `${data.cpu.used.toFixed(1)} / ${data.cpu.total.toFixed(0)} 核心`; dom.cpuDetail.textContent = `${data.cpu.used.toFixed(1)}/${data.cpu.total.toFixed(0)} 核心`;
// Memory // Memory
const memPct = data.memory.percent; const memPct = data.memory.percent;
dom.memPercent.textContent = formatPercent(memPct); dom.memPercent.textContent = formatPercent(memPct);
dom.memDetail.textContent = `${formatBytes(data.memory.used)} / ${formatBytes(data.memory.total)}`; dom.memDetail.textContent = `${formatBytes(data.memory.used)}/${formatBytes(data.memory.total)}`;
// Disk // Disk
const diskPct = data.disk.percent; const diskPct = data.disk.percent;
dom.diskPercent.textContent = formatPercent(diskPct); dom.diskPercent.textContent = formatPercent(diskPct);
dom.diskDetail.textContent = `${formatBytes(data.disk.used)} / ${formatBytes(data.disk.total)}`; dom.diskDetail.textContent = `${formatBytes(data.disk.used)}/${formatBytes(data.disk.total)}`;
// Bandwidth // Bandwidth
dom.totalBandwidth.textContent = formatBandwidth(data.network.total || 0); dom.totalBandwidth.textContent = formatBandwidth(data.network.total || 0);
@@ -392,7 +392,7 @@
if (currentSourceFilter !== 'all') { if (currentSourceFilter !== 'all') {
filtered = allServersData.filter(s => s.source === currentSourceFilter); filtered = allServersData.filter(s => s.source === currentSourceFilter);
} }
// Sort servers: online first, then alphabetically by name (job) // Sort servers: online first, then alphabetically by name (job)
filtered.sort((a, b) => { filtered.sort((a, b) => {
if (a.up !== b.up) return a.up ? -1 : 1; if (a.up !== b.up) return a.up ? -1 : 1;
@@ -403,7 +403,7 @@
const totalFiltered = filtered.length; const totalFiltered = filtered.length;
const totalPages = Math.ceil(totalFiltered / pageSize) || 1; const totalPages = Math.ceil(totalFiltered / pageSize) || 1;
if (currentPage > totalPages) currentPage = totalPages; if (currentPage > totalPages) currentPage = totalPages;
const startIndex = (currentPage - 1) * pageSize; const startIndex = (currentPage - 1) * pageSize;
@@ -415,7 +415,7 @@
function renderPagination(totalPages) { function renderPagination(totalPages) {
if (!dom.paginationControls) return; if (!dom.paginationControls) return;
if (totalPages <= 1) { if (totalPages <= 1) {
dom.paginationControls.innerHTML = ''; dom.paginationControls.innerHTML = '';
return; return;
@@ -424,25 +424,25 @@
let html = ''; let html = '';
// Previous button // Previous button
html += `<button class="page-btn" ${currentPage === 1 ? 'disabled' : ''} onclick="changePage(${currentPage - 1})">上页</button>`; html += `<button class="page-btn" ${currentPage === 1 ? 'disabled' : ''} onclick="changePage(${currentPage - 1})">上页</button>`;
// Page numbers // Page numbers
for (let i = 1; i <= totalPages; i++) { for (let i = 1; i <= totalPages; i++) {
if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) { if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) {
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="changePage(${i})">${i}</button>`; html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="changePage(${i})">${i}</button>`;
} else if (i === currentPage - 3 || i === currentPage + 3) { } else if (i === currentPage - 3 || i === currentPage + 3) {
html += `<span style="color: var(--text-muted); padding: 0 4px;">...</span>`; html += `<span style="color: var(--text-muted); padding: 0 4px;">...</span>`;
} }
} }
// Next button // Next button
html += `<button class="page-btn" ${currentPage === totalPages ? 'disabled' : ''} onclick="changePage(${currentPage + 1})">下页</button>`; html += `<button class="page-btn" ${currentPage === totalPages ? 'disabled' : ''} onclick="changePage(${currentPage + 1})">下页</button>`;
dom.paginationControls.innerHTML = html; dom.paginationControls.innerHTML = html;
} }
window.changePage = function(page) { window.changePage = function (page) {
currentPage = page; currentPage = page;
renderFilteredServers(); renderFilteredServers();
}; };
// ---- Server Table ---- // ---- Server Table ----
@@ -548,24 +548,22 @@
// Define metrics to show // Define metrics to show
const cpuValueHtml = ` const cpuValueHtml = `
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: 2px;"> <div style="display: flex; align-items: baseline; gap: 8px;">
<span style="font-weight: 700;">${formatPercent(data.cpuBusy)}</span> <span style="font-weight: 700; font-size: 1.1rem;">${formatPercent(data.cpuBusy)}</span>
<div style="font-size: 0.65rem; color: var(--text-secondary); display: flex; gap: 6px; font-weight: normal;"> <span style="font-size: 0.7rem; color: var(--text-secondary); font-weight: normal;">(IO Wait: ${data.cpuIowait.toFixed(1)}%)</span>
<span>I/O Wait: ${data.cpuIowait.toFixed(1)}%</span>
</div>
</div> </div>
`; `;
const metrics = [ const metrics = [
{ key: 'cpuBusy', label: 'CPU 使用率 (Busy / IO Wait)', value: cpuValueHtml }, { key: 'cpuBusy', label: 'CPU 使用率', value: cpuValueHtml },
{ key: 'sysLoad', label: '系统负载 (Load)', value: data.sysLoad.toFixed(1) + '%' }, { key: 'sysLoad', label: '系统负载 (Load)', value: data.sysLoad.toFixed(1) + '%' },
{ key: 'memUsedPct', label: '内存使用率 (RAM)', value: formatPercent(data.memUsedPct) }, { key: 'memUsedPct', label: '内存使用率 (RAM)', value: formatPercent(data.memUsedPct) },
{ key: 'swapUsedPct', label: 'SWAP 使用率', value: formatPercent(data.swapUsedPct) }, { key: 'swapUsedPct', label: 'SWAP 使用率', value: formatPercent(data.swapUsedPct) },
{ key: 'rootFsUsedPct', label: '根分区使用率 (/)', value: formatPercent(data.rootFsUsedPct) }, { key: 'rootFsUsedPct', label: '根分区使用率 (/)', value: formatPercent(data.rootFsUsedPct) },
{ key: 'netRx', label: '网络接收速率 (RX)', value: formatBandwidth(data.netRx) }, { key: 'netRx', label: '网络接收速率 (RX)', value: formatBandwidth(data.netRx) },
{ key: 'netTx', label: '网络发送速率 (TX)', value: formatBandwidth(data.netTx) }, { key: 'netTx', label: '网络发送速率 (TX)', value: formatBandwidth(data.netTx) },
{ key: 'sockstatTcp', label: 'TCP 链接数 (Sockstat)', value: data.sockstatTcp.toFixed(0) }, { key: 'sockstatTcp', label: 'TCP 链接数 (Sockstat)', value: data.sockstatTcp.toFixed(0) },
{ key: 'sockstatTcpMem', label: 'TCP 内存占用', value: formatBytes(data.sockstatTcpMem) } { key: 'sockstatTcpMem', label: 'TCP 内存占用', value: formatBytes(data.sockstatTcpMem) }
]; ];
dom.detailMetricsList.innerHTML = metrics.map(m => ` dom.detailMetricsList.innerHTML = metrics.map(m => `
@@ -642,7 +640,7 @@
if (metricKey.includes('Pct') || metricKey === 'cpuBusy') unit = '%'; if (metricKey.includes('Pct') || metricKey === 'cpuBusy') unit = '%';
if (metricKey.startsWith('net')) unit = 'B/s'; if (metricKey.startsWith('net')) unit = 'B/s';
if (metricKey === 'sockstatTcpMem') unit = 'B'; if (metricKey === 'sockstatTcpMem') unit = 'B';
chart = new MetricChart(canvas, unit); chart = new MetricChart(canvas, unit);
currentServerDetail.charts[metricKey] = chart; currentServerDetail.charts[metricKey] = chart;
} }
@@ -660,6 +658,14 @@
const res = await fetch(url); const res = await fetch(url);
if (!res.ok) throw new Error('Query failed'); if (!res.ok) throw new Error('Query failed');
const data = await res.json(); const data = await res.json();
if (metricKey === 'cpuBusy' && data.series) {
// Simplify: ONLY show total busy CPU usage (everything except idle)
// Since it's a percentage, 100 - idle is the total busy percentage
data.values = data.series.idle.map(idleVal => Math.max(0, 100 - idleVal));
data.series = null; // This tells MetricChart to draw a single line instead of stacked area
}
chart.setData(data); chart.setData(data);
} catch (err) { } catch (err) {
console.error(`Error loading history for ${metricKey}:`, err); console.error(`Error loading history for ${metricKey}:`, err);