diff --git a/web/src/views/HandDevice/Home/components/OpenLayerMap.vue b/web/src/views/HandDevice/Home/components/OpenLayerMap.vue index eaf47d2..770186a 100644 --- a/web/src/views/HandDevice/Home/components/OpenLayerMap.vue +++ b/web/src/views/HandDevice/Home/components/OpenLayerMap.vue @@ -107,7 +107,6 @@ enableStatus 1启用 0未启用 // 使用组合式函数 const { services, - layerRefs, initializeMapAndLayers, setMarkersVisible, @@ -187,7 +186,7 @@ const initMap = () => { setTrajectoriesVisible(showTrajectories.value, props.markers) setFencesVisible(showFences.value) - // 设置事件监听器 + // 设置事件监听器,打开弹窗,点击设备 marker 时触发 setupMapEventListeners( map, popupOverlay, @@ -206,9 +205,7 @@ const initMap = () => { services.markerService?.createMarkerLayer(props) // updateMarkers(props.markers || [],props) }, - - markerLayer: layerRefs.value?.markerLayer, - refreshMarkerStyles + // refreshMarkerStyles } ) // 设置轨迹监听器 @@ -270,6 +267,15 @@ watch( }, { deep: true, immediate: false } ) +watch( + () => props.fences, + (newFences) => { + if (newFences && newFences.length > 0 && isMapInitialized) { + refreshFences() + } + }, + { deep: true, immediate: false } +) onMounted(() => { setTimeout(() => { diff --git a/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts b/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts index 94aa4c3..e1dbaca 100644 --- a/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts +++ b/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts @@ -87,8 +87,8 @@ export const useMapEvents = () => { isDrawing?: () => boolean onMarkerClick?: (markerData: any) => void onZoomEnd?: (zoom: number) => void - markerLayer?: any - refreshMarkerStyles?: () => void + // markerLayer?: any + // refreshMarkerStyles?: () => void } ) => { const popupGenerator = createPopupContentGenerator(trajectoryService, popupService) @@ -129,9 +129,9 @@ export const useMapEvents = () => { // console.log('handleMoveEnd'); // OpenLayers的Cluster会自动重新计算聚合,只需要刷新样式 - if (opts?.markerLayer) { - opts.markerLayer.changed() - } + // if (opts?.markerLayer) { + // opts.markerLayer.changed() + // } if (opts?.onZoomEnd) { const zoom = map.getView().getZoom() || 0 opts.onZoomEnd(zoom) diff --git a/web/src/views/HandDevice/Home/components/composables/useMapServices.ts b/web/src/views/HandDevice/Home/components/composables/useMapServices.ts index 01181ac..3ae891c 100644 --- a/web/src/views/HandDevice/Home/components/composables/useMapServices.ts +++ b/web/src/views/HandDevice/Home/components/composables/useMapServices.ts @@ -23,13 +23,6 @@ interface ServiceInstances { fenceDrawService: FenceDrawService | null } -interface LayerRefs { - markerLayer: any - rippleLayer: any - trajectoryLayer: any - fenceLayer: any -} - export const useMapServices = () => { // 服务实例状态 const services = reactive({ @@ -42,9 +35,6 @@ export const useMapServices = () => { fenceDrawService: null }) - // 图层引用状态 - const layerRefs = ref(null) - /** * 初始化地图和图层 */ @@ -66,33 +56,16 @@ export const useMapServices = () => { services.fenceDrawService = new FenceDrawService(map) // 创建marker图层 - const markerLayer = services.markerService.createMarkerLayer(props) + services.markerService.createMarkerLayer(props) // 创建波纹图层 - const rippleLayer = services.animationService.createRippleLayer( - props.markers || [], - props.enableCluster - ) + services.animationService.createRippleLayer(props.markers || [], props.enableCluster) // 创建轨迹图层 - const trajectoryLayer = services.trajectoryService.createTrajectoryLayer() + services.trajectoryService.createTrajectoryLayer() // 创建围栏图层 - const fenceLayer = services.fenceService.createFenceLayer(props.fences || []) - - // // 添加图层到地图 - // map.addLayer(markerLayer) - map.addLayer(rippleLayer) - map.addLayer(trajectoryLayer) - map.addLayer(fenceLayer) - + services.fenceService.createFenceLayer(props.fences || []) // 初始化围栏绘制服务 services.fenceDrawService.createDrawLayer() - // 存储图层引用 - layerRefs.value = { - markerLayer, - rippleLayer, - trajectoryLayer, - fenceLayer - } return { map, popupOverlay @@ -103,18 +76,12 @@ export const useMapServices = () => { * 设置标记显示状态 */ const setMarkersVisible = (visible: boolean) => { - if (!layerRefs.value || !services.animationService) return - - const { markerLayer, rippleLayer } = layerRefs.value - if (visible) { - markerLayer.setVisible(true) - rippleLayer.setVisible(true) - services.animationService.startAnimation() + services.markerService?.show() + services.animationService?.show() } else { - markerLayer.setVisible(false) - rippleLayer.setVisible(false) - services.animationService.stopAnimation() + services.markerService?.hide() + services.animationService?.hide() } } @@ -139,9 +106,9 @@ export const useMapServices = () => { if (!services.fenceService) return if (visible) { - services.fenceService.showFences() + services.fenceService.show() } else { - services.fenceService.hideFences() + services.fenceService.hide() } } @@ -174,20 +141,11 @@ export const useMapServices = () => { * 更新标记数据 */ const updateMarkers = (markers: any[], currentProps?: MapProps) => { - if ( - services.markerService && - services.animationService && - layerRefs.value?.markerLayer && - layerRefs.value?.rippleLayer - ) { + if (services.markerService && services.animationService) { const map = services.mapService?.getMap() const enableCluster = currentProps?.enableCluster ?? true if (map) { - // 从地图中移除旧的marker layer - // map.removeLayer(layerRefs.value.markerLayer) - // map.removeLayer(layerRefs.value.rippleLayer) - console.log('updateMarkers', markers); - + console.log('updateMarkers', markers) // 更新marker service(这会创建新的layer) services.markerService.createMarkerLayer({ ...currentProps, @@ -196,17 +154,6 @@ export const useMapServices = () => { // 重新创建波纹图层 services.animationService.createRippleLayer(markers, enableCluster) - - // 获取新的layer并添加到地图 - // const newMarkerLayer = services.markerService.getMarkerLayer() - // if (newMarkerLayer && newRippleLayer) { - // // // 确保markerService有最新的地图实例 - // // services.markerService.setMap(map) - // map.addLayer(newMarkerLayer) - // map.addLayer(newRippleLayer) - // layerRefs.value.markerLayer = newMarkerLayer - // layerRefs.value.rippleLayer = newRippleLayer - // } } } } @@ -248,9 +195,6 @@ export const useMapServices = () => { services.trajectoryService = null services.fenceService = null services.fenceDrawService = null - - // 重置图层引用 - layerRefs.value = null } // 组件卸载时自动清理 @@ -261,7 +205,6 @@ export const useMapServices = () => { return { // 状态 services, - layerRefs, // 方法 diff --git a/web/src/views/HandDevice/Home/components/services/animation.service.ts b/web/src/views/HandDevice/Home/components/services/animation.service.ts index 93e000e..bfaa9f0 100644 --- a/web/src/views/HandDevice/Home/components/services/animation.service.ts +++ b/web/src/views/HandDevice/Home/components/services/animation.service.ts @@ -54,6 +54,7 @@ export class AnimationService { this.rippleLayer = new VectorLayer({ source: source, + zIndex: 1, style: (feature) => { // 检查当前缩放级别,如果缩放级别较低(聚合状态),不显示波纹 const currentZoom = this.map?.getView().getZoom() || 0 @@ -117,7 +118,14 @@ export class AnimationService { return this.rippleLayer } - + show() { + this.rippleLayer?.setVisible(true) + this.startAnimation() + } + hide() { + this.stopAnimation() + this.rippleLayer?.setVisible(false) + } /** * 启动波纹动画 */ diff --git a/web/src/views/HandDevice/Home/components/services/fence.service.ts b/web/src/views/HandDevice/Home/components/services/fence.service.ts index 1368755..dd26bb4 100644 --- a/web/src/views/HandDevice/Home/components/services/fence.service.ts +++ b/web/src/views/HandDevice/Home/components/services/fence.service.ts @@ -26,7 +26,9 @@ export class FenceService { * 创建围栏图层 */ createFenceLayer(fences: FenceData[]): VectorLayer { - + if( this.fenceLayer) { + this.map?.removeLayer(this.fenceLayer) + } this.fenceData = fences const source = new VectorSource() @@ -63,9 +65,9 @@ export class FenceService { this.fenceLayer = new VectorLayer({ source: source, - zIndex: 1 // 确保围栏在标记点下方 + zIndex: 0 // 确保围栏在标记点下方 }) - + this.map?.addLayer(this.fenceLayer) return this.fenceLayer } @@ -119,8 +121,10 @@ export class FenceService { if (coordinates.length === 0) return null // 计算围栏中心点 - const centerX = coordinates.reduce((sum, coord) => sum + coord[0], 0) / coordinates.length - const centerY = coordinates.reduce((sum, coord) => sum + coord[1], 0) / coordinates.length + // const centerX = coordinates.reduce((sum, coord) => sum + coord[0], 0) / coordinates.length + // const centerY = coordinates.reduce((sum, coord) => sum + coord[1], 0) / coordinates.length + const centerX = coordinates[0][0] + const centerY = coordinates[0][1] const labelFeature = new Feature({ geometry: new Point([centerX, centerY]), @@ -168,7 +172,7 @@ export class FenceService { /** * 显示围栏 */ - showFences(): void { + show(): void { if (this.fenceLayer) { this.fenceLayer.setVisible(true) this.isVisible = true @@ -178,7 +182,7 @@ export class FenceService { /** * 隐藏围栏 */ - hideFences(): void { + hide(): void { if (this.fenceLayer) { this.fenceLayer.setVisible(false) this.isVisible = false @@ -190,9 +194,9 @@ export class FenceService { */ toggleFences(): boolean { if (this.isVisible) { - this.hideFences() + this.hide() } else { - this.showFences() + this.show() } return this.isVisible } diff --git a/web/src/views/HandDevice/Home/components/services/marker.service.ts b/web/src/views/HandDevice/Home/components/services/marker.service.ts index 6cecb0e..65c9e0c 100644 --- a/web/src/views/HandDevice/Home/components/services/marker.service.ts +++ b/web/src/views/HandDevice/Home/components/services/marker.service.ts @@ -13,15 +13,19 @@ import { createMarkerStyle, getClusterMarkerData } from '../utils/map.utils' import { ANIMATION_CONFIG } from '../constants/map.constants' export class MarkerService { private map: Map | null = null - private markerLayer: VectorLayer | null = null + markerLayer: VectorLayer | null = null private vectorSource: VectorSource constructor(map: Map) { this.map = map this.vectorSource = new VectorSource() - } - + show(){ + this.markerLayer?.setVisible(true) + } + hide(){ + this.markerLayer?.setVisible(false) + } /** * 创建标记图层 */ @@ -73,14 +77,14 @@ export class MarkerService { const shouldForceSingleMark = () => { if (!props.forceSingleMark || !this.map) return false const currentZoom = this.map.getView().getZoom() - console.log('currentZoom',currentZoom) + console.log('currentZoom', currentZoom) // return currentZoom && currentZoom >= props.forceSingleMark return currentZoom && currentZoom >= ANIMATION_CONFIG.clusterThreshold } // console.log('createMarkerLayerFromProps shouldForceSingleMark', shouldForceSingleMark()) // 如果启用聚合且不强制使用单个marker模式 - console.log('shouldForceSingleMark()',shouldForceSingleMark()); - + console.log('shouldForceSingleMark()', shouldForceSingleMark()) + if (props.enableCluster && !shouldForceSingleMark()) { const clusterSource = new Cluster({ source: this.vectorSource, @@ -89,6 +93,7 @@ export class MarkerService { this.markerLayer = new VectorLayer({ source: clusterSource, + zIndex: 1, style: (feature) => { const features = feature.get('features') @@ -112,12 +117,18 @@ export class MarkerService { // console.log('基础marker') this.markerLayer = new VectorLayer({ - source: this.vectorSource + source: this.vectorSource, + zIndex: 1, + renderOrder: (a, b) => { + // console.log('renderOrder',a,b.get('markerData').time); + + // 按priority属性降序排列 + return b.get('markerData').time - a.get('markerData').time + } }) } this.map?.addLayer(this.markerLayer) - return this.markerLayer } diff --git a/web/src/views/HandDevice/Home/index.vue b/web/src/views/HandDevice/Home/index.vue index f3bd2a9..079fafc 100644 --- a/web/src/views/HandDevice/Home/index.vue +++ b/web/src/views/HandDevice/Home/index.vue @@ -2,7 +2,7 @@ { return await getLastDetectorData().then((res: HandDetectorData[]) => { res = res.filter((i) => i.enableStatus === 1) var res2 = res.map((i) => { - console.log([i.longitude, i.latitude]); - + console.log([i.longitude, i.latitude]) + return { ...i, coordinates: [i.longitude, i.latitude], @@ -102,7 +102,10 @@ const getFences = async () => { } onMounted(() => { getMarkers() - getFences() + setTimeout(() => { + getFences() + }, 2000) + // getFences() console.log('定时器,暂时关掉,太烦了') // getDataTimer.value = setInterval(() => { @@ -137,6 +140,7 @@ onUnmounted(() => { .top-panel__left { // flex: 0 0 260px; + width: 260px; background: rgba(255, 255, 255, 0.7); border: 1px solid rgba(0, 0, 0, 0.06); border-radius: 10px; @@ -144,39 +148,27 @@ onUnmounted(() => { height: 100%; display: flex; align-items: center; - gap: 8px; + padding: 10px; .search-group { - display: flex; - align-items: center; - gap: 8px; - - // .search-type { - // flex: 0 0 120px; - // } - .search-input { - width: 220px; + width: 100%; } } } .top-panel__center { flex: 1 1 auto; - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 12px; + display: flex; align-items: center; .data_item { + width: 200px; background: rgba(255, 255, 255, 0.9); border: 1px solid rgba(0, 0, 0, 0.06); border-radius: 8px; padding: 12px 12px; - // min-height: 56px; - // display: flex; - // flex-direction: column; - // justify-content: center; + margin-right: 12px; .data_item__title { font-size: 14px; @@ -204,7 +196,7 @@ onUnmounted(() => { display: flex; align-items: center; gap: 8px; - flex: 0 0 auto; + background: rgba(255, 255, 255, 0.85); border: 1px solid rgba(0, 0, 0, 0.06); padding: 12px 12px; @@ -217,59 +209,27 @@ onUnmounted(() => { color: #606266; font-size: 14px; } - - .normal-legend { + .normal-legend, + .alarm1-legend, + .alarm2-legend { padding: 4px 8px; border-radius: 999px; color: #fff; font-size: 12px; line-height: 1; + } + + .normal-legend { background: #67c23a; } .alarm1-legend { - padding: 4px 8px; - border-radius: 999px; - color: #fff; - font-size: 12px; - line-height: 1; background: #e6a23c; } .alarm2-legend { - padding: 4px 8px; - border-radius: 999px; - color: #fff; - font-size: 12px; - line-height: 1; background: #f56c6c; } } } - -@media (max-width: 992px) { - .top-panel { - .top-panel__left { - flex: 1 1 100%; - } - - .top-panel__center { - flex: 1 1 100%; - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .top-panel__right { - flex: 1 1 100%; - justify-content: flex-start; - } - } -} - -@media (max-width: 600px) { - .top-panel { - .top-panel__center { - grid-template-columns: 1fr; - } - } -}