Files
d8d-vite-starter/src/client/admin/pages/pages_alert_trend_chart.tsx
D8D Developer b9a3c991d0 update
2025-06-27 01:56:30 +00:00

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>
);
};