'use client'; import { useState } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Box, Typography, List, ListItem, ListItemText, IconButton, CircularProgress, Alert, TextField, Tabs, Tab, Paper, Chip, Card } from '@mui/material'; import FolderOpenIcon from '@mui/icons-material/FolderOpen'; import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; import UploadFileIcon from '@mui/icons-material/UploadFile'; import FolderZipIcon from '@mui/icons-material/FolderZip'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; import axios from 'axios'; export default function ImportDialog({ open, projectId, onClose, onSuccess }) { const { t } = useTranslation(); const [mode, setMode] = useState(0); // 0: 目录导入, 1: PDF 导入, 2: 压缩包导入 const [directories, setDirectories] = useState([]); const [loading, setLoading] = useState(false); const [inputPath, setInputPath] = useState(''); const [selectedPdf, setSelectedPdf] = useState(null); const [selectedZip, setSelectedZip] = useState(null); const handleAddDirectory = () => { if (inputPath.trim() && !directories.includes(inputPath.trim())) { setDirectories([...directories, inputPath.trim()]); setInputPath(''); } }; const handleRemoveDirectory = index => { setDirectories(directories.filter((_, i) => i !== index)); }; const handleImport = async () => { if (directories.length === 0) { toast.error(t('images.selectAtLeastOne')); return; } try { setLoading(true); const response = await axios.post(`/api/projects/${projectId}/images`, { directories }); toast.success(t('images.importSuccess', { count: response.data.count })); setDirectories([]); onSuccess?.(); } catch (error) { console.error('Failed to import images:', error); toast.error(error.response?.data?.error || t('images.importFailed')); } finally { setLoading(false); } }; const handlePdfSelect = event => { const file = event.target.files?.[0]; if (file && file.type === 'application/pdf') { setSelectedPdf(file); } else { toast.error(t('images.invalidPdfFile', { defaultValue: '请选择有效的 PDF 文件' })); } }; const handlePdfImport = async () => { if (!selectedPdf) { toast.error(t('images.selectPdfFile', { defaultValue: '请选择 PDF 文件' })); return; } try { setLoading(true); const formData = new FormData(); formData.append('file', selectedPdf); // 调用 PDF 转换 API const response = await axios.post(`/api/projects/${projectId}/images/pdf-convert`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); toast.success( t('images.pdfImportSuccess', { defaultValue: `成功从 PDF "${response.data.pdfName}" 导入 ${response.data.count} 张图片`, count: response.data.count, name: response.data.pdfName }) ); setSelectedPdf(null); onSuccess?.(); } catch (error) { console.error('Failed to import PDF:', error); toast.error(error.response?.data?.error || t('images.pdfImportFailed', { defaultValue: 'PDF 导入失败' })); } finally { setLoading(false); } }; const handleZipSelect = event => { const file = event.target.files?.[0]; if (file && file.name.toLowerCase().endsWith('.zip')) { setSelectedZip(file); } else { toast.error(t('images.invalidZipFile', { defaultValue: '请选择有效的 ZIP 文件' })); } }; const handleZipImport = async () => { if (!selectedZip) { toast.error(t('images.selectZipFile', { defaultValue: '请选择 ZIP 文件' })); return; } try { setLoading(true); const formData = new FormData(); formData.append('file', selectedZip); // 调用压缩包导入 API const response = await axios.post(`/api/projects/${projectId}/images/zip-import`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }); toast.success( t('images.zipImportSuccess', { defaultValue: `成功从压缩包 "${response.data.zipName}" 导入 ${response.data.count} 张图片`, count: response.data.count, name: response.data.zipName }) ); setSelectedZip(null); onSuccess?.(); } catch (error) { console.error('Failed to import ZIP:', error); toast.error(error.response?.data?.error || t('images.zipImportFailed', { defaultValue: '压缩包导入失败' })); } finally { setLoading(false); } }; const handleClose = () => { if (!loading) { setDirectories([]); setSelectedPdf(null); setSelectedZip(null); setMode(0); onClose(); } }; return ( {t('images.importImages')} setMode(newValue)} sx={{ mb: 3, borderBottom: 1, borderColor: 'divider' }} > } iconPosition="start" /> } iconPosition="start" /> } iconPosition="start" /> {mode === 0 ? ( <> {t('images.importTip')} setInputPath(e.target.value)} onKeyPress={e => { if (e.key === 'Enter') { handleAddDirectory(); } }} disabled={loading} sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }} /> {directories.length > 0 && ( {t('images.selectedDirectories')} ({directories.length}) {directories.map((dir, index) => ( handleRemoveDirectory(index)} disabled={loading} icon={} sx={{ borderRadius: 1.5, fontWeight: 500, maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }} /> ))} )} ) : mode === 1 ? ( <> {t('images.pdfImportTip', { defaultValue: '选择 PDF 文件,系统会自动将其转换为图片并导入' })} document.getElementById('pdf-file-input').click()} > {selectedPdf ? selectedPdf.name : t('images.clickToSelectPdf', { defaultValue: '点击选择 PDF 文件' })} {t('images.supportedFormat', { defaultValue: '支持格式:PDF' })} {selectedPdf && ( {t('images.fileSize', { defaultValue: '文件大小' })}: {(selectedPdf.size / 1024 / 1024).toFixed(2)} MB )} ) : ( <> {t('images.zipImportTip', { defaultValue: '选择 ZIP 压缩包文件,系统会自动解压并导入其中的图片' })} document.getElementById('zip-file-input').click()} > {selectedZip ? selectedZip.name : t('images.clickToSelectZip', { defaultValue: '点击选择 ZIP 文件' })} {t('images.supportedZipFormat', { defaultValue: '支持格式:ZIP' })} {selectedZip && ( {t('images.fileSize', { defaultValue: '文件大小' })}: {(selectedZip.size / 1024 / 1024).toFixed(2)} MB )} )} {mode === 0 ? ( ) : mode === 1 ? ( ) : ( )} ); }