|
|
@ -659,6 +659,7 @@ function cancelPendingMapHandlers() { |
|
|
} |
|
|
} |
|
|
if (pendingLoadHandler) { |
|
|
if (pendingLoadHandler) { |
|
|
mapInstance.off('load', pendingLoadHandler) |
|
|
mapInstance.off('load', pendingLoadHandler) |
|
|
|
|
|
mapInstance.off('style.load', pendingLoadHandler) |
|
|
pendingLoadHandler = null |
|
|
pendingLoadHandler = null |
|
|
} |
|
|
} |
|
|
if (pendingStyleHandler) { |
|
|
if (pendingStyleHandler) { |
|
|
@ -670,6 +671,11 @@ function cancelPendingMapHandlers() { |
|
|
async function rebuildAfterStyle() { |
|
|
async function rebuildAfterStyle() { |
|
|
pendingStyleHandler = null |
|
|
pendingStyleHandler = null |
|
|
if (!mapInstance) return |
|
|
if (!mapInstance) return |
|
|
|
|
|
try { |
|
|
|
|
|
mapInstance.setProjection('globe') |
|
|
|
|
|
} catch (_) { |
|
|
|
|
|
/* ignore */ |
|
|
|
|
|
} |
|
|
layersReady = false |
|
|
layersReady = false |
|
|
await ensureMonitorLayers() |
|
|
await ensureMonitorLayers() |
|
|
syncMarkers() |
|
|
syncMarkers() |
|
|
@ -695,16 +701,15 @@ function setMapMode(mode) { |
|
|
pendingStyleHandler = rebuildAfterStyle |
|
|
pendingStyleHandler = rebuildAfterStyle |
|
|
mapInstance.once('style.load', pendingStyleHandler) |
|
|
mapInstance.once('style.load', pendingStyleHandler) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function resetOverviewCamera() { |
|
|
function resetOverviewCamera() { |
|
|
if (!mapInstance) return |
|
|
if (!mapInstance) return |
|
|
mapInstance.easeTo({ |
|
|
|
|
|
|
|
|
mapInstance.jumpTo({ |
|
|
center: MAP_OVERVIEW.center, |
|
|
center: MAP_OVERVIEW.center, |
|
|
zoom: MAP_OVERVIEW.zoom, |
|
|
zoom: MAP_OVERVIEW.zoom, |
|
|
pitch: 0, |
|
|
pitch: 0, |
|
|
bearing: 0, |
|
|
bearing: 0, |
|
|
duration: 450, |
|
|
|
|
|
}) |
|
|
}) |
|
|
|
|
|
updateMapScale() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function fitAll() { |
|
|
function fitAll() { |
|
|
@ -722,39 +727,14 @@ function flyToSelected() { |
|
|
const lon = Number(asset.longitude) |
|
|
const lon = Number(asset.longitude) |
|
|
const lat = Number(asset.latitude) |
|
|
const lat = Number(asset.latitude) |
|
|
if (!Number.isFinite(lon) || !Number.isFinite(lat)) return |
|
|
if (!Number.isFinite(lon) || !Number.isFinite(lat)) return |
|
|
mapInstance.easeTo({ |
|
|
|
|
|
|
|
|
const nextZoom = Math.max(Number(mapInstance.getZoom()) || 0, MAP_VIEW.detailZoom) |
|
|
|
|
|
mapInstance.jumpTo({ |
|
|
center: [lon, lat], |
|
|
center: [lon, lat], |
|
|
zoom: Math.max(mapInstance.getZoom(), MAP_VIEW.detailZoom), |
|
|
|
|
|
duration: 450, |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onMapClick(e) { |
|
|
|
|
|
if (suppressMapClick) return |
|
|
|
|
|
if (!layersReady) return |
|
|
|
|
|
const layers = [DEVICE_SYMBOL, DEVICE_LABEL].filter((id) => mapInstance.getLayer(id)) |
|
|
|
|
|
if (!layers.length) return |
|
|
|
|
|
const feats = mapInstance.queryRenderedFeatures(e.point, { |
|
|
|
|
|
layers, |
|
|
|
|
|
|
|
|
zoom: nextZoom, |
|
|
|
|
|
pitch: 0, |
|
|
|
|
|
bearing: 0, |
|
|
}) |
|
|
}) |
|
|
const id = feats[0]?.properties?.assetId |
|
|
|
|
|
if (id) { |
|
|
|
|
|
suppressMapClick = true |
|
|
|
|
|
select(String(id)) |
|
|
|
|
|
setTimeout(() => { suppressMapClick = false }, 0) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
clearSelection() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onSymbolEnter() { |
|
|
|
|
|
if (!mapInstance) return |
|
|
|
|
|
mapInstance.getCanvas().style.cursor = 'pointer' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onSymbolLeave() { |
|
|
|
|
|
if (!mapInstance) return |
|
|
|
|
|
mapInstance.getCanvas().style.cursor = '' |
|
|
|
|
|
|
|
|
updateMapScale() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async function setupMap() { |
|
|
async function setupMap() { |
|
|
@ -765,8 +745,12 @@ async function setupMap() { |
|
|
} |
|
|
} |
|
|
mapInstance = map |
|
|
mapInstance = map |
|
|
mapHelper.toggleMapMode({ is2D: true }) |
|
|
mapHelper.toggleMapMode({ is2D: true }) |
|
|
|
|
|
updateMapScale() |
|
|
|
|
|
|
|
|
|
|
|
let readyStarted = false |
|
|
const onReady = async () => { |
|
|
const onReady = async () => { |
|
|
|
|
|
if (readyStarted) return |
|
|
|
|
|
readyStarted = true |
|
|
pendingLoadHandler = null |
|
|
pendingLoadHandler = null |
|
|
if (!mapInstance) return |
|
|
if (!mapInstance) return |
|
|
await ensureMonitorLayers() |
|
|
await ensureMonitorLayers() |
|
|
@ -777,10 +761,23 @@ async function setupMap() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
cancelPendingMapHandlers() |
|
|
cancelPendingMapHandlers() |
|
|
if (map.isStyleLoaded()) onReady() |
|
|
|
|
|
else { |
|
|
|
|
|
|
|
|
if (map.isStyleLoaded()) { |
|
|
|
|
|
await onReady() |
|
|
|
|
|
} else { |
|
|
pendingLoadHandler = onReady |
|
|
pendingLoadHandler = onReady |
|
|
map.once('load', pendingLoadHandler) |
|
|
map.once('load', pendingLoadHandler) |
|
|
|
|
|
map.once('style.load', pendingLoadHandler) |
|
|
|
|
|
// 双事件都可能已触发:短轮询兜底,避免首屏卡在默认比例尺 |
|
|
|
|
|
const startedAt = Date.now() |
|
|
|
|
|
const poll = () => { |
|
|
|
|
|
if (readyStarted || mapInstance !== map) return |
|
|
|
|
|
if (map.isStyleLoaded()) { |
|
|
|
|
|
onReady() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
if (Date.now() - startedAt < 5000) setTimeout(poll, 50) |
|
|
|
|
|
} |
|
|
|
|
|
setTimeout(poll, 0) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
map.on('click', onMapClick) |
|
|
map.on('click', onMapClick) |
|
|
@ -790,6 +787,37 @@ async function setupMap() { |
|
|
map.on('move', updateMapScale) |
|
|
map.on('move', updateMapScale) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function onMapClick(e) { |
|
|
|
|
|
if (suppressMapClick) return |
|
|
|
|
|
if (!layersReady) return |
|
|
|
|
|
const layers = [DEVICE_SYMBOL, DEVICE_LABEL].filter((id) => mapInstance.getLayer(id)) |
|
|
|
|
|
if (!layers.length) return |
|
|
|
|
|
const feats = mapInstance.queryRenderedFeatures(e.point, { |
|
|
|
|
|
layers, |
|
|
|
|
|
}) |
|
|
|
|
|
const id = feats[0]?.properties?.assetId |
|
|
|
|
|
if (id) { |
|
|
|
|
|
suppressMapClick = true |
|
|
|
|
|
select(String(id)) |
|
|
|
|
|
setTimeout(() => { suppressMapClick = false }, 0) |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
clearSelection() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onSymbolEnter() { |
|
|
|
|
|
if (!mapInstance) return |
|
|
|
|
|
mapInstance.getCanvas().style.cursor = 'pointer' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onSymbolLeave() { |
|
|
|
|
|
if (!mapInstance) return |
|
|
|
|
|
mapInstance.getCanvas().style.cursor = '' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function teardownMap() { |
|
|
function teardownMap() { |
|
|
cancelPendingMapHandlers() |
|
|
cancelPendingMapHandlers() |
|
|
if (mapInstance) { |
|
|
if (mapInstance) { |
|
|
@ -812,10 +840,14 @@ onMounted(async () => { |
|
|
if (route.query.deviceId && devices.record(route.query.deviceId)) selectedId.value = String(route.query.deviceId) |
|
|
if (route.query.deviceId && devices.record(route.query.deviceId)) selectedId.value = String(route.query.deviceId) |
|
|
const rec = selectedId.value ? devices.record(selectedId.value) : null |
|
|
const rec = selectedId.value ? devices.record(selectedId.value) : null |
|
|
controlExpanded.value = route.query.control === '1' && rec?.kind === 'dock' |
|
|
controlExpanded.value = route.query.control === '1' && rec?.kind === 'dock' |
|
|
await setupMap() |
|
|
|
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
ui.toast(e.message || '加载设备失败') |
|
|
ui.toast(e.message || '加载设备失败') |
|
|
} |
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
await setupMap() |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
ui.toast(e.message || '地图初始化失败') |
|
|
|
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
onUnmounted(() => { |
|
|
onUnmounted(() => { |
|
|
|