app.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. const endpoints = {
  2. orders: "/api/orders",
  3. risks: "/api/risks",
  4. flow: "/api/flow",
  5. nodeTimes: "/api/node-times",
  6. cutoffs: "/api/order-cutoffs",
  7. exceptionMap: "/api/exception-map",
  8. };
  9. const labels = {
  10. orderNo: "订单号",
  11. customerName: "客户",
  12. status: "状态",
  13. currentNodeName: "当前节点",
  14. ownerName: "责任人",
  15. riskName: "风险",
  16. exceptionName: "异常",
  17. affectedNodeName: "影响节点",
  18. sequence: "顺序",
  19. nodeName: "节点",
  20. nextNodeName: "下一节点",
  21. deadlineName: "关键时限",
  22. plannedTime: "计划时间",
  23. cutoffTime: "截止时间",
  24. actualTime: "实际完成",
  25. isCompleted: "完成",
  26. isDelayed: "超时",
  27. customsCutoffTime: "截关",
  28. terminalCutoffTime: "截港",
  29. vgmCutoffTime: "VGM截止",
  30. estimatedSailingTime: "预计开船",
  31. };
  32. document.getElementById("refreshBtn").addEventListener("click", loadAll);
  33. loadAll();
  34. async function loadAll() {
  35. await Promise.all([
  36. loadTable("orders", endpoints.orders, ["orderNo", "customerName", "status", "currentNodeName", "ownerName"]),
  37. loadTable("risks", endpoints.risks, ["orderNo", "riskName", "exceptionName", "affectedNodeName"]),
  38. loadFlow(),
  39. loadTable("nodeTimes", endpoints.nodeTimes, ["sequence", "nodeName", "deadlineName", "plannedTime", "cutoffTime", "actualTime", "isCompleted", "isDelayed"]),
  40. loadTable("cutoffs", endpoints.cutoffs, ["orderNo", "customsCutoffTime", "terminalCutoffTime", "vgmCutoffTime", "estimatedSailingTime"]),
  41. loadTable("exceptionMap", endpoints.exceptionMap, ["exceptionName", "affectedNodeName", "riskName"]),
  42. ]);
  43. }
  44. async function fetchRows(url) {
  45. const response = await fetch(url);
  46. if (!response.ok) {
  47. const detail = await response.text();
  48. throw new Error(detail || `HTTP ${response.status}`);
  49. }
  50. const data = await response.json();
  51. return data.rows || [];
  52. }
  53. async function loadTable(id, url, columns) {
  54. const target = document.getElementById(id);
  55. const counter = document.getElementById(`${id}Count`);
  56. try {
  57. const rows = await fetchRows(url);
  58. if (counter) counter.textContent = rows.length;
  59. target.innerHTML = renderTable(rows, columns);
  60. } catch (error) {
  61. target.innerHTML = `<div class="error">读取失败:请确认 Fuseki 正在运行,并且 gesli 数据集已导入。</div>`;
  62. }
  63. }
  64. async function loadFlow() {
  65. const target = document.getElementById("flow");
  66. try {
  67. const rows = await fetchRows(endpoints.flow);
  68. if (!rows.length) {
  69. target.innerHTML = `<div class="empty">暂无流程数据</div>`;
  70. return;
  71. }
  72. target.innerHTML = rows.map(row => `
  73. <div class="step">
  74. <strong>${escapeHtml(row.sequence)}. ${escapeHtml(row.nodeName)}</strong>
  75. <span>${row.deadlineName ? `时限:${escapeHtml(row.deadlineName)}` : "无关键时限"}</span>
  76. <span>${row.nextNodeName ? `下一步:${escapeHtml(row.nextNodeName)}` : "流程结束"}</span>
  77. </div>
  78. `).join("");
  79. } catch (error) {
  80. target.innerHTML = `<div class="error">流程读取失败</div>`;
  81. }
  82. }
  83. function renderTable(rows, columns) {
  84. if (!rows.length) return `<div class="empty">暂无数据</div>`;
  85. const head = columns.map(column => `<th>${labels[column] || column}</th>`).join("");
  86. const body = rows.map(row => `
  87. <tr>
  88. ${columns.map(column => `<td>${formatCell(column, row[column])}</td>`).join("")}
  89. </tr>
  90. `).join("");
  91. return `<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`;
  92. }
  93. function formatCell(column, value) {
  94. if (!value) return "";
  95. if (column === "isDelayed" && value === "true") return `<span class="badge-danger">是</span>`;
  96. if (column === "isDelayed" && value === "false") return "否";
  97. if (column === "isCompleted" && value === "true") return "是";
  98. if (column === "isCompleted" && value === "false") return `<span class="badge-warning">否</span>`;
  99. if (column.endsWith("Time")) return escapeHtml(value.replace("T", " ").replace("+08:00", ""));
  100. return escapeHtml(value);
  101. }
  102. function escapeHtml(value) {
  103. return String(value)
  104. .replaceAll("&", "&amp;")
  105. .replaceAll("<", "&lt;")
  106. .replaceAll(">", "&gt;")
  107. .replaceAll('"', "&quot;")
  108. .replaceAll("'", "&#039;");
  109. }