'use client'; import React, { useState, useRef, useCallback, use } from 'react'; import { useTusUpload, UploadMetadata } from '@/hooks/use-tus-upload'; import { cn } from '@nice/ui/lib/utils'; import { Upload, Trash2 } from 'lucide-react'; import { toast } from '@nice/ui/components/sonner'; export interface AvatarUploadProps { className?: string; value?: string; onChange?: (url: string) => void; accept?: string; maxSize?: number; disabled?: boolean; placeholder?: string; metadata?: UploadMetadata; } export function AvatarUpload({ className, value, onChange, accept = 'image/*', maxSize = 10 * 1024 * 1024, disabled = false, placeholder = '点击上传图片', metadata = {}, }: AvatarUploadProps) { const [preview, setPreview] = useState(value || null); const fileInputRef = useRef(null); React.useEffect(() => { setPreview(value || null); }, [value]); const { state, uploadFile, reset, isUploading, isSuccess, isError, isPaused, } = useTusUpload(metadata); const validateFile = useCallback((file: File): string | null => { if (!file.type.startsWith('image/')) { return '请选择图片文件'; } if (file.size > maxSize) { return `文件大小不能超过 ${Math.round(maxSize / (1024 * 1024))}MB`; } return null; }, [maxSize]); const handleFileSelect = useCallback((file: File) => { const error = validateFile(file); if (error) { toast.error(error); return; } const reader = new FileReader(); reader.onload = (e) => { setPreview(e.target?.result as string); }; reader.readAsDataURL(file); // 重置之前的状态 reset(); uploadFile(file, metadata); }, [validateFile, uploadFile, metadata, reset]); const handleInputChange = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { handleFileSelect(file); } }, [handleFileSelect]); const handleClick = useCallback(() => { if (!disabled && !isUploading && !isPaused) { fileInputRef.current?.click(); } }, [disabled, isUploading, isPaused]); React.useEffect(() => { if (isSuccess && state.uploadUrl) { toast.success('图片上传成功'); onChange?.(state.uploadUrl); } }, [isSuccess, state.uploadUrl, onChange]); React.useEffect(() => { if (isError && state.error) { toast.error(`上传失败: ${state.error}`); } }, [isError, state.error]); return (
{preview ? (
预览 {!isUploading && ( )}
) : (

{placeholder}

)} {(isUploading || isPaused) && (
{/* 进度条 */}
{/* 进度文字 */}
{state.progress}%
)}
); } export default AvatarUpload;