// TusUploader.tsx import { useCallback, useState, useEffect, forwardRef, useImperativeHandle, } 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; } export interface TusUploaderRef { reset: () => void; } interface UploadingFile { name: string; progress: number; status: "uploading" | "done" | "error"; fileId?: string; fileKey?: string; } export const TusUploader = forwardRef( ({ value = [], onChange }, ref) => { const { handleFileUpload, uploadProgress } = useTusUpload(); const [uploadingFiles, setUploadingFiles] = useState( [] ); const [completedFiles, setCompletedFiles] = useState( [] ); // 同步父组件value到completedFiles useEffect(() => { setCompletedFiles( value.map((fileId) => ({ name: `文件 ${fileId}`, progress: 100, status: "done" as const, fileId, })) ); }, [value]); // 暴露重置方法 useImperativeHandle(ref, () => ({ reset: () => { setCompletedFiles([]); setUploadingFiles([]); }, })); const handleRemoveFile = useCallback( (fileId: string) => { setCompletedFiles((prev) => prev.filter((f) => f.fileId !== fileId) ); onChange?.(value.filter((id) => id !== fileId)); }, [onChange, value] ); const handleRemoveUploadingFile = useCallback((fileKey: string) => { setUploadingFiles((prev) => prev.filter((f) => f.fileKey !== fileKey) ); }, []); const handleBeforeUpload = useCallback( (file: File) => { const fileKey = `${file.name}-${Date.now()}`; setUploadingFiles((prev) => [ ...prev, { name: file.name, progress: 0, status: "uploading", fileKey, }, ]); handleFileUpload( file, (result) => { const newValue = [...value, result.fileId]; onChange?.(newValue); setUploadingFiles((prev) => prev.filter((f) => f.fileKey !== fileKey) ); }, (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, value] ); return (

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

支持单个或批量上传文件

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