139 lines
4.2 KiB
JavaScript
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();
|
|
}
|