You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
17 KiB

2 months ago
/**
*
*/
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
) => {
2 months ago
return dict?.find((item) => item.value === value)
2 months ago
}
/**
*
*/
export const getStatusMapping = (type: keyof typeof STATUS_DICT, value: string): string => {
2 months ago
const info = findStatusInfo(STATUS_DICT[type], value)
return info ? `${type}_${value}` : ''
2 months ago
}
// 获取状态优先级,越小优先级越高
export const getStatusPriority = (statusStr: string | keyof typeof STATUS_PRIORITY): number => {
return STATUS_PRIORITY[statusStr] || 0
}
2 months ago
/**
*
*/
export const getHighestPriorityStatus = (markerData: {
gasStatus?: number
batteryStatus?: number
fenceStatus?: number
onlineStatus?: number
}): keyof typeof STATUS_PRIORITY => {
1 month ago
const onlineStatus = String(markerData.onlineStatus) !== '1' ? 'offline' : null
if (onlineStatus === 'offline') {
return 'offline'
}
const statuses: string[] = []
2 months ago
// 收集非正常状态
2 months ago
if (markerData.gasStatus === 1) {
const gasStatusStr = getStatusMapping('gasStatus', String(markerData.gasStatus))
gasStatusStr && statuses.push(gasStatusStr)
}
2 months ago
if (markerData.batteryStatus === 1) {
const batteryStatusStr = getStatusMapping('batteryStatus', String(markerData.batteryStatus))
statuses.push(batteryStatusStr)
}
2 months ago
if (markerData.fenceStatus === 1) {
const fenceStatusStr = getStatusMapping('fenceStatus', String(markerData.fenceStatus))
fenceStatusStr && statuses.push(fenceStatusStr)
}
1 month ago
// console.log('statuses', statuses)
2 months ago
// 如果没有报警状态,则为正常
if (statuses.length === 0) return 'normal'
// 返回优先级最高的状态
return statuses.reduce((prev, current) =>
STATUS_PRIORITY[prev] < STATUS_PRIORITY[current] ? prev : current
) as keyof typeof STATUS_PRIORITY
2 months ago
}
/**
*
*/
export const getStatusColor = (status: string | keyof typeof STATUS_PRIORITY): string => {
2 months ago
if (status === 'normal') return '#67c23a'
if (status === 'offline') return STATUS_DICT.onlineStatus[0].cssClass
2 months ago
// 安全校验,确保状态字符串格式正确
if (!status.includes('_')) return ''
2 months ago
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 => {
2 months ago
if (status === 'normal') return '正常'
if (status === 'offline') return STATUS_DICT.onlineStatus[0].label
2 months ago
// 安全校验,确保状态字符串格式正确
if (!status.includes('_')) return ''
2 months ago
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 || '-'
}
2 months ago
/**
* SVG
*/
export const createStartIconSVG = (size: number = 24, color: string = '#1296db') => {
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
<svg width="${size}" height="${size}" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M998.4 968.533333c-132.266667 0-209.066667-8.533333-256-21.333333v-64h51.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-51.2v-42.666667h51.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-51.2v-42.666666h51.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-51.2v-34.133334c0-12.8-12.8-25.6-25.6-25.6s-25.6 12.8-25.6 25.6v34.133334H640c-12.8 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h51.2v42.666666H640c-12.8 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h51.2v128c-8.533333-8.533333-17.066667-21.333333-25.6-29.866666v-46.933334c0-12.8-12.8-25.6-25.6-25.6s-25.6 12.8-25.6 25.6c0 38.4 0 110.933333-21.333333 136.533334-8.533333 12.8-4.266667 25.6 4.266666 34.133333 4.266667 4.266667 8.533333 4.266667 12.8 4.266667 8.533333 0 17.066667-4.266667 21.333334-12.8 8.533333-12.8 17.066667-34.133333 21.333333-51.2 51.2 46.933333 136.533333 64 341.333333 64 12.8 0 25.6-12.8 25.6-25.6s-8.533333-21.333333-21.333333-21.333334z" p-id="13416" fill="${color}"></path>
<path d="M998.4 840.533333c-12.8 0-25.6 12.8-25.6 25.6v17.066667c0 4.266667 0 4.266667-4.266667 4.266667h-68.266666l-4.266667-4.266667v-93.866667h93.866667c12.8 0 25.6-12.8 25.6-25.6v-93.866666c0-12.8-12.8-25.6-25.6-25.6h-119.466667c-12.8 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h93.866667v42.666666h-93.866667c-12.8 0-25.6 12.8-25.6 25.6v119.466667c0 17.066667 4.266667 29.866667 17.066667 38.4 8.533333 8.533333 25.6 17.066667 38.4 17.066667h68.266666c29.866667 0 55.466667-25.6 55.466667-55.466667v-17.066667c0-12.8-12.8-25.6-25.6-25.6zM516.266667 819.2c-21.333333-12.8-46.933333-8.533333-59.733334 12.8-12.8 17.066667-21.333333 34.133333-25.6 55.466667-4.266667 8.533333-8.533333 21.333333-12.8 29.866666-4.266667-8.533333-8.533333-21.333333-12.8-34.133333-25.6-72.533333-81.066667-128-179.2-200.533333C136.533333 622.933333 85.333333 524.8 85.333333 418.133333 85.333333 234.666667 234.666667 85.333333 418.133333 85.333333s332.8 149.333333 332.8 332.8c0 21.333333 0 42.666667-4.266666 64s8.533333 46.933333 34.133333 51.2c21.333333 4.266667 46.933333-8.533333 51.2-34.133333 4.266667-25.6 8.533333-55.466667 8.533333-81.066667 0-230.4-187.733333-418.133333-418.133333-418.133333S0 187.733333 0 418.133333c0 136.533333 64 260.266667 174.933333 337.066667 93.866667 72.533333 132.266667 115.2 149.333334 162.133333 17.066667 46.933333 34.133333 98.133333 85.333333 102.4h8.533333c55.466667 0 72.533333-55.466667 89.6-110.933333 4.266667-12.8 8.533333-25.6 17.066667-34.133333 17.066667-17.066667 8.533333-42.666667-8.533333-55.466667z" fill="${color}"></path>
<path d="M418.133333 200.533333c-119.466667 0-213.333333 93.866667-213.333333 213.333334s93.866667 213.333333 213.333333 213.333333 213.333333-93.866667 213.333334-213.333333c0-115.2-98.133333-213.333333-213.333334-213.333334z m0 341.333334c-72.533333 0-128-55.466667-128-128s55.466667-128 128-128 128 55.466667 128 128-59.733333 128-128 128z" p-id="13418" fill="${color}"></path>
</svg>
`)}`
}
/**
* SVG
*/
export const createEndIconSVG = (size: number = 24, color: string = '#dd4e3b') => {
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
<svg width="${size}" height="${size}" viewBox="0 0 1025 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M820.049793 497.128631c4.248963-25.493776 8.497925-55.236515 8.497925-80.730291 0-229.443983-186.954357-416.39834-416.39834-416.39834S0 191.20332 0 420.647303c0 135.966805 63.73444 259.186722 174.207469 335.66805 93.477178 72.232365 131.717842 114.721992 148.713693 161.460581 16.995851 46.738589 33.991701 97.726141 84.979253 101.975103h8.497925c55.236515 0 72.232365-55.236515 89.228216-110.473029 4.248963-12.746888 8.497925-25.493776 16.995851-33.991701 12.746888-21.244813 8.497925-46.738589-12.746888-59.485477-21.244813-12.746888-46.738589-8.497925-59.485478 12.746888-12.746888 16.995851-21.244813 33.991701-25.493775 55.236514-4.248963 8.497925-8.497925 21.244813-12.746888 29.742739-4.248963-8.497925-8.497925-21.244813-12.746888-33.991701-25.493776-72.232365-80.73029-127.46888-178.456432-199.701245-84.979253-55.236515-135.966805-152.962656-135.966805-259.186722C84.979253 237.941909 233.692946 89.228216 416.39834 89.228216s331.419087 148.713693 331.419087 331.419087c0 21.244813 0 42.489627-4.248962 63.73444s8.497925 46.738589 33.991701 50.987552c16.995851 0 38.240664-12.746888 42.489627-38.240664z" fill="${color}"></path>
<path d="M416.39834 203.950207c-118.970954 0-212.448133 93.477178-212.448133 212.448133s93.477178 212.448133 212.448133 212.448133 212.448133-93.477178 212.448133-212.448133c0-114.721992-97.726141-212.448133-212.448133-212.448133z m0 339.917013c-72.232365 0-127.46888-55.236515-127.468879-127.46888s55.236515-127.46888 127.468879-127.468879 127.46888 55.236515 127.46888 127.468879-59.485477 127.46888-127.46888 127.46888zM590.605809 900.780083c4.248963 8.497925 12.746888 12.746888 21.244813 12.746888h4.248963l110.473029-29.742739c12.746888-4.248963 21.244813-16.995851 16.995851-29.742738-4.248963-12.746888-16.995851-21.244813-29.742739-16.995851l-42.489626 12.746888 59.485477-80.73029c4.248963-8.497925 8.497925-16.995851 0-29.742739s-12.746888-12.746888-21.244814-12.746888l-46.738589 4.248963 67.983403-106.224067c8.497925-12.746888 4.248963-25.493776-8.497926-33.991701-12.746888-8.497925-25.493776-4.248963-33.991701 8.497925l-101.975104 148.713693c-4.248963 8.497925-4.248963 16.995851 0 25.493776 4.248963 8.497925 12.746888 12.746888 25.493776 12.746888l42.489627-4.248962-67.983403 89.228215c0 8.497925 0 21.244813 4.248963 29.742739zM726.572614 917.775934l-118.970954 29.742738c-12.746888 4.248963-21.244813 16.995851-16.995851 29.742739 4.248963 12.746888 12.746888 21.244813 25.493776 21.244813h4.248963l118.970954-29.742739c12.746888-4.248963 21.244813-16.995851 16.995851-29.742738-4.248963-16.995851-16.995851-25.493776-29.742739-21.244813zM1002.755187 820.049793c-29.742739-8.497925-55.236515-25.493776-76.481328-42.489627 46.738589-50.987552 63.73444-101.975104 63.73444-106.224066 4.248963-8.497925 0-16.995851-4.248963-21.244814s-8.497925-8.497925-16.995851-8.497925h-80.73029c4.248963-12.746888 8.497925-21.244813 8.497925-21.244813 4.248963-12.746888-4.248963-29.742739-16.99585-33.991702-12.746888-4.248963-29.742739 4.248963-33.991702 16.995851 0 0-25.493776 67.983402-72.232365 114.721992-8.497925 8.497925-12.746888 25.493776 0 33.991701 4.248963 4.248963 12.746888 8.497925 16.995851 8.497925 4.248963 0 12.746888-4.248963 16.995851-8.497925l16.99585-16.995851c8.497925 12.746888 16.995851 25.493776 29.742739 38.240664-21.244813 12.746888-42.489627 29.742739-72.232365 38.240664-12.746888 4.248963-21.244813 21.244813-16.995851 33.991701 4.248963 8.497925 12.746888 16.995851 25.493776 16.995851h8.497925c33.991701-12.746888 63.73444-29.742739 89.228216-46.738589 25.493776 21.244813 59.485477 42.489627 97.726141 55.236514h8.497925c12.746888 0 21.244813-8.497925 25.493776-16.99585 4.248963-16.995851-4.248963-29.742739-16.99585-33.991701zM892.282158 743.568465c-16.995851-21.244813-25.493776-38.240664-33.991702-50.987552h67.983403c-8.497925 12.746888-16.995851 29.742739-33.991701 50.987552z" fill="${color}"></path>
<path d="M926.273859 926.273859h8.497925c8.497925 0 21.244813-4.248963 25.493776-16.995851 4.248963-12.746888 0-29.742739-12.746888-33.991701l-72.232365-21.244813c-12.746888-4.248963-29.742739 0-33.991701 12.746888-4.248963 12.746888 0 29.742739 12.746888 33.991701l72.232365 25.493776zM960.26556 973.012448l-101.975104-46.738589c-12.746888-4.248963-29.742739 0-33.991701 12.746888-4.248963 12.746888 0 29.742739 12.746888 33.991701l101.975104 46.738589c4.248963 0 8.497925 4.248963 8.497925 4.248963 8.497925 0 16.995851-4.248963 21.244813-16.995851 12.746888-16.995851 4.248963-29.742739-8.497925-33.991701z" fill="${color}"></path>
</svg>
`)}`
}
2 months ago
/**
* SVG
*/
2 months ago
export const createLocationIconSVG = (size: number = 24,color: string='#1296db') => {
2 months ago
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>
`)}`
}
1 week ago
const styleCache: { [key: string]: Style } = {
};
2 months ago
/**
*
*/
export const createMarkerStyle = (
2 months ago
color: string,
2 months ago
isCluster: boolean = false,
clusterSize?: number
) => {
if (isCluster && clusterSize) {
// 聚合标记样式
1 week ago
const key=`${color}-${clusterSize}`
if(styleCache[key]) return styleCache[key]
styleCache[key]= new Style({
2 months ago
image: new Circle({
1 week ago
radius: Math.min(20 + clusterSize, 40),
2 months ago
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'
})
})
1 week ago
return styleCache[key]
2 months ago
} else {
// 单个标记样式 - 使用位置图标
1 week ago
if(styleCache[color]) return styleCache[color]
styleCache[color]= new Style({
2 months ago
image: new Icon({
2 months ago
src: createLocationIconSVG(24,color),
2 months ago
scale: 1,
2 months ago
anchor: [0.5, 0.8], // 锚点设置在底部中心
2 months ago
anchorXUnits: 'fraction',
anchorYUnits: 'fraction'
})
})
1 week ago
return styleCache[color]
2 months ago
}
}
/**
* 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>
`
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>
`
}
2 months ago
/**
* HTML
*/
export const createClusterPopupHTML = (detectorList: DetectorInfo[]) => {
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('')
}
2 months ago
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[] = []
2 months ago
features.forEach((feature) => {
const markerData = feature.get('markerData') as MarkerData
if (markerData) {
2 months ago
const status = markerData.statusStr || ''
2 months ago
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
2 months ago
}
/**
*
*/
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
})
}