Browse Source

聚合数量大于5时转为统计

master
xh 2 days ago
parent
commit
fd293ad677
  1. 103
      web/src/views/HandDevice/Home/components/OpenLayerMap.vue
  2. 57
      web/src/views/HandDevice/Home/components/utils/map.utils.ts
  3. 22
      web/src/views/HandDevice/Home/index.vue

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

@ -25,22 +25,7 @@
@time-range-change="setTrajectoryTimeRangeCustom" @time-range-change="setTrajectoryTimeRangeCustom"
/> />
<div v-if="panelVisible" class="info-panel">
<div class="info-panel__header">
<span class="info-panel__title">设备详情</span>
<button class="info-panel__close" @click="panelVisible = false">×</button>
</div>
<div class="info-panel__body">
<div v-if="selectedMarker">
<div class="info-panel__name">{{ selectedMarker.name }}</div>
<div class="info-panel__coord"
>坐标{{ selectedMarker.coordinates[0].toFixed(6) }},
{{ selectedMarker.coordinates[1].toFixed(6) }}</div
>
</div>
<div v-else class="info-panel__empty">未选择设备</div>
</div>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -79,6 +64,7 @@ const props = withDefaults(defineProps<MapProps>(), {
}) })
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'on-click-marker', marker: MarkerData): void
(e: 'fence-draw-complete', coordinates: [number, number][]): void (e: 'fence-draw-complete', coordinates: [number, number][]): void
(e: 'refresh-fences'): void (e: 'refresh-fences'): void
(e: 'time-range-change', range: { startTime: number; endTime: number }): void (e: 'time-range-change', range: { startTime: number; endTime: number }): void
@ -90,10 +76,6 @@ const showFences = ref(props.showFences)
const showDrawFences = ref(false) const showDrawFences = ref(false)
const mapContainerRef = ref<HTMLElement | null>(null) const mapContainerRef = ref<HTMLElement | null>(null)
//
const panelVisible = ref(false)
const selectedMarker = ref<MarkerData | null>(null)
/** /**
gasStatus 气体报警状态 gasStatus 气体报警状态
@ -197,8 +179,7 @@ const init = () => {
onMarkerClick: (marker: MarkerData) => { onMarkerClick: (marker: MarkerData) => {
console.log('marker clicked', marker) console.log('marker clicked', marker)
selectedMarker.value = marker
panelVisible.value = true
emit('on-click-marker', marker)
}, },
onZoomEnd: (zoom: number) => { onZoomEnd: (zoom: number) => {
console.log('onZoomEnd', zoom) console.log('onZoomEnd', zoom)
@ -342,81 +323,5 @@ defineExpose({ refreshFences, fitToMarkers, setCenter, showTrajectory })
height: 100% !important; height: 100% !important;
} }
} }
.info-panel {
position: absolute;
top: 100px;
right: 20px;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 1000;
width: 313px;
max-height: 80vh;
overflow-y: auto;
display: flex;
flex-direction: column;
.info-panel__header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
border-bottom: 1px solid #eee;
background-color: #f5f5f5;
border-radius: 8px 8px 0 0;
.info-panel__title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.info-panel__close {
background-color: #ff4d4f;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 18px;
font-weight: bold;
transition: background-color 0.3s ease;
&:hover {
background-color: #d9363e;
}
}
}
.info-panel__body {
padding: 15px;
flex-grow: 1;
display: flex;
flex-direction: column;
.info-panel__empty {
text-align: center;
color: #888;
padding: 20px;
}
.info-panel__name {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
color: #555;
}
.info-panel__coord {
font-size: 14px;
color: #666;
}
}
}
</style> </style>

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

@ -77,7 +77,7 @@ export const getStatusColor = (status: string | keyof typeof STATUS_PRIORITY): s
if (status === 'offline') return STATUS_DICT.onlineStatus[0].cssClass if (status === 'offline') return STATUS_DICT.onlineStatus[0].cssClass
// 安全校验,确保状态字符串格式正确 // 安全校验,确保状态字符串格式正确
if (!status.includes('_')) return '' if (!status.includes('_')) return ''
const [type, value] = status.split('_') as [keyof typeof STATUS_DICT, string] const [type, value] = status.split('_') as [keyof typeof STATUS_DICT, string]
const info = findStatusInfo(STATUS_DICT[type], value) const info = findStatusInfo(STATUS_DICT[type], value)
return info?.cssClass || '#67c23a' return info?.cssClass || '#67c23a'
@ -177,12 +177,65 @@ export const createDetectorListItem = (detector: DetectorInfo) => `
</div> </div>
</div> </div>
` `
export const createDetectorListItem2 = (type: string, label: string, count: number) => {
if (count === 0) return ''
return `
<div style="display: flex; align-items: center; padding: 6px 0; border-bottom: 1px solid #f0f0f0;">
<div style="width: 50px; margin-right: 10px; flex-shrink: 0;">
${label}
</div>
<div style="flex: 1; min-width: 0;">
<div style="font-weight: 400;">${count}</div>
</div>
</div>
`
}
/** /**
* HTML * HTML
*/ */
export const createClusterPopupHTML = (detectorList: DetectorInfo[]) => { export const createClusterPopupHTML = (detectorList: DetectorInfo[]) => {
const detectorListHTML = detectorList.map(createDetectorListItem).join('')
let detectorListHTML = ''
if (detectorList.length > 5) {
// 正常探测器数量
const normalCount = detectorList.filter((detector) => detector.status === 'normal').length
// if (normalCount === detectorList.length) return ''
// 气体报警数量
const gasAlarmCount = detectorList.filter(
(detector) => detector.status === 'gasStatus_1'
).length
// 围栏报警
const fenceAlarmCount = detectorList.filter(
(detector) => detector.status === 'fenceStatus_1'
).length
// 低电量数量
const lowBatteryCount = detectorList.filter(
(detector) => detector.status === 'batteryStatus_1'
).length
// 离线探测器数量
const offlineCount = detectorList.filter((detector) => detector.status === 'offline').length
detectorListHTML =
createDetectorListItem2('gasAlarm', '气体报警', gasAlarmCount) +
createDetectorListItem2('fenceAlarm', '围栏报警', fenceAlarmCount) +
createDetectorListItem2('lowBattery', '低电量', lowBatteryCount) +
createDetectorListItem2('offline', '离线', offlineCount) +
createDetectorListItem2('normal', '正常', normalCount)
// `
// <div style="font-size: 12px; color: #666; margin-bottom: 8px;">
// <div>气体报警${gasAlarmCount}个</div>
// <div>围栏报警${fenceAlarmCount}个</div>
// <div>离线${offlineCount}个</div>
// <div>低电量${lowBatteryCount}个</div>
// <div>正常${normalCount}个</div>
// </div>
// `
} else {
detectorListHTML = detectorList.map(createDetectorListItem).join('')
}
return ` return `
<div style="max-height: 250px; overflow-y: auto; padding-right: 4px;"> <div style="max-height: 250px; overflow-y: auto; padding-right: 4px;">

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

@ -9,6 +9,7 @@
:markers="filterMarkers" :markers="filterMarkers"
:fences="fences" :fences="fences"
@time-range-change="timeRangeChange" @time-range-change="timeRangeChange"
@on-click-marker="onClickMarker"
/> />
<TopPanel <TopPanel
v-model="search" v-model="search"
@ -17,10 +18,10 @@
/> />
</div> </div>
<div class="markerList"> <div class="markerList">
<el-scrollbar height="100%">
<el-scrollbar height="100%" ref="scrollbarRef">
<!--marker列表 --> <!--marker列表 -->
<el-collapse accordion>
<el-collapse-item :name="item.sn" v-for="item in filterMarkers" :key="item.id">
<el-collapse accordion v-model="activeName">
<el-collapse-item :name="item.id" v-for="item in filterMarkers" :key="item.id">
<template #title> <template #title>
<div class="flex flex-row w-100%"> <div class="flex flex-row w-100%">
<div class="flex-1 text-left font-500"> <div class="flex-1 text-left font-500">
@ -103,7 +104,7 @@ import {
import { MarkerData, FenceData } from './components/types/map.types' import { MarkerData, FenceData } from './components/types/map.types'
import { useHandDetectorStore } from '@/store/modules/handDetector' import { useHandDetectorStore } from '@/store/modules/handDetector'
import { ElMessage } from 'element-plus'
import { ElMessage, ElScrollbar } from 'element-plus'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { getDistance } from 'ol/sphere' import { getDistance } from 'ol/sphere'
const componentsIsActive = ref(false) const componentsIsActive = ref(false)
@ -318,7 +319,20 @@ function onClickTrajectory(item: MarkerData) {
trajectoryTimeRange.value = [dayjs().subtract(1, 'hour').valueOf(), dayjs().valueOf()] trajectoryTimeRange.value = [dayjs().subtract(1, 'hour').valueOf(), dayjs().valueOf()]
showTrajectory(item) showTrajectory(item)
} }
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
const activeName = ref<number>()
//
function onClickMarker(item: MarkerData) {
console.log('onClickMarker', item)
activeName.value = item.id
var findIndex = markers.value.findIndex((item) => item.id === activeName.value)
if (findIndex === -1) {
return
}
var top = findIndex * 48
scrollbarRef.value?.setScrollTop(top)
}
onMounted(() => { onMounted(() => {
getMarkers() getMarkers()

Loading…
Cancel
Save