import { useCallback, useEffect, useRef, useState } from 'react'; import { ArrowLeft, Upload, Zap } from 'lucide-react'; import { fetchJson } from '../../auth/api-client'; import { useAuth } from '../../auth/useAuth'; import FeedbackFab from '../../components/FeedbackFab'; import RotatingFooterHint from '../../components/RotatingFooterHint'; import { PageFrame } from '../../components/ui/surface'; import { BatchTable } from './import-page/BatchTable'; import { RecordFilters } from './import-page/RecordFilters'; import { RecordTable } from './import-page/RecordTable'; import { SummaryKpis } from './import-page/SummaryKpis'; import { UploadDropzone } from './import-page/UploadDropzone'; import { UploadFeedback } from './import-page/UploadFeedback'; import type { BatchRow, ListItem, OverallRow, UploadResult, VehicleKindFilter, } from './import-page/types'; function getJwt(): string | null { return sessionStorage.getItem('bi_jwt'); } async function uploadFile(file: File): Promise { const fd = new FormData(); fd.append('file', file); const token = getJwt(); const res = await fetch('/api/ele/import', { method: 'POST', headers: token ? { Authorization: `Bearer ${token}` } : {}, body: fd, }); const json = await res.json(); if (!res.ok || !json.ok) throw new Error(json.message || `上传失败 (${res.status})`); return json as UploadResult; } export default function EleImportPage() { const { user } = useAuth(); const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [overall, setOverall] = useState([]); const [batches, setBatches] = useState([]); const [filter, setFilter] = useState(''); const [batchFilter, setBatchFilter] = useState(''); const [search, setSearch] = useState(''); const [searchInput, setSearchInput] = useState(''); const [dragOver, setDragOver] = useState(false); const inputRef = useRef(null); const reload = useCallback(async () => { const params = new URLSearchParams({ page: '1', limit: '50' }); if (filter) params.set('kind', filter); if (batchFilter) params.set('batchId', batchFilter); if (search) params.set('search', search); const [list, agg, b] = await Promise.all([ fetchJson<{ items: ListItem[]; total: number }>(`/api/ele/list?${params.toString()}`), fetchJson<{ overall: OverallRow[] }>(`/api/ele/aggregate`), fetchJson<{ items: BatchRow[] }>(`/api/ele/batches`), ]); setItems(list.items); setTotal(list.total); setOverall(agg.overall); setBatches(b.items); }, [filter, batchFilter, search]); useEffect(() => { reload().catch(e => console.error(e)); }, [reload]); const handleUpload = async (selectedFile: File) => { setUploading(true); setError(null); setResult(null); try { const nextResult = await uploadFile(selectedFile); setResult(nextResult); await reload(); } catch (e) { setError(e instanceof Error ? e.message : String(e)); } finally { setUploading(false); } }; const onPick = (selectedFile: File | null) => { setFile(selectedFile); if (selectedFile) handleUpload(selectedFile); }; return ( )} > { e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={(e) => { e.preventDefault(); setDragOver(false); const droppedFile = e.dataTransfer.files?.[0]; if (droppedFile) onPick(droppedFile); }} onClick={() => inputRef.current?.click()} onInputChange={(e) => onPick(e.target.files?.[0] || null)} /> setResult(null)} onCloseError={() => setError(null)} /> setBatchFilter('')} onSelectBatch={(batchId) => setBatchFilter(batchFilter === batchId ? '' : batchId)} />
setSearchInput(e.target.value)} onSearchKeyDown={(e) => { if (e.key === 'Enter') setSearch(searchInput); }} onFilterChange={setFilter} />
); }