From 89023e867bd1f8c68af51a13488ca87798471b3a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 24 Mar 2026 14:58:07 +0800 Subject: [PATCH] feat(frontend): show runtime state and auto/ack buttons on unit cards --- web/js/units.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/web/js/units.js b/web/js/units.js index 105da74..ca71781 100644 --- a/web/js/units.js +++ b/web/js/units.js @@ -74,6 +74,27 @@ async function selectUnit(unitId) { await loadEvents(); } +function runtimeBadge(runtime) { + if (!runtime) return 'OFFLINE'; + const stateLabels = { + stopped: 'STOPPED', + running: 'RUNNING', + distributor_running: 'DIST RUN', + fault_locked: 'FAULT', + comm_locked: 'COMM ERR', + }; + const stateCls = { + stopped: '', + running: 'online', + distributor_running: 'online', + fault_locked: 'danger', + comm_locked: 'offline', + }; + const label = stateLabels[runtime.state] ?? runtime.state; + const cls = stateCls[runtime.state] ?? ''; + return `${label}`; +} + export function renderUnits() { dom.unitList.innerHTML = ""; @@ -86,13 +107,15 @@ export function renderUnits() { const card = document.createElement("div"); const selected = state.selectedUnitId === unit.id; card.className = `list-item unit-card ${selected ? "selected" : ""}`; + const runtime = state.runtimes.get(unit.id); card.innerHTML = `
${unit.code} - ${unit.enabled ? "ENABLED" : "DISABLED"} + ${runtimeBadge(runtime)} + ${unit.enabled ? "EN" : "DIS"}
${unit.name}
-
设备 ${equipmentCount(unit.id)} 台
+
设备 ${equipmentCount(unit.id)} 台 | Acc ${runtime ? Math.floor(runtime.accumulated_run_sec / 1000) : 0}s
Run ${unit.run_time_sec}s / Stop ${unit.stop_time_sec}s / Acc ${unit.acc_time_sec}s / BL ${unit.bl_time_sec}s
`; @@ -124,6 +147,32 @@ export function renderUnits() { }); actions.append(editBtn, deleteBtn); + + const isAutoOn = runtime?.auto_enabled; + const autoBtn = document.createElement("button"); + autoBtn.className = isAutoOn ? "danger" : "secondary"; + autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto"; + autoBtn.title = isAutoOn ? "停止自动控制" : "启动自动控制"; + autoBtn.addEventListener("click", (e) => { + e.stopPropagation(); + const url = `/api/control/unit/${unit.id}/${isAutoOn ? "stop-auto" : "start-auto"}`; + apiFetch(url, { method: "POST" }).then(() => loadUnits()).catch(() => {}); + }); + actions.append(autoBtn); + + if (runtime?.manual_ack_required) { + const ackBtn = document.createElement("button"); + ackBtn.className = "danger"; + ackBtn.textContent = "Ack Fault"; + ackBtn.title = "人工确认解除故障锁定"; + ackBtn.addEventListener("click", (e) => { + e.stopPropagation(); + apiFetch(`/api/control/unit/${unit.id}/ack-fault`, { method: "POST" }) + .then(() => loadUnits()).catch(() => {}); + }); + actions.append(ackBtn); + } + dom.unitList.appendChild(card); }); }