fix: insights crash, dup grids, .map() guards

Add /api/insights/cards endpoint with proper card format matching
frontend expectations. Fixes "Cannot read properties of undefined
(reading 'map')" crash. Switch frontend to use new endpoint.

Replace alert marker rectangles with circleMarkers so they don't
look like a second grid. Default showAlertMarkers to false.

Add || [] guards on data.features.map() and alerts.map().
Reduce RiskMap grid count 3000→1500, debounce 150ms→300ms.
This commit is contained in:
2026-06-05 02:37:03 +08:00
parent e64ca3b4f5
commit 58a6df0e06
9 changed files with 185 additions and 26 deletions

View File

@@ -34,7 +34,7 @@ export function AlertsDashboard() {
const [selectedPriority, setSelectedPriority] = useState<'all' | 'P1' | 'P2'>('all');
const [sortBy, setSortBy] = useState<'risk' | 'time'>('risk');
const [showMap, setShowMap] = useState(true);
const [showAlertMarkers, setShowAlertMarkers] = useState(true);
const [showAlertMarkers, setShowAlertMarkers] = useState(false);
const [selectedAlert, setSelectedAlert] = useState<string | null>(null);
const [riskRange, setRiskRange] = useState<[number, number]>([0.6, 1.0]);
const [debouncedRiskRange, setDebouncedRiskRange] = useState<[number, number]>([0.6, 1.0]);
@@ -59,7 +59,7 @@ export function AlertsDashboard() {
}, [fetchRiskMap, fetchAlerts]);
const extendedAlerts: ExtendedAlert[] = useMemo(() => {
return alerts.map((alert) => {
return (alerts || []).map((alert) => {
const forecastDate = new Date(alert.forecast_time);
const now = new Date();
const diffDays = Math.ceil((forecastDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));

View File

@@ -54,28 +54,28 @@ export function Insights() {
? [
{
label: '总洞察数',
value: insights.total_insights,
value: insights.total_insights || 0,
icon: Lightbulb,
color: 'text-primary',
bg: 'bg-primary-muted',
},
{
label: '预警',
value: insights.warning_count + ((insights as any).danger_count || 0),
value: (insights.warning_count || 0) + ((insights as any).danger_count || 0),
icon: AlertTriangle,
color: 'text-warning',
bg: 'bg-warning-light',
},
{
label: '正常',
value: insights.success_count,
value: insights.success_count || 0,
icon: CheckCircle,
color: 'text-success',
bg: 'bg-success-light',
},
{
label: '信息',
value: insights.info_count,
value: insights.info_count || 0,
icon: Info,
color: 'text-primary',
bg: 'bg-primary-muted',
@@ -130,7 +130,7 @@ export function Insights() {
{insights && (
<div className="grid grid-cols-2 gap-4">
{insights.cards.map((card) => {
{(insights.cards || []).map((card) => {
const config = TYPE_CONFIG[card.type];
const Icon = config.icon;
return (