plc_control/web/feeder/js/equipment.js

246 lines
7.4 KiB
JavaScript

import { apiFetch } from "./api.js";
import { dom } from "./dom.js";
import { renderEquipmentKindOptions, renderRoleOptions } from "./roles.js";
import { clearSelectedPoints, loadPoints, updatePointFilterSummary } from "./points.js";
import { state } from "./state.js";
function equipmentOf(item) {
return item && item.equipment ? item.equipment : item;
}
function currentUnitLabel(unitId) {
if (!unitId) {
return "未绑定单元";
}
const unit = state.unitMap.get(unitId);
return unit ? `${unit.code} / ${unit.name}` : "未知单元";
}
function filteredEquipments() {
if (!state.selectedUnitId) {
return state.equipments;
}
return state.equipments.filter((item) => {
const equipment = equipmentOf(item);
return equipment.unit_id === state.selectedUnitId;
});
}
function renderEquipmentUnitOptions(selected = "", target = dom.equipmentUnitId) {
if (!target) {
return;
}
const options = ['<option value="">未绑定单元</option>'];
state.units.forEach((unit) => {
const isSelected = unit.id === selected ? "selected" : "";
options.push(`<option value="${unit.id}" ${isSelected}>${unit.code} / ${unit.name}</option>`);
});
target.innerHTML = options.join("");
}
export function renderBindingEquipmentOptions(selected = "", target = dom.bindingEquipmentId) {
const options = ['<option value="">未绑定</option>'];
filteredEquipments().forEach((item) => {
const equipment = equipmentOf(item);
const isSelected = equipment.id === selected ? "selected" : "";
options.push(
`<option value="${equipment.id}" ${isSelected}>${equipment.code} / ${equipment.name}</option>`,
);
});
target.innerHTML = options.join("");
}
export function renderBatchBindingDefaults() {
renderBindingEquipmentOptions("", dom.batchBindingEquipmentId);
dom.batchBindingSignalRole.innerHTML = renderRoleOptions("");
}
export function resetEquipmentForm() {
dom.equipmentForm.reset();
dom.equipmentId.value = "";
renderEquipmentUnitOptions("");
dom.equipmentKind.innerHTML = renderEquipmentKindOptions("");
}
function openEquipmentModal() {
dom.equipmentModal.classList.remove("hidden");
}
export function closeEquipmentModal() {
dom.equipmentModal.classList.add("hidden");
}
export function openCreateEquipmentModal() {
resetEquipmentForm();
if (state.selectedUnitId && dom.equipmentUnitId) {
dom.equipmentUnitId.value = state.selectedUnitId;
}
openEquipmentModal();
}
function openEditEquipmentModal(equipment) {
dom.equipmentId.value = equipment.id || "";
dom.equipmentUnitId.value = equipment.unit_id || "";
dom.equipmentCode.value = equipment.code || "";
dom.equipmentName.value = equipment.name || "";
dom.equipmentKind.innerHTML = renderEquipmentKindOptions(equipment.kind || "");
dom.equipmentDescription.value = equipment.description || "";
openEquipmentModal();
}
async function selectEquipment(equipmentId) {
state.selectedEquipmentId = state.selectedEquipmentId === equipmentId ? null : equipmentId;
state.pointsPage = 1;
clearSelectedPoints();
renderEquipments();
updatePointFilterSummary();
await loadPoints();
}
export function clearEquipmentFilter() {
state.selectedEquipmentId = null;
state.pointsPage = 1;
renderEquipments();
updatePointFilterSummary();
return loadPoints();
}
export function renderEquipments() {
dom.equipmentList.innerHTML = "";
const items = filteredEquipments();
if (!items.length) {
dom.equipmentList.innerHTML = '<div class="list-item"><div class="muted">暂无设备</div></div>';
return;
}
items.forEach((item) => {
const equipment = equipmentOf(item);
const box = document.createElement("div");
box.className = `list-item equipment-card ${state.selectedEquipmentId === equipment.id ? "selected" : ""}`;
box.innerHTML = `
<div class="row">
<strong>${equipment.code}</strong>
<span class="badge">${item.point_count ?? 0} pts</span>
</div>
<div>${equipment.name}</div>
<div class="muted">${equipment.kind || "未分类"}</div>
<div class="muted">单元: ${currentUnitLabel(equipment.unit_id)}</div>
<div class="row equipment-card-actions"></div>
`;
box.addEventListener("click", () => {
selectEquipment(equipment.id).catch((error) => {
dom.statusText.textContent = error.message;
});
});
const actionRow = box.querySelector(".equipment-card-actions");
const editBtn = document.createElement("button");
editBtn.className = "secondary";
editBtn.textContent = "编辑";
editBtn.addEventListener("click", (event) => {
event.stopPropagation();
openEditEquipmentModal(equipment);
});
const deleteBtn = document.createElement("button");
deleteBtn.className = "danger";
deleteBtn.textContent = "删除";
deleteBtn.addEventListener("click", (event) => {
event.stopPropagation();
deleteEquipment(equipment.id).catch((error) => {
dom.statusText.textContent = error.message;
});
});
actionRow.append(editBtn, deleteBtn);
dom.equipmentList.appendChild(box);
});
}
export async function loadEquipments() {
const keyword = dom.equipmentKeyword.value.trim();
const query = keyword
? `?page=1&page_size=-1&keyword=${encodeURIComponent(keyword)}`
: "?page=1&page_size=-1";
const data = await apiFetch(`/api/equipment${query}`);
state.equipments = data.data || [];
state.equipmentMap = new Map(
state.equipments.map((item) => {
const equipment = equipmentOf(item);
return [equipment.id, equipment];
}),
);
renderEquipmentUnitOptions(dom.equipmentUnitId?.value || "");
dom.equipmentKind.innerHTML = renderEquipmentKindOptions(dom.equipmentKind?.value || "");
renderBindingEquipmentOptions();
renderBatchBindingDefaults();
if (state.selectedEquipmentId && !state.equipmentMap.has(state.selectedEquipmentId)) {
state.selectedEquipmentId = null;
}
renderEquipments();
updatePointFilterSummary();
document.dispatchEvent(new Event("equipments-updated"));
}
export async function saveEquipment(event) {
event.preventDefault();
const unitId = dom.equipmentUnitId.value || null;
const payload = {
unit_id: unitId,
code: dom.equipmentCode.value.trim(),
name: dom.equipmentName.value.trim(),
kind: dom.equipmentKind.value.trim() || null,
description: dom.equipmentDescription.value.trim() || null,
};
const id = dom.equipmentId.value;
const result = await apiFetch(id ? `/api/equipment/${id}` : "/api/equipment", {
method: id ? "PUT" : "POST",
body: JSON.stringify(payload),
});
closeEquipmentModal();
await loadEquipments();
if (!id && result?.id) {
state.selectedEquipmentId = result.id;
}
renderEquipments();
updatePointFilterSummary();
await loadPoints();
}
export async function deleteEquipment(equipmentId) {
if (!window.confirm("确认删除该设备?")) {
return;
}
await apiFetch(`/api/equipment/${equipmentId}`, { method: "DELETE" });
if (state.selectedEquipmentId === equipmentId) {
state.selectedEquipmentId = null;
}
resetEquipmentForm();
closeEquipmentModal();
clearSelectedPoints();
await loadEquipments();
await loadPoints();
}
export async function clearPointBinding(pointId = dom.bindingPointId.value) {
await apiFetch(`/api/point/${pointId}`, {
method: "PUT",
body: JSON.stringify({ equipment_id: null, signal_role: null }),
});
dom.pointBindingModal.classList.add("hidden");
await loadEquipments();
await loadPoints();
}