import { stationApi } from "./api.js"; import { el, escapeHtml, setBanner } from "./dom.js"; const STATION_TYPES = [ "load", "dry_in", "dry_step", "dry_out", "fire_in", "fire_step", "fire_out", "transfer", "unload", "return", ]; const SIGNAL_ROLES = ["presence", "vacancy", "arrived", "allow_in", "done", "fault"]; const stations = new Map(); const expanded = new Set(); let stationDetails = new Map(); // station_id -> { signals: [...] } let editing = null; // station_id being edited inline let creating = false; function renderForm(initial) { const data = initial || {}; return `
`; } function renderSignalForm() { return `
`; } function renderSignals(signals) { if (!signals?.length) { return `
未绑定信号
`; } return ` ${signals .map( (sig) => ` `, ) .join("")}
角色Point推导取反
${escapeHtml(sig.signal_role)} ${escapeHtml(sig.point_id || "")} ${escapeHtml(sig.derived_from_role || "")} ${sig.invert_value ? "是" : "否"}
`; } function renderRow(station) { const isExpanded = expanded.has(station.id); const isEditing = editing === station.id; const detail = stationDetails.get(station.id); return `
${escapeHtml(station.code)} ${escapeHtml(station.name)} ${station.line_code ? `${escapeHtml(station.line_code)}` : ""} ${escapeHtml(station.station_type)} ${station.enabled ? "" : `已禁用`}
${isEditing ? `
${renderForm(station)}
` : ""} ${ isExpanded ? `
${renderSignals(detail?.signals)} ${renderSignalForm()}
` : "" }
`; } function renderAll() { const root = el("stationList"); if (!root) return; const list = Array.from(stations.values()).sort((a, b) => a.code.localeCompare(b.code)); root.innerHTML = ` ${creating ? `
${renderForm({})}
` : ""} ${list.length === 0 ? `
尚无工位
` : list.map(renderRow).join("")} `; } function formToPayload(form) { const data = Object.fromEntries(new FormData(form)); const payload = { code: data.code?.trim(), name: data.name?.trim(), station_type: data.station_type, enabled: form.elements.enabled.checked, }; if (data.line_code) payload.line_code = data.line_code.trim(); if (data.segment_code) payload.segment_code = data.segment_code.trim(); if (data.description) payload.description = data.description; return payload; } function signalFormToPayload(form) { const data = Object.fromEntries(new FormData(form)); const payload = { signal_role: data.signal_role }; if (data.point_id) payload.point_id = data.point_id.trim(); if (data.derived_from_role) payload.derived_from_role = data.derived_from_role; payload.invert_value = form.elements.invert_value.checked; return payload; } async function refreshDetail(stationId) { try { const detail = await stationApi.detail(stationId); stationDetails.set(stationId, { signals: detail.signals || [] }); } catch (err) { setBanner(el("stationList"), err.message || String(err), "error"); } } async function handleClick(event) { const button = event.target.closest("button[data-action]"); if (!button) return; const action = button.dataset.action; const row = event.target.closest(".config-row"); const stationId = row?.dataset?.stationId; switch (action) { case "cancel-form": creating = false; editing = null; return renderAll(); case "toggle": if (!stationId) return; if (expanded.has(stationId)) { expanded.delete(stationId); } else { expanded.add(stationId); await refreshDetail(stationId); } return renderAll(); case "edit": editing = editing === stationId ? null : stationId; return renderAll(); case "delete": if (!stationId) return; if (!window.confirm("确认删除该工位?此操作不可恢复。")) return; try { await stationApi.remove(stationId); stations.delete(stationId); expanded.delete(stationId); if (editing === stationId) editing = null; renderAll(); setBanner(el("stationList"), "工位已删除", "info"); } catch (err) { setBanner(el("stationList"), err.message || String(err), "error"); } return; case "delete-signal": { if (!stationId) return; const role = button.dataset.role; try { await stationApi.deleteSignal(stationId, role); await refreshDetail(stationId); renderAll(); setBanner(el("stationList"), `已解除 ${role} 绑定`, "info"); } catch (err) { setBanner(el("stationList"), err.message || String(err), "error"); } return; } default: return; } } async function handleSubmit(event) { const form = event.target.closest("form[data-form]"); if (!form) return; event.preventDefault(); const row = form.closest(".config-row"); const stationId = row?.dataset?.stationId; if (form.dataset.form === "station") { const payload = formToPayload(form); try { if (stationId && editing === stationId) { await stationApi.update(stationId, payload); setBanner(el("stationList"), "工位已更新", "info"); } else { await stationApi.create(payload); setBanner(el("stationList"), "工位已创建", "info"); } creating = false; editing = null; await loadStations(); } catch (err) { setBanner(el("stationList"), err.message || String(err), "error"); } return; } if (form.dataset.form === "signal") { if (!stationId) return; const payload = signalFormToPayload(form); try { await stationApi.upsertSignal(stationId, payload); await refreshDetail(stationId); renderAll(); setBanner(el("stationList"), "信号绑定已保存", "info"); } catch (err) { setBanner(el("stationList"), err.message || String(err), "error"); } } } export async function loadStations() { try { const rows = await stationApi.list(); stations.clear(); rows.forEach((s) => stations.set(s.id, s)); renderAll(); } catch (err) { setBanner(el("stationList"), err.message || String(err), "error"); } } export function bindStationEvents() { const root = el("stationList"); if (root) { root.addEventListener("click", handleClick); root.addEventListener("submit", handleSubmit); } const addBtn = el("addStationBtn"); if (addBtn) { addBtn.addEventListener("click", () => { creating = !creating; editing = null; renderAll(); }); } const refreshBtn = el("refreshStationsBtn"); if (refreshBtn) refreshBtn.addEventListener("click", () => loadStations()); }