fix(feeder): render units in app-config panel, remove from platform-config
- Delete feeder source-panel override (units no longer in platform config) - Extract buildUnitCard() and render to both unitList and unitConfigList - Guard null DOM refs for removed unit buttons Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
63cf3d8c67
commit
bc8b5a24ab
|
|
@ -1,22 +0,0 @@
|
||||||
<section class="panel bottom-left">
|
|
||||||
<div class="stack-panel">
|
|
||||||
<div class="stack-section">
|
|
||||||
<div class="panel-head">
|
|
||||||
<h2>控制单元</h2>
|
|
||||||
<div class="toolbar">
|
|
||||||
<button type="button" class="secondary" id="refreshUnitBtn">刷新</button>
|
|
||||||
<button type="button" id="newUnitBtn">+ 新增</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list unit-list" id="unitList"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stack-section stack-section-bordered">
|
|
||||||
<div class="panel-head">
|
|
||||||
<h2>数据源</h2>
|
|
||||||
<button type="button" id="openSourceForm">+ 新增</button>
|
|
||||||
</div>
|
|
||||||
<div class="source-panels" id="sourceList"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
@ -96,8 +96,8 @@ function bindEvents() {
|
||||||
dom.batchBindingForm.addEventListener("submit", (event) => withStatus(saveBatchBinding(event)));
|
dom.batchBindingForm.addEventListener("submit", (event) => withStatus(saveBatchBinding(event)));
|
||||||
|
|
||||||
dom.unitResetBtn.addEventListener("click", resetUnitForm);
|
dom.unitResetBtn.addEventListener("click", resetUnitForm);
|
||||||
dom.refreshUnitBtn.addEventListener("click", () => withStatus(loadUnits().then(loadEvents)));
|
if (dom.refreshUnitBtn) dom.refreshUnitBtn.addEventListener("click", () => withStatus(loadUnits().then(loadEvents)));
|
||||||
dom.newUnitBtn.addEventListener("click", openCreateUnitModal);
|
if (dom.newUnitBtn) dom.newUnitBtn.addEventListener("click", openCreateUnitModal);
|
||||||
dom.closeUnitModalBtn.addEventListener("click", closeUnitModal);
|
dom.closeUnitModalBtn.addEventListener("click", closeUnitModal);
|
||||||
|
|
||||||
dom.sourceResetBtn.addEventListener("click", () => dom.sourceForm.reset());
|
dom.sourceResetBtn.addEventListener("click", () => dom.sourceForm.reset());
|
||||||
|
|
@ -183,7 +183,7 @@ function bindEvents() {
|
||||||
dom.tabAppConfig.addEventListener("click", () => switchView("app-config"));
|
dom.tabAppConfig.addEventListener("click", () => switchView("app-config"));
|
||||||
dom.tabConfig.addEventListener("click", () => switchView("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);
|
dom.newUnitBtn2.addEventListener("click", openCreateUnitModal);
|
||||||
|
|
||||||
document.addEventListener("equipments-updated", () => {
|
document.addEventListener("equipments-updated", () => {
|
||||||
|
|
|
||||||
|
|
@ -97,92 +97,104 @@ function runtimeBadge(runtime) {
|
||||||
return `<span class="badge ${cls}">${label}</span>`;
|
return `<span class="badge ${cls}">${label}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderUnits() {
|
function buildUnitCard(unit, interactive) {
|
||||||
dom.unitList.innerHTML = "";
|
const card = document.createElement("div");
|
||||||
|
const selected = interactive && state.selectedUnitId === unit.id;
|
||||||
if (!state.units.length) {
|
card.className = `list-item unit-card ${selected ? "selected" : ""}`;
|
||||||
dom.unitList.innerHTML = '<div class="list-item"><div class="muted">暂无控制单元</div></div>';
|
const runtime = state.runtimes.get(unit.id);
|
||||||
return;
|
card.innerHTML = `
|
||||||
}
|
<div class="row">
|
||||||
|
<strong>${unit.code}</strong>
|
||||||
state.units.forEach((unit) => {
|
${runtimeBadge(runtime)}
|
||||||
const card = document.createElement("div");
|
<span class="badge ${unit.enabled ? "" : "offline"}">${unit.enabled ? "EN" : "DIS"}</span>
|
||||||
const selected = state.selectedUnitId === unit.id;
|
</div>
|
||||||
card.className = `list-item unit-card ${selected ? "selected" : ""}`;
|
<div>${unit.name}</div>
|
||||||
const runtime = state.runtimes.get(unit.id);
|
<div class="muted">设备 ${equipmentCount(unit.id)} 台 | Acc ${runtime ? Math.floor(runtime.display_acc_sec / 1000) : 0}s</div>
|
||||||
card.innerHTML = `
|
<div class="muted">Run ${unit.run_time_sec}s / Stop ${unit.stop_time_sec}s / Acc ${unit.acc_time_sec}s / BL ${unit.bl_time_sec}s</div>
|
||||||
<div class="row">
|
<div class="row unit-card-actions"></div>
|
||||||
<strong>${unit.code}</strong>
|
`;
|
||||||
${runtimeBadge(runtime)}
|
|
||||||
<span class="badge ${unit.enabled ? "" : "offline"}">${unit.enabled ? "EN" : "DIS"}</span>
|
|
||||||
</div>
|
|
||||||
<div>${unit.name}</div>
|
|
||||||
<div class="muted">设备 ${equipmentCount(unit.id)} 台 | Acc ${runtime ? Math.floor(runtime.display_acc_sec / 1000) : 0}s</div>
|
|
||||||
<div class="muted">Run ${unit.run_time_sec}s / Stop ${unit.stop_time_sec}s / Acc ${unit.acc_time_sec}s / BL ${unit.bl_time_sec}s</div>
|
|
||||||
<div class="row unit-card-actions"></div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
if (interactive) {
|
||||||
card.addEventListener("click", () => {
|
card.addEventListener("click", () => {
|
||||||
selectUnit(unit.id).catch((error) => {
|
selectUnit(unit.id).catch((error) => {
|
||||||
dom.statusText.textContent = error.message;
|
dom.statusText.textContent = error.message;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const actions = card.querySelector(".unit-card-actions");
|
const actions = card.querySelector(".unit-card-actions");
|
||||||
|
|
||||||
const editBtn = document.createElement("button");
|
const editBtn = document.createElement("button");
|
||||||
editBtn.className = "secondary";
|
editBtn.className = "secondary";
|
||||||
editBtn.textContent = "Edit";
|
editBtn.textContent = "Edit";
|
||||||
editBtn.addEventListener("click", (event) => {
|
editBtn.addEventListener("click", (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
openEditUnitModal(unit);
|
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 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 = '<div class="list-item"><div class="muted">暂无控制单元</div></div>';
|
||||||
|
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() {
|
export async function loadUnits() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue