157 lines
5.2 KiB
TypeScript
157 lines
5.2 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Button, Form, Select, Card, Typography, DatePicker
|
|
} from 'antd';
|
|
import {
|
|
useQuery,
|
|
} from '@tanstack/react-query';
|
|
import dayjs from 'dayjs';
|
|
import { Line } from "@ant-design/plots";
|
|
import 'dayjs/locale/zh-cn';
|
|
|
|
import { bigClient } from '@/client/api';
|
|
|
|
interface ChartTooltipInfo {
|
|
items: Array<Record<string, any>>;
|
|
title: string;
|
|
}
|
|
|
|
|
|
// 告警数据变化图表页面
|
|
export const AlertTrendChartPage = () => {
|
|
const [timeRange, setTimeRange] = useState<[dayjs.Dayjs, dayjs.Dayjs]>([
|
|
dayjs().subtract(1, 'day').startOf('day'),
|
|
dayjs().endOf('day')
|
|
]);
|
|
const [dimension, setDimension] = useState<'hour' | 'day' | 'month'>('hour');
|
|
|
|
const { data: alarmData, isLoading, refetch } = useQuery({
|
|
queryKey: ['adminZichanAlarm', timeRange, dimension],
|
|
queryFn: async () => {
|
|
const res = await bigClient.charts['zichan-alarm'].$get({
|
|
query:{
|
|
startTime: timeRange[0].toDate(),
|
|
endTime: timeRange[1].toDate(),
|
|
timeUnit: dimension
|
|
}
|
|
})
|
|
if(res.status !== 200) throw new Error('数据查询失败');
|
|
return await res.json();
|
|
}
|
|
});
|
|
|
|
const { Title } = Typography;
|
|
const { RangePicker } = DatePicker;
|
|
|
|
const handleSearch = () => {
|
|
refetch();
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<Title level={2}>告警数据趋势</Title>
|
|
|
|
<Card>
|
|
<Form layout="inline" style={{ marginBottom: '16px' }}>
|
|
<Form.Item label="时间范围">
|
|
<RangePicker
|
|
value={timeRange}
|
|
onChange={(dates) => dates && setTimeRange(dates as [dayjs.Dayjs, dayjs.Dayjs])}
|
|
showTime
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item label="时间维度">
|
|
<Select
|
|
value={dimension}
|
|
onChange={setDimension}
|
|
options={[
|
|
{ label: '小时', value: 'hour' },
|
|
{ label: '天', value: 'day' },
|
|
{ label: '月', value: 'month' }
|
|
]}
|
|
style={{ width: '100px' }}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item>
|
|
<Button type="primary" onClick={handleSearch}>查询</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
|
|
<div style={{ height: '500px' }}>
|
|
{!isLoading && alarmData && (
|
|
<Line
|
|
data={alarmData}
|
|
xField="time_interval"
|
|
yField="total_devices"
|
|
smooth={true}
|
|
color="#36cfc9"
|
|
label={{
|
|
position: 'top',
|
|
style: {
|
|
fill: '#000',
|
|
fontSize: 12,
|
|
fontWeight: 500,
|
|
},
|
|
text: (items: Record<string, any>) => {
|
|
const value = items['total_devices'];
|
|
|
|
// if (value === 0) return null;
|
|
|
|
// const maxValue = Math.max(...(alarmData || []).map(item => item.total_devices));
|
|
|
|
// if (value < maxValue * 0.3 && alarmData && alarmData.length > 8) return null;
|
|
|
|
return `${items['time_interval']}\n(${value})`;
|
|
},
|
|
transform: [{ type: 'overlapDodgeY' }],
|
|
}}
|
|
point={{
|
|
size: 5,
|
|
shape: 'diamond',
|
|
}}
|
|
xAxis={{
|
|
label: {
|
|
style: {
|
|
fill: '#000',
|
|
},
|
|
autoHide: true,
|
|
autoRotate: true,
|
|
},
|
|
}}
|
|
yAxis={{
|
|
label: {
|
|
style: {
|
|
fill: '#000',
|
|
},
|
|
},
|
|
}}
|
|
autoFit={true}
|
|
interaction={{
|
|
tooltip: {
|
|
render: (_: unknown, { items, title }: ChartTooltipInfo) => {
|
|
if (!items || items.length === 0) return '';
|
|
|
|
// 获取当前选中项的数据
|
|
const item = items[0];
|
|
|
|
// 根据value找到对应的完整数据项
|
|
const fullData = alarmData?.find(d => d.total_devices === item.value);
|
|
if (!fullData) return '';
|
|
|
|
return `<div class="bg-white p-2 rounded">
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 rounded-full mr-2" style="background:${item.color}"></div>
|
|
<span class="font-semibold text-gray-900">${fullData.time_interval}</span>
|
|
</div>
|
|
<p class="text-sm text-gray-800">数量: ${item.value}</p>
|
|
</div>`;
|
|
}
|
|
}
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}; |