Files
iot-device-management-frontend/apps/web-antd/src/views/aiot/alarm/summary/chart-options.ts
16337 a0d4e6d05d 优化:告警趋势图从堆叠面积改为独立折线图
去掉 stack + areaStyle,每种告警类型独立一条折线,
都从 Y 轴 0 开始,更直观易读。
2026-03-19 11:42:50 +08:00

203 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { AiotAlarmApi } from '#/api/aiot/alarm';
const TYPE_NAMES: Record<string, string> = {
leave_post: '离岗检测',
intrusion: '周界入侵',
illegal_parking: '车辆违停',
vehicle_congestion: '车辆拥堵',
};
const TYPE_COLORS: Record<string, string> = {
leave_post: '#1890ff',
intrusion: '#f5222d',
illegal_parking: '#fa8c16',
vehicle_congestion: '#722ed1',
};
const LEVEL_NAMES: Record<number, string> = {
0: '紧急',
1: '重要',
2: '普通',
3: '轻微',
};
const LEVEL_COLORS: Record<number, string> = {
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<string, number>,
): 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<string, number>,
): 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,
},
],
};
}