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.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", () => {
|
||||
|
|
|
|||
|
|
@ -97,92 +97,104 @@ function runtimeBadge(runtime) {
|
|||
return `<span class="badge ${cls}">${label}</span>`;
|
||||
}
|
||||
|
||||
export function renderUnits() {
|
||||
dom.unitList.innerHTML = "";
|
||||
|
||||
if (!state.units.length) {
|
||||
dom.unitList.innerHTML = '<div class="list-item"><div class="muted">暂无控制单元</div></div>';
|
||||
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 = `
|
||||
<div class="row">
|
||||
<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>
|
||||
`;
|
||||
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 = `
|
||||
<div class="row">
|
||||
<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", () => {
|
||||
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 = '<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() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue