import React, { useState } from 'react'; import { Modal, Upload, Button, Progress, message, Table, Alert, Space, Spin } from 'antd'; import { UploadOutlined, DownloadOutlined, CheckCircleOutlined, WarningOutlined, FileTextOutlined, } from '@ant-design/icons'; import { deviceAPI } from '../../api'; import { designTokens } from '../../config/theme'; const modalHeaderStyle = { display: 'flex', alignItems: 'center', gap: '8px', fontSize: '18px', fontWeight: 600, }; const ImportModal = ({ visible, deviceFields, onImport, onCancel }) => { const [step, setStep] = useState('upload'); const [isPreviewing, setIsPreviewing] = useState(false); const [isConfirming, setIsConfirming] = useState(false); const [previewLoading, setPreviewLoading] = useState(false); const [previewData, setPreviewData] = useState(null); const [selectedFile, setSelectedFile] = useState(null); const [importProgress, setImportProgress] = useState(0); const [importPhase, setImportPhase] = useState(''); const [importResult, setImportResult] = useState(null); const resetState = () => { setStep('upload'); setIsPreviewing(false); setIsConfirming(false); setPreviewLoading(false); setPreviewData(null); setSelectedFile(null); setImportProgress(0); setImportPhase(''); setImportResult(null); }; const handleClose = () => { resetState(); onCancel(); }; const handlePreview = async file => { const actualFile = file.originFileObj || file; setSelectedFile(actualFile); setPreviewLoading(true); setIsPreviewing(true); try { const response = await deviceAPI.importPreview(actualFile); if (response.success) { setPreviewData(response.data); setStep('preview'); } else { message.error(response.error || '预览失败'); resetState(); } } catch (error) { console.error('预览失败:', error); message.error(error || '预览失败,请检查文件格式'); resetState(); } finally { setPreviewLoading(false); } return false; }; const handleConfirmImport = async () => { if (!selectedFile) { message.error('请先选择文件'); return; } setIsConfirming(true); setImportProgress(0); setImportPhase('正在上传文件...'); try { await onImport(selectedFile, { onProgress: (progress, phase) => { setImportProgress(progress); setImportPhase(phase); }, onSuccess: result => { setImportResult(result); setImportProgress(100); setImportPhase('导入完成'); setIsConfirming(false); setStep('result'); }, onError: error => { setImportResult({ success: false, statistics: { total: 0, success: 0, failed: 1, errors: [{ row: 0, error: error.message || '导入失败' }], }, }); setIsConfirming(false); setStep('result'); }, }); } catch (error) { setIsConfirming(false); message.error('导入失败'); resetState(); } }; const requiredFields = deviceFields.filter(f => f.visible && f.required); const optionalFields = deviceFields.filter(f => f.visible && !f.required); const previewColumns = previewData?.fieldList ? [ ...previewData.fieldList .filter(field => field.fieldName !== 'rackId') .map(field => ({ title: field.displayName + (field.required ? ' *' : ''), dataIndex: field.fieldName, key: field.fieldName, width: 120, ellipsis: true, })), { title: '所在机房', dataIndex: 'roomName', key: 'roomName', width: 100 }, { title: '所在机柜', dataIndex: 'rackName', key: 'rackName', width: 100 }, ] : [ { title: '行号', dataIndex: '_rowNum', key: '_rowNum', width: 60 }, { title: '设备名称', dataIndex: 'name', key: 'name', width: 120 }, { title: '设备类型', dataIndex: 'type', key: 'type', width: 80 }, { title: '品牌', dataIndex: 'model', key: 'model', width: 100 }, { title: '序列号', dataIndex: 'serialNumber', key: 'serialNumber', width: 140 }, { title: '所在机房', dataIndex: 'roomName', key: 'roomName', width: 100 }, { title: '所在机柜', dataIndex: 'rackName', key: 'rackName', width: 100 }, { title: '状态', dataIndex: 'status', key: 'status', width: 80 }, ]; const getRowClassName = record => { if (record._hasError) { return 'ant-table-row-error'; } return ''; }; const handleDownloadTemplate = async () => { try { const response = await deviceAPI.getImportTemplate(); const blob = new Blob([response], { type: 'text/csv; charset=gbk' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = '设备导入模板.csv'; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); } catch (error) { console.error('下载模板失败:', error); message.error(error || '下载模板失败'); } }; const renderUploadStep = () => (
请上传CSV格式的设备数据文件
支持的编码格式:GBK
CSV文件格式要求:
| 行号 | 失败原因 |
|---|---|
| {item.row || index + 1} | {item.error || '未知错误'} |