import { useEffect, useState } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, AreaChart, Area, } from 'recharts'; import { useAnalysisStore } from '@/stores/analysisStore'; import { ErrorBanner } from '@/components/ErrorBanner'; import { TrendingUp, Calendar, Activity } from 'lucide-react'; const POLLUTANT_OPTIONS = [ { key: 'aqi', label: 'AQI', color: '#2563EB', unit: '' }, { key: 'pm25', label: 'PM2.5', color: '#DC2626', unit: 'μg/m³' }, { key: 'pm10', label: 'PM10', color: '#D97706', unit: 'μg/m³' }, { key: 'so2', label: 'SO₂', color: '#7C3AED', unit: 'μg/m³' }, { key: 'no2', label: 'NO₂', color: '#059669', unit: 'μg/m³' }, { key: 'co', label: 'CO', color: '#0891B2', unit: 'mg/m³' }, { key: 'o3', label: 'O₃', color: '#EA580C', unit: 'μg/m³' }, ]; const DAY_OPTIONS = [ { label: '7天', value: 7 }, { label: '14天', value: 14 }, { label: '30天', value: 30 }, ]; export function TrendAnalysis() { const trendData = useAnalysisStore((s) => s.trendData); const isLoading = useAnalysisStore((s) => s.isLoading); const error = useAnalysisStore((s) => s.error); const clearError = useAnalysisStore((s) => s.clearError); const selectedDays = useAnalysisStore((s) => s.selectedDays); const setSelectedDays = useAnalysisStore((s) => s.setSelectedDays); const fetchTrend = useAnalysisStore((s) => s.fetchTrend); const [selectedPollutants, setSelectedPollutants] = useState(['aqi', 'pm25']); useEffect(() => { fetchTrend(selectedDays); }, [selectedDays, fetchTrend]); const togglePollutant = (key: string) => { setSelectedPollutants((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key] ); }; const formatDate = (dateStr: string) => { const d = new Date(dateStr); return `${d.getMonth() + 1}/${d.getDate()}`; }; const latestData = trendData[trendData.length - 1]; const firstData = trendData[0]; const getChange = (key: string) => { if (!latestData || !firstData) return 0; const latest = latestData[key as keyof typeof latestData] as number; const first = firstData[key as keyof typeof firstData] as number; if (!first) return 0; return ((latest - first) / first) * 100; }; return (
{error && ( { clearError(); fetchTrend(selectedDays); }} onDismiss={clearError} /> )}

趋势分析

空气质量与污染物浓度时间序列分析

时间范围:
{DAY_OPTIONS.map((opt) => ( ))}
指标选择: {POLLUTANT_OPTIONS.map((p) => ( ))}
{isLoading && (
数据加载中...
)}
污染物浓度趋势
{POLLUTANT_OPTIONS.filter((p) => selectedPollutants.includes(p.key)).map( (p) => ( ) )}
{selectedPollutants.includes('aqi') && (
AQI 变化趋势
)} {latestData && (
{POLLUTANT_OPTIONS.filter((p) => selectedPollutants.includes(p.key)).slice(0, 4).map((p) => { const value = latestData[p.key as keyof typeof latestData] as number; const change = getChange(p.key); return (
{p.label}
{typeof value === 'number' ? value.toFixed(p.key === 'co' ? 1 : 0) : value} {p.unit}
0 ? 'text-danger' : change < 0 ? 'text-success' : 'text-text-muted' }`} > {change > 0 ? '↑' : change < 0 ? '↓' : '→'} {Math.abs(change).toFixed(1)}%
); })}
)}
); }