Compare commits

...

2 Commits

  1. 27
      web/src/components/VirtualCollapsePanel/index.vue
  2. 22
      web/src/views/HandDevice/Home/components/OpenLayerMap.vue
  3. 36
      web/src/views/HandDevice/Home/components/composables/useMapServices.ts
  4. 15
      web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts
  5. 6
      web/src/views/HandDevice/Home/components/services/map.service.ts
  6. 73
      web/src/views/HandDevice/Home/components/services/marker.service.ts
  7. 2
      web/src/views/HandDevice/Home/components/utils/map.utils.ts
  8. 36
      web/src/views/HandDevice/Home/index.vue

27
web/src/components/VirtualCollapsePanel/index.vue

@ -37,7 +37,7 @@
</DynamicScroller>
</template>
<script setup lang="ts">
import { ref, computed, onBeforeUnmount, onDeactivated, useTemplateRef } from 'vue'
import { ref, computed, onBeforeUnmount, onDeactivated, useTemplateRef, nextTick } from 'vue'
import type { PropType } from 'vue'
import { ArrowRight } from '@element-plus/icons-vue'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
@ -86,8 +86,10 @@ const list = computed(() => {
const scrollbarRef = useTemplateRef('scrollbarRef')
const scrollbarScrollTop = ref(0)
function onScroll(e) {
// console.log('scroll-end',e);
scrollbarScrollTop.value = e.target.scrollTop
emit('scroll', e)
// emit('scroll', e)
}
/**
* 切换折叠面板展开状态
@ -120,11 +122,20 @@ function scrollToIndex(index) {
if (index < 0 || index >= list.value.length) return
activeItem.value = list.value[index]
const top = props.minItemSize * index
//
cancelAnimationFrame(AnimationId.value as number)
scrollTo(scrollbarScrollTop.value, top, scrollbarScrollTop.value)
// const top = props.minItemSize * index
// console.log('top', top)
// //
// cancelAnimationFrame(AnimationId.value as number)
// scrollTo(scrollbarScrollTop.value, top, scrollbarScrollTop.value)
// nextTick(() => {
// // scrollbarRef.value?.scrollToItem(index)
// scrollbarRef.value?.$refs?.scroller?.scrollToPosition(top)
// })
setTimeout(() => {
scrollbarRef.value?.$refs?.scroller?.scrollToPosition(top)
}, 200)
}
/**
* 滚动到指定位置
@ -143,7 +154,7 @@ function scrollTo(from: number, to: number, current: number) {
if (scrollbarScrollTop.value === to) {
return
}
const speed = (to - from) / 30
const speed = (to - from) / props.minItemSize
if (speed < 0) {
if (current <= to) {
@ -203,7 +214,7 @@ defineExpose({
// collapse-content
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s ease;
transition: all 0.1s ease;
}
.fade-enter-from,

22
web/src/views/HandDevice/Home/components/OpenLayerMap.vue

@ -93,8 +93,7 @@ const {
setTrajectoriesVisible,
setFencesVisible,
toggleFenceDrawing,
clearFenceDrawLayer,
updateMarkers
clearFenceDrawLayer
} = useMapServices()
const {
@ -175,22 +174,21 @@ const init = () => {
{
isDrawing: () => !!services.fenceDrawService?.isCurrentlyDrawing?.(),
onMarkerClick: (marker: MarkerData) => {
console.log('marker clicked', marker)
// console.log('marker clicked', marker)
emit('on-click-marker', marker)
},
onClusterClick: (features: FeatureLike[]) => {
console.log('onClusterClick', features)
// console.log('onClusterClick', features)
const markerCoords = features.map((feature) => feature.get('markerData').coordinates)
services.mapService?.fitToMarkers(markerCoords)
},
onZoomEnd: (zoom: number) => {
console.log('onZoomEnd', zoom)
//
// services.markerService?.createMarkerLayerFromProps(props)
services.markerService?.setClusterDistance()
services.markerService?.getSinglePointsInView()
// console.log('marker', marker)
}
}
)
@ -252,6 +250,7 @@ const setCenter = (coords: [number, number]) => {
if (isMapInitialized) {
services.mapService?.setCenter(coords)
services.mapService?.setZoom(17)
}
}
/**
@ -290,25 +289,24 @@ const refreshFences = () => {
watch(
() => props.markers,
(newMarkers, oldMarkers) => {
updateMarkers(newMarkers, props)
if (newMarkers.length !== oldMarkers.length) {
console.log('markers changed', newMarkers, oldMarkers)
services.markerService?.updateData(newMarkers)
if (oldMarkers == undefined || newMarkers.length !== oldMarkers.length) {
fitToMarkers()
}
},
{ deep: true, immediate: false }
{ deep: false, immediate: true }
)
watch(
() => props.fences,
() => {
refreshFences()
},
{ deep: true, immediate: false }
{ deep: false, immediate: false }
)
onMounted(() => {
setTimeout(() => {
init()
}, 100)
})
defineExpose({ refreshFences, fitToMarkers, setCenter, showTrajectory })

36
web/src/views/HandDevice/Home/components/composables/useMapServices.ts

@ -54,8 +54,6 @@ export const useMapServices = () => {
services.fenceService = new FenceService(mapService.map)
services.fenceDrawService = new FenceDrawService(mapService.map)
// 创建marker图层
services.markerService.createMarkerLayer(props)
// 创建轨迹图层
services.trajectoryService.createTrajectoryLayer()
// 创建围栏图层
@ -70,10 +68,8 @@ export const useMapServices = () => {
const setMarkersVisible = (visible: boolean) => {
if (visible) {
services.markerService?.show()
} else {
services.markerService?.hide()
}
}
@ -129,34 +125,6 @@ export const useMapServices = () => {
}
}
/**
*
*/
const updateMarkers = (markers: any[], currentProps?: MapProps) => {
if (services.markerService) {
const map = services.mapService?.getMap()
if (map) {
// console.log('updateMarkers', markers)
// 更新marker service(这可能会创建新的layer)
console.time('createMarkerLayer');
services.markerService.createMarkerLayer({
...currentProps,
markers
})
console.timeEnd('createMarkerLayer');
}
}
}
/**
*
*/
const refreshMarkerStyles = () => {
if (services.markerService) {
services.markerService.refreshStyles()
}
}
/**
*
@ -204,8 +172,8 @@ export const useMapServices = () => {
setFencesVisible,
toggleFenceDrawing,
clearFenceDrawLayer,
updateMarkers,
refreshMarkerStyles,
destroyServices
}
}

15
web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts

@ -86,21 +86,6 @@ 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 }
// )
// }
/**
*

6
web/src/views/HandDevice/Home/components/services/map.service.ts

@ -108,7 +108,11 @@ export class MapService {
center = fromLonLat(center)
this.map.getView().setCenter(center)
}
// 设置缩放级别
setZoom(zoom: number): void {
if (!this.map) return
this.map.getView().setZoom(zoom)
}
/**
*
*/

73
web/src/views/HandDevice/Home/components/services/marker.service.ts

@ -13,14 +13,13 @@ import { AnimationService } from './animation.service'
import type { MarkerData, MapProps } from '../types/map.types'
import { createMarkerStyle, getClusterMarkerData, getStatusColor } from '../utils/map.utils'
import { ANIMATION_CONFIG } from '../constants/map.constants'
// 防抖
import { debounce } from 'lodash-es'
export class MarkerService {
private map: Map | null = null
markerLayer: VectorLayer<VectorSource | Cluster> | null = null
// 当前图层模式(single或cluster聚合):避免重复创建图层
private currentLayerMode: 'single' | 'cluster' | '' = ''
markerLayer: VectorLayer<Cluster> | null = null
private vectorSource: VectorSource
private animationService: AnimationService | null = null
@ -28,6 +27,7 @@ export class MarkerService {
this.map = map
this.vectorSource = new VectorSource()
this.animationService = new AnimationService(map)
this.createMarkerLayer()
}
show() {
this.markerLayer?.setVisible(true)
@ -37,27 +37,14 @@ export class MarkerService {
this.markerLayer?.setVisible(false)
this.animationService?.hide()
}
/**
*
*/
createMarkerLayer = debounce((props: MapProps) => {
console.time('updateData')
this.updateData(props)
console.timeEnd('updateData')
console.time('createMarkerLayerFromProps')
this.createMarkerLayerFromProps(props)
console.timeEnd('createMarkerLayerFromProps')
}, 1000)
/**
*
*/
updateData(props: MapProps): void {
updateData(markers: MarkerData[]): void {
this.animationService?.clear()
this.vectorSource.clear()
// 添加标记
const markers = props.markers || []
// debugger
const features: Feature<Point>[] = []
markers.forEach((marker) => {
const feature = new Feature({
@ -107,6 +94,7 @@ export class MarkerService {
// 获取聚合图层的源
const clusterSource = clusterLayer.getSource()
if (!clusterSource) return
// 获取当前视图范围
const view = map.getView()
@ -127,7 +115,7 @@ export class MarkerService {
}
})
this.animationService?.addAll(singlePoints || [])
return singlePoints
}, 300)
/**
@ -135,25 +123,9 @@ export class MarkerService {
*
*/
// : VectorLayer<VectorSource | Cluster>
createMarkerLayerFromProps(props: MapProps) {
// console.log('createMarkerLayerFromProps')
createMarkerLayer() {
let newLayer: VectorLayer<Cluster> | null = null
// this.updateData(props)
// 检查是否应该强制使用单个marker模式
const shouldForceSingleMark = () => {
if (!props.forceSingleMark || !this.map) return false
const currentZoom = this.map.getView().getZoom()
return currentZoom && currentZoom >= ANIMATION_CONFIG.clusterThreshold
}
let newLayer: VectorLayer<VectorSource | Cluster> | null = null
// 如果启用聚合且不强制使用单个marker模式
if (props.enableCluster && !shouldForceSingleMark()) {
if (this.currentLayerMode === 'cluster') return
this.currentLayerMode = 'cluster'
// this.animationService?.clear()
console.log('聚合图层')
const clusterSource = new Cluster({
@ -161,7 +133,6 @@ export class MarkerService {
distance: 20 // 单位是像素
})
newLayer = new VectorLayer({
source: clusterSource,
zIndex: 1,
@ -189,27 +160,6 @@ export class MarkerService {
}
}
})
} else {
if (this.currentLayerMode === 'single') return
this.currentLayerMode = 'single'
console.log('基础marker图层')
newLayer = new VectorLayer({
source: this.vectorSource,
zIndex: 1,
renderOrder: (a, b) => {
// 按xxx属性排列
return b.get('markerData').statusPriority - a.get('markerData').statusPriority
}
})
}
if (this.markerLayer) {
const isVisible = this.markerLayer?.getVisible() || false
newLayer.setVisible(isVisible) // 新图层保持当前可见状态
this.map?.removeLayer(this.markerLayer)
}
this.markerLayer = newLayer
this.map?.addLayer(this.markerLayer)
@ -230,8 +180,7 @@ export class MarkerService {
destroy(): void {
this.markerLayer = null
this.animationService?.destroy()
// this.currentProps = null
this.createMarkerLayer.cancel()
this.map = null
}
}

2
web/src/views/HandDevice/Home/components/utils/map.utils.ts

@ -158,7 +158,7 @@ export const createMarkerStyle = (
styleCache[key]= new Style({
image: new Circle({
radius: Math.min(20 + clusterSize, 40),
radius: Math.min(20 + clusterSize/4, 40),
fill: new Fill({
color: color + '80' // 添加透明度
}),

36
web/src/views/HandDevice/Home/index.vue

@ -30,10 +30,7 @@
<template #header="{ item }">
<div class="marker-item">
<div class="flex-1 text-13px"> {{ item.name }}</div>
<div
class="text-12px pr-1"
:style="{ color: item.statusColor }"
>
<div class="text-12px pr-1" :style="{ color: item.statusColor }">
{{ item.statusLabel }}
</div>
</div>
@ -113,7 +110,7 @@ import { MarkerData, FenceData } from './components/types/map.types'
import { useHandDetectorStore } from '@/store/modules/handDetector'
import { ElMessage, ElScrollbar } from 'element-plus'
import dayjs, { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import { getDistance } from 'ol/sphere'
import { shallowRef } from 'vue'
const componentsIsActive = ref(false)
@ -152,7 +149,6 @@ const filterMarkers = computed(() => {
})
}
if (selectStatus.value.length !== 0) {
arr = arr.filter((item) => {
// console.log('selectStatus', selectStatus.value,item.statusStr);
@ -162,13 +158,11 @@ const filterMarkers = computed(() => {
if (item.statusStr == 'gasStatus_2' || item.statusStr == 'gasStatus_1') {
return selectStatus.value.includes('alarm')
}
// if (item.statusStr == 'fenceStatus_1') {
// return selectStatus.value.includes('fenceStatus_1')
// }
return selectStatus.value.includes(item.statusStr)
})
}
// console.log('markers.value', markers.value)
return arr
})
@ -176,9 +170,9 @@ const filterMarkers = computed(() => {
const filterMarkers2 = function getFilterMarkers2() {
var arr: MarkerData[] = []
var nowTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
for (let i = 0; i < 10000; i++) {
const lon= 80+Math.random()*20
const lat= 30+Math.random()*20
for (let i = 0; i < 50000; i++) {
const lon = 100 + Math.random() * 2
const lat = 30 + Math.random() * 2
arr.push({
id: i + 1,
sn: '867989072728120',
@ -227,12 +221,9 @@ const filterMarkers2 = function getFilterMarkers2() {
const getMarkers = async () => {
console.log('getMarkers')
return await getLastDetectorData().then((res: HandDetectorData[]) => {
console.time('getLastDetectorData');
res = res.filter((i) => i.enableStatus === 1)
var res2 = res
.map((i) => {
// console.log([i.longitude, i.latitude])
let statusStr = getHighestPriorityStatus({
gasStatus: i.gasStatus, //
batteryStatus: i.batteryStatus, //
@ -252,13 +243,13 @@ const getMarkers = async () => {
}
})
.sort((a, b) => a.statusPriority - b.statusPriority)
console.timeEnd('getLastDetectorData');
markers.value = res2
})
}
const getFences = async () => {
return await handDetectorStore.getAllFences().then((res) => {
// console.log('getFences', res)
let fencesData = res
.map((i) => {
return {
@ -272,7 +263,6 @@ const getFences = async () => {
}
//
function setCenter(item: MarkerData) {
console.log('setCenter', item)
if (item.longitude && item.latitude) {
mapRef.value?.setCenter([item.longitude || 0, item.latitude || 0])
}
@ -396,13 +386,11 @@ async function showTrajectory(item: MarkerData) {
const historicalCurveRef = ref<InstanceType<typeof HistoricalCurve>>()
// 线
function onClickHistoricalCurve(item: MarkerData) {
// console.log('onClickHistoricalCurve', item)
historicalCurveRef.value?.openDrawer(toRaw(item))
}
//
function onClickTrajectory(item: MarkerData) {
console.log('onClickTrajectory', item)
trajectoryTimeRange.value = [dayjs().subtract(1, 'hour').valueOf(), dayjs().valueOf()]
showTrajectory(item)
}
@ -410,15 +398,13 @@ function onClickTrajectory(item: MarkerData) {
const scrollbarRef = useTemplateRef<InstanceType<typeof VirtualCollapsePanel>>('scrollbarRef')
//
function onClickMarker(markerItem: MarkerData) {
console.log('onClickMarker', markerItem)
var findIndex = filterMarkers.value.findIndex((item) => item.id === markerItem.id)
if (findIndex === -1) {
return
}
setTimeout(() => {
// console.log('findIndex', findIndex, filterMarkers.value[findIndex])
scrollbarRef.value?.scrollToIndex(findIndex)
}, 300)
}
onMounted(() => {

Loading…
Cancel
Save