import { apiFetch } from "./api.js"; import { openChart } from "./chart.js"; import { dom } from "./dom.js"; import { loadEquipments, renderEquipmentOptions } from "./equipment.js"; import { state } from "./state.js"; export function formatValue(monitor) { if (!monitor) { return "--"; } if (monitor.value_text) { return monitor.value_text; } if (monitor.value === null || monitor.value === undefined) { return "--"; } return typeof monitor.value === "string" ? monitor.value : JSON.stringify(monitor.value); } export function renderSelectedNodes() { dom.selectedCount.textContent = `已选中 ${state.selectedNodeIds.size} 个节点`; } function renderNode(node) { const details = document.createElement("details"); const summary = document.createElement("summary"); if (node.children?.length) { summary.classList.add("has-children"); } const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = state.selectedNodeIds.has(node.id); checkbox.addEventListener("change", () => { if (checkbox.checked) { state.selectedNodeIds.add(node.id); } else { state.selectedNodeIds.delete(node.id); } renderSelectedNodes(); }); const label = document.createElement("span"); label.className = "node-label"; label.textContent = `${node.display_name || node.browse_name} (${node.node_class})`; summary.append(checkbox, label); details.appendChild(summary); (node.children || []).forEach((child) => { details.appendChild(renderNode(child)); }); return details; } export async function loadTree() { if (!state.selectedSourceId) { dom.nodeTree.innerHTML = '
请选择数据源
'; return; } const data = await apiFetch(`/api/source/${state.selectedSourceId}/node-tree`); dom.nodeTree.innerHTML = ""; (data || []).forEach((node) => dom.nodeTree.appendChild(renderNode(node))); } export async function createPoints() { if (!state.selectedNodeIds.size) { return; } await apiFetch("/api/point/batch", { method: "POST", body: JSON.stringify({ node_ids: Array.from(state.selectedNodeIds) }), }); state.selectedNodeIds.clear(); renderSelectedNodes(); dom.pointModal.classList.add("hidden"); await loadPoints(); } export async function loadPoints() { const sourceQuery = state.selectedSourceId ? `&source_id=${state.selectedSourceId}` : ""; const data = await apiFetch( `/api/point?page=${state.pointsPage}&page_size=${state.pointsPageSize}${sourceQuery}`, ); const items = data.data || []; state.pointsTotal = typeof data.total === "number" ? data.total : items.length; state.pointEls.clear(); dom.pointList.innerHTML = ""; if (!items.length) { dom.pointList.innerHTML = '暂无点位'; dom.pointsPageInfo.textContent = `${state.pointsPage} / 1`; return; } items.forEach((item) => { const point = item.point || item; const monitor = item.point_monitor || null; const equipment = point.equipment_id ? state.equipmentMap.get(point.equipment_id) : null; const tr = document.createElement("tr"); tr.addEventListener("click", () => { openChart(point.id, point.name).catch((error) => { dom.statusText.textContent = error.message; }); }); tr.innerHTML = `
${point.name}
${point.node_id}
${formatValue(monitor)} ${(monitor?.quality || "unknown").toUpperCase()}
${equipment ? equipment.name : '未绑定'}
${point.signal_role || "--"}
${monitor?.timestamp || "--"} `; const actionCell = tr.lastElementChild; const bindBtn = document.createElement("button"); bindBtn.className = "secondary"; bindBtn.textContent = "绑定"; bindBtn.addEventListener("click", (event) => { event.stopPropagation(); openPointBinding(point); }); const deleteBtn = document.createElement("button"); deleteBtn.className = "danger"; deleteBtn.textContent = "删除"; deleteBtn.addEventListener("click", (event) => { event.stopPropagation(); deletePoint(point.id).catch((error) => { dom.statusText.textContent = error.message; }); }); actionCell.append(bindBtn, deleteBtn); dom.pointList.appendChild(tr); state.pointEls.set(point.id, { row: tr, value: tr.querySelector(".point-value"), quality: tr.querySelector(".badge"), time: tr.querySelector("td:nth-child(5) .muted"), }); }); const totalPages = Math.max(1, Math.ceil(state.pointsTotal / state.pointsPageSize)); dom.pointsPageInfo.textContent = `${state.pointsPage} / ${totalPages}`; } export function openPointBinding(point) { dom.bindingPointId.value = point.id; dom.bindingPointName.value = point.name || ""; dom.bindingSignalRole.value = point.signal_role || ""; renderEquipmentOptions(point.equipment_id || ""); dom.pointBindingModal.classList.remove("hidden"); } export async function savePointBinding(event) { event.preventDefault(); await apiFetch(`/api/point/${dom.bindingPointId.value}`, { method: "PUT", body: JSON.stringify({ equipment_id: dom.bindingEquipmentId.value || null, signal_role: dom.bindingSignalRole.value.trim() || null, }), }); dom.pointBindingModal.classList.add("hidden"); await loadEquipments(); await loadPoints(); } export async function deletePoint(pointId) { if (!window.confirm("确认删除该点位?")) { return; } await apiFetch(`/api/point/${pointId}`, { method: "DELETE" }); await loadPoints(); }