import { hasActualValue } from './alarmReviewModel.js' // GIS 曲线和研判候选共用;按报警 ID 去重,保留实际值为 0 的事件。 export function alarmCurveChoices(rows = []) { return [...new Map(rows.map(row => [row.id, row])).values()].filter(hasActualValue) } export const levelTone = level => ({ 1: 'danger', 2: 'warning', 3: 'primary', 4: 'success' }[level] || 'info') // 只映射展示颜色,状态名称仍以接口返回值为准。 export const monitorStatusTone = status => ({ 0: 'danger', 1: 'success' }[status] || 'info') export const auditStatusTone = status => ({ 0: 'warning', 1: 'primary', 2: 'info' }[status] || 'info') export const levelColor = level => ({ 1: '#f56c6c', 2: '#e6a23c', 3: '#409eff', 4: '#67c23a' }[level] || '#909399') export const scopeFromRoute = query => ({ rootTypeId: Number(query.rootTypeId), warningCode: query.warning_code || query.warningCode || undefined }) export const sampleTime = time => Date.parse(/[zZ]$|[+-]\d\d:\d\d$/.test(time) ? time : String(time).replace(' ', 'T') + '+08:00') // 原始采样间隔超过本区间中位间隔的3倍时断线;不补零或插值。 export function seriesPoints(samples, raw = false) { const points = samples.map(p => [sampleTime(p.time), p.value]).filter(p => Number.isFinite(p[0])).sort((a, b) => a[0] - b[0]) if (!raw || points.length < 3) return points const intervals = points.slice(1).map((p, i) => p[0] - points[i][0]).filter(n => n > 0).sort((a, b) => a - b) const median = intervals[Math.floor((intervals.length - 1) / 2)] return points.flatMap((p, i) => i && p[0] - points[i - 1][0] > median * 3 ? [[points[i - 1][0] + 1, null], p] : [p]) } export function rangeParams(range) { return range?.length === 2 ? { startTime: `${range[0]} 00:00:00`, endTime: `${range[1]} 23:59:59.999999` } : {} } export function groupAlarmDevices(rows = []) { const devices = new Map() for (const row of rows) { const key = row.equipmentId || row.deviceCode if (!key) continue if (!devices.has(key)) devices.set(key, { ...row, key, name: row.deviceName || row.deviceCode, alarms: [] }) devices.get(key).alarms.push(row) } return [...devices.values()].map(device => { device.alarms.sort((a, b) => String(b.createTime).localeCompare(String(a.createTime))) const current = device.alarms.filter(row => row.alarmStatus === 0) device.alarmLevel = Math.min(...(current.length ? current : device.alarms.slice(0, 1)).map(row => Number(row.alarmLevel) || 4)) device.lng = device.longitude === null || device.longitude === '' ? NaN : Number(device.longitude) device.lat = device.latitude === null || device.latitude === '' ? NaN : Number(device.latitude) device.hasCoordinates = Number.isFinite(device.lng) && Number.isFinite(device.lat) && Math.abs(device.lng) <= 180 && Math.abs(device.lat) <= 90 && !(device.lng === 0 && device.lat === 0) device.iconUrl = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(`${device.alarmLevel}`) return device }) } export function findAlarmMenu(routes, leaf, rootTypeId, parent = '') { for (const route of routes || []) { const path = route.path?.startsWith('/') ? route.path : `${parent}/${route.path || ''}`.replace(/\/+/g, '/') let query = route.query || {} try { if (typeof query === 'string') query = JSON.parse(query) } catch { query = {} } if (path.split('/').at(-1) === leaf && Number(query.rootTypeId) === Number(rootTypeId)) return { path, query } const child = findAlarmMenu(route.children, leaf, rootTypeId, path) if (child) return child } return null }