66 lines
2.8 KiB
TypeScript
66 lines
2.8 KiB
TypeScript
import { AnimatePresence, motion } from 'motion/react';
|
|
import { AlertCircle, CheckCircle2 } from 'lucide-react';
|
|
import type { UploadResult } from './types';
|
|
|
|
interface UploadFeedbackProps {
|
|
result: UploadResult | null;
|
|
error: string | null;
|
|
onCloseResult: () => void;
|
|
onCloseError: () => void;
|
|
}
|
|
|
|
export function UploadFeedback({ result, error, onCloseResult, onCloseError }: UploadFeedbackProps) {
|
|
return (
|
|
<>
|
|
<AnimatePresence>
|
|
{result && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 8 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -8 }}
|
|
className="bg-white rounded-2xl border border-emerald-100 shadow-sm p-4 flex items-start gap-3"
|
|
>
|
|
<CheckCircle2 size={18} className="text-emerald-500 flex-shrink-0 mt-0.5" />
|
|
<div className="flex-1 min-w-0">
|
|
<div className="text-[12px] font-bold text-slate-700 mb-1">
|
|
上传成功:<span className="text-slate-500 font-mono">{result.filename}</span>
|
|
</div>
|
|
<div className="grid grid-cols-2 md:grid-cols-5 gap-2 text-[11px]">
|
|
<Stat label="解析" value={result.parsed} color="text-slate-700" />
|
|
<Stat label="新增" value={result.inserted} color="text-blue-600" />
|
|
<Stat label="重复跳过" value={result.fileDuplicates + result.dbDuplicates} color="text-slate-500" />
|
|
<Stat label="内部" value={result.breakdown.internal} color="text-blue-600" />
|
|
<Stat label="外部(含无车牌)" value={result.breakdown.external} color="text-amber-600" />
|
|
</div>
|
|
</div>
|
|
<button onClick={onCloseResult} className="text-slate-300 hover:text-slate-600 text-[10px] font-bold">关闭</button>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
<AnimatePresence>
|
|
{error && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 8 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -8 }}
|
|
className="bg-red-50 rounded-2xl border border-red-200 shadow-sm p-4 flex items-start gap-3"
|
|
>
|
|
<AlertCircle size={18} className="text-red-500 flex-shrink-0 mt-0.5" />
|
|
<div className="flex-1 text-[12px] font-bold text-red-700">{error}</div>
|
|
<button onClick={onCloseError} className="text-red-300 hover:text-red-600 text-[10px] font-bold">关闭</button>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function Stat({ label, value, color }: { label: string; value: number; color: string }) {
|
|
return (
|
|
<div className="bg-slate-50 rounded-lg px-2 py-1.5">
|
|
<div className="text-[9px] text-slate-400 uppercase font-bold">{label}</div>
|
|
<div className={`text-sm font-black tabular-nums ${color}`}>{value}</div>
|
|
</div>
|
|
);
|
|
}
|