From 1ff99ae4be66216de9d9c5e3573586dff13771cb Mon Sep 17 00:00:00 2001 From: xh <11675084@qq.com> Date: Wed, 22 Oct 2025 18:59:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E7=BC=A9=E6=94=BE=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HandDevice/Home/components/OpenLayerMap.vue | 63 +++--- .../views/HandDevice/Home/components/TopPanel.vue | 149 ++++++++++++++ .../Home/components/composables/useMapEvents.ts | 56 +++--- .../Home/components/composables/useMapServices.ts | 32 ++- .../Home/components/composables/useMapWatchers.ts | 32 ++- .../Home/components/services/map.service.ts | 29 ++- .../Home/components/services/marker.service.ts | 9 +- .../HandDevice/Home/components/types/map.types.ts | 10 - web/src/views/HandDevice/Home/index.vue | 215 ++++++--------------- 9 files changed, 321 insertions(+), 274 deletions(-) create mode 100644 web/src/views/HandDevice/Home/components/TopPanel.vue diff --git a/web/src/views/HandDevice/Home/components/OpenLayerMap.vue b/web/src/views/HandDevice/Home/components/OpenLayerMap.vue index 770186a..027e4df 100644 --- a/web/src/views/HandDevice/Home/components/OpenLayerMap.vue +++ b/web/src/views/HandDevice/Home/components/OpenLayerMap.vue @@ -44,7 +44,6 @@ diff --git a/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts b/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts index e1dbaca..802b3b9 100644 --- a/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts +++ b/web/src/views/HandDevice/Home/components/composables/useMapEvents.ts @@ -3,25 +3,28 @@ */ import type { Map } from 'ol' import type Overlay from 'ol/Overlay' +import type FeatureLike from 'ol/Feature' +import { MarkerData, FenceData } from '../types/map.types' + import dayjs from 'dayjs' import { fromLonLat } from 'ol/proj' import { TrajectoryService } from '../services/trajectory.service' import { PopupService } from '../services/popup.service' import { DICT_TYPE, getDictLabel } from '@/utils/dict' interface PopupContentGenerator { - handleTrajectoryPoint: (feature: any) => string - handleTrajectoryLine: (feature: any) => string - handleFence: (feature: any) => string - handleMarker: (feature: any) => string + handleTrajectoryPoint: (feature: FeatureLike) => string + handleTrajectoryLine: (feature: FeatureLike) => string + handleFence: (feature: FeatureLike) => string + handleMarker: (feature: FeatureLike) => string } export const useMapEvents = () => { // 创建弹窗内容生成器 const createPopupContentGenerator = ( - trajectoryService: any, - popupService: any + trajectoryService: TrajectoryService, + popupService: PopupService ): PopupContentGenerator => ({ - handleTrajectoryPoint: (feature: any): string => { + handleTrajectoryPoint: (feature: FeatureLike): string => { const timeText = feature.get('timeText') || '' const trajectoryId = feature.get('trajectoryId') || '' const timestamp = feature.get('timestamp') @@ -40,7 +43,7 @@ export const useMapEvents = () => { ` }, - handleTrajectoryLine: (feature: any): string => { + handleTrajectoryLine: (feature: FeatureLike): string => { const deviceId = feature.get('deviceId') || '' const deviceName = trajectoryService?.getTrajectoryData().find((t) => t.deviceId === deviceId)?.name || @@ -53,8 +56,8 @@ export const useMapEvents = () => { ` }, - handleFence: (feature: any): string => { - const fenceData = feature.get('fenceData') + handleFence: (feature: FeatureLike): string => { + const fenceData: FenceData = feature.get('fenceData') const statusText = getDictLabel(DICT_TYPE.HAND_DETECTOR_FENCE_STATUS, fenceData.status) const typeText = getDictLabel(DICT_TYPE.HAND_DETECTOR_FENCE_TYPE, fenceData.type) @@ -70,7 +73,7 @@ export const useMapEvents = () => { ` }, - handleMarker: (feature: any): string => { + handleMarker: (feature: FeatureLike): string => { return popupService?.handlePopupContent(feature) || '' } }) @@ -85,12 +88,16 @@ export const useMapEvents = () => { popupService: PopupService | null, opts?: { isDrawing?: () => boolean - onMarkerClick?: (markerData: any) => void + onMarkerClick?: (markerData: MarkerData) => void onZoomEnd?: (zoom: number) => void // markerLayer?: any // refreshMarkerStyles?: () => void } ) => { + if (!trajectoryService || !popupService) { + return + } + const popupGenerator = createPopupContentGenerator(trajectoryService, popupService) // 鼠标悬停事件 @@ -101,7 +108,7 @@ export const useMapEvents = () => { return } // - const feature = map.forEachFeatureAtPixel(event.pixel, (feature: any) => feature) + const feature = map.forEachFeatureAtPixel(event.pixel, (feature: FeatureLike) => feature) if (feature) { map.getTargetElement().style.cursor = 'pointer' @@ -118,7 +125,7 @@ export const useMapEvents = () => { if (opts?.isDrawing && opts.isDrawing()) { return } - const feature = map.forEachFeatureAtPixel(event.pixel, (feature: any) => feature) + const feature = map.forEachFeatureAtPixel(event.pixel, (feature: FeatureLike) => feature) if (feature) { handleFeatureClick(feature, map, opts) } @@ -127,7 +134,7 @@ export const useMapEvents = () => { // 地图移动结束事件(包括放缩) const handleMoveEnd = () => { // console.log('handleMoveEnd'); - + // OpenLayers的Cluster会自动重新计算聚合,只需要刷新样式 // if (opts?.markerLayer) { // opts.markerLayer.changed() @@ -142,7 +149,6 @@ export const useMapEvents = () => { map.on('click', handleClick) map.on('moveend', handleMoveEnd) - return { destroy: () => { map.un('pointermove', handlePointerMove) @@ -157,7 +163,7 @@ export const useMapEvents = () => { */ const showPopup = ( event: any, - feature: any, + feature: FeatureLike, popupOverlay: Overlay | null, popupGenerator: PopupContentGenerator ) => { @@ -202,8 +208,8 @@ export const useMapEvents = () => { * 处理特征点击事件 */ const handleFeatureClick = ( - feature: any, - map: any, + feature: FeatureLike, + map: Map, opts?: { onMarkerClick?: (markerData: any) => void } ) => { const featureType = feature.get('type') @@ -226,8 +232,8 @@ export const useMapEvents = () => { * 处理标记点击事件 */ const handleMarkerClick = ( - feature: any, - map: any, + feature: FeatureLike, + map: Map, opts?: { onMarkerClick?: (markerData: any) => void } ) => { const markerData = feature.get('markerData') @@ -245,7 +251,7 @@ export const useMapEvents = () => { } } else if (markerData) { // 处理非聚合的单个标记点击 - animateToCoordinate(markerData.coordinates, map, 15) + animateToCoordinate(markerData.coordinates, map, 17) opts?.onMarkerClick?.(markerData) } } @@ -253,9 +259,9 @@ export const useMapEvents = () => { /** * 处理聚合标记点击 */ - const handleClusterClick = (features: any[], map: any) => { + const handleClusterClick = (features: FeatureLike[], map: Map) => { // 计算聚合标记的中心点 - const coordinates = features.map((f: any) => f.get('markerData').coordinates) + const coordinates = features.map((f: FeatureLike) => f.get('markerData').coordinates) const centerLon = coordinates.reduce((sum: number, coord: any) => sum + coord[0], 0) / coordinates.length const centerLat = @@ -267,7 +273,7 @@ export const useMapEvents = () => { /** * 动画移动到指定坐标 */ - const animateToCoordinate = (coordinates: [number, number], map: any, zoom: number) => { + const animateToCoordinate = (coordinates: [number, number], map: Map, zoom: number) => { const view = map.getView() view.animate({ diff --git a/web/src/views/HandDevice/Home/components/composables/useMapServices.ts b/web/src/views/HandDevice/Home/components/composables/useMapServices.ts index 3ae891c..dd01e63 100644 --- a/web/src/views/HandDevice/Home/components/composables/useMapServices.ts +++ b/web/src/views/HandDevice/Home/components/composables/useMapServices.ts @@ -4,7 +4,7 @@ import type { Map } from 'ol' import { ref, onUnmounted, reactive } from 'vue' -import type { MapProps, MapInstance } from '../types/map.types' +import type { MapProps } from '../types/map.types' import { MapService } from '../services/map.service' import { MarkerService } from '../services/marker.service' import { AnimationService } from '../services/animation.service' @@ -25,7 +25,7 @@ interface ServiceInstances { export const useMapServices = () => { // 服务实例状态 - const services = reactive({ + const services: ServiceInstances = { mapService: null, markerService: null, animationService: null, @@ -33,27 +33,26 @@ export const useMapServices = () => { trajectoryService: null, fenceService: null, fenceDrawService: null - }) + } /** * 初始化地图和图层 */ - const initializeMapAndLayers = (mapInstance: MapService, props: MapProps) => { + const initMap = (mapContainerRef: HTMLElement, props: MapProps) => { // 初始化地图 - const map = mapInstance.map - const popupOverlay = mapInstance.popupOverlay - if (!map) { + const mapService = new MapService(mapContainerRef, props) + if (!mapService.map) { throw new Error('Map instance is null') } // 重新初始化服务,确保markerService有地图实例 - services.mapService = mapInstance - services.markerService = new MarkerService(map) - services.animationService = new AnimationService(map) + services.mapService = mapService + services.markerService = new MarkerService(mapService.map) + services.animationService = new AnimationService(mapService.map) services.popupService = new PopupService() - services.trajectoryService = new TrajectoryService(map) - services.fenceService = new FenceService(map) - services.fenceDrawService = new FenceDrawService(map) + services.trajectoryService = new TrajectoryService(mapService.map) + services.fenceService = new FenceService(mapService.map) + services.fenceDrawService = new FenceDrawService(mapService.map) // 创建marker图层 services.markerService.createMarkerLayer(props) @@ -65,11 +64,6 @@ export const useMapServices = () => { services.fenceService.createFenceLayer(props.fences || []) // 初始化围栏绘制服务 services.fenceDrawService.createDrawLayer() - - return { - map, - popupOverlay - } } /** @@ -208,7 +202,7 @@ export const useMapServices = () => { // 方法 - initializeMapAndLayers, + initMap, setMarkersVisible, setTrajectoriesVisible, setFencesVisible, diff --git a/web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts b/web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts index c49bab5..27416d9 100644 --- a/web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts +++ b/web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts @@ -15,7 +15,6 @@ interface WatchOptions { isDrawing: boolean, onComplete?: (coordinates: [number, number][]) => void ) => void - updateMarkers: (markers: any[]) => void markers: any[] } @@ -29,7 +28,7 @@ export const useMapWatchers = (options: WatchOptions) => { setTrajectoriesVisible, setFencesVisible, toggleFenceDrawing, - updateMarkers, + markers } = options @@ -93,18 +92,18 @@ export const useMapWatchers = (options: WatchOptions) => { /** * 设置标记数据变化监听器 */ - const setupMarkersDataWatcher = () => { - return watch( - markers, - (newMarkers) => { - if (newMarkers && newMarkers.length > 0) { - console.log('Markers data changed, updating markers:', newMarkers.length) - updateMarkers(newMarkers) - } - }, - { deep: true, immediate: false } - ) - } + // const setupMarkersDataWatcher = () => { + // return watch( + // markers, + // (newMarkers = []) => { + // // if (newMarkers && newMarkers.length > 0) { + // console.log('Markers data changed, updating markers:', newMarkers.length) + // updateMarkers(newMarkers) + // // } + // }, + // { deep: true, immediate: false } + // ) + // } /** * 初始化所有监听器 @@ -114,8 +113,7 @@ export const useMapWatchers = (options: WatchOptions) => { setupMarkersWatcher(), setupTrajectoriesWatcher(), setupFencesWatcher(), - setupDrawFencesWatcher(), - setupMarkersDataWatcher() + setupDrawFencesWatcher() ] // 返回清理函数 @@ -133,7 +131,7 @@ export const useMapWatchers = (options: WatchOptions) => { setupTrajectoriesWatcher, setupFencesWatcher, setupDrawFencesWatcher, - setupMarkersDataWatcher, + setupAllWatchers } } diff --git a/web/src/views/HandDevice/Home/components/services/map.service.ts b/web/src/views/HandDevice/Home/components/services/map.service.ts index a34467e..5d4ac73 100644 --- a/web/src/views/HandDevice/Home/components/services/map.service.ts +++ b/web/src/views/HandDevice/Home/components/services/map.service.ts @@ -2,11 +2,14 @@ * 地图服务类 */ import { Map, View } from 'ol' + import { Tile as TileLayer } from 'ol/layer' import { OSM, XYZ } from 'ol/source' -import { fromLonLat } from 'ol/proj' +import { fromLonLat,transformExtent } from 'ol/proj' +import { boundingExtent } from 'ol/extent' + import Overlay from 'ol/Overlay' -import type { MapProps, MapInstance } from '../types/map.types' +import type { MapProps } from '../types/map.types' export class MapService { map: Map | null = null @@ -35,14 +38,6 @@ export class MapService { minZoom: props.minZoom }) }) - - // return { - // map: this.map, - // tileLayer: this.tileLayer, - // markerLayer: null, - // rippleLayer: null, - // popupOverlay: this.popupOverlay - // } } /** @@ -89,6 +84,20 @@ export class MapService { } /** + * 调整地图视野以包含所有标记 + */ + fitToMarkers(lonLats: number[][]): void { + if (!this.map || lonLats.length === 0) return + + const extent = boundingExtent(lonLats) + const webMercatorExtent = transformExtent(extent, 'EPSG:4326', 'EPSG:3857') + + this.map.getView().fit(webMercatorExtent, { + padding: [80, 50, 50, 50], + maxZoom: 15 + }) + } + /** * 获取地图实例 */ getMap(): Map | null { 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 65c9e0c..5e4a823 100644 --- a/web/src/views/HandDevice/Home/components/services/marker.service.ts +++ b/web/src/views/HandDevice/Home/components/services/marker.service.ts @@ -8,6 +8,7 @@ import { Feature } from 'ol' import { Point } from 'ol/geom' import { Style } from 'ol/style' import { fromLonLat } from 'ol/proj' + import type { MarkerData, MapProps } from '../types/map.types' import { createMarkerStyle, getClusterMarkerData } from '../utils/map.utils' import { ANIMATION_CONFIG } from '../constants/map.constants' @@ -30,7 +31,7 @@ export class MarkerService { * 创建标记图层 */ createMarkerLayer(props: MapProps): VectorLayer { - console.log('createMarkerLayer') + // console.log('createMarkerLayer') if (this.markerLayer) { this.map?.removeLayer(this.markerLayer) } @@ -77,13 +78,13 @@ 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({ @@ -122,7 +123,7 @@ export class MarkerService { renderOrder: (a, b) => { // console.log('renderOrder',a,b.get('markerData').time); - // 按priority属性降序排列 + // 按xxx属性降序排列 return b.get('markerData').time - a.get('markerData').time } }) diff --git a/web/src/views/HandDevice/Home/components/types/map.types.ts b/web/src/views/HandDevice/Home/components/types/map.types.ts index 53520eb..efb50a7 100644 --- a/web/src/views/HandDevice/Home/components/types/map.types.ts +++ b/web/src/views/HandDevice/Home/components/types/map.types.ts @@ -168,13 +168,3 @@ export interface TrajectoryPlayState { /** 播放结束时间 */ endTime?: number } - -// 地图实例接口 -export interface MapInstance { - map: Map - tileLayer: TileLayer - markerLayer: any - rippleLayer: any - popupOverlay: Overlay|null - trajectoryLayer?: any -} diff --git a/web/src/views/HandDevice/Home/index.vue b/web/src/views/HandDevice/Home/index.vue index 079fafc..b760ba3 100644 --- a/web/src/views/HandDevice/Home/index.vue +++ b/web/src/views/HandDevice/Home/index.vue @@ -1,67 +1,72 @@