const endpoints = { orders: "/api/orders", risks: "/api/risks", flow: "/api/flow", nodeTimes: "/api/node-times", cutoffs: "/api/order-cutoffs", exceptionMap: "/api/exception-map", }; const labels = { orderNo: "订单号", customerName: "客户", status: "状态", currentNodeName: "当前节点", ownerName: "责任人", riskName: "风险", exceptionName: "异常", affectedNodeName: "影响节点", sequence: "顺序", nodeName: "节点", nextNodeName: "下一节点", deadlineName: "关键时限", plannedTime: "计划时间", cutoffTime: "截止时间", actualTime: "实际完成", isCompleted: "完成", isDelayed: "超时", customsCutoffTime: "截关", terminalCutoffTime: "截港", vgmCutoffTime: "VGM截止", estimatedSailingTime: "预计开船", }; document.getElementById("refreshBtn").addEventListener("click", loadAll); loadAll(); async function loadAll() { await Promise.all([ loadTable("orders", endpoints.orders, ["orderNo", "customerName", "status", "currentNodeName", "ownerName"]), loadTable("risks", endpoints.risks, ["orderNo", "riskName", "exceptionName", "affectedNodeName"]), loadFlow(), loadTable("nodeTimes", endpoints.nodeTimes, ["sequence", "nodeName", "deadlineName", "plannedTime", "cutoffTime", "actualTime", "isCompleted", "isDelayed"]), loadTable("cutoffs", endpoints.cutoffs, ["orderNo", "customsCutoffTime", "terminalCutoffTime", "vgmCutoffTime", "estimatedSailingTime"]), loadTable("exceptionMap", endpoints.exceptionMap, ["exceptionName", "affectedNodeName", "riskName"]), ]); } async function fetchRows(url) { const response = await fetch(url); if (!response.ok) { const detail = await response.text(); throw new Error(detail || `HTTP ${response.status}`); } const data = await response.json(); return data.rows || []; } async function loadTable(id, url, columns) { const target = document.getElementById(id); const counter = document.getElementById(`${id}Count`); try { const rows = await fetchRows(url); if (counter) counter.textContent = rows.length; target.innerHTML = renderTable(rows, columns); } catch (error) { target.innerHTML = `
读取失败:请确认 Fuseki 正在运行,并且 gesli 数据集已导入。
`; } } async function loadFlow() { const target = document.getElementById("flow"); try { const rows = await fetchRows(endpoints.flow); if (!rows.length) { target.innerHTML = `
暂无流程数据
`; return; } target.innerHTML = rows.map(row => `
${escapeHtml(row.sequence)}. ${escapeHtml(row.nodeName)} ${row.deadlineName ? `时限:${escapeHtml(row.deadlineName)}` : "无关键时限"} ${row.nextNodeName ? `下一步:${escapeHtml(row.nextNodeName)}` : "流程结束"}
`).join(""); } catch (error) { target.innerHTML = `
流程读取失败
`; } } function renderTable(rows, columns) { if (!rows.length) return `
暂无数据
`; const head = columns.map(column => `${labels[column] || column}`).join(""); const body = rows.map(row => ` ${columns.map(column => `${formatCell(column, row[column])}`).join("")} `).join(""); return `${head}${body}
`; } function formatCell(column, value) { if (!value) return ""; if (column === "isDelayed" && value === "true") return ``; if (column === "isDelayed" && value === "false") return "否"; if (column === "isCompleted" && value === "true") return "是"; if (column === "isCompleted" && value === "false") return ``; if (column.endsWith("Time")) return escapeHtml(value.replace("T", " ").replace("+08:00", "")); return escapeHtml(value); } function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); }