diff --git a/web/feeder/html/source-panel.html b/web/feeder/html/source-panel.html
deleted file mode 100644
index 90f9fe6..0000000
--- a/web/feeder/html/source-panel.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
diff --git a/web/feeder/js/app.js b/web/feeder/js/app.js
index 11daa1b..dc6b147 100644
--- a/web/feeder/js/app.js
+++ b/web/feeder/js/app.js
@@ -96,8 +96,8 @@ function bindEvents() {
dom.batchBindingForm.addEventListener("submit", (event) => withStatus(saveBatchBinding(event)));
dom.unitResetBtn.addEventListener("click", resetUnitForm);
- dom.refreshUnitBtn.addEventListener("click", () => withStatus(loadUnits().then(loadEvents)));
- dom.newUnitBtn.addEventListener("click", openCreateUnitModal);
+ if (dom.refreshUnitBtn) dom.refreshUnitBtn.addEventListener("click", () => withStatus(loadUnits().then(loadEvents)));
+ if (dom.newUnitBtn) dom.newUnitBtn.addEventListener("click", openCreateUnitModal);
dom.closeUnitModalBtn.addEventListener("click", closeUnitModal);
dom.sourceResetBtn.addEventListener("click", () => dom.sourceForm.reset());
@@ -183,7 +183,7 @@ function bindEvents() {
dom.tabAppConfig.addEventListener("click", () => switchView("app-config"));
dom.tabConfig.addEventListener("click", () => switchView("config"));
- dom.refreshUnitBtn2.addEventListener("click", () => withStatus(loadUnits()));
+ dom.refreshUnitBtn2.addEventListener("click", () => withStatus(loadUnits().then(loadEvents)));
dom.newUnitBtn2.addEventListener("click", openCreateUnitModal);
document.addEventListener("equipments-updated", () => {
diff --git a/web/feeder/js/units.js b/web/feeder/js/units.js
index 3de467d..30006bb 100644
--- a/web/feeder/js/units.js
+++ b/web/feeder/js/units.js
@@ -97,92 +97,104 @@ function runtimeBadge(runtime) {
return `${label}`;
}
-export function renderUnits() {
- dom.unitList.innerHTML = "";
-
- if (!state.units.length) {
- dom.unitList.innerHTML = '
';
- return;
- }
-
- state.units.forEach((unit) => {
- 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}
- ${runtimeBadge(runtime)}
- ${unit.enabled ? "EN" : "DIS"}
-
- ${unit.name}
- 设备 ${equipmentCount(unit.id)} 台 | Acc ${runtime ? Math.floor(runtime.display_acc_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
-
- `;
+function buildUnitCard(unit, interactive) {
+ const card = document.createElement("div");
+ const selected = interactive && state.selectedUnitId === unit.id;
+ card.className = `list-item unit-card ${selected ? "selected" : ""}`;
+ const runtime = state.runtimes.get(unit.id);
+ card.innerHTML = `
+
+ ${unit.code}
+ ${runtimeBadge(runtime)}
+ ${unit.enabled ? "EN" : "DIS"}
+
+ ${unit.name}
+ 设备 ${equipmentCount(unit.id)} 台 | Acc ${runtime ? Math.floor(runtime.display_acc_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
+
+ `;
+ if (interactive) {
card.addEventListener("click", () => {
selectUnit(unit.id).catch((error) => {
dom.statusText.textContent = error.message;
});
});
+ }
- const actions = card.querySelector(".unit-card-actions");
+ const actions = card.querySelector(".unit-card-actions");
- const editBtn = document.createElement("button");
- editBtn.className = "secondary";
- editBtn.textContent = "Edit";
- editBtn.addEventListener("click", (event) => {
- event.stopPropagation();
- openEditUnitModal(unit);
- });
-
- const deleteBtn = document.createElement("button");
- deleteBtn.className = "danger";
- deleteBtn.textContent = "Delete";
- deleteBtn.addEventListener("click", (event) => {
- event.stopPropagation();
- deleteUnit(unit.id).catch((error) => {
- dom.statusText.textContent = error.message;
- });
- });
-
- actions.append(editBtn, deleteBtn);
-
- const isAutoOn = runtime?.auto_enabled;
- const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required || runtime?.rem_local);
- const autoBtn = document.createElement("button");
- autoBtn.className = isAutoOn ? "danger" : "secondary";
- autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
- autoBtn.disabled = startBlocked;
- autoBtn.title = startBlocked
- ? (runtime?.fault_locked ? "设备故障中,无法启动自动控制"
- : runtime?.rem_local ? "设备处于本地模式(REM关),无法启动自动控制"
- : "需人工确认故障后才可启动自动控制")
- : (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);
+ const editBtn = document.createElement("button");
+ editBtn.className = "secondary";
+ editBtn.textContent = "Edit";
+ editBtn.addEventListener("click", (event) => {
+ event.stopPropagation();
+ openEditUnitModal(unit);
});
+
+ const deleteBtn = document.createElement("button");
+ deleteBtn.className = "danger";
+ deleteBtn.textContent = "Delete";
+ deleteBtn.addEventListener("click", (event) => {
+ event.stopPropagation();
+ deleteUnit(unit.id).catch((error) => {
+ dom.statusText.textContent = error.message;
+ });
+ });
+
+ actions.append(editBtn, deleteBtn);
+
+ const isAutoOn = runtime?.auto_enabled;
+ const startBlocked = !isAutoOn && (runtime?.fault_locked || runtime?.manual_ack_required || runtime?.rem_local);
+ const autoBtn = document.createElement("button");
+ autoBtn.className = isAutoOn ? "danger" : "secondary";
+ autoBtn.textContent = isAutoOn ? "Stop Auto" : "Start Auto";
+ autoBtn.disabled = startBlocked;
+ autoBtn.title = startBlocked
+ ? (runtime?.fault_locked ? "设备故障中,无法启动自动控制"
+ : runtime?.rem_local ? "设备处于本地模式(REM关),无法启动自动控制"
+ : "需人工确认故障后才可启动自动控制")
+ : (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);
+ }
+
+ return card;
+}
+
+function renderToContainer(container, interactive) {
+ if (!container) return;
+ container.innerHTML = "";
+
+ if (!state.units.length) {
+ container.innerHTML = '';
+ return;
+ }
+
+ state.units.forEach((unit) => {
+ container.appendChild(buildUnitCard(unit, interactive));
+ });
+}
+
+export function renderUnits() {
+ renderToContainer(dom.unitList, true);
+ renderToContainer(dom.unitConfigList, false);
}
export async function loadUnits() {