-
地球信息
+
+
+
地球信息
+
+
电缆系统:
0个
@@ -234,6 +255,57 @@
+
diff --git a/frontend/public/earth/js/controls.js b/frontend/public/earth/js/controls.js
index 22b58736..c0f012f1 100644
--- a/frontend/public/earth/js/controls.js
+++ b/frontend/public/earth/js/controls.js
@@ -26,6 +26,11 @@ export let layoutExpanded = false;
let earthObj = null;
let listeners = [];
let cleanupFns = [];
+const HUD_PANEL_IDS = [
+ "legend",
+ "coordinates-display",
+ "earth-stats",
+];
function getFloatingGroups() {
return [
@@ -44,6 +49,12 @@ function isFloatingMenuVisible() {
});
}
+function isSettingsModalOpen() {
+ return document
+ .getElementById("settings-modal")
+ ?.classList.contains("is-open");
+}
+
function closeFloatingMenus() {
getFloatingGroups().forEach((group) => {
group.classList.remove("open");
@@ -55,6 +66,164 @@ function closeFloatingMenus() {
}
}
+function openSettingsModal() {
+ const modal = document.getElementById("settings-modal");
+ if (!modal) return;
+ closeFloatingMenus();
+ modal.classList.add("is-open");
+ modal.setAttribute("aria-hidden", "false");
+}
+
+function closeSettingsModal() {
+ const modal = document.getElementById("settings-modal");
+ if (!modal) return;
+ modal.classList.remove("is-open");
+ modal.setAttribute("aria-hidden", "true");
+}
+
+function setHudPanelVisibility(panelId, visible) {
+ const panel = document.getElementById(panelId);
+ if (!panel) return;
+ panel.classList.toggle("hud-panel-hidden", !visible);
+ syncSettingsToggle(panelId, visible);
+}
+
+function syncSettingsToggle(panelId, visible) {
+ const input = document.querySelector(
+ `[data-settings-panel="${panelId}"]`,
+ );
+ if (input instanceof HTMLInputElement) {
+ input.checked = visible;
+ }
+}
+
+function syncAllHudPanelToggles() {
+ HUD_PANEL_IDS.forEach((panelId) => {
+ const panel = document.getElementById(panelId);
+ syncSettingsToggle(panelId, !panel?.classList.contains("hud-panel-hidden"));
+ });
+}
+
+function setupSettingsControls() {
+ const settingsTrigger = document.getElementById("settings-trigger");
+ const settingsClose = document.getElementById("settings-close");
+ const settingsBackdrop = document.getElementById("settings-backdrop");
+ const settingsModal = document.getElementById("settings-modal");
+
+ bindListener(settingsTrigger, "click", () => {
+ openSettingsModal();
+ });
+
+ bindListener(settingsClose, "click", () => {
+ closeSettingsModal();
+ });
+
+ bindListener(settingsBackdrop, "click", () => {
+ closeSettingsModal();
+ });
+
+ bindListener(settingsModal, "click", (event) => {
+ const sheet = event.target.closest(".earth-settings-sheet");
+ if (!sheet) {
+ closeSettingsModal();
+ }
+ });
+
+ const toggleInputs = document.querySelectorAll("[data-settings-panel]");
+ toggleInputs.forEach((input) => {
+ bindListener(input, "change", (event) => {
+ const target = event.currentTarget;
+ if (!(target instanceof HTMLInputElement)) return;
+ const panelId = target.dataset.settingsPanel;
+ if (!panelId) return;
+ setHudPanelVisibility(panelId, target.checked);
+ });
+ });
+
+ syncAllHudPanelToggles();
+}
+
+function setupHudPanelControls() {
+ const closeButtons = document.querySelectorAll("[data-close-panel]");
+ closeButtons.forEach((button) => {
+ bindListener(button, "click", (event) => {
+ event.stopPropagation();
+ const target = event.currentTarget;
+ if (!(target instanceof HTMLElement)) return;
+ const panelId = target.dataset.closePanel;
+ if (!panelId) return;
+ setHudPanelVisibility(panelId, false);
+ });
+ });
+}
+
+function setupDraggableHudPanels() {
+ const app = document.getElementById("container");
+ const draggablePanels = document.querySelectorAll(".hud-panel-draggable");
+ if (!app || draggablePanels.length === 0) return;
+
+ draggablePanels.forEach((panel) => {
+ const handle = panel.querySelector(".hud-panel-drag-handle");
+ if (!handle) return;
+
+ let isDragging = false;
+ let startPointerX = 0;
+ let startPointerY = 0;
+ let startLeft = 0;
+ let startTop = 0;
+
+ const stopDragging = () => {
+ isDragging = false;
+ panel.classList.remove("is-dragging");
+ document.body.style.userSelect = "";
+ };
+
+ const onMove = (event) => {
+ if (!isDragging) return;
+ const appRect = app.getBoundingClientRect();
+ const panelRect = panel.getBoundingClientRect();
+ const nextLeft = Math.min(
+ Math.max(startLeft + (event.clientX - startPointerX), 0),
+ appRect.width - panelRect.width,
+ );
+ const nextTop = Math.min(
+ Math.max(startTop + (event.clientY - startPointerY), 0),
+ appRect.height - panelRect.height,
+ );
+
+ panel.style.left = `${nextLeft}px`;
+ panel.style.top = `${nextTop}px`;
+ panel.style.right = "auto";
+ panel.style.bottom = "auto";
+ panel.style.transform = "none";
+ };
+
+ bindListener(handle, "pointerdown", (event) => {
+ if (event.target.closest(".hud-panel-close")) return;
+ isDragging = true;
+ startPointerX = event.clientX;
+ startPointerY = event.clientY;
+ const appRect = app.getBoundingClientRect();
+ const panelRect = panel.getBoundingClientRect();
+ startLeft = panelRect.left - appRect.left;
+ startTop = panelRect.top - appRect.top;
+ panel.style.left = `${startLeft}px`;
+ panel.style.top = `${startTop}px`;
+ panel.style.right = "auto";
+ panel.style.bottom = "auto";
+ panel.style.transform = "none";
+ panel.classList.add("is-dragging");
+ document.body.style.userSelect = "none";
+ handle.setPointerCapture?.(event.pointerId);
+ });
+
+ bindListener(handle, "pointermove", onMove);
+ bindListener(handle, "pointerup", stopDragging);
+ bindListener(handle, "pointercancel", stopDragging);
+ bindListener(handle, "lostpointercapture", stopDragging);
+ });
+}
+
function clearForcedFloatingClose() {
getFloatingGroups().forEach((group) => {
group.classList.remove("force-closed");
@@ -379,6 +548,9 @@ function setupTerrainControls() {
const reloadBtn = document.getElementById("reload-data");
const zoomGroup = document.getElementById("zoom-control-group");
const zoomTrigger = document.getElementById("zoom-trigger");
+ setupSettingsControls();
+ setupHudPanelControls();
+ setupDraggableHudPanels();
if (trailsBtn) {
trailsBtn.classList.add("active");
@@ -487,6 +659,11 @@ function setupKeyboardControls() {
bindListener(document, "keydown", (event) => {
if (event.key !== "Escape") return;
+ if (isSettingsModalOpen()) {
+ closeSettingsModal();
+ return;
+ }
+
if (isFloatingMenuVisible()) {
closeFloatingMenus();
return;
diff --git a/frontend/public/earth/js/legend.js b/frontend/public/earth/js/legend.js
index 45f9fc35..cb932e29 100644
--- a/frontend/public/earth/js/legend.js
+++ b/frontend/public/earth/js/legend.js
@@ -49,6 +49,9 @@ function renderLegend(mode) {
const config = LEGEND_MODES[mode] || LEGEND_MODES.cables;
const items = legendItemsByMode[mode] || [];
+ const titleEl = legend.querySelector(".legend-title");
+ const listEl = legend.querySelector(".legend-list");
+ if (!titleEl || !listEl) return;
const itemsHtml = items
.map(
(item) => `
@@ -60,8 +63,6 @@ function renderLegend(mode) {
)
.join("");
- legend.innerHTML = `
-
${config.title}
-
${itemsHtml}
- `;
+ titleEl.textContent = config.title;
+ listEl.innerHTML = itemsHtml;
}