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()}
|
车牌号
|
在线状态 |
客户项目
|
业务部门
|
{
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' ? :
)}
|
状态 |
{filteredVehicles.map((v) => (
| {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 */}
{/* Bottom Row: Quick Filters & Advanced Filter Icon */}
{/* Expandable Filter Panel */}
{isFilterOpen && (
{/* Search Key */}
{/* Plate Number */}
{/* Date Range */}
{/* Single Date */}
{/* Project */}
{/* Department */}
{/* Entity */}
{/* Region Code */}
{/* Mileage Range */}
)}
{/* 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 && (
)}
>
);
}