import { useState, useRef } from 'react'; import { motion } from 'framer-motion'; import { Upload, FileSpreadsheet, Database, History, CheckCircle, AlertCircle, X, Download, Calendar, Settings } from 'lucide-react'; interface ImportTask { id: string; type: 'stock' | 'sector' | 'trade' | 'kline'; name: string; fileName: string; status: 'pending' | 'processing' | 'completed' | 'error'; progress: number; totalRecords: number; importedRecords: number; errorMessage?: string; createdAt: string; } const importTemplates = [ { type: 'stock', name: '股票基础数据', format: 'CSV/Excel', fields: ['code', 'name', 'industry', 'market_cap'] }, { type: 'sector', name: '版块数据', format: 'CSV/Excel', fields: ['code', 'name', 'parent_code'] }, { type: 'trade', name: '交易数据', format: 'CSV/Excel', fields: ['code', 'date', 'open', 'high', 'low', 'close', 'volume'] }, { type: 'kline', name: 'K线数据', format: 'CSV/Excel', fields: ['code', 'date', 'open', 'high', 'low', 'close', 'volume'] }, ]; export default function DataImport() { const [tasks, setTasks] = useState([]); const [dragActive, setDragActive] = useState(false); const [selectedType, setSelectedType] = useState('stock'); const [showTemplateModal, setShowTemplateModal] = useState(false); const fileInputRef = useRef(null); const handleDrag = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.type === 'dragenter' || e.type === 'dragover') { setDragActive(true); } else if (e.type === 'dragleave') { setDragActive(false); } }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFile(e.dataTransfer.files[0]); } }; const handleFile = (file: File) => { const newTask: ImportTask = { id: Date.now().toString(), type: selectedType as ImportTask['type'], name: importTemplates.find(t => t.type === selectedType)?.name || '数据导入', fileName: file.name, status: 'pending', progress: 0, totalRecords: 0, importedRecords: 0, createdAt: new Date().toLocaleString('zh-CN'), }; setTasks(prev => [newTask, ...prev]); // 模拟开始导入 setTimeout(() => { startImport(newTask.id); }, 500); }; const startImport = async (taskId: string) => { setTasks(prev => prev.map(t => t.id === taskId ? { ...t, status: 'processing' } : t )); // 模拟导入进度 const totalSteps = 10; for (let i = 1; i <= totalSteps; i++) { await new Promise(resolve => setTimeout(resolve, 500)); setTasks(prev => prev.map(t => t.id === taskId ? { ...t, progress: (i / totalSteps) * 100, totalRecords: 10000, importedRecords: Math.round((i / totalSteps) * 10000), } : t )); } // 模拟随机成功或失败 const success = Math.random() > 0.2; setTasks(prev => prev.map(t => t.id === taskId ? { ...t, status: success ? 'completed' : 'error', progress: success ? 100 : 60, errorMessage: success ? undefined : '部分数据格式错误,请检查文件格式', } : t )); }; const handleDeleteTask = (taskId: string) => { setTasks(prev => prev.filter(t => t.id !== taskId)); }; const getStatusIcon = (status: ImportTask['status']) => { switch (status) { case 'completed': return ; case 'error': return ; case 'processing': return
; default: return
; } }; const downloadTemplate = (type: string) => { const template = importTemplates.find(t => t.type === type); if (!template) return; const headers = template.fields.join(','); const sample = template.fields.map(() => '示例数据').join(','); const content = `${headers}\n${sample}`; const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `${type}_template.csv`; link.click(); }; return (
{/* Header */}

数据导入

批量导入股票、交易历史等数据

{/* Upload Area */} e.target.files?.[0] && handleFile(e.target.files[0])} className="hidden" />

拖拽文件到此处上传

支持 CSV、Excel 格式文件

{/* Data Type Selector */}
{importTemplates.map(template => ( ))}
{/* Import Tasks */} {tasks.length > 0 && (

导入记录

{tasks.map((task) => (
{getStatusIcon(task.status)}

{task.name}

{task.fileName}

{task.createdAt} 类型: {task.type} {task.status !== 'pending' && ( 记录: {task.importedRecords.toLocaleString()} / {task.totalRecords.toLocaleString()} )}
{task.errorMessage && (

{task.errorMessage}

)}
{task.status === 'processing' && (
导入进度 {task.progress.toFixed(0)}%
)} {task.status === 'completed' && (
导入成功
)}
))}
)} {/* Import Guide */}

导入说明

文件格式

支持 CSV (.csv) 和 Excel (.xlsx, .xls) 格式,文件大小不超过 100MB

数据验证

系统会自动验证数据格式,错误数据将被跳过并生成报告

数据去重

已存在的记录将被更新,新记录将被插入,不会重复导入

自动计算

导入交易数据后,系统会自动重新计算动量指标和排名

{/* Template Modal */} {showTemplateModal && ( setShowTemplateModal(false)} > e.stopPropagation()} >

下载导入模板

{importTemplates.map(template => (

{template.name}

字段: {template.fields.join(', ')}

))}
)}
); }