import { useCallback, useState } from "react"; import { UploadOutlined, CheckCircleOutlined, DeleteOutlined, } from "@ant-design/icons"; import { Upload, Progress, Button } from "antd"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import toast from "react-hot-toast"; export interface TusUploaderProps { value?: string[]; onChange?: (value: string[]) => void; multiple?: boolean; allowTypes?: string[]; } interface UploadingFile { name: string; progress: number; status: "uploading" | "done" | "error"; fileId?: string; fileKey?: string; } export const TusUploader = ({ value = [], onChange, multiple = true, allowTypes = undefined, }: TusUploaderProps) => { const { handleFileUpload, uploadProgress } = useTusUpload(); const [uploadingFiles, setUploadingFiles] = useState([]); const [completedFiles, setCompletedFiles] = useState( () => value?.map((fileId) => ({ name: `文件 ${fileId}`, progress: 100, status: "done" as const, fileId, })) || [] ); const [uploadResults, setUploadResults] = useState(value || []); const handleRemoveFile = useCallback( (fileId: string) => { setCompletedFiles((prev) => prev.filter((f) => f.fileId !== fileId) ); setUploadResults((prev) => { const newValue = prev.filter((id) => id !== fileId); onChange?.(newValue); return newValue; }); }, [onChange] ); // 新增:处理删除上传中的失败文件 const handleRemoveUploadingFile = useCallback((fileKey: string) => { setUploadingFiles((prev) => prev.filter((f) => f.fileKey !== fileKey)); }, []); const handleBeforeUpload = useCallback( (file: File) => { if (allowTypes && !allowTypes.includes(file.type)) { toast.error(`文件类型 ${file.type} 不在允许范围内`); return Upload.LIST_IGNORE; // 使用 antd 的官方阻止方式 } const fileKey = `${file.name}-${Date.now()}`; setUploadingFiles((prev) => [ ...prev, { name: file.name, progress: 0, status: "uploading", fileKey, }, ]); handleFileUpload( file, (result) => { setCompletedFiles((prev) => [ ...prev, { name: file.name, progress: 100, status: "done", fileId: result.fileId, }, ]); setUploadingFiles((prev) => prev.filter((f) => f.fileKey !== fileKey) ); setUploadResults((prev) => { // 如果是单文件模式,则替换现有文件 const newValue = multiple ? [...prev, result.fileId] : [result.fileId]; onChange?.(newValue); return newValue; }); // 单文件模式下,清除之前的完成文件 if (!multiple) { setCompletedFiles([ { name: file.name, progress: 100, status: "done", fileId: result.fileId, }, ]); } }, (error) => { console.error("上传错误:", error); toast.error( `上传失败: ${error instanceof Error ? error.message : "未知错误"}` ); setUploadingFiles((prev) => prev.map((f) => f.fileKey === fileKey ? { ...f, status: "error" } : f ) ); }, fileKey ); return false; }, [handleFileUpload, onChange] ); return (

点击或拖拽文件到此区域进行上传

{multiple ? "支持单个或批量上传文件" : "仅支持上传单个文件"} {allowTypes && ( 允许类型: {allowTypes.join(", ")} )}

{uploadingFiles.map((file) => (
{file.name}
{file.status === "error" && (
))} {completedFiles.map((file) => (
{file.name}
))}
); };