<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<title>场景 - Board</title>
|
<link rel="stylesheet" href="<%= basePath %>/static/style.css" />
|
</head>
|
<body class="page-home">
|
<div class="shell">
|
<header class="topbar">
|
<div class="brand brand-nav">
|
<a href="<%= basePath %>/">Board</a>
|
<span class="brand-sep" aria-hidden="true">·</span>
|
<a href="<%= basePath %>/warehouse-calendar">仓库计划日历</a>
|
</div>
|
<div class="userbar">
|
<a
|
class="userbar-edit"
|
href="<%= basePath %>/config/scenarios"
|
target="_blank"
|
rel="noopener"
|
>设置</a>
|
<span class="userbar-sep" aria-hidden="true">|</span>
|
<span class="username"><%= username %></span>
|
<button type="button" class="btn ghost" id="btnLogout">退出</button>
|
</div>
|
</header>
|
<div class="home-body">
|
<section class="panel recent-panel">
|
<h2>最近使用</h2>
|
<div id="recentList" class="recent-list page-link-grid"></div>
|
</section>
|
<section class="panel catalog-panel">
|
<h2>场景清单</h2>
|
<div class="catalog">
|
<% categories.forEach(function (cat) { %>
|
<div class="cat-block">
|
<div class="cat-title"><%= cat.name %></div>
|
<div class="scenario-tiles page-link-grid">
|
<% cat.scenarios.forEach(function (sc) { %>
|
<a
|
class="page-link page-tile"
|
target="_blank"
|
rel="noopener"
|
href="<%= basePath %>/view?scenarioId=<%= encodeURIComponent(sc.id) %>"
|
data-scenario-id="<%= sc.id %>"
|
data-scenario-name="<%= sc.name %>"
|
data-scenario-image="<%= sc.image %>"
|
>
|
<img
|
class="page-thumb"
|
src="<%= basePath %>/static/<%= sc.image %>"
|
alt=""
|
loading="lazy"
|
/>
|
<span class="page-link-text"><%= sc.name %></span>
|
</a>
|
<% }); %>
|
</div>
|
</div>
|
<% }); %>
|
</div>
|
</section>
|
</div>
|
</div>
|
<script type="application/json" id="boardScenarioImages"><%- scenarioImagesJson %></script>
|
<script>
|
(function () {
|
const LS_KEY = "board_recent_v2";
|
const MAX = 5;
|
const base = "<%= basePath %>";
|
|
const SCENARIO_IMAGE_MAP = (function () {
|
try {
|
const el = document.getElementById("boardScenarioImages");
|
const raw = el ? el.textContent : "{}";
|
return JSON.parse(raw || "{}");
|
} catch {
|
return {};
|
}
|
})();
|
|
function resolveScenarioImage(it) {
|
if (it.image) {
|
return it.image;
|
}
|
return SCENARIO_IMAGE_MAP[it.scenarioId] || "";
|
}
|
|
function readRecent() {
|
try {
|
const raw = localStorage.getItem(LS_KEY);
|
const arr = raw ? JSON.parse(raw) : [];
|
return Array.isArray(arr) ? arr : [];
|
} catch {
|
return [];
|
}
|
}
|
function writeRecent(items) {
|
localStorage.setItem(LS_KEY, JSON.stringify(items.slice(0, MAX)));
|
}
|
function pushRecent(entry) {
|
const cur = readRecent().filter(
|
(x) => x.scenarioId !== entry.scenarioId
|
);
|
cur.unshift(entry);
|
writeRecent(cur);
|
}
|
function renderRecent() {
|
const box = document.getElementById("recentList");
|
const items = readRecent();
|
box.innerHTML = "";
|
if (!items.length) {
|
box.innerHTML = '<span class="muted recent-empty">暂无记录,打开场景后将显示在此</span>';
|
return;
|
}
|
items.forEach((it) => {
|
const a = document.createElement("a");
|
a.className = "page-link page-tile";
|
a.target = "_blank";
|
a.rel = "noopener";
|
a.href =
|
base + "/view?scenarioId=" + encodeURIComponent(it.scenarioId);
|
a.setAttribute("data-scenario-id", it.scenarioId);
|
a.setAttribute("data-scenario-name", it.scenarioName);
|
const imgPath = resolveScenarioImage(it);
|
if (imgPath) {
|
a.setAttribute("data-scenario-image", imgPath);
|
const img = document.createElement("img");
|
img.className = "page-thumb";
|
img.alt = "";
|
img.loading = "lazy";
|
img.src = base + "/static/" + imgPath;
|
a.appendChild(img);
|
} else {
|
const ph = document.createElement("div");
|
ph.className = "page-thumb page-thumb--placeholder";
|
ph.setAttribute("aria-hidden", "true");
|
a.appendChild(ph);
|
}
|
const span = document.createElement("span");
|
span.className = "page-link-text";
|
span.textContent = it.scenarioName;
|
a.appendChild(span);
|
box.appendChild(a);
|
});
|
}
|
function readRecentEntryFromLink(anchor) {
|
const scenarioId = anchor.getAttribute("data-scenario-id") || "";
|
const fromAttr = anchor.getAttribute("data-scenario-image") || "";
|
const image =
|
fromAttr || resolveScenarioImage({ scenarioId, image: "" });
|
return {
|
scenarioId,
|
scenarioName: anchor.getAttribute("data-scenario-name") || "",
|
image,
|
};
|
}
|
|
document.body.addEventListener("click", (e) => {
|
const a = e.target.closest("a.page-tile");
|
if (!a || !a.href || a.href.indexOf("/view") === -1) {
|
return;
|
}
|
pushRecent(readRecentEntryFromLink(a));
|
});
|
document.getElementById("btnLogout").addEventListener("click", async () => {
|
await fetch(base + "/api/logout", { method: "POST", credentials: "include" });
|
location.href = base + "/login";
|
});
|
renderRecent();
|
})();
|
</script>
|
</body>
|
</html>
|