feat: add Guanghan module detail modal

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
2026-07-09 16:59:39 +08:00
co-authored by Codex
parent 2780847c50
commit b1a91159d9
2 changed files with 229 additions and 0 deletions
+155
View File
@@ -153,10 +153,93 @@
text-align: center;
}
body.modal-open { overflow: hidden; }
.modal-backdrop {
position: fixed;
inset: 0;
z-index: 20;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
background: rgba(2, 7, 14, 0.82);
backdrop-filter: blur(8px);
}
.modal-backdrop[aria-hidden="false"] { display: flex; }
.detail-dialog {
position: relative;
width: min(760px, 100%);
max-height: min(820px, calc(100vh - 48px));
overflow: auto;
padding: 30px;
border: 1px solid #405572;
border-radius: 18px;
outline: none;
background: linear-gradient(145deg, #13233a, #0b1424 72%);
box-shadow: 0 30px 100px rgba(0, 0, 0, 0.64);
}
.detail-dialog:focus-visible { box-shadow: 0 0 0 3px var(--pressurized), 0 30px 100px rgba(0, 0, 0, 0.64); }
.modal-close {
position: absolute;
top: 14px;
right: 14px;
width: 40px;
height: 40px;
border: 1px solid #4e627e;
border-radius: 50%;
background: #182a43;
color: var(--text);
font: 500 1.65rem/1 system-ui, sans-serif;
cursor: pointer;
}
.modal-close:hover,
.modal-close:focus-visible { border-color: var(--pressurized); outline: none; background: #203754; }
.detail-kicker {
margin: 0 46px 6px 0;
color: var(--pressurized);
font-size: 0.77rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
#detail-title { margin: 0 50px 10px 0; font-size: 1.85rem; }
#detail-role { margin: 0 0 22px; color: #c4d2e4; line-height: 1.7; }
.detail-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin: 0;
}
.detail-grid > div {
min-width: 0;
padding: 14px;
border: 1px solid #293c57;
border-radius: 10px;
background: rgba(8, 18, 33, 0.7);
}
.detail-grid dt { margin-bottom: 7px; color: #7f94b1; font-size: 0.75rem; }
.detail-grid dd { margin: 0; color: #e1ebf8; line-height: 1.55; overflow-wrap: anywhere; }
.detail-grid dd span { display: block; }
.detail-grid dd span + span { margin-top: 4px; }
@media (max-width: 760px) {
body { padding: 14px; }
.topology-map { min-width: 980px; }
.legend { justify-content: flex-start; }
.modal-backdrop { padding: 12px; }
.detail-dialog { padding: 25px 18px 20px; max-height: calc(100vh - 24px); }
.detail-grid { grid-template-columns: 1fr; }
}
</style>
</head>
@@ -331,6 +414,23 @@
<p class="click-note">模块与车辆支持详情查看;走道、管线和散热面板仅用于表达拓扑关系。</p>
</main>
<div class="modal-backdrop" id="detail-modal" aria-hidden="true">
<section class="detail-dialog" role="dialog" aria-modal="true" aria-labelledby="detail-title" tabindex="-1">
<button class="modal-close" type="button" aria-label="关闭详情">×</button>
<p class="detail-kicker" id="detail-type"></p>
<h2 id="detail-title"></h2>
<p id="detail-role"></p>
<dl class="detail-grid">
<div><dt>安装位置</dt><dd id="detail-location"></dd></div>
<div><dt>运行状态</dt><dd id="detail-status"></dd></div>
<div><dt>关键参数</dt><dd id="detail-metrics"></dd></div>
<div><dt>尺寸</dt><dd id="detail-dimensions"></dd></div>
<div><dt>连接关系</dt><dd id="detail-connections"></dd></div>
<div><dt>任务节点</dt><dd id="detail-mission"></dd></div>
</dl>
</section>
</div>
<script>
const MODULE_DETAILS = {
"core": {
@@ -488,6 +588,61 @@
mission: "BL-03 · 2024-09-23首次着陆;GHC-05转运广寒03模块"
}
};
const modal = document.querySelector("#detail-modal");
const dialog = modal.querySelector(".detail-dialog");
const closeButton = modal.querySelector(".modal-close");
let opener = null;
function renderLines(element, items) {
element.replaceChildren(...items.map((item) => {
const line = document.createElement("span");
line.textContent = item;
return line;
}));
}
function openDetails(moduleId, trigger) {
const detail = MODULE_DETAILS[moduleId];
if (!detail) return;
opener = trigger;
document.querySelector("#detail-type").textContent = detail.type;
document.querySelector("#detail-title").textContent = detail.name;
document.querySelector("#detail-role").textContent = detail.role;
document.querySelector("#detail-location").textContent = detail.location;
document.querySelector("#detail-status").textContent = detail.status;
renderLines(document.querySelector("#detail-metrics"), detail.metrics);
renderLines(document.querySelector("#detail-dimensions"), detail.dimensions);
renderLines(document.querySelector("#detail-connections"), detail.connections);
document.querySelector("#detail-mission").textContent = detail.mission;
modal.setAttribute("aria-hidden", "false");
document.body.classList.add("modal-open");
dialog.focus();
}
function closeDetails() {
modal.setAttribute("aria-hidden", "true");
document.body.classList.remove("modal-open");
opener?.focus();
}
document.querySelectorAll("[data-module]").forEach((node) => {
node.addEventListener("click", () => openDetails(node.dataset.module, node));
node.addEventListener("keydown", (event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
openDetails(node.dataset.module, node);
}
});
});
closeButton.addEventListener("click", closeDetails);
modal.addEventListener("click", (event) => {
if (event.target === modal) closeDetails();
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && modal.getAttribute("aria-hidden") === "false") closeDetails();
});
</script>
</body>
</html>