fix(web): exclude local hub from device list and align 6-grid profile metrics

This commit is contained in:
kory
2026-09-28 09:42:46 +08:00
parent d84db86486
commit 80db7a482a
4 changed files with 36 additions and 36 deletions
+7 -8
View File
@@ -31,11 +31,10 @@ header strong{font-size:18px}header span{margin-left:12px;color:var(--text-muted
.device-meta{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* 资源画像 2列紧凑栅格 */
.profile-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;padding:10px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;font-size:12px}
.p-item{display:flex;justify-content:space-between;align-items:center;gap:6px}
.p-label{color:var(--text-muted)}
.p-val{font-family:ui-monospace,monospace;font-weight:600;color:var(--text-main)}
.p-docker{grid-column:1/-1;display:flex;justify-content:flex-end}
.badge-docker{font-size:11px;padding:2px 8px;border-radius:4px;font-weight:600}
.badge-docker.yes{background:rgba(34,197,94,.15);color:var(--dot-online);border:1px solid rgba(34,197,94,.3)}
.badge-docker.no{background:rgba(148,163,184,.15);color:var(--text-muted);border:1px solid var(--border)}
.profile-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px 12px;padding:10px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;font-size:12px}
.p-item{display:flex;justify-content:space-between;align-items:center;gap:6px;min-height:20px}
.p-label{color:var(--text-muted);font-weight:500}
.p-val{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-weight:600;color:var(--text-main);white-space:nowrap}
.badge-docker-text{padding:1px 6px;border-radius:4px;font-size:11px;font-weight:700}
.badge-docker-text.yes{background:rgba(34,197,94,.15);color:var(--dot-online);border:1px solid rgba(34,197,94,.3)}
.badge-docker-text.no{background:rgba(148,163,184,.15);color:var(--text-muted);border:1px solid var(--border)}
+11 -10
View File
@@ -32,17 +32,19 @@ async function removeDevice(name){
}
async function loadDevices(){
let ds=await api("/devices");
let all=await api("/devices");
let ds=all.filter(d=>!d.is_local);
$("#devices").innerHTML=ds.map(d=>{
const p = d.profile || {};
const role = p.role || (d.is_local ? "CONTROL" : "APPLICATION");
const role = p.role || "APPLICATION";
const isOnline = d.status === "online";
const cpuStr = p.cpu_cores ? `${p.cpu_cores}C · ${p.cpu_usage_percent ?? 0}%` : '-';
const memStr = p.mem_percent !== undefined ? `${p.mem_percent}%` + (p.mem_available ? ` (${p.mem_available})` : '') : '-';
const swapStr = p.swap_percent !== undefined ? `${p.swap_percent}%` : '-';
const swapStr = (p.swap_percent !== undefined && p.swap_percent !== null) ? `${p.swap_percent}%` : '-';
const diskStr = p.disk_percent !== undefined ? `${p.disk_percent}%` : '-';
const psiStr = p.io_psi !== undefined ? p.io_psi.toFixed(2) : '-';
const dockerBadge = p.docker ? '<span class="badge-docker yes">Docker YES</span>' : '<span class="badge-docker no">Docker NO</span>';
const dockerVal = p.docker ? 'YES' : 'NO';
const dockerClass = p.docker ? 'yes' : 'no';
return `
<div class="device-card ${isOnline ? "online" : "offline"}">
@@ -52,20 +54,19 @@ async function loadDevices(){
<strong class="device-name">${esc(d.name)}</strong>
<span class="role-badge role-${role.toLowerCase()}">${esc(role)}</span>
</div>
${d.is_local ? '' : `<button class="btn-revoke" onclick="removeDevice('${esc(d.name)}')">注销</button>`}
<button class="btn-revoke" onclick="removeDevice('${esc(d.name)}')">注销</button>
</div>
<div class="device-meta">
<span>${esc(d.status)}</span> ·
<span>${d.is_local ? "local" : "remote"}</span> ·
<span>${esc(d.arch||"")}/${esc(d.os||"")}</span>
</div>
<div class="profile-grid">
<div class="p-item"><span class="p-label">CPU</span><span class="p-val">${cpuStr}</span></div>
<div class="p-item"><span class="p-label">Mem</span><span class="p-val">${memStr}</span></div>
<div class="p-item"><span class="p-label">Swap</span><span class="p-val">${swapStr}</span></div>
<div class="p-item"><span class="p-label">Disk</span><span class="p-val">${diskStr}</span></div>
<div class="p-item"><span class="p-label">MEM</span><span class="p-val">${memStr}</span></div>
<div class="p-item"><span class="p-label">DISK</span><span class="p-val">${diskStr}</span></div>
<div class="p-item"><span class="p-label">SWAP</span><span class="p-val">${swapStr}</span></div>
<div class="p-item"><span class="p-label">IO PSI</span><span class="p-val">${psiStr}</span></div>
<div class="p-item p-docker">${dockerBadge}</div>
<div class="p-item"><span class="p-label">DOCKER</span><span class="p-val badge-docker-text ${dockerClass}">${dockerVal}</span></div>
</div>
</div>
`;
+7 -8
View File
@@ -31,11 +31,10 @@ header strong{font-size:18px}header span{margin-left:12px;color:var(--text-muted
.device-meta{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* 资源画像 2列紧凑栅格 */
.profile-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;padding:10px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;font-size:12px}
.p-item{display:flex;justify-content:space-between;align-items:center;gap:6px}
.p-label{color:var(--text-muted)}
.p-val{font-family:ui-monospace,monospace;font-weight:600;color:var(--text-main)}
.p-docker{grid-column:1/-1;display:flex;justify-content:flex-end}
.badge-docker{font-size:11px;padding:2px 8px;border-radius:4px;font-weight:600}
.badge-docker.yes{background:rgba(34,197,94,.15);color:var(--dot-online);border:1px solid rgba(34,197,94,.3)}
.badge-docker.no{background:rgba(148,163,184,.15);color:var(--text-muted);border:1px solid var(--border)}
.profile-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px 12px;padding:10px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;font-size:12px}
.p-item{display:flex;justify-content:space-between;align-items:center;gap:6px;min-height:20px}
.p-label{color:var(--text-muted);font-weight:500}
.p-val{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-weight:600;color:var(--text-main);white-space:nowrap}
.badge-docker-text{padding:1px 6px;border-radius:4px;font-size:11px;font-weight:700}
.badge-docker-text.yes{background:rgba(34,197,94,.15);color:var(--dot-online);border:1px solid rgba(34,197,94,.3)}
.badge-docker-text.no{background:rgba(148,163,184,.15);color:var(--text-muted);border:1px solid var(--border)}
+11 -10
View File
@@ -32,17 +32,19 @@ async function removeDevice(name){
}
async function loadDevices(){
let ds=await api("/devices");
let all=await api("/devices");
let ds=all.filter(d=>!d.is_local);
$("#devices").innerHTML=ds.map(d=>{
const p = d.profile || {};
const role = p.role || (d.is_local ? "CONTROL" : "APPLICATION");
const role = p.role || "APPLICATION";
const isOnline = d.status === "online";
const cpuStr = p.cpu_cores ? `${p.cpu_cores}C · ${p.cpu_usage_percent ?? 0}%` : '-';
const memStr = p.mem_percent !== undefined ? `${p.mem_percent}%` + (p.mem_available ? ` (${p.mem_available})` : '') : '-';
const swapStr = p.swap_percent !== undefined ? `${p.swap_percent}%` : '-';
const swapStr = (p.swap_percent !== undefined && p.swap_percent !== null) ? `${p.swap_percent}%` : '-';
const diskStr = p.disk_percent !== undefined ? `${p.disk_percent}%` : '-';
const psiStr = p.io_psi !== undefined ? p.io_psi.toFixed(2) : '-';
const dockerBadge = p.docker ? '<span class="badge-docker yes">Docker YES</span>' : '<span class="badge-docker no">Docker NO</span>';
const dockerVal = p.docker ? 'YES' : 'NO';
const dockerClass = p.docker ? 'yes' : 'no';
return `
<div class="device-card ${isOnline ? "online" : "offline"}">
@@ -52,20 +54,19 @@ async function loadDevices(){
<strong class="device-name">${esc(d.name)}</strong>
<span class="role-badge role-${role.toLowerCase()}">${esc(role)}</span>
</div>
${d.is_local ? '' : `<button class="btn-revoke" onclick="removeDevice('${esc(d.name)}')">注销</button>`}
<button class="btn-revoke" onclick="removeDevice('${esc(d.name)}')">注销</button>
</div>
<div class="device-meta">
<span>${esc(d.status)}</span> ·
<span>${d.is_local ? "local" : "remote"}</span> ·
<span>${esc(d.arch||"")}/${esc(d.os||"")}</span>
</div>
<div class="profile-grid">
<div class="p-item"><span class="p-label">CPU</span><span class="p-val">${cpuStr}</span></div>
<div class="p-item"><span class="p-label">Mem</span><span class="p-val">${memStr}</span></div>
<div class="p-item"><span class="p-label">Swap</span><span class="p-val">${swapStr}</span></div>
<div class="p-item"><span class="p-label">Disk</span><span class="p-val">${diskStr}</span></div>
<div class="p-item"><span class="p-label">MEM</span><span class="p-val">${memStr}</span></div>
<div class="p-item"><span class="p-label">DISK</span><span class="p-val">${diskStr}</span></div>
<div class="p-item"><span class="p-label">SWAP</span><span class="p-val">${swapStr}</span></div>
<div class="p-item"><span class="p-label">IO PSI</span><span class="p-val">${psiStr}</span></div>
<div class="p-item p-docker">${dockerBadge}</div>
<div class="p-item"><span class="p-label">DOCKER</span><span class="p-val badge-docker-text ${dockerClass}">${dockerVal}</span></div>
</div>
</div>
`;