diff --git a/src/layout/MapLayer.vue b/src/layout/MapLayer.vue index f1a7b62..03fa013 100644 --- a/src/layout/MapLayer.vue +++ b/src/layout/MapLayer.vue @@ -7,6 +7,7 @@ import mapHelper from '@/core/mapHelper' import 'mapbox-gl/dist/mapbox-gl.css' let map = null +let initTimer = null function initMap() { const tk = mapConfig.token @@ -31,7 +32,8 @@ function initMap() { } onMounted(() => { - setTimeout(() => { + initTimer = setTimeout(() => { + initTimer = null try { initMap() } catch (error) { @@ -42,6 +44,10 @@ onMounted(() => { }) onUnmounted(() => { + if (initTimer != null) { + clearTimeout(initTimer) + initTimer = null + } if (map) { map.remove() commonRefs.removeRef('map') diff --git a/src/views/MonitorView/MonitorView.vue b/src/views/MonitorView/MonitorView.vue index b348bb1..7af3f6f 100644 --- a/src/views/MonitorView/MonitorView.vue +++ b/src/views/MonitorView/MonitorView.vue @@ -405,6 +405,8 @@ const selectedDockIds = reactive(new Set()) let mapInstance = null let layersReady = false +let pendingLoadHandler = null +let pendingStyleHandler = null const mapMode = ref('satellite') const mapScaleText = ref('2 km') let suppressMapClick = false @@ -460,7 +462,7 @@ function devicesFeatureCollection() { assetId: String(asset.id), name: asset.name || '', kind: asset.type, - selected, + selected: selected ? 1 : 0, color: statusColor(asset.statusClass), radius: asset.type === 'drone' ? (selected ? 10 : 8) : (selected ? 12 : 10), }, @@ -520,7 +522,7 @@ function ensureMonitorLayers() { 'circle-stroke-color': '#fff', 'circle-stroke-width': [ 'case', - ['to-boolean', ['get', 'selected']], + ['==', ['get', 'selected'], 1], 3, 2, ], @@ -605,7 +607,25 @@ function syncBindingLine() { if (src) src.setData(bindingFeatureCollection()) } +function cancelPendingMapHandlers() { + if (!mapInstance) { + pendingLoadHandler = null + pendingStyleHandler = null + return + } + if (pendingLoadHandler) { + mapInstance.off('load', pendingLoadHandler) + pendingLoadHandler = null + } + if (pendingStyleHandler) { + mapInstance.off('style.load', pendingStyleHandler) + pendingStyleHandler = null + } +} + function rebuildAfterStyle() { + pendingStyleHandler = null + if (!mapInstance) return layersReady = false ensureMonitorLayers() syncMarkers() @@ -623,8 +643,13 @@ function setMapMode(mode) { } mapMode.value = mode layersReady = false + if (pendingStyleHandler) { + mapInstance.off('style.load', pendingStyleHandler) + pendingStyleHandler = null + } mapInstance.setStyle(MAP_STYLES[mode] || MAP_STYLES.satellite) - mapInstance.once('style.load', rebuildAfterStyle) + pendingStyleHandler = rebuildAfterStyle + mapInstance.once('style.load', pendingStyleHandler) } function fitAll() { @@ -698,6 +723,8 @@ async function setupMap() { mapHelper.toggleMapMode({ is2D: true }) const onReady = () => { + pendingLoadHandler = null + if (!mapInstance) return ensureMonitorLayers() syncMarkers() if (selectedId.value) flyToSelected() @@ -705,8 +732,12 @@ async function setupMap() { updateMapScale() } + cancelPendingMapHandlers() if (map.isStyleLoaded()) onReady() - else map.once('load', onReady) + else { + pendingLoadHandler = onReady + map.once('load', pendingLoadHandler) + } map.on('click', onMapClick) map.on('zoom', updateMapScale) @@ -714,6 +745,7 @@ async function setupMap() { } function teardownMap() { + cancelPendingMapHandlers() if (mapInstance) { mapInstance.off('click', onMapClick) mapInstance.off('zoom', updateMapScale) @@ -721,6 +753,7 @@ function teardownMap() { clearMonitorLayers() } mapInstance = null + layersReady = false commonRefs.clearPendingList('map') } diff --git a/src/views/TasksView/TasksView.vue b/src/views/TasksView/TasksView.vue index 5c5112e..1857141 100644 --- a/src/views/TasksView/TasksView.vue +++ b/src/views/TasksView/TasksView.vue @@ -479,6 +479,7 @@ function clearTaskLayers(map) { } function ensureTaskLayers(map) { + if (!map || !map.isStyleLoaded()) return false if (!map.getSource(ROUTE_SOURCE)) { map.addSource(ROUTE_SOURCE, { type: 'geojson', @@ -529,11 +530,12 @@ function ensureTaskLayers(map) { paint: { 'text-color': '#fff' }, }) } + return true } function syncRouteLayer() { - if (!routeMapInstance) return - ensureTaskLayers(routeMapInstance) + if (!routeMapInstance || !routeMapInstance.isStyleLoaded()) return + if (!ensureTaskLayers(routeMapInstance)) return const coords = routePoints.value .map((p) => [Number(p.longitude), Number(p.latitude)]) .filter(([lon, lat]) => Number.isFinite(lon) && Number.isFinite(lat)) @@ -608,17 +610,18 @@ async function attachRouteMap() { mapHelper.replaceMapContainer(host) mapHelper.toggleMapMode({ is2D: true }) routeMapAttached = true - routeMapReady.value = true + routeMapReady.value = false const boot = () => { pendingStyleHandler = null if (!routeMapAttached || routeMapInstance !== map) return clearTaskLayers(map) - ensureTaskLayers(map) + if (!ensureTaskLayers(map)) return map.on('click', onRouteMapClick) map.on('mousemove', onRouteMapMove) const c = map.getCenter() routeCursor.value = formatCoord(c.lng, c.lat) + routeMapReady.value = true requestAnimationFrame(() => { map.resize() syncRouteLayer()