From f00f030ef39effa109c671caaca31bec97a7b129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=A0=E9=97=BB=E9=A3=8E?= Date: Tue, 16 Jun 2026 18:56:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=A7=BB=E5=87=BA=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E4=B8=8D=E6=B6=88=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meshmap_frontend/src/App.vue | 42 ++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/meshmap_frontend/src/App.vue b/meshmap_frontend/src/App.vue index dec51af..596fbe3 100644 --- a/meshmap_frontend/src/App.vue +++ b/meshmap_frontend/src/App.vue @@ -19,7 +19,7 @@ import MeshMap from './components/MeshMap.vue' import NodeDetailedPage from './components/NodeDetailedPage.vue' import NodeListPanel from './components/NodeListPanel.vue' import { fallbackMapSource, loadEnabledMapSources } from './mapSource' -import type { AdminUser, HealthStatus, MapBoundsChangePayload, MapBoundsQuery, MapRenderable, MapViewportItem, NodeInfo, NodeInfoById, PositionRecord, PublicMapTileSource, TextMessage } from './types' +import type { AdminUser, HealthStatus, MapBoundsChangePayload, MapBoundsQuery, MapRenderable, MapViewportItem, MapViewportPoint, NodeInfo, NodeInfoById, PositionRecord, PublicMapTileSource, TextMessage } from './types' const currentPath = window.location.pathname const adminPath = currentPath @@ -41,6 +41,7 @@ const selectedNodeId = ref(null) const health = ref(null) const nodeInfoSource = ref([]) const mapViewportItems = ref([]) +const selectedMapPoint = ref(null) const mapViewportMode = ref<'points' | 'clusters'>('points') const pagedNodeInfo = ref([]) const nodePage = ref(1) @@ -82,7 +83,13 @@ const nodesById = computed(() => { }) const mapItems = computed(() => { - return mapViewportItems.value + const items = mapViewportItems.value + const selectedItem = selectedMapPoint.value + const renderItems = selectedItem && selectedItem.type === 'point' && !items.some((item) => item.type === 'point' && item.node_id === selectedItem.node_id) + ? [...items, selectedItem] + : items + + return renderItems .filter((item) => item.type === 'cluster' || (item.latitude != null && item.longitude != null)) .map((item) => { if (item.type === 'cluster') { @@ -187,6 +194,21 @@ function isSameJSON(left: unknown, right: unknown): boolean { return JSON.stringify(left) === JSON.stringify(right) } +function isMapViewportPoint(item: MapViewportItem): item is MapViewportPoint { + return item.type === 'point' +} + +function selectNode(nodeId: string) { + selectedNodeId.value = nodeId + const item = mapViewportItems.value.find((item): item is MapViewportPoint => isMapViewportPoint(item) && item.node_id === nodeId) + selectedMapPoint.value = item ?? (selectedMapPoint.value?.node_id === nodeId ? selectedMapPoint.value : null) +} + +function clearSelectedNode() { + selectedNodeId.value = null + selectedMapPoint.value = null +} + async function loadInitialChatMessages() { const response = await getTextMessages(chatPageSize, 0) messages.value = toChronological(response.items) @@ -248,6 +270,12 @@ async function loadMapReportsForBounds(bounds: MapBoundsQuery, zoom: number, sho if (!isSameJSON(mapViewportItems.value, response.items)) { mapViewportItems.value = response.items } + const selectedItem = selectedNodeId.value + ? response.items.find((item): item is MapViewportPoint => isMapViewportPoint(item) && item.node_id === selectedNodeId.value) + : null + if (selectedItem) { + selectedMapPoint.value = selectedItem + } mapViewportMode.value = response.mode mapReportTotal.value = response.total } catch (err) { @@ -391,7 +419,7 @@ async function removeNodeFromLocalState(nodeId: string) { pagedNodeInfo.value = pagedNodeInfo.value.filter((node) => node.node_id !== nodeId) mapViewportItems.value = mapViewportItems.value.filter((item) => item.type === 'cluster' || item.node_id !== nodeId) if (selectedNodeId.value === nodeId) { - selectedNodeId.value = null + clearSelectedNode() } await loadNodePage(nodePage.value, false) } @@ -587,7 +615,7 @@ onBeforeUnmount(() => { :loading-older="chatLoadingOlder" :has-more-messages="chatHasMore" :is-admin="!!adminUser" - @select-node="selectedNodeId = $event" + @select-node="selectNode" @load-older="loadOlderMessages" @delete-message="requestDeleteMessage" @delete-and-block-node="requestDeleteAndBlockNode" @@ -602,8 +630,8 @@ onBeforeUnmount(() => { :map-sources="mapSources" @map-source-change="selectMapSource" @bounds-change="handleMapBoundsChange" - @select-node="selectedNodeId = $event" - @clear-node="selectedNodeId = null" + @select-node="selectNode" + @clear-node="clearSelectedNode" @delete-node="requestDeleteNode" @delete-and-block-node="requestDeleteAndBlockNode" /> @@ -617,7 +645,7 @@ onBeforeUnmount(() => { :total="nodeTotal" :loading="nodePageLoading || loading" :is-admin="!!adminUser" - @select-node="selectedNodeId = $event" + @select-node="selectNode" @page-change="loadNodePage" @delete-node="requestDeleteNode" @delete-and-block-node="requestDeleteAndBlockNode"