|
|
|
|
/**
|
|
|
|
|
* 地图工具函数
|
|
|
|
|
*/
|
|
|
|
|
import { Style, Text, Circle, Fill, Stroke, Icon } from 'ol/style'
|
|
|
|
|
import { Feature } from 'ol'
|
|
|
|
|
import type { MarkerData, DetectorInfo } from '../types/map.types'
|
|
|
|
|
import { STATUS_DICT, STATUS_PRIORITY, STATUS_ORDER } from '../constants/map.constants'
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从字典中查找状态信息
|
|
|
|
|
*/
|
|
|
|
|
export const findStatusInfo = (
|
|
|
|
|
dict: (typeof STATUS_DICT)[keyof typeof STATUS_DICT],
|
|
|
|
|
value: string
|
|
|
|
|
) => {
|
|
|
|
|
return dict.find((item) => item.value === value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取状态映射
|
|
|
|
|
*/
|
|
|
|
|
export const getStatusMapping = (type: keyof typeof STATUS_DICT, value: string): string => {
|
|
|
|
|
const info = findStatusInfo(STATUS_DICT[type], value)
|
|
|
|
|
return info ? `${type}_${value}` : ''
|
|
|
|
|
}
|
|
|
|
|
// 获取状态优先级,越小优先级越高
|
|
|
|
|
export const getStatusPriority = (statusStr: string | keyof typeof STATUS_PRIORITY): number => {
|
|
|
|
|
return STATUS_PRIORITY[statusStr] || 0
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 根据字典数据获取设备最高优先级状态
|
|
|
|
|
*/
|
|
|
|
|
export const getHighestPriorityStatus = (markerData: {
|
|
|
|
|
gasStatus?: number
|
|
|
|
|
batteryStatus?: number
|
|
|
|
|
fenceStatus?: number
|
|
|
|
|
onlineStatus?: number
|
|
|
|
|
}): keyof typeof STATUS_PRIORITY => {
|
|
|
|
|
const statuses: string[] = []
|
|
|
|
|
|
|
|
|
|
// 收集非正常状态
|
|
|
|
|
if (markerData.gasStatus !== 0) {
|
|
|
|
|
const gasStatusStr = getStatusMapping('gasStatus', String(markerData.gasStatus))
|
|
|
|
|
gasStatusStr && statuses.push(gasStatusStr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (markerData.batteryStatus !== 0) {
|
|
|
|
|
const batteryStatusStr = getStatusMapping('batteryStatus', String(markerData.batteryStatus))
|
|
|
|
|
statuses.push(batteryStatusStr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
|
|
|
|
|
// 返回优先级最高的状态
|
|
|
|
|
return statuses.reduce((prev, current) =>
|
|
|
|
|
STATUS_PRIORITY[prev] < STATUS_PRIORITY[current] ? prev : current
|
|
|
|
|
) as keyof typeof STATUS_PRIORITY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据字典数据获取状态颜色
|
|
|
|
|
*/
|
|
|
|
|
export const getStatusColor = (status: string | keyof typeof STATUS_PRIORITY): string => {
|
|
|
|
|
if (status === 'normal') return '#67c23a'
|
|
|
|
|
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)
|
|
|
|
|
return info?.cssClass || '#67c23a'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据字典数据获取状态标签
|
|
|
|
|
*/
|
|
|
|
|
export const getStatusLabel = (status: string | keyof typeof STATUS_PRIORITY): string => {
|
|
|
|
|
if (status === 'normal') return '正常'
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
export const createLocationIconSVG = (color: string, size: number = 24) => {
|
|
|
|
|
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
|
|
|
|
|
<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z" fill="${color}"/>
|
|
|
|
|
<circle cx="12" cy="9" r="2" fill="white"/>
|
|
|
|
|
</svg>
|
|
|
|
|
`)}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建标记样式
|
|
|
|
|
*/
|
|
|
|
|
export const createMarkerStyle = (
|
|
|
|
|
statusStr: keyof typeof STATUS_PRIORITY,
|
|
|
|
|
isCluster: boolean = false,
|
|
|
|
|
clusterSize?: number
|
|
|
|
|
) => {
|
|
|
|
|
// 如果是字符串,说明是状态值
|
|
|
|
|
// const statusStr:string =
|
|
|
|
|
// typeof markerData === 'string'
|
|
|
|
|
// ? (markerData as keyof typeof STATUS_PRIORITY)
|
|
|
|
|
// : getHighestPriorityStatus(markerData)
|
|
|
|
|
|
|
|
|
|
const color = getStatusColor(statusStr)
|
|
|
|
|
|
|
|
|
|
if (isCluster && clusterSize) {
|
|
|
|
|
// 聚合标记样式
|
|
|
|
|
return new Style({
|
|
|
|
|
image: new Circle({
|
|
|
|
|
radius: Math.min(20 + clusterSize * 2, 40),
|
|
|
|
|
fill: new Fill({
|
|
|
|
|
color: color + '80' // 添加透明度
|
|
|
|
|
}),
|
|
|
|
|
stroke: new Stroke({
|
|
|
|
|
color: color,
|
|
|
|
|
width: 2
|
|
|
|
|
})
|
|
|
|
|
}),
|
|
|
|
|
text: new Text({
|
|
|
|
|
text: clusterSize.toString(),
|
|
|
|
|
fill: new Fill({
|
|
|
|
|
color: '#ffffff'
|
|
|
|
|
}),
|
|
|
|
|
font: 'bold 14px Arial'
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
// 单个标记样式 - 使用位置图标
|
|
|
|
|
return new Style({
|
|
|
|
|
image: new Icon({
|
|
|
|
|
src: createLocationIconSVG(color, 24),
|
|
|
|
|
scale: 1,
|
|
|
|
|
anchor: [0.5, 0.8], // 锚点设置在底部中心
|
|
|
|
|
anchorXUnits: 'fraction',
|
|
|
|
|
anchorYUnits: 'fraction'
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成探测器列表项HTML
|
|
|
|
|
*/
|
|
|
|
|
export const createDetectorListItem = (detector: DetectorInfo) => `
|
|
|
|
|
<div style="display: flex; align-items: center; padding: 6px 0; border-bottom: 1px solid #f0f0f0;">
|
|
|
|
|
<div style="width: 10px; height: 10px; border-radius: 50%; background-color: ${detector.statusColor}; margin-right: 10px; flex-shrink: 0;"></div>
|
|
|
|
|
<div style="flex: 1; min-width: 0;">
|
|
|
|
|
<div style="font-weight: 500; font-size: 13px; color: #333; margin-bottom: 2px;">${detector.name}</div>
|
|
|
|
|
<div style="color: ${detector.statusColor}; font-size: 11px; font-weight: 400;">${detector.statusLabel}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成聚合标记弹窗HTML
|
|
|
|
|
*/
|
|
|
|
|
export const createClusterPopupHTML = (detectorList: DetectorInfo[]) => {
|
|
|
|
|
const detectorListHTML = detectorList.map(createDetectorListItem).join('')
|
|
|
|
|
|
|
|
|
|
return `
|
|
|
|
|
<div style="max-height: 250px; overflow-y: auto; padding-right: 4px;">
|
|
|
|
|
${detectorListHTML}
|
|
|
|
|
</div>
|
|
|
|
|
`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取聚合标记数据
|
|
|
|
|
*/
|
|
|
|
|
export const getClusterMarkerData = (features: Feature[]): keyof typeof STATUS_PRIORITY => {
|
|
|
|
|
// 收集所有标记的状态
|
|
|
|
|
const allStatuses: string[] = []
|
|
|
|
|
|
|
|
|
|
features.forEach((feature) => {
|
|
|
|
|
const markerData = feature.get('markerData') as MarkerData
|
|
|
|
|
if (markerData) {
|
|
|
|
|
const status = getHighestPriorityStatus(markerData)
|
|
|
|
|
allStatuses.push(status)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 返回优先级最高的状态
|
|
|
|
|
if (allStatuses.length === 0) return 'normal'
|
|
|
|
|
|
|
|
|
|
return allStatuses.reduce((prev, current) =>
|
|
|
|
|
STATUS_PRIORITY[prev] < STATUS_PRIORITY[current] ? prev : current
|
|
|
|
|
) as keyof typeof STATUS_PRIORITY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按优先级排序探测器列表
|
|
|
|
|
*/
|
|
|
|
|
export const sortDetectorsByPriority = (detectorList: DetectorInfo[]): DetectorInfo[] => {
|
|
|
|
|
return detectorList.sort((a, b) => {
|
|
|
|
|
const aPriority = STATUS_ORDER.indexOf(a.status as keyof typeof STATUS_PRIORITY)
|
|
|
|
|
const bPriority = STATUS_ORDER.indexOf(b.status as keyof typeof STATUS_PRIORITY)
|
|
|
|
|
return aPriority - bPriority
|
|
|
|
|
})
|
|
|
|
|
}
|