Browse Source

获取即处理报警状态、颜色

master
xh 4 days ago
parent
commit
dbde586760
  1. 8
      web/src/api/gas/handdetector/index.ts
  2. 9
      web/src/views/HandDevice/Home/components/TrajectoryControls.vue
  3. 35
      web/src/views/HandDevice/Home/components/constants/map.constants.ts
  4. 14
      web/src/views/HandDevice/Home/components/services/marker.service.ts
  5. 23
      web/src/views/HandDevice/Home/components/services/popup.service.ts
  6. 43
      web/src/views/HandDevice/Home/components/types/map.types.ts
  7. 67
      web/src/views/HandDevice/Home/components/utils/map.utils.ts
  8. 144
      web/src/views/HandDevice/Home/index.vue

8
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

9
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 },
]
//

35
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,

14
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)

23
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 `
<div style="font-weight: bold; margin-bottom: 4px;">${markerData.name}</div>
<div style="color: ${getStatusColor(status)};">
状态: ${getStatusLabel(status)}
<div style="color: ${getStatusColor(statusStr)};">
状态: ${getStatusLabel(statusStr)}
</div>
<div>gasStatus: ${markerData.gasStatus}</div>
<div>onlineStatus: ${markerData.onlineStatus}</div>
<div>enableStatus: ${markerData.enableStatus}</div>
<div>fenceStatus: ${markerData.fenceStatus}</div>
<div>batteryStatus: ${markerData.batteryStatus}</div>
<div>在线状态: ${getLabelWithTypeValue('onlineStatus', markerData.onlineStatus)}</div>
<div>气体状态: ${getLabelWithTypeValue('gasStatus', markerData.gasStatus)}</div>
<div>围栏状态: ${getLabelWithTypeValue('fenceStatus', markerData.fenceStatus)}</div>
<div>电池状态: ${getLabelWithTypeValue('batteryStatus', markerData.batteryStatus)}</div>
<div>${markerData.gasChemical}</div>
<div>${markerData.value} ${markerData.unit ? markerData.unit : ''}</div>
<div>${markerData.time ? markerData.time : '-'} </div>
<div>${markerData.timeStr ? markerData.timeStr : '-'} </div>
<div>
${markerData.coordinates[0].toFixed(6)}, ${markerData.coordinates[1].toFixed(6)}
</div>

43
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
}
// 围栏数据接口

67
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<keyof typeof STATUS_PRIORITY> = []
// 检查各种状态
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<keyof typeof STATUS_PRIORITY> = []
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
}
/**

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

@ -26,34 +26,43 @@
<div class="flex-1 text-left font-500">
{{ item.name }}
</div>
<div class="text-gray-500 font-400 text-12px">
{{ item.onlineStatus === 1 ? '在线' : '在线' }}
<div class="text-gray-500 font-400 text-12px" :style="{ color: item.statusColor }">
{{ item.statusLabel }}
</div>
</div>
</template>
<div class="markerList-content">
<div> <span>SN</span>{{ item.sn }}</div>
<div> <span>类型</span>{{ item.gasChemical }}</div>
<hr />
<div> <span>气体状态</span>{{ item.gasStatus }}</div>
<div> <span>电池告警状态</span>{{ item.batteryStatus }}</div>
<div> <span>围栏状态</span>{{ item.fenceStatus }}</div>
<div> <span>在线状态</span>{{ item.onlineStatus }}</div>
<div> <span>启用状态</span>{{ item.enableStatus === 1 ? '启用' : '备用' }}</div>
<hr />
<div> <span>电量</span>{{ item.battery }}</div>
<div> <span>数值</span>{{ item.value }} {{ item.unit }}</div>
<div> <span>时间</span>{{ dayjs(item.time).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div>
<div><span>SN</span>{{ item.sn }}</div>
<div><span>类型</span>{{ item.gasChemical }}</div>
<!-- <hr /> -->
<div
><span>气体状态</span
>{{ getLabelWithTypeValue('gasStatus', item.gasStatus) }}</div
>
<div
><span>围栏状态</span
>{{ getLabelWithTypeValue('fenceStatus', item.fenceStatus) }}</div
>
<div
><span>电池状态</span
>{{ getLabelWithTypeValue('batteryStatus', item.batteryStatus) }}</div
>
<!-- <div><span>启用状态</span>{{ item.enableStatus === 1 ? '启用' : '备用' }}</div> -->
<!-- <hr /> -->
<div><span>电量</span>{{ item.battery }}</div>
<div><span>数值</span>{{ item.value }} {{ item.unit }}</div>
<div><span>时间</span>{{ item.timeStr }}</div>
<div style="margin-top: 10px">
<el-button
type="primary"
type="success"
size="small"
v-if="item.latitude && item.longitude"
@click="setCenter(item)"
>定位</el-button
>
<el-button type="primary" size="small" @click="onClickTrajectory(item)"
<el-button type="success" size="small" @click="onClickTrajectory(item)"
>轨迹</el-button
>
</div>
@ -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 {
}

Loading…
Cancel
Save