diff --git a/src/modules/scheduling/SuggestionDetail.tsx b/src/modules/scheduling/SuggestionDetail.tsx index dc8e78d..e745ffc 100644 --- a/src/modules/scheduling/SuggestionDetail.tsx +++ b/src/modules/scheduling/SuggestionDetail.tsx @@ -1,12 +1,15 @@ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { - X, MapPin, AlertTriangle, CheckCircle, ArrowDown, ArrowUp, ArrowRight, + X, MapPin, AlertTriangle, CheckCircle, ArrowDown, ArrowUp, ArrowRight, ArrowUpDown, } from 'lucide-react'; import { motion } from 'motion/react'; import type { SchedulingSuggestion, CandidateVehicle } from './types'; import Blur from '../../components/Blur'; import SwapPreview from './SwapPreview'; +type SortKey = 'predicted' | 'current'; +type SortDir = 'asc' | 'desc'; + interface Props { suggestion: SchedulingSuggestion; onClose: () => void; @@ -25,10 +28,36 @@ function fmtRate(rate: number): string { export default function SuggestionDetail({ suggestion: s, onClose, onNotifySuccess }: Props) { const [previewCandidate, setPreviewCandidate] = useState(null); const [sentPlates, setSentPlates] = useState>(new Set()); + const [batchFilter, setBatchFilter] = useState(''); + const [sortKey, setSortKey] = useState('predicted'); + const [sortDir, setSortDir] = useState('desc'); const v = s.currentVehicle; const isRescue = s.type === 'rescue_hopeless'; + // Batch options from candidates + const batchOptions = useMemo(() => { + const set = new Set(); + for (const c of s.candidates) if (c.targetName) set.add(c.targetName); + return [...set].sort(); + }, [s.candidates]); + + // Filtered + sorted candidates + const displayCandidates = useMemo(() => { + let list = s.candidates; + if (batchFilter) list = list.filter(c => c.targetName === batchFilter); + return [...list].sort((a, b) => { + const va = sortKey === 'predicted' ? a.predictedAfterSwap : a.totalMileage; + const vb = sortKey === 'predicted' ? b.predictedAfterSwap : b.totalMileage; + return sortDir === 'desc' ? vb - va : va - vb; + }); + }, [s.candidates, batchFilter, sortKey, sortDir]); + + const toggleSort = (key: SortKey) => { + if (sortKey === key) { setSortDir(d => d === 'desc' ? 'asc' : 'desc'); } + else { setSortKey(key); setSortDir('desc'); } + }; + return (
-
-
- 当前区域所有可替换在库车辆 - {s.candidates.length} 辆 -
+
+ 当前区域可替换在库车辆 + {displayCandidates.length}/{s.candidates.length} 辆 +
+ + {/* Filter + Sort controls */} +
+ {/* Batch filter */} + + + {/* Sort buttons */} + +
- {s.candidates.map(c => { + {displayCandidates.map(c => { const sent = sentPlates.has(c.plateNumber); return (