import { useState, useRef } from "react"; import { type MetaFunction, type ActionFunctionArgs } from "@remix-run/node"; import { Form, useNavigation } from "@remix-run/react"; import { UploadArea, type UploadAreaRef } from "~/components/ui/UploadArea"; import { Button } from "~/components/ui/Button"; import { messageService } from "~/components/ui/MessageModal"; import { toastService } from "~/components/ui/Toast"; import crossCheckingUploadStyles from "~/styles/pages/cross-checking-upload.css?url"; import { CaseType, CASE_TYPE_TO_TYPE_ID, type CrossCheckingUploadedFile, generateFileId, formatFileSize, batchUploadCrossCheckingFiles } from "~/api/cross-checking/cross-files-upload"; export const meta: MetaFunction = () => { return [ { title: "交叉评查上传 - 中国烟草AI合同及卷宗审核系统" }, { name: "description", content: "交叉评查案卷上传和任务创建" } ]; }; export const handle = { breadcrumb: "交叉评查上传" }; export function links() { return [ { rel: "stylesheet", href: crossCheckingUploadStyles } ]; } // 步骤枚举 const STEPS = [ { id: 1, label: "创建任务" }, { id: 2, label: "创建评查小组" }, { id: 3, label: "选择卷宗" } ]; export const action = async ({ request }: ActionFunctionArgs) => { const formData = await request.formData(); const caseType = formData.get("caseType") as string; const uploadType = formData.get("uploadType") as string; console.log("交叉评查上传:", { caseType, uploadType }); // 这里可以处理上传后的业务逻辑 // 例如创建任务记录等 return Response.json({ success: true, message: "文件上传成功" }); }; export default function CrossCheckingUpload() { // 基础状态 const [caseType, setCaseType] = useState(CaseType.ADMINISTRATIVE_PENALTY); const [currentStep] = useState(1); const navigation = useNavigation(); // 上传配置状态 - 设置默认值 const [priority] = useState("normal"); const [documentNumber] = useState(""); const [remark] = useState(""); const [isTestDocument] = useState(false); // 文件管理状态 const [singleFiles, setSingleFiles] = useState([]); const [multipleFiles, setMultipleFiles] = useState([]); const [uploadType, setUploadType] = useState<'none' | 'single' | 'multiple'>('none'); const [isUploading, setIsUploading] = useState(false); // 引用 const singleUploadRef = useRef(null); const multipleUploadRef = useRef(null); // 获取当前typeId const currentTypeId = CASE_TYPE_TO_TYPE_ID[caseType]; // 处理案卷类型切换 const handleCaseTypeChange = (type: CaseType) => { if (isUploading) { toastService.warning("上传进行中,无法切换案卷类型"); return; } setCaseType(type); // 清空已选择的文件和重置上传方式 clearAllFiles(); console.log("案卷类型切换为:", type, "typeId:", CASE_TYPE_TO_TYPE_ID[type]); }; // 清空所有文件 const clearAllFiles = () => { setSingleFiles([]); setMultipleFiles([]); setUploadType('none'); // 重置文件输入框 singleUploadRef.current?.resetFileInput(); multipleUploadRef.current?.resetFileInput(); }; // 处理单案件文件选择 const handleSingleFilesSelected = (files: FileList) => { if (uploadType === 'multiple') { toastService.warning("已选择多案件导入方式,无法选择单案件文件"); return; } const validFiles: CrossCheckingUploadedFile[] = []; let hasInvalidFiles = false; Array.from(files).forEach(file => { if (file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')) { validFiles.push({ id: generateFileId(), file, name: file.name, size: file.size, type: file.type, uploadType: 'single' }); } else { hasInvalidFiles = true; } }); if (hasInvalidFiles) { messageService.error('只能上传PDF格式的文件', { title: '文件类型错误', confirmText: '确定', }); } if (validFiles.length > 0) { setSingleFiles(prev => [...prev, ...validFiles]); setUploadType('single'); console.log("选择单案件文件:", validFiles.length, "个"); } }; // 处理多案件文件选择 const handleMultipleFilesSelected = (files: FileList) => { if (uploadType === 'single') { toastService.warning("已选择单案件导入方式,无法选择多案件文件"); return; } const validFiles: CrossCheckingUploadedFile[] = []; let hasInvalidFiles = false; Array.from(files).forEach(file => { const isZip = file.type === 'application/zip' || file.type === 'application/x-zip-compressed' || file.name.toLowerCase().endsWith('.zip'); const isRar = file.type === 'application/x-rar-compressed' || file.name.toLowerCase().endsWith('.rar'); const is7z = file.type === 'application/x-7z-compressed' || file.name.toLowerCase().endsWith('.7z'); const isTar = file.type === 'application/x-tar' || file.name.toLowerCase().endsWith('.tar'); if (isZip || isRar || is7z || isTar) { validFiles.push({ id: generateFileId(), file, name: file.name, size: file.size, type: file.type, uploadType: 'multiple' }); } else { hasInvalidFiles = true; } }); if (hasInvalidFiles) { messageService.error('只能上传ZIP或RAR格式的压缩文件', { title: '文件类型错误', confirmText: '确定', }); } if (validFiles.length > 0) { setMultipleFiles(prev => [...prev, ...validFiles]); setUploadType('multiple'); console.log("选择多案件文件:", validFiles.length, "个"); } }; // 删除单个文件 const handleRemoveFile = (fileId: string, type: 'single' | 'multiple') => { if (isUploading) { toastService.warning("上传进行中,无法删除文件"); return; } if (type === 'single') { setSingleFiles(prev => { const newFiles = prev.filter(f => f.id !== fileId); if (newFiles.length === 0) { setUploadType('none'); singleUploadRef.current?.resetFileInput(); } return newFiles; }); } else { setMultipleFiles(prev => { const newFiles = prev.filter(f => f.id !== fileId); if (newFiles.length === 0) { setUploadType('none'); multipleUploadRef.current?.resetFileInput(); } return newFiles; }); } }; // 清空文件列表 const handleClearFiles = (type: 'single' | 'multiple') => { if (isUploading) { toastService.warning("上传进行中,无法清空文件"); return; } if (type === 'single') { setSingleFiles([]); singleUploadRef.current?.resetFileInput(); } else { setMultipleFiles([]); multipleUploadRef.current?.resetFileInput(); } setUploadType('none'); }; // 处理完成上传 const handleCompleteUpload = async () => { const filesToUpload = uploadType === 'single' ? singleFiles : multipleFiles; if (filesToUpload.length === 0) { toastService.error("请先选择要上传的文件"); return; } setIsUploading(true); try { console.log("开始批量上传文件:", filesToUpload.length, "个,案卷类型:", caseType, "typeId:", currentTypeId); const result = await batchUploadCrossCheckingFiles( filesToUpload, currentTypeId, priority, documentNumber, remark, isTestDocument ); const { successes, failures } = result; if (failures.length === 0) { // 全部成功 toastService.success(`成功上传 ${successes.length} 个文件`); messageService.success(`文件上传完成!成功上传 ${successes.length} 个文件,现在可以进行下一步操作。`, { title: '上传成功', confirmText: '确定', onConfirm: () => { // 清空文件列表 clearAllFiles(); } }); } else if (successes.length === 0) { // 全部失败 toastService.error(`文件上传失败,共 ${failures.length} 个文件上传失败`); messageService.error(`所有文件上传失败。失败原因:${failures[0].error}`, { title: '上传失败', confirmText: '确定', }); } else { // 部分成功 toastService.warning(`部分文件上传成功:成功 ${successes.length} 个,失败 ${failures.length} 个`); messageService.warning( `部分文件上传完成:\n成功:${successes.length} 个文件\n失败:${failures.length} 个文件\n\n失败文件:\n${failures.map(f => `${f.file.name}: ${f.error}`).join('\n')}`, { title: '部分上传成功', confirmText: '确定', } ); } } catch (error) { console.error("批量上传失败:", error); toastService.error("文件上传过程中发生错误"); messageService.error(`文件上传失败:${error instanceof Error ? error.message : '未知错误'}`, { title: '上传失败', confirmText: '确定', }); } finally { setIsUploading(false); } }; // 检查是否可以完成 const canComplete = (singleFiles.length > 0 || multipleFiles.length > 0) && !isUploading; const isSubmitting = navigation.state === "submitting"; return (
{/* 步骤指示器 */}
{STEPS.map((step) => (
{step.id}
{step.label}
))}
{/* 案卷类型选择器 */}
{/* 文件上传区域 */}
{/* 单案件导入 */}
单案件导入 {uploadType === 'single' && singleFiles.length > 0 && ( )}
请上传案件相关PDF文件
} disabled={uploadType === 'multiple' || isUploading} /> {/* 单案件文件列表 */} {singleFiles.length > 0 && (
已选择 {singleFiles.length} 个文件:
{singleFiles.map((file) => (
{file.name} {formatFileSize(file.size)}
))}
)}
{/* 多案件导入 */}
多案件导入 {uploadType === 'multiple' && multipleFiles.length > 0 && ( )}
请上传多个案件作为压缩包zip、rar、7z、tar文件
} disabled={uploadType === 'single' || isUploading} /> {/* 多案件文件列表 */} {multipleFiles.length > 0 && (
已选择 {multipleFiles.length} 个压缩包:
{multipleFiles.map((file) => (
{file.name} {formatFileSize(file.size)}
))}
)}
{/* 完成按钮 */}
{/* 文件选择状态提示 */} {!canComplete && !isUploading && (
请至少选择一种导入方式的文件
)} {/* 上传进度提示 */} {isUploading && (
正在上传文件...

正在上传 {uploadType === 'single' ? singleFiles.length : multipleFiles.length} 个文件,请稍候

)} ); }