import type { AiotAlarmApi } from '#/api/aiot/alarm'; const TYPE_NAMES: Record = { leave_post: '离岗检测', intrusion: '周界入侵', illegal_parking: '车辆违停', vehicle_congestion: '车辆拥堵', }; const TYPE_COLORS: Record = { leave_post: '#1890ff', intrusion: '#f5222d', illegal_parking: '#fa8c16', vehicle_congestion: '#722ed1', }; const LEVEL_NAMES: Record = { 0: '紧急', 1: '重要', 2: '普通', 3: '轻微', }; const LEVEL_COLORS: Record = { 0: '#f5222d', 1: '#fa8c16', 2: '#1890ff', 3: '#8c8c8c', }; /** 告警趋势折线图(每种类型独立一条线,都从0开始) */ export function getTrendChartOptions(data: AiotAlarmApi.TrendItem[]): any { const dates = data.map((d) => d.date.slice(5)); // MM-DD const types = Object.keys(TYPE_NAMES); return { tooltip: { trigger: 'axis', axisPointer: { type: 'line' }, }, legend: { data: types.map((t) => TYPE_NAMES[t]), bottom: 0, }, grid: { left: '3%', right: '4%', bottom: '12%', top: '8%', containLabel: true }, xAxis: { type: 'category', boundaryGap: false, data: dates, }, yAxis: { type: 'value', minInterval: 1 }, series: types.map((type) => ({ name: TYPE_NAMES[type], type: 'line', smooth: true, symbol: 'circle', symbolSize: 6, emphasis: { focus: 'series' }, itemStyle: { color: TYPE_COLORS[type] }, lineStyle: { width: 2 }, data: data.map((d) => (d[type] as number) || 0), })), }; } /** 告警类型环形图 */ export function getTypePieChartOptions( byType: Record, ): any { const data = Object.entries(byType) .map(([type, count]) => ({ name: TYPE_NAMES[type] || type, value: count, itemStyle: { color: TYPE_COLORS[type] }, })) .filter((d) => d.value > 0); const total = data.reduce((s, d) => s + d.value, 0); return { tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, legend: { bottom: 0, left: 'center' }, graphic: { type: 'text', left: 'center', top: '42%', style: { text: `${total}`, fontSize: 28, fontWeight: 'bold', fill: '#333', textAlign: 'center', }, }, series: [ { type: 'pie', radius: ['45%', '70%'], center: ['50%', '48%'], avoidLabelOverlap: false, label: { show: false }, data, }, ], }; } /** 设备告警 Top10 横向条形图 */ export function getDeviceTopChartOptions( data: AiotAlarmApi.DeviceTopItem[], ): any { const sorted = [...data].reverse(); // 最多的在上面 return { tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: '3%', right: '8%', bottom: '3%', top: '3%', containLabel: true }, xAxis: { type: 'value', minInterval: 1 }, yAxis: { type: 'category', data: sorted.map((d) => { const name = d.deviceName || d.deviceId; return name.length > 12 ? `${name.slice(0, 12)}...` : name; }), axisLabel: { fontSize: 11 }, }, series: [ { type: 'bar', data: sorted.map((d) => d.count), itemStyle: { color: { type: 'linear', x: 0, y: 0, x2: 1, y2: 0, colorStops: [ { offset: 0, color: '#1890ff' }, { offset: 1, color: '#36cfc9' }, ], }, borderRadius: [0, 4, 4, 0], }, barMaxWidth: 20, label: { show: true, position: 'right', fontSize: 11 }, }, ], }; } /** 告警级别分布柱状图 */ export function getLevelBarChartOptions( byLevel: Record, ): any { const levels = [0, 1, 2, 3]; return { tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: '3%', right: '4%', bottom: '3%', top: '8%', containLabel: true }, xAxis: { type: 'category', data: levels.map((l) => LEVEL_NAMES[l]), }, yAxis: { type: 'value', minInterval: 1 }, series: [ { type: 'bar', data: levels.map((l) => ({ value: byLevel[l] || 0, itemStyle: { color: LEVEL_COLORS[l], borderRadius: [4, 4, 0, 0] }, })), barMaxWidth: 40, label: { show: true, position: 'top', fontSize: 11 }, }, ], }; } /** 24小时时段分布柱状图 */ export function getHourDistChartOptions( data: AiotAlarmApi.HourDistItem[], ): any { const counts = data.map((d) => d.count); const maxCount = Math.max(...counts, 1); return { tooltip: { trigger: 'axis', formatter: '{b}时: {c}次' }, grid: { left: '3%', right: '4%', bottom: '3%', top: '8%', containLabel: true }, xAxis: { type: 'category', data: data.map((d) => `${d.hour}`), axisLabel: { formatter: '{value}时', fontSize: 10 }, }, yAxis: { type: 'value', minInterval: 1 }, series: [ { type: 'bar', data: counts.map((c) => ({ value: c, itemStyle: { color: c >= maxCount * 0.8 ? '#f5222d' : c >= maxCount * 0.5 ? '#fa8c16' : '#1890ff', borderRadius: [3, 3, 0, 0], }, })), barMaxWidth: 16, }, ], }; }