refactor: persist mileage drill context in URL
This commit is contained in:
@@ -18,6 +18,11 @@ import Blur from '../../components/Blur';
|
||||
import PlateMultiSelect from './PlateMultiSelect';
|
||||
import { exportMileageXlsx } from './xlsx-export';
|
||||
import VehicleDetailModal from './VehicleDetailModal';
|
||||
import {
|
||||
buildMileageDrillUrl,
|
||||
parseMileageDrillContext,
|
||||
type MileageDrillContext,
|
||||
} from './drill-context';
|
||||
|
||||
const HIGH_MILEAGE_ALERT_TARGETS = new Set([
|
||||
'交投40辆4.5T普货',
|
||||
@@ -444,6 +449,9 @@ const BatchMultiSelect = ({
|
||||
};
|
||||
|
||||
export default function MonitoringView() {
|
||||
const [drillContext, setDrillContext] = useState<MileageDrillContext>(() => (
|
||||
parseMileageDrillContext(window.location.search)
|
||||
));
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [filterDept, setFilterDept] = useState('All');
|
||||
const [sortBy, setSortBy] = useState<'today' | 'total' | 'statisticTime'>('today');
|
||||
@@ -456,7 +464,9 @@ export default function MonitoringView() {
|
||||
const [fullscreenLoading, setFullscreenLoading] = useState(false);
|
||||
|
||||
// New filters from image
|
||||
const [filterPlates, setFilterPlates] = useState<string[]>([]);
|
||||
const [filterPlates, setFilterPlates] = useState<string[]>(() => (
|
||||
drillContext.level === 'vehicle' && drillContext.plate ? [drillContext.plate] : []
|
||||
));
|
||||
const [filterCustomer, setFilterCustomer] = useState('All');
|
||||
const [filterProject, setFilterProject] = useState('All');
|
||||
const [filterEntity, setFilterEntity] = useState('All');
|
||||
@@ -468,10 +478,9 @@ export default function MonitoringView() {
|
||||
const [filterMileageRange, setFilterMileageRange] = useState({ min: '', max: '' });
|
||||
const [appliedMileageRange, setAppliedMileageRange] = useState({ min: '', max: '' });
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [detailVehicle, setDetailVehicle] = useState<MonitoringVehicle | null>(null);
|
||||
const [rangeStart, setRangeStart] = useState(defaultMileageDate);
|
||||
const [rangeEnd, setRangeEnd] = useState(defaultMileageDate);
|
||||
const [sourcePriority, setSourcePriority] = useState<MileageSourceGroup[]>(['instrument']);
|
||||
const [rangeStart, setRangeStart] = useState(() => drillContext.startDate || defaultMileageDate());
|
||||
const [rangeEnd, setRangeEnd] = useState(() => drillContext.endDate || defaultMileageDate());
|
||||
const [sourcePriority, setSourcePriority] = useState<MileageSourceGroup[]>(drillContext.sourcePriority);
|
||||
|
||||
const [vehicles, setVehicles] = useState<MonitoringVehicle[]>([]);
|
||||
const [stats, setStats] = useState<MonitoringStats>({ totalToday: 0, totalAll: 0, vehicleCount: 0, yesterdayTotal: 0 });
|
||||
@@ -488,12 +497,53 @@ export default function MonitoringView() {
|
||||
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||
const [relativeNow, setRelativeNow] = useState(() => Date.now());
|
||||
const PAGE_SIZE = 50;
|
||||
const detailVehicle = drillContext.level === 'vehicle' && drillContext.plate
|
||||
? vehicles.find(vehicle => vehicle.plate === drillContext.plate) || null
|
||||
: null;
|
||||
|
||||
const commitDrillContext = useCallback((next: MileageDrillContext, mode: 'push' | 'replace') => {
|
||||
const url = buildMileageDrillUrl(window.location, next);
|
||||
window.history[mode === 'push' ? 'pushState' : 'replaceState'](null, '', url);
|
||||
setDrillContext(next);
|
||||
}, []);
|
||||
|
||||
const openVehicleDetail = useCallback((vehicle: MonitoringVehicle) => {
|
||||
commitDrillContext({
|
||||
level: 'vehicle',
|
||||
plate: vehicle.plate,
|
||||
startDate: effectiveRange.start,
|
||||
endDate: effectiveRange.end,
|
||||
sourcePriority,
|
||||
}, 'push');
|
||||
}, [commitDrillContext, effectiveRange.end, effectiveRange.start, sourcePriority]);
|
||||
|
||||
const closeVehicleDetail = useCallback(() => {
|
||||
commitDrillContext({
|
||||
level: 'overview',
|
||||
startDate: effectiveRange.start,
|
||||
endDate: effectiveRange.end,
|
||||
sourcePriority,
|
||||
}, 'replace');
|
||||
}, [commitDrillContext, effectiveRange.end, effectiveRange.start, sourcePriority]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => setRelativeNow(Date.now()), 5000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
const next = parseMileageDrillContext(window.location.search);
|
||||
setDrillContext(next);
|
||||
if (next.startDate) setRangeStart(next.startDate);
|
||||
if (next.endDate) setRangeEnd(next.endDate);
|
||||
setSourcePriority(next.sourcePriority);
|
||||
if (next.level === 'vehicle' && next.plate) setFilterPlates([next.plate]);
|
||||
};
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, []);
|
||||
|
||||
const departments = filterOptions.departments;
|
||||
const plateNumbers = filterOptions.plates;
|
||||
const rangeLabel = normalizeRangeLabel(effectiveRange.start, effectiveRange.end);
|
||||
@@ -1494,7 +1544,7 @@ export default function MonitoringView() {
|
||||
animate={{ opacity: 1 }}
|
||||
key={v.plate}
|
||||
className="grid cursor-pointer grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-xl border border-slate-200 bg-white px-3 py-2.5 shadow-sm transition-all hover:border-blue-200 hover:shadow-md active:bg-slate-50 md:grid-cols-[minmax(190px,1.1fr)_minmax(160px,1fr)_130px_130px] md:px-4"
|
||||
onClick={() => setDetailVehicle(v)}
|
||||
onClick={() => openVehicleDetail(v)}
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-3 overflow-hidden">
|
||||
<div className="relative flex-shrink-0">
|
||||
@@ -1588,7 +1638,7 @@ export default function MonitoringView() {
|
||||
|
||||
<VehicleDetailModal
|
||||
vehicle={detailVehicle}
|
||||
onClose={() => setDetailVehicle(null)}
|
||||
onClose={closeVehicleDetail}
|
||||
sourcePriority={sourcePriority}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user