Files
CA/frontend/src/stores/analysisStore.ts

118 lines
3.1 KiB
TypeScript
Raw Normal View History

import { create } from 'zustand';
import axios from 'axios';
import { analysisApi, insightsApi } from '@/services/api';
function isCancelError(e: unknown): boolean {
return axios.isCancel(e) || (e as Error)?.message === 'canceled';
}
interface TrendDataPoint {
date: string;
aqi: number;
pm25: number;
pm10: number;
so2: number;
no2: number;
co: number;
o3: number;
}
interface DistrictData {
district: string;
avg_aqi: number;
avg_risk: number;
high_risk_count: number;
population: number;
}
interface InsightCard {
id: string;
title: string;
description: string;
type: 'warning' | 'info' | 'success' | 'danger';
metric?: string;
metricValue?: string;
timestamp: string;
}
interface InsightsOverview {
total_insights: number;
warning_count: number;
info_count: number;
success_count: number;
cards: InsightCard[];
}
interface AnalysisState {
trendData: TrendDataPoint[];
districtData: DistrictData[];
insights: InsightsOverview | null;
isLoading: boolean;
error: string | null;
selectedDays: number;
setSelectedDays: (days: number) => void;
fetchTrend: (days?: number) => Promise<void>;
fetchDistricts: () => Promise<void>;
fetchInsights: () => Promise<void>;
clearError: () => void;
}
export const useAnalysisStore = create<AnalysisState>((set, get) => ({
trendData: [],
districtData: [],
insights: null,
isLoading: false,
error: null,
selectedDays: 7,
setSelectedDays: (days) => {
set({ selectedDays: days });
get().fetchTrend(days);
},
clearError: () => set({ error: null }),
fetchTrend: async (days = 7) => {
set({ isLoading: true, error: null });
try {
const data = await analysisApi.getTrend(days);
const trendData: TrendDataPoint[] = (data.dates || []).map((date: string, i: number) => ({
date,
aqi: Math.round((data.values?.[i] || 0.5) * 200),
pm25: Math.round((data.values?.[i] || 0.5) * 100),
pm10: Math.round((data.values?.[i] || 0.5) * 150),
so2: Math.round((data.values?.[i] || 0.5) * 30),
no2: Math.round((data.values?.[i] || 0.5) * 80),
co: Math.round((data.values?.[i] || 0.5) * 2 * 100) / 100,
o3: Math.round((data.values?.[i] || 0.5) * 150),
}));
set({ trendData, isLoading: false });
} catch (e) {
if (isCancelError(e)) return;
set({ error: (e as Error).message || '加载趋势数据失败', isLoading: false });
}
},
fetchDistricts: async () => {
set({ isLoading: true, error: null });
try {
const data = await analysisApi.getDistricts();
set({ districtData: data.districts || [], isLoading: false });
} catch (e) {
if (isCancelError(e)) return;
set({ error: (e as Error).message || '加载区域数据失败', isLoading: false });
}
},
fetchInsights: async () => {
set({ isLoading: true, error: null });
try {
const data = await insightsApi.getOverview();
set({ insights: data, isLoading: false });
} catch (e) {
if (isCancelError(e)) return;
set({ error: (e as Error).message || '加载洞察数据失败', isLoading: false });
}
},
}));