import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { Truck, Search, Filter, ChevronDown, Maximize2, Minimize2, RotateCcw, ArrowUp, ArrowDown, ChevronsUp, } from 'lucide-react'; import type { MonitoringVehicle, MonitoringStats, MonitoringFilters } from './types'; import { fetchMonitoring } from './api'; const SearchableSelect = ({ options, value, onChange, placeholder }: { options: string[], value: string, onChange: (val: string) => void, placeholder: string }) => { const [isOpen, setIsOpen] = useState(false); const [search, setSearch] = useState(''); const filtered = useMemo(() => { if (!search) return options; return options.filter(opt => opt.toLowerCase().includes(search.toLowerCase())); }, [options, search]); return (
setIsOpen(true)} onChange={(e) => setSearch(e.target.value)} onBlur={() => { // Delay to allow clicking an option setTimeout(() => setIsOpen(false), 200); }} />
{isOpen && (
{ onChange('All'); setSearch(''); setIsOpen(false); }} > 无限制
{filtered.map((opt: string) => (
{ onChange(opt); setSearch(''); setIsOpen(false); }} > {opt}
))} {filtered.length === 0 && (
无匹配项
)}
)}
); }; export default function MonitoringView() { const [searchTerm, setSearchTerm] = useState(''); const [filterDept, setFilterDept] = useState('All'); const [sortBy, setSortBy] = useState<'today' | 'total'>('today'); const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); const [isFilterOpen, setIsFilterOpen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false); // New filters from image const [filterPlate, setFilterPlate] = useState('All'); const [filterDateRange, setFilterDateRange] = useState({ start: '', end: '' }); const [filterDate, setFilterDate] = useState('2026-04-01'); const [filterProject, setFilterProject] = useState('All'); const [filterEntity, setFilterEntity] = useState('All'); const [filterRegionCode, setFilterRegionCode] = useState('All'); const [filterMileageRange, setFilterMileageRange] = useState({ min: '', max: '' }); const [appliedMileageRange, setAppliedMileageRange] = useState({ min: '', max: '' }); const [vehicles, setVehicles] = useState([]); const [stats, setStats] = useState({ totalToday: 0, totalAll: 0, onlineCount: 0, vehicleCount: 0 }); const [filterOptions, setFilterOptions] = useState({ departments: [], customers: [], plates: [], projects: [], entities: [] }); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [showBackToTop, setShowBackToTop] = useState(false); const PAGE_SIZE = 50; const listEndRef = useRef(null); const scrollContainerRef = useRef(null); const departments = filterOptions.departments; const plateNumbers = filterOptions.plates; const projects = filterOptions.customers; // 加载首页数据 const loadFirstPage = useCallback(() => { fetchMonitoring({ sortBy, sortOrder, limit: PAGE_SIZE, page: 1, search: searchTerm || undefined, dept: filterDept !== 'All' ? filterDept : undefined, project: filterProject !== 'All' ? filterProject : undefined, entity: filterEntity !== 'All' ? filterEntity : undefined, plate: filterPlate !== 'All' ? filterPlate : undefined, mileageMin: appliedMileageRange.min || undefined, mileageMax: appliedMileageRange.max || undefined, }).then(d => { setVehicles(d.vehicles); setStats(d.stats); setFilterOptions(d.filters); setTotal(d.total); setPage(1); setHasMore(d.page < d.totalPages); }).catch(() => {}); }, [sortBy, sortOrder, searchTerm, filterDept, filterProject, filterEntity, filterPlate, appliedMileageRange]); // 加载更多 const loadMore = useCallback(() => { if (loadingMore || !hasMore) return; const nextPage = page + 1; setLoadingMore(true); fetchMonitoring({ sortBy, sortOrder, limit: PAGE_SIZE, page: nextPage, search: searchTerm || undefined, dept: filterDept !== 'All' ? filterDept : undefined, project: filterProject !== 'All' ? filterProject : undefined, entity: filterEntity !== 'All' ? filterEntity : undefined, plate: filterPlate !== 'All' ? filterPlate : undefined, mileageMin: appliedMileageRange.min || undefined, mileageMax: appliedMileageRange.max || undefined, }).then(d => { setVehicles(prev => [...prev, ...d.vehicles]); setPage(nextPage); setHasMore(nextPage < d.totalPages); }).catch(() => {}).finally(() => setLoadingMore(false)); }, [sortBy, sortOrder, searchTerm, filterDept, filterProject, filterEntity, filterPlate, appliedMileageRange, page, loadingMore, hasMore]); // 筛选/排序变化时重新加载 useEffect(() => { loadFirstPage(); }, [loadFirstPage]); // 滚动触底检测 + 回到顶部按钮 useEffect(() => { const handleScroll = () => { const scrollY = window.scrollY; setShowBackToTop(scrollY > 600); const scrollHeight = document.documentElement.scrollHeight; const clientHeight = window.innerHeight; if (scrollHeight - scrollY - clientHeight < 200) { loadMore(); } }; window.addEventListener('scroll', handleScroll, { passive: true }); return () => window.removeEventListener('scroll', handleScroll); }, [loadMore]); const scrollToTop = () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }; const filteredVehicles = vehicles; const toggleFullscreen = () => { if (!isFullscreen) { const elem = document.documentElement; if (elem.requestFullscreen) { elem.requestFullscreen().catch(() => {}); } } else { if (document.exitFullscreen) { document.exitFullscreen().catch(() => {}); } } setIsFullscreen(!isFullscreen); }; return ( <> {/* Fullscreen Landscape View Overlay */} {isFullscreen && ( {/* Sidebar / Top Stats in Landscape */}

全屏监控

{/* KPI Cards in Fullscreen */}
今日总里程
{stats.totalToday.toLocaleString()} KM
累计总里程
{stats.totalAll.toLocaleString()} KM
监控台数
{stats.vehicleCount}
平均单车 ({sortBy === 'today' ? '今日' : '累计'})
{(stats.vehicleCount > 0 ? (sortBy === 'today' ? stats.totalToday : stats.totalAll) / stats.vehicleCount : 0).toFixed(0)} KM
{/* Main Table Area */}
车辆实时明细数据
在线
离线
未对接车机
最后更新: {new Date().toLocaleTimeString()}
{filteredVehicles.map((v) => ( ))}
车牌号
在线状态
客户项目
业务部门
{ if (sortBy === 'today') { setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc'); } else { setSortBy('today'); setSortOrder('desc'); } }} >
今日里程 {sortBy === 'today' && ( sortOrder === 'desc' ? : )}
{ if (sortBy === 'total') { setSortOrder(sortOrder === 'desc' ? 'asc' : 'desc'); } else { setSortBy('total'); setSortOrder('desc'); } }} >
累计里程 {sortBy === 'total' && ( sortOrder === 'desc' ? : )}
状态
{v.plate}
{v.isOnline ? '在线' : '离线'}
{v.customer} {v.department || v.rentStatus || ''}
{!v.isDataSynced && (
)} {v.dailyKm?.toLocaleString()} km
{!v.isDataSynced && 未对接}
{v.totalKm?.toLocaleString()} km
{v.isOnline ? '运行中' : '静止/离线'}
)}
{/* Ultra Compact Header - Two Rows */}
{/* Top Row: Title & Sort */}

里程看板

实时监控 • 40min更新
{/* Bottom Row: Quick Filters & Advanced Filter Icon */}
{/* Expandable Filter Panel */} {isFilterOpen && (
{/* Search Key */}
setSearchTerm(e.target.value)} />
{/* Plate Number */}
{/* Date Range */}
setFilterDateRange(prev => ({ ...prev, start: e.target.value }))} /> - setFilterDateRange(prev => ({ ...prev, end: e.target.value }))} />
{/* Single Date */}
setFilterDate(e.target.value)} />
{/* Project */}
{/* Department */}
{/* Entity */}
{/* Region Code */}
{/* Mileage Range */}
setFilterMileageRange(prev => ({ ...prev, min: e.target.value }))} />
setFilterMileageRange(prev => ({ ...prev, max: e.target.value }))} />
)}
{/* Active Filter Tags */} {(() => { const tags: { label: string; onClear: () => void }[] = []; if (filterDept !== 'All') tags.push({ label: `部门: ${filterDept}`, onClear: () => setFilterDept('All') }); if (filterProject !== 'All') tags.push({ label: `项目: ${filterProject}`, onClear: () => setFilterProject('All') }); if (filterEntity !== 'All') tags.push({ label: `主体: ${filterEntity}`, onClear: () => setFilterEntity('All') }); if (filterPlate !== 'All') tags.push({ label: `车牌: ${filterPlate}`, onClear: () => setFilterPlate('All') }); if (searchTerm) tags.push({ label: `搜索: ${searchTerm}`, onClear: () => setSearchTerm('') }); if (appliedMileageRange.min) tags.push({ label: `里程≥${appliedMileageRange.min}`, onClear: () => { setFilterMileageRange(prev => ({ ...prev, min: '' })); setAppliedMileageRange(prev => ({ ...prev, min: '' })); } }); if (appliedMileageRange.max) tags.push({ label: `里程≤${appliedMileageRange.max}`, onClear: () => { setFilterMileageRange(prev => ({ ...prev, max: '' })); setAppliedMileageRange(prev => ({ ...prev, max: '' })); } }); if (filterRegionCode !== 'All') tags.push({ label: `地区: ${filterRegionCode}`, onClear: () => setFilterRegionCode('All') }); if (tags.length === 0) return null; const clearAll = () => { setFilterDept('All'); setFilterProject('All'); setFilterEntity('All'); setFilterPlate('All'); setSearchTerm(''); setFilterRegionCode('All'); setFilterMileageRange({ min: '', max: '' }); setAppliedMileageRange({ min: '', max: '' }); }; return (
{tags.map((tag, i) => ( {tag.label} ))}
); })()} {/* High Density KPI Grid */}
{sortBy === 'today' ? '今日' : '累计'}总里程 (KM)
{Math.round(sortBy === 'today' ? stats.totalToday : stats.totalAll).toLocaleString()} {sortBy === 'today' && {'\u2191'}12%}
平均单车
{(stats.vehicleCount > 0 ? (sortBy === 'today' ? stats.totalToday : stats.totalAll) / stats.vehicleCount : 0).toFixed(0)}
KM/台
监控台数
{stats.vehicleCount}
{/* High-Density Vehicle List - Two Columns on Tablet, One on Mobile */}
车辆详情清单 {total} 条
{filteredVehicles.map((v) => ( { navigator.clipboard.writeText(v.plate); }} >
{v.plate} {v.isOnline ? '在线' : '离线'}
{v.department ? v.department.replace('业务', '') : v.rentStatus || ''} {v.customer || '-'}
{!v.isDataSynced && (
)}
{v.dailyKm?.toLocaleString()} km
{v.totalKm?.toLocaleString()} km {!v.isDataSynced && ( 未同步 )}
))}
{filteredVehicles.length === 0 && !loadingMore && (

未找到匹配数据

)} {/* 加载更多提示 */} {loadingMore && (
加载中...
)} {!hasMore && filteredVehicles.length > 0 && (
已加载全部 {total} 条
)}
{/* 回到顶部按钮 */} {showBackToTop && ( )} ); }