diff --git a/web/src/api/gas/handdetector/index.ts b/web/src/api/gas/handdetector/index.ts index 60deb22..3133341 100644 --- a/web/src/api/gas/handdetector/index.ts +++ b/web/src/api/gas/handdetector/index.ts @@ -26,6 +26,10 @@ export interface HandDetector { } // GAS手持探测器报警信息 export interface HandDetectorData extends HandDetector { + // coordinates: [number,number] // 坐标 + // GPS类型 (0:基站;其他:GPS) + gpsType?: number + time?: number // 时间 value?: number // 数值 battery?: number // 电池电量 @@ -33,6 +37,10 @@ export interface HandDetectorData extends HandDetector { batteryStatus?: number //电池报警状态 (0:正常;1:报警) fenceStatus?: number //电子围栏报警状态 (0:正常;1:报警) onlineStatus?: number //在线状态 (0:离线;1:在线) + statusStr?: string // 状态字符串 (normal, offline, gas_1,gas_2, battery_1, fence_1) + statusLabel?: string // 状态字符串 (正常, 离线, 气体报警, 电池报警, 围栏报警) + statusColor?: string // 状态颜色 + } // GAS手持探测器 API diff --git a/web/src/views/HandDevice/Home/components/TrajectoryControls.vue b/web/src/views/HandDevice/Home/components/TrajectoryControls.vue index 57724cd..efa3251 100644 --- a/web/src/views/HandDevice/Home/components/TrajectoryControls.vue +++ b/web/src/views/HandDevice/Home/components/TrajectoryControls.vue @@ -144,11 +144,12 @@ const appStore = useAppStore() // 播放速度选项 const speedOptions = [ - { label: '0.5x', value: 0.5 }, { label: '1x', value: 1 }, - { label: '2x', value: 2 }, - { label: '4x', value: 4 }, - { label: '8x', value: 8 } + { label: '5x', value: 5 }, + { label: '10x', value: 10 }, + { label: '20x', value: 20 }, + { label: '50x', value: 50 }, + { label: '100x', value: 100 }, ] // 响应式状态 diff --git a/web/src/views/HandDevice/Home/components/constants/map.constants.ts b/web/src/views/HandDevice/Home/components/constants/map.constants.ts index dc250fa..7abbe89 100644 --- a/web/src/views/HandDevice/Home/components/constants/map.constants.ts +++ b/web/src/views/HandDevice/Home/components/constants/map.constants.ts @@ -5,35 +5,35 @@ import type { StatusDictItem, FenceData } from '../types/map.types' // 状态字典配置 export const STATUS_DICT = { - online: [ + onlineStatus: [ { value: '0', label: '离线', cssClass: '#909399' }, { value: '1', label: '在线', cssClass: '#67c23a' } ] as StatusDictItem[], - gas: [ + gasStatus: [ { value: '0', label: '正常', cssClass: '#67c23a' }, - { value: '1', label: '一级气体报警', cssClass: '#e6a23c' }, - { value: '2', label: '二级气体告警', cssClass: '#f56c6c' } + { value: '1', label: '气体报警', cssClass: '#e6a23c' } + // { value: '2', label: '二级气体告警', cssClass: '#f56c6c' } ] as StatusDictItem[], - battery: [ + batteryStatus: [ { value: '0', label: '正常', cssClass: '#67c23a' }, - { value: '1', label: '一级低电量报警', cssClass: '#e6a23c' }, - { value: '2', label: '二级低电量报警', cssClass: '#f56c6c' } + { value: '1', label: '低电量报警', cssClass: '#e6a23c' } + // { value: '2', label: '二级低电量报警', cssClass: '#f56c6c' } ] as StatusDictItem[], - fence: [ + fenceStatus: [ { value: '0', label: '正常', cssClass: '#67c23a' }, - { value: '1', label: '一级围栏报警', cssClass: '#e6a23c' }, - { value: '2', label: '二级围栏报警', cssClass: '#f56c6c' } + { value: '1', label: '一级围栏报警', cssClass: '#e6a23c' } + // { value: '2', label: '二级围栏报警', cssClass: '#f56c6c' } ] as StatusDictItem[] } // 状态优先级定义 (数字越小优先级越高) export const STATUS_PRIORITY = { - gas_2: 1, - gas_1: 2, - battery_2: 3, - battery_1: 4, - fence_2: 5, - fence_1: 6, + gasStatus_2: 1, + gasStatus_1: 2, + // battery_2: 3, + batteryStatus_1: 4, + // fence_2: 5, + fenceStatus_1: 6, offline: 7, normal: 8 } as const @@ -47,7 +47,8 @@ export const DEFAULT_MARKERS = [] // 地图默认配置 export const MAP_DEFAULTS = { // tileUrl: 'http://qtbj.icpcdev.site/roadmap/{z}/{x}/{y}.png', - tileUrl: 'https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}', + tileUrl: + 'https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}', center: [116.3912757, 39.906217] as [number, number], zoom: 10, maxZoom: 18, 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 f9d86f5..3f7ca00 100644 --- a/web/src/views/HandDevice/Home/components/services/marker.service.ts +++ b/web/src/views/HandDevice/Home/components/services/marker.service.ts @@ -10,7 +10,11 @@ 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 { + createMarkerStyle, + getHighestPriorityStatus, + getClusterMarkerData +} from '../utils/map.utils' import { ANIMATION_CONFIG } from '../constants/map.constants' export class MarkerService { private map: Map | null = null @@ -52,7 +56,8 @@ export class MarkerService { geometry: new Point(fromLonLat(marker.coordinates)), markerData: marker }) - feature.setStyle(createMarkerStyle(marker)) + const statusStr = getHighestPriorityStatus(marker) + feature.setStyle(createMarkerStyle(statusStr)) this.vectorSource.addFeature(feature) }) } @@ -98,8 +103,9 @@ export class MarkerService { if (features.length === 1) { // 单个marker - const markerData = features[0].get('markerData') - return markerData ? createMarkerStyle(markerData) : new Style() + const markerData: MarkerData = features[0].get('markerData') + const statusStr = getHighestPriorityStatus(markerData) + return markerData ? createMarkerStyle(statusStr) : new Style() } else { // 聚合marker const highestStatus = getClusterMarkerData(features) diff --git a/web/src/views/HandDevice/Home/components/services/popup.service.ts b/web/src/views/HandDevice/Home/components/services/popup.service.ts index 5404ef7..df587fc 100644 --- a/web/src/views/HandDevice/Home/components/services/popup.service.ts +++ b/web/src/views/HandDevice/Home/components/services/popup.service.ts @@ -7,7 +7,8 @@ import { getStatusLabel, getStatusColor, createClusterPopupHTML, - sortDetectorsByPriority + sortDetectorsByPriority, + getLabelWithTypeValue } from '../utils/map.utils' export class PopupService { @@ -20,7 +21,7 @@ export class PopupService { const markerData = f.get('markerData') as MarkerData const status = getHighestPriorityStatus(markerData) return { - name: markerData.name, + name: markerData?.name || '-', status, statusLabel: getStatusLabel(status), statusColor: getStatusColor(status) @@ -38,23 +39,23 @@ export class PopupService { * 处理单个标记弹窗 */ handleSingleMarkerPopup(markerData: MarkerData): string { - const status = getHighestPriorityStatus(markerData) + const statusStr = getHighestPriorityStatus(markerData) return `
${markerData.name}
-
- 状态: ${getStatusLabel(status)} +
+ 状态: ${getStatusLabel(statusStr)}
-
gasStatus: ${markerData.gasStatus}
-
onlineStatus: ${markerData.onlineStatus}
-
enableStatus: ${markerData.enableStatus}
-
fenceStatus: ${markerData.fenceStatus}
-
batteryStatus: ${markerData.batteryStatus}
+
在线状态: ${getLabelWithTypeValue('onlineStatus', markerData.onlineStatus)}
+ +
气体状态: ${getLabelWithTypeValue('gasStatus', markerData.gasStatus)}
+
围栏状态: ${getLabelWithTypeValue('fenceStatus', markerData.fenceStatus)}
+
电池状态: ${getLabelWithTypeValue('batteryStatus', markerData.batteryStatus)}
类型:${markerData.gasChemical}
数值:${markerData.value} ${markerData.unit ? markerData.unit : ''}
-
时间:${markerData.time ? markerData.time : '-'}
+
时间:${markerData.timeStr ? markerData.timeStr : '-'}
坐标:${markerData.coordinates[0].toFixed(6)}, ${markerData.coordinates[1].toFixed(6)}
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 efb50a7..343bfc5 100644 --- a/web/src/views/HandDevice/Home/components/types/map.types.ts +++ b/web/src/views/HandDevice/Home/components/types/map.types.ts @@ -5,7 +5,7 @@ import type Overlay from 'ol/Overlay' /** * 地图组件相关类型定义 */ -import { HandDetector } from '@/api/gas/handdetector' +import { HandDetectorData } from '@/api/gas/handdetector' // 围栏状态枚举 export const FENCE_STATUS = { @@ -36,26 +36,30 @@ export interface StatusDictItem { export type MarkerStatus = string // 标记数据接口 -export interface MarkerData extends HandDetector { - /** 坐标 [经度, 纬度] */ +export interface MarkerData extends HandDetectorData { + // /** 坐标 [经度, 纬度] */ coordinates: [number, number] - /** 气体状态 */ - gasStatus?: number - /** 电池状态 */ - batteryStatus?: number - /** 围栏状态 */ - fenceStatus?: number - /** 在线状态 */ - onlineStatus?: number - /** 标记标题 */ - name: string - /** 自定义数据 */ + // // GPS类型 (0:基站;其他:GPS) + // gpsType?: number + // /** 气体状态 */ + // gasStatus?: number + // /** 电池状态 */ + // batteryStatus?: number + // /** 围栏状态 */ + // fenceStatus?: number + // /** 在线状态 */ + // onlineStatus?: number + // /** 标记标题 */ + // name?: string + // /** 自定义数据 */ + // value?: number + + timeStr?: string + // battery?: number //电池电量 + // /** 状态字符串 */ + // statusStr: string + data?: any - value?: number - unit?: string - time?: number - - enableStatus?: number //启用状态 } // 地图组件 Props 接口 @@ -88,7 +92,6 @@ export interface MapProps { showFences?: boolean /** 是否显示绘制围栏 */ showDrawFences?: boolean - } // 围栏数据接口 diff --git a/web/src/views/HandDevice/Home/components/utils/map.utils.ts b/web/src/views/HandDevice/Home/components/utils/map.utils.ts index 0ca31cf..171c78a 100644 --- a/web/src/views/HandDevice/Home/components/utils/map.utils.ts +++ b/web/src/views/HandDevice/Home/components/utils/map.utils.ts @@ -19,39 +19,43 @@ export const findStatusInfo = ( /** * 获取状态映射 */ -export const getStatusMapping = (type: keyof typeof STATUS_DICT, value: string) => { +export const getStatusMapping = (type: keyof typeof STATUS_DICT, value: string): string => { const info = findStatusInfo(STATUS_DICT[type], value) - return info ? `${type}_${value}` : null + return info ? `${type}_${value}` : '' } /** * 根据字典数据获取设备最高优先级状态 */ -export const getHighestPriorityStatus = (markerData: MarkerData): keyof typeof STATUS_PRIORITY => { - const statuses: Array = [] - - // 检查各种状态 - const gasStatus = getStatusMapping('gas', String(markerData.gasStatus)) - const batteryStatus = getStatusMapping('battery', String(markerData.batteryStatus)) - const fenceStatus = getStatusMapping('fence', String(markerData.fenceStatus)) - const onlineStatus = String(markerData.onlineStatus) === '0' ? 'offline' : null +export const getHighestPriorityStatus = (markerData: { + gasStatus?: number + batteryStatus?: number + fenceStatus?: number + onlineStatus?: number +}): keyof typeof STATUS_PRIORITY => { + const statuses: string[] = [] // 收集非正常状态 - if (gasStatus && markerData.gasStatus !== 0) { - statuses.push(gasStatus as keyof typeof STATUS_PRIORITY) + if (markerData.gasStatus !== 0) { + const gasStatusStr = getStatusMapping('gasStatus', String(markerData.gasStatus)) + gasStatusStr && statuses.push(gasStatusStr) } - if (batteryStatus && markerData.batteryStatus !== 0) { - statuses.push(batteryStatus as keyof typeof STATUS_PRIORITY) + if (markerData.batteryStatus !== 0) { + const batteryStatusStr = getStatusMapping('batteryStatus', String(markerData.batteryStatus)) + statuses.push(batteryStatusStr) } - if (fenceStatus && markerData.fenceStatus !== 0) { - statuses.push(fenceStatus as keyof typeof STATUS_PRIORITY) + if (markerData.fenceStatus !== 0) { + const fenceStatusStr = getStatusMapping('fenceStatus', String(markerData.fenceStatus)) + fenceStatusStr && statuses.push(fenceStatusStr) } - + // 检查各种状态 + const onlineStatus = String(markerData.onlineStatus) === '0' ? 'offline' : null if (onlineStatus) { statuses.push(onlineStatus) } + console.log('statuses', statuses) // 如果没有报警状态,则为正常 if (statuses.length === 0) return 'normal' @@ -59,7 +63,7 @@ export const getHighestPriorityStatus = (markerData: MarkerData): keyof typeof S // 返回优先级最高的状态 return statuses.reduce((prev, current) => STATUS_PRIORITY[prev] < STATUS_PRIORITY[current] ? prev : current - ) + ) as keyof typeof STATUS_PRIORITY } /** @@ -67,7 +71,7 @@ export const getHighestPriorityStatus = (markerData: MarkerData): keyof typeof S */ export const getStatusColor = (status: keyof typeof STATUS_PRIORITY): string => { if (status === 'normal') return '#67c23a' - if (status === 'offline') return STATUS_DICT.online[0].cssClass + if (status === 'offline') return STATUS_DICT.onlineStatus[0].cssClass const [type, value] = status.split('_') as [keyof typeof STATUS_DICT, string] const info = findStatusInfo(STATUS_DICT[type], value) @@ -77,14 +81,19 @@ export const getStatusColor = (status: keyof typeof STATUS_PRIORITY): string => /** * 根据字典数据获取状态标签 */ -export const getStatusLabel = (status: keyof typeof STATUS_PRIORITY): string => { +export const getStatusLabel = (status:string| keyof typeof STATUS_PRIORITY): string => { if (status === 'normal') return '正常' - if (status === 'offline') return STATUS_DICT.online[0].label + if (status === 'offline') return STATUS_DICT.onlineStatus[0].label const [type, value] = status.split('_') as [keyof typeof STATUS_DICT, string] const info = findStatusInfo(STATUS_DICT[type], value) return info?.label || '正常' } +export const getLabelWithTypeValue = (type: string, value: number | undefined): string => { + if (value === undefined) return '-' + const info = findStatusInfo(STATUS_DICT[type], String(value)) + return info?.label || '-' +} /** * 创建位置图标SVG @@ -102,17 +111,17 @@ export const createLocationIconSVG = (color: string, size: number = 24) => { * 创建标记样式 */ export const createMarkerStyle = ( - markerData: MarkerData | keyof typeof STATUS_PRIORITY, + statusStr: keyof typeof STATUS_PRIORITY, isCluster: boolean = false, clusterSize?: number ) => { // 如果是字符串,说明是状态值 - const status: keyof typeof STATUS_PRIORITY = - typeof markerData === 'string' - ? (markerData as keyof typeof STATUS_PRIORITY) - : getHighestPriorityStatus(markerData) + // const statusStr:string = + // typeof markerData === 'string' + // ? (markerData as keyof typeof STATUS_PRIORITY) + // : getHighestPriorityStatus(markerData) - const color = getStatusColor(status) + const color = getStatusColor(statusStr) if (isCluster && clusterSize) { // 聚合标记样式 @@ -180,7 +189,7 @@ export const createClusterPopupHTML = (detectorList: DetectorInfo[]) => { */ export const getClusterMarkerData = (features: Feature[]): keyof typeof STATUS_PRIORITY => { // 收集所有标记的状态 - const allStatuses: Array = [] + const allStatuses: string[] = [] features.forEach((feature) => { const markerData = feature.get('markerData') as MarkerData @@ -195,7 +204,7 @@ export const getClusterMarkerData = (features: Feature[]): keyof typeof STATUS_P return allStatuses.reduce((prev, current) => STATUS_PRIORITY[prev] < STATUS_PRIORITY[current] ? prev : current - ) + ) as keyof typeof STATUS_PRIORITY } /** diff --git a/web/src/views/HandDevice/Home/index.vue b/web/src/views/HandDevice/Home/index.vue index e0c7dab..4a739de 100644 --- a/web/src/views/HandDevice/Home/index.vue +++ b/web/src/views/HandDevice/Home/index.vue @@ -26,34 +26,43 @@
{{ item.name }}
-
- {{ item.onlineStatus === 1 ? '在线' : '在线' }} +
+ {{ item.statusLabel }}
-
SN:{{ item.sn }}
-
类型:{{ item.gasChemical }}
-
-
气体状态:{{ item.gasStatus }}
-
电池告警状态:{{ item.batteryStatus }}
- -
围栏状态:{{ item.fenceStatus }}
-
在线状态:{{ item.onlineStatus }}
-
启用状态:{{ item.enableStatus === 1 ? '启用' : '备用' }}
-
-
电量:{{ item.battery }}
-
数值:{{ item.value }} {{ item.unit }}
-
时间:{{ dayjs(item.time).format('YYYY-MM-DD HH:mm:ss') }}
-
+
SN:{{ item.sn }}
+
类型:{{ item.gasChemical }}
+ +
气体状态:{{ getLabelWithTypeValue('gasStatus', item.gasStatus) }}
+
围栏状态:{{ getLabelWithTypeValue('fenceStatus', item.fenceStatus) }}
+
电池状态:{{ getLabelWithTypeValue('batteryStatus', item.batteryStatus) }}
+ + + +
电量:{{ item.battery }}
+
数值:{{ item.value }} {{ item.unit }}
+
时间:{{ item.timeStr }}
+ +
定位 - 轨迹
@@ -77,11 +86,14 @@ import { getLastDetectorData } from '@/api/gas' import { HandDetectorData } from '@/api/gas/handdetector' import { tdengineApi, tdStruct, tdQuery } from '@/api/gas/tdengine/index' +import { getLabelWithTypeValue, getHighestPriorityStatus,getStatusLabel,getStatusColor } from './components/utils/map.utils' + import { MarkerData, FenceData } from './components/types/map.types' import { useHandDetectorStore } from '@/store/modules/handDetector' import { ElMessage } from 'element-plus' import dayjs from 'dayjs' +import { getDistance } from 'ol/sphere' const handDetectorStore = useHandDetectorStore() // 手持探测器 store @@ -107,7 +119,7 @@ const onlineCount = computed(() => markers.value.filter((item) => item.onlineSta const filterMarkers = computed(() => { if (search.value) { return markers.value.filter((item) => { - var isName = item.name.includes(search.value) + var isName = item?.name?.includes(search.value) var isSn = item.sn?.includes(search.value) var isGasChemical = item.gasChemical?.includes(search.value) @@ -123,22 +135,30 @@ const getMarkers = async () => { res = res.filter((i) => i.enableStatus === 1) var res2 = res.map((i) => { // console.log([i.longitude, i.latitude]) - - return { - ...i, - coordinates: [i.longitude, i.latitude], - data: [], - time: i.time ? dayjs(i.time).format('YYYY-MM-DD HH:mm:ss') : '', - value: i.value, - unit: i.unit, + let statusStr = getHighestPriorityStatus({ gasStatus: i.gasStatus, //气体报警状态 batteryStatus: i.batteryStatus, //电池报警状态 fenceStatus: i.fenceStatus, //电子围栏报警状态 onlineStatus: i.onlineStatus, //在线状态 - enableStatus: i.enableStatus //启用状态 + }) //状态字符串 + return { + ...i, + coordinates: [i.longitude || 0, i.latitude || 0] as [number,number], + // data: [], + timeStr: i.time ? dayjs(i.time).format('YYYY-MM-DD HH:mm:ss') : '', + // value: i.value, + // unit: i.unit, + // gasStatus: i.gasStatus, //气体报警状态 + // batteryStatus: i.batteryStatus, //电池报警状态 + // fenceStatus: i.fenceStatus, //电子围栏报警状态 + // onlineStatus: i.onlineStatus, //在线状态 + // enableStatus: i.enableStatus, //启用状态 + statusStr:statusStr, //状态字符串 + statusColor: getStatusColor(statusStr), //状态颜色 + statusLabel: getStatusLabel(statusStr), //状态标签 } }) - markers.value = res2 as unknown as any[] + markers.value = res2 }) } const getFences = async () => { @@ -168,7 +188,7 @@ var trajectoryTimeRange = ref([dayjs().subtract(1, 'hour').valueOf(), dayjs().va // 时间范围改变 function timeRangeChange(range: { startTime: number; endTime: number }) { console.log('timeRangeChange', range) - trajectoryTimeRange.value = [range.startTime,range.endTime] + trajectoryTimeRange.value = [range.startTime, range.endTime] if (currentMarker) { showTrajectory(currentMarker) } @@ -189,9 +209,67 @@ async function showTrajectory(item: MarkerData) { // sn: '867989072729904' }) + // 过滤前 + console.log('过滤前', data.length) + // 过滤掉重复点位和无效点位 + var newData = data.filter((item, index, arr) => { + if (!item.longitude || !item.latitude) { + return false + } + if (index === 0) { + return true + } + // 过滤掉前后点位一样的 + return ( + item.longitude !== arr[index - 1]?.longitude || item.latitude !== arr[index - 1]?.latitude + ) + }) + // 过滤后 + console.log('过滤后', newData.length) + + item.data = newData + .filter((item, index, arr) => { + if (index === 0) { + return true + } + if (index == arr.length - 2) { + return true + } + + // 过滤掉前后点位相差超过100m的 + let before = arr[index - 1] + let after = arr[index + 1] + + let beforeDistance = getDistance( + [before.longitude, before.latitude], + [item.longitude, item.latitude] + ) + console.log('beforeDistance', beforeDistance) + + if (beforeDistance < 40) { + return true + } + let afterDistance = getDistance( + [after.longitude, after.latitude], + [item.longitude, item.latitude] + ) + console.log('afterDistance', afterDistance) - item.data = data - .filter((i) => i.longitude && i.latitude) + if (afterDistance < 40) { + return true + } + let before_after_distance = getDistance( + [after.longitude, after.latitude], + [before.longitude, before.latitude] + ) + console.log('before_after_distance', before_after_distance) + + if (before_after_distance < 20) { + return true + } + + return false + }) .map((j) => { return { ...j, @@ -201,6 +279,8 @@ async function showTrajectory(item: MarkerData) { timeStr: dayjs(j.ts).format('YYYY-MM-DD HH:mm:ss') } }) + console.log('item.data', item.data) + mapRef.value?.showTrajectory(trajectoryTimeRange.value[0], trajectoryTimeRange.value[1], [item]) } finally { }