Files
web-Iot/src/components/alerts/AlarmStatistics.tsx

172 lines
5.6 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { Card, Row, Col, Space } from 'antd';
import {
BellOutlined,
ExclamationCircleOutlined,
ClockCircleOutlined,
CheckCircleOutlined
} from '@ant-design/icons';
import * as echarts from 'echarts';
// 接口引入
import { getAlarmStatistics } from '../../api/alarmApi.js';
import { useTranslation } from 'react-i18next';
const AlarmStatistics = () => {
const { t } = useTranslation();
const barChartRef = useRef(null);
const pieChartRef = useRef(null);
const [statData, setStatData] = useState({
total: 0,
levelStats: {},
pending: 0,
processing: 0,
closed: 0,
});
// 获取统计(修复:接口 code = 0 才是成功)
const fetchStat = async () => {
try {
const res = await getAlarmStatistics();
if (res.code === 200) {
setStatData(res.data || {});
}
} catch (err) { }
};
// 柱状图
useEffect(() => {
if (!barChartRef.current) return;
const chart = echarts.init(barChartRef.current);
const option = {
title: { text: t('alerts.sevenDayTrend'), textStyle: { fontSize: 14 } },
xAxis: { type: 'category', data: [t('devices.daySuffix', { day: 1 }), t('devices.daySuffix', { day: 2 }), t('devices.daySuffix', { day: 3 }), t('devices.daySuffix', { day: 4 }), t('devices.daySuffix', { day: 5 }), t('devices.daySuffix', { day: 6 }), t('devices.daySuffix', { day: 7 })] },
yAxis: { type: 'value' },
series: [{
data: [5, 8, 12, 9, 15, 7, statData.total || 2],
type: 'bar',
itemStyle: { color: '#165DFF', borderRadius: 4 }
}]
};
chart.setOption(option);
const resize = () => chart.resize();
window.addEventListener('resize', resize);
return () => {
chart.dispose();
window.removeEventListener('resize', resize);
};
}, [statData]);
// 饼图
useEffect(() => {
if (!pieChartRef.current) return;
const chart = echarts.init(pieChartRef.current);
const pieData = [
{ value: statData.levelStats?.[1] || 0, name: t('alerts.criticalAlert') },
{ value: statData.levelStats?.[2] || 0, name: t('alerts.importantAlert') },
{ value: statData.levelStats?.[3] || 0, name: t('alerts.generalAlert') },
{ value: statData.levelStats?.[4] || 0, name: t('alerts.infoAlert') },
].filter(i => i.value > 0);
const option = {
title: { text: t('alerts.alertLevelDistribution') },
tooltip: { trigger: 'item' },
legend: { bottom: 0 },
series: [{
type: 'pie',
radius: ['40%', '70%'],
data: pieData.length ? pieData : [{ value: 1, name: t('common.noData') }],
color: ['#F53F3F', '#FF7D00', '#F7BA1E', '#165DFF'],
}]
};
chart.setOption(option);
const resize = () => chart.resize();
window.addEventListener('resize', resize);
return () => {
chart.dispose();
window.removeEventListener('resize', resize);
};
}, [statData]);
useEffect(() => {
fetchStat();
}, []);
return (
<div style={{ padding: 24, background: '#f5f7fa', minHeight: '100vh' }}>
<Card title={t('alerts.alertOverview')} variant="borderless" style={{ marginBottom: 24, borderRadius: 8 }}>
<Row gutter={16} align="middle">
<Col xs={24} sm={12} md={6}>
<Space size={16}>
<div style={{ width: 48, height: 48, background: '#FFF1F0', borderRadius: '50%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<BellOutlined style={{ fontSize: 24, color: '#F53F3F' }} />
</div>
<div>
<div style={{ fontSize: 14, color: '#4E5969' }}>{t('report.totalAlerts')}</div>
<div style={{ fontSize: 24, fontWeight: 700 }}>{statData.total || 0}</div>
</div>
</Space>
</Col>
<Col xs={24} sm={12} md={6}>
<Space size={16}>
<div style={{ width: 48, height: 48, background: '#FFF1F0', borderRadius: '50%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<ExclamationCircleOutlined style={{ fontSize: 24, color: '#F53F3F' }} />
</div>
<div>
<div style={{ fontSize: 14, color: '#4E5969' }}>{t('alerts.criticalAlert')}</div>
<div style={{ fontSize: 24, fontWeight: 700 }}>{statData.levelStats?.[1] || 0}</div>
</div>
</Space>
</Col>
<Col xs={24} sm={12} md={6}>
<Space size={16}>
<div style={{ width: 48, height: 48, background: '#FFF7E8', borderRadius: '50%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<ClockCircleOutlined style={{ fontSize: 24, color: '#FF7D00' }} />
</div>
<div>
<div style={{ fontSize: 14, color: '#4E5969' }}>{t('constants.pending')}</div>
<div style={{ fontSize: 24, fontWeight: 700 }}>{statData.pending || 0}</div>
</div>
</Space>
</Col>
<Col xs={24} sm={12} md={6}>
<Space size={16}>
<div style={{ width: 48, height: 48, background: '#E8FFEA', borderRadius: '50%', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<CheckCircleOutlined style={{ fontSize: 24, color: '#00B42A' }} />
</div>
<div>
<div style={{ fontSize: 14, color: '#4E5969' }}>{t('constants.closed')}</div>
<div style={{ fontSize: 24, fontWeight: 700 }}>{statData.closed || 0}</div>
</div>
</Space>
</Col>
</Row>
</Card>
<Row gutter={16}>
<Col xs={24} md={12}>
<Card variant="borderless" style={{ borderRadius: 8 }}>
<div ref={barChartRef} style={{ width: '100%', height: 360 }} />
</Card>
</Col>
<Col xs={24} md={12}>
<Card variant="borderless" style={{ borderRadius: 8 }}>
<div ref={pieChartRef} style={{ width: '100%', height: 360 }} />
</Card>
</Col>
</Row>
</div>
);
};
export default AlarmStatistics;