plc_control/web/feeder/js/sources.js

139 lines
4.2 KiB
JavaScript

import { apiFetch } from "./api.js";
import { dom } from "./dom.js";
import { loadPoints, updatePointFilterSummary } from "./points.js";
import { state } from "./state.js";
function renderPointSourceOptions() {
if (!dom.pointSourceSelect) {
return;
}
const options = ['<option value="">选择数据源</option>'];
state.sources.forEach((source) => {
const selected = source.id === state.selectedSourceId ? "selected" : "";
options.push(`<option value="${source.id}" ${selected}>${source.name}</option>`);
});
dom.pointSourceSelect.innerHTML = options.join("");
}
export function renderSources() {
dom.sourceList.innerHTML = "";
state.sources.forEach((source) => {
const card = document.createElement("div");
card.className = `list-item source-card ${state.selectedSourceId === source.id ? "selected" : ""}`;
card.innerHTML = `
<div class="row">
<strong>${source.name}</strong>
<span class="badge ${source.is_connected ? "" : "offline"}">${source.is_connected ? "ONLINE" : "OFFLINE"}</span>
</div>
<div class="muted">${source.endpoint}</div>
<div class="row source-card-actions"></div>
`;
card.addEventListener("click", () => {
selectSource(source.id).catch((error) => {
dom.statusText.textContent = error.message;
});
});
const actionRow = card.querySelector(".source-card-actions");
const editBtn = document.createElement("button");
editBtn.className = "secondary";
editBtn.textContent = "编辑";
editBtn.addEventListener("click", (event) => {
event.stopPropagation();
dom.sourceId.value = source.id;
dom.sourceName.value = source.name || "";
dom.sourceEndpoint.value = source.endpoint || "";
dom.sourceEnabled.checked = !!source.enabled;
dom.sourceModal.classList.remove("hidden");
});
const reconnectBtn = document.createElement("button");
reconnectBtn.className = "secondary";
reconnectBtn.textContent = "重连";
reconnectBtn.addEventListener("click", (event) => {
event.stopPropagation();
reconnectSource(source.id, source.name).catch((error) => {
dom.statusText.textContent = error.message;
});
});
const deleteBtn = document.createElement("button");
deleteBtn.className = "danger";
deleteBtn.textContent = "删除";
deleteBtn.addEventListener("click", (event) => {
event.stopPropagation();
deleteSource(source.id).catch((error) => {
dom.statusText.textContent = error.message;
});
});
actionRow.append(editBtn, reconnectBtn, deleteBtn);
card.appendChild(actionRow);
dom.sourceList.appendChild(card);
});
renderPointSourceOptions();
}
export async function loadSources() {
state.sources = await apiFetch("/api/source");
if (state.selectedSourceId && !state.sources.some((item) => item.id === state.selectedSourceId)) {
state.selectedSourceId = null;
}
renderSources();
updatePointFilterSummary();
}
export async function selectSource(sourceId) {
state.selectedSourceId = state.selectedSourceId === sourceId ? null : sourceId;
state.selectedNodeIds.clear();
state.pointsPage = 1;
renderSources();
updatePointFilterSummary();
await loadPoints();
}
export async function saveSource(event) {
event.preventDefault();
const payload = {
name: dom.sourceName.value.trim(),
endpoint: dom.sourceEndpoint.value.trim(),
enabled: dom.sourceEnabled.checked,
};
const id = dom.sourceId.value;
await apiFetch(id ? `/api/source/${id}` : "/api/source", {
method: id ? "PUT" : "POST",
body: JSON.stringify(payload),
});
dom.sourceModal.classList.add("hidden");
dom.sourceForm.reset();
await loadSources();
}
export async function reconnectSource(sourceId, name) {
dom.statusText.textContent = `正在重连 ${name || "数据源"}...`;
await apiFetch(`/api/source/${sourceId}/reconnect`, { method: "POST" });
await loadSources();
dom.statusText.textContent = "就绪";
}
export async function deleteSource(sourceId) {
if (!window.confirm("确认删除该数据源?")) {
return;
}
await apiFetch(`/api/source/${sourceId}`, { method: "DELETE" });
if (state.selectedSourceId === sourceId) {
state.selectedSourceId = null;
}
await loadSources();
await loadPoints();
}