|
|
@ -162,11 +162,9 @@ |
|
|
|
|
|
|
|
|
<div class="map-panel"> |
|
|
<div class="map-panel"> |
|
|
<div class="map-toolbar"> |
|
|
<div class="map-toolbar"> |
|
|
<span class="map-type active">天地图</span> |
|
|
|
|
|
<button class="icon-button" title="放大" @click="zoomIn"><svg><use href="#i-plus" /></svg></button> |
|
|
|
|
|
<button class="icon-button" title="缩小" @click="zoomOut"><svg><use href="#i-minus" /></svg></button> |
|
|
|
|
|
<button class="icon-button" title="刷新" @click="reload"><svg><use href="#i-refresh" /></svg></button> |
|
|
|
|
|
<button class="map-view-all" type="button" @click="clearSelection">查看全部设备</button> |
|
|
|
|
|
|
|
|
<button class="map-type" type="button" :class="{ active: mapMode === 'satellite' }" @click="setMapMode('satellite')">卫星</button> |
|
|
|
|
|
<button class="map-type" type="button" :class="{ active: mapMode === 'street' }" @click="setMapMode('street')">地图</button> |
|
|
|
|
|
<button class="icon-button" title="查看全部设备" @click="fitAll"><svg><use href="#i-maximize" /></svg></button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="map-legend"> |
|
|
<div class="map-legend"> |
|
|
@ -175,6 +173,7 @@ |
|
|
<span><i class="red"></i>告警</span> |
|
|
<span><i class="red"></i>告警</span> |
|
|
<span><i class="gray"></i>离线</span> |
|
|
<span><i class="gray"></i>离线</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="map-scale">{{ mapScaleText }}</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<aside class="detail-panel" :class="{ 'drone-selected': isDrone }"> |
|
|
<aside class="detail-panel" :class="{ 'drone-selected': isDrone }"> |
|
|
@ -369,11 +368,12 @@ import { useUiStore } from '@/stores/modules/uiStore' |
|
|
import { useUserStore } from '@/stores/modules/userStore' |
|
|
import { useUserStore } from '@/stores/modules/userStore' |
|
|
import commonRefs from '@/utils/commonRefs' |
|
|
import commonRefs from '@/utils/commonRefs' |
|
|
import mapHelper from '@/core/mapHelper' |
|
|
import mapHelper from '@/core/mapHelper' |
|
|
import mapConfig from '@/config/map' |
|
|
|
|
|
|
|
|
import mapConfig, { createTiandituBaseLayer } from '@/config/map' |
|
|
import LivePlayer from '@/components/LivePlayer.vue' |
|
|
import LivePlayer from '@/components/LivePlayer.vue' |
|
|
import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live' |
|
|
import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live' |
|
|
|
|
|
|
|
|
const LAYER_ID = 'monitor-devices' |
|
|
const LAYER_ID = 'monitor-devices' |
|
|
|
|
|
const BINDING_LAYER_ID = 'monitor-binding' |
|
|
|
|
|
|
|
|
const STATUS_COLORS = { |
|
|
const STATUS_COLORS = { |
|
|
online: '#21a06a', |
|
|
online: '#21a06a', |
|
|
@ -402,6 +402,10 @@ const selectedDockIds = reactive(new Set()) |
|
|
|
|
|
|
|
|
let mapInstance = null |
|
|
let mapInstance = null |
|
|
let deviceLayer = null |
|
|
let deviceLayer = null |
|
|
|
|
|
let bindingLayer = null |
|
|
|
|
|
let bindingLine = null |
|
|
|
|
|
const mapMode = ref('satellite') |
|
|
|
|
|
const mapScaleText = ref('2 km') |
|
|
let suppressMapClick = false |
|
|
let suppressMapClick = false |
|
|
const markerById = new Map() |
|
|
const markerById = new Map() |
|
|
|
|
|
|
|
|
@ -502,6 +506,88 @@ function syncMarkers() { |
|
|
markerById.delete(id) |
|
|
markerById.delete(id) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
syncBindingLine() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function updateMapScale() { |
|
|
|
|
|
if (!mapInstance) return |
|
|
|
|
|
const zoom = mapInstance.getZoom() |
|
|
|
|
|
// rough WebMercator meters-per-pixel at equator * 100px bar |
|
|
|
|
|
const meters = (156543.03392 / (2 ** zoom)) * 100 |
|
|
|
|
|
if (meters >= 1000) mapScaleText.value = `${Math.round(meters / 1000)} km` |
|
|
|
|
|
else if (meters >= 100) mapScaleText.value = `${Math.round(meters / 10) * 10} m` |
|
|
|
|
|
else mapScaleText.value = `${Math.max(1, Math.round(meters))} m` |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function bindingEndpoints() { |
|
|
|
|
|
if (!selectedId.value) return null |
|
|
|
|
|
const asset = devices.asset(selectedId.value) |
|
|
|
|
|
if (!asset) return null |
|
|
|
|
|
let dock = null |
|
|
|
|
|
let drone = null |
|
|
|
|
|
if (asset.type === 'dock') { |
|
|
|
|
|
dock = asset |
|
|
|
|
|
const child = (asset.children || []).map((c) => devices.asset(c.id || c)).find((a) => a && !a.inDock && a.hasCoordinates) |
|
|
|
|
|
drone = child || null |
|
|
|
|
|
} else { |
|
|
|
|
|
drone = asset.inDock ? null : asset |
|
|
|
|
|
dock = asset.parent ? devices.asset(asset.parent) : null |
|
|
|
|
|
} |
|
|
|
|
|
if (!dock?.hasCoordinates || !drone?.hasCoordinates) return null |
|
|
|
|
|
if (drone.inDock) return null |
|
|
|
|
|
const a = [Number(dock.longitude), Number(dock.latitude)] |
|
|
|
|
|
const b = [Number(drone.longitude), Number(drone.latitude)] |
|
|
|
|
|
if (!a.every(Number.isFinite) || !b.every(Number.isFinite)) return null |
|
|
|
|
|
return [a, b] |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function syncBindingLine() { |
|
|
|
|
|
if (!bindingLayer) return |
|
|
|
|
|
const ends = bindingEndpoints() |
|
|
|
|
|
if (!ends) { |
|
|
|
|
|
if (bindingLine) { |
|
|
|
|
|
bindingLine.remove() |
|
|
|
|
|
bindingLine = null |
|
|
|
|
|
} |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
if (!bindingLine) { |
|
|
|
|
|
bindingLine = new maptalks.LineString(ends, { |
|
|
|
|
|
symbol: { |
|
|
|
|
|
lineColor: 'rgba(117,191,246,0.95)', |
|
|
|
|
|
lineWidth: 2, |
|
|
|
|
|
lineDasharray: [8, 6], |
|
|
|
|
|
lineOpacity: 0.95, |
|
|
|
|
|
}, |
|
|
|
|
|
properties: { label: '绑定设备' }, |
|
|
|
|
|
}) |
|
|
|
|
|
bindingLine.addTo(bindingLayer) |
|
|
|
|
|
} else { |
|
|
|
|
|
bindingLine.setCoordinates(ends) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function setMapMode(mode) { |
|
|
|
|
|
if (!mapInstance || mapMode.value === mode) { |
|
|
|
|
|
mapMode.value = mode |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
const tk = mapConfig.token |
|
|
|
|
|
if (!tk) { |
|
|
|
|
|
ui.toast('缺少天地图 token,无法切换底图') |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
mapMode.value = mode |
|
|
|
|
|
mapInstance.setBaseLayer(createTiandituBaseLayer(maptalks, tk, mode)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function fitAll() { |
|
|
|
|
|
if (selectedId.value) { |
|
|
|
|
|
clearSelection() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
fitAllMarkers() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function fitAllMarkers() { |
|
|
function fitAllMarkers() { |
|
|
@ -557,10 +643,17 @@ async function setupMap() { |
|
|
|
|
|
|
|
|
const existing = map.getLayer(LAYER_ID) |
|
|
const existing = map.getLayer(LAYER_ID) |
|
|
if (existing) existing.remove() |
|
|
if (existing) existing.remove() |
|
|
|
|
|
const existingBinding = map.getLayer(BINDING_LAYER_ID) |
|
|
|
|
|
if (existingBinding) existingBinding.remove() |
|
|
|
|
|
|
|
|
deviceLayer = new maptalks.VectorLayer(LAYER_ID, [], { zIndex: 120, forceRenderOnMoving: true }) |
|
|
deviceLayer = new maptalks.VectorLayer(LAYER_ID, [], { zIndex: 120, forceRenderOnMoving: true }) |
|
|
deviceLayer.addTo(map) |
|
|
deviceLayer.addTo(map) |
|
|
|
|
|
bindingLayer = new maptalks.VectorLayer(BINDING_LAYER_ID, [], { zIndex: 110, forceRenderOnMoving: true }) |
|
|
|
|
|
bindingLayer.addTo(map) |
|
|
map.on('click', onMapBlankClick) |
|
|
map.on('click', onMapBlankClick) |
|
|
|
|
|
map.on('zoomend', updateMapScale) |
|
|
|
|
|
map.on('moveend', updateMapScale) |
|
|
|
|
|
updateMapScale() |
|
|
|
|
|
|
|
|
syncMarkers() |
|
|
syncMarkers() |
|
|
if (selectedId.value) flyToSelected() |
|
|
if (selectedId.value) flyToSelected() |
|
|
@ -570,10 +663,16 @@ async function setupMap() { |
|
|
function teardownMap() { |
|
|
function teardownMap() { |
|
|
if (mapInstance) { |
|
|
if (mapInstance) { |
|
|
mapInstance.off('click', onMapBlankClick) |
|
|
mapInstance.off('click', onMapBlankClick) |
|
|
|
|
|
mapInstance.off('zoomend', updateMapScale) |
|
|
|
|
|
mapInstance.off('moveend', updateMapScale) |
|
|
const layer = mapInstance.getLayer(LAYER_ID) |
|
|
const layer = mapInstance.getLayer(LAYER_ID) |
|
|
if (layer) layer.remove() |
|
|
if (layer) layer.remove() |
|
|
|
|
|
const binding = mapInstance.getLayer(BINDING_LAYER_ID) |
|
|
|
|
|
if (binding) binding.remove() |
|
|
} |
|
|
} |
|
|
markerById.clear() |
|
|
markerById.clear() |
|
|
|
|
|
bindingLine = null |
|
|
|
|
|
bindingLayer = null |
|
|
deviceLayer = null |
|
|
deviceLayer = null |
|
|
mapInstance = null |
|
|
mapInstance = null |
|
|
commonRefs.clearPendingList('map') |
|
|
commonRefs.clearPendingList('map') |
|
|
|