diff --git a/src/modules/scheduling/SchedulingModule.tsx b/src/modules/scheduling/SchedulingModule.tsx new file mode 100644 index 0000000..d99720a --- /dev/null +++ b/src/modules/scheduling/SchedulingModule.tsx @@ -0,0 +1,149 @@ +import { useState, useEffect, useCallback } from 'react'; +import { Activity, AlertTriangle, CheckCircle, TrendingUp, RotateCcw } from 'lucide-react'; +import { motion } from 'motion/react'; +import { fetchSuggestions } from './api'; +import type { SchedulingResponse, SchedulingSuggestion } from './types'; +import SuggestionList from './SuggestionList'; +import SuggestionDetail from './SuggestionDetail'; + +function shortTargetName(name: string): string { + const match = name.match(/(\d+)[辆台](.+)/); + if (!match) return name; + const count = match[1]; + let desc = match[2]; + desc = desc.replace('4.5T普货', '普货'); + desc = desc.replace('4.5T冷链车', '冷藏车'); + desc = desc.replace('4.5T冷链', '冷藏车'); + return `${count}台${desc}`; +} + +export default function SchedulingModule() { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + const [selectedTargetId, setSelectedTargetId] = useState(undefined); + const [selectedSuggestion, setSelectedSuggestion] = useState(null); + + const loadData = useCallback(async () => { + setLoading(true); + try { + const result = await fetchSuggestions(selectedTargetId); + setData(result); + } finally { + setLoading(false); + } + }, [selectedTargetId]); + + useEffect(() => { + loadData(); + }, [loadData]); + + const handleNotifySuccess = useCallback(() => { + loadData(); + }, [loadData]); + + return ( +
+
+ + {/* Batch Selector */} +
+ + {data?.targets.map((target) => ( + + ))} +
+ + {loading && !data ? ( + /* Loading State */ +
+
+
+ ) : data ? ( + <> + {/* Summary Cards */} +
+ {/* Card 1: 已达标车辆 */} +
+
+ + 已达标车辆 +
+
{data.summary.qualifiedCount}
+
达标概率 ≥ 120%
+
+ + {/* Card 2: 无望达标 */} +
+
+ + 无望达标 +
+
{data.summary.hopelessCount}
+
达标概率 < 60%
+
+ + {/* Card 3: 可干预 */} +
+
+ + 可干预 +
+
{data.summary.suggestionCount}
+
+ 预计可新增达标 +{data.summary.estimatedGain} 台 +
+
+
+ + {/* Refresh Button */} +
+ +
+ + {/* Suggestion List */} + + + ) : null} + + {/* Detail Modal */} + {selectedSuggestion && ( + setSelectedSuggestion(null)} + onNotifySuccess={handleNotifySuccess} + /> + )} + +
+
+ ); +}