// ===================================================== // 頭像上傳組件 // ===================================================== 'use client'; import { useState, useRef, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Upload, X, Check, AlertCircle } from 'lucide-react'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { isValidImageType, isValidImageSize } from '@/lib/image-utils'; interface AvatarUploadProps { userId: string; currentAvatar?: string; userName?: string; onUploadSuccess?: (imageUrl: string) => void; onUploadError?: (error: string) => void; } export function AvatarUpload({ userId, currentAvatar, userName, onUploadSuccess, onUploadError }: AvatarUploadProps) { const [isUploading, setIsUploading] = useState(false); const [uploadStatus, setUploadStatus] = useState<'idle' | 'success' | 'error'>('idle'); const [errorMessage, setErrorMessage] = useState(''); const [previewUrl, setPreviewUrl] = useState(null); const fileInputRef = useRef(null); // 監聽 currentAvatar 變化,清除預覽 useEffect(() => { if (currentAvatar) { setPreviewUrl(null); } }, [currentAvatar]); const handleFileSelect = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; // 驗證文件類型 if (!isValidImageType(file)) { setErrorMessage('只支援 JPEG、PNG、WebP 格式的圖片'); setUploadStatus('error'); return; } // 驗證文件大小(限制 5MB) if (!isValidImageSize(file, 5)) { setErrorMessage('圖片大小不能超過 5MB'); setUploadStatus('error'); return; } // 創建預覽 URL const url = URL.createObjectURL(file); setPreviewUrl(url); setUploadStatus('idle'); setErrorMessage(''); }; const handleUpload = async () => { const file = fileInputRef.current?.files?.[0]; if (!file) return; setIsUploading(true); setUploadStatus('idle'); setErrorMessage(''); try { const formData = new FormData(); formData.append('avatar', file); formData.append('userId', userId); const response = await fetch('/api/upload/avatar', { method: 'POST', body: formData, }); const result = await response.json(); if (result.success) { setUploadStatus('success'); setPreviewUrl(null); // 清除預覽,讓組件顯示實際頭像 onUploadSuccess?.(result.data.imageUrl); console.log('頭像上傳成功:', result.data); } else { setUploadStatus('error'); setErrorMessage(result.error || '上傳失敗'); onUploadError?.(result.error || '上傳失敗'); } } catch (error) { setUploadStatus('error'); const errorMsg = error instanceof Error ? error.message : '上傳失敗'; setErrorMessage(errorMsg); onUploadError?.(errorMsg); } finally { setIsUploading(false); } }; const handleRemoveFile = () => { if (fileInputRef.current) { fileInputRef.current.value = ''; } setPreviewUrl(null); setUploadStatus('idle'); setErrorMessage(''); }; const getDisplayAvatar = () => { if (previewUrl) return previewUrl; if (currentAvatar && currentAvatar !== '') return currentAvatar; return null; }; const getInitials = () => { if (userName) { return userName.split(' ').map(n => n[0]).join('').toUpperCase(); } return 'U'; }; return ( 頭像上傳 支援 JPEG、PNG、WebP 格式,最大 5MB {/* 頭像預覽 */}
{getInitials()}
{/* 文件選擇 */}
{/* 預覽和操作按鈕 */} {previewUrl && (
預覽
預覽
)} {/* 上傳按鈕 */} {/* 狀態提示 */} {uploadStatus === 'success' && ( 頭像上傳成功! )} {uploadStatus === 'error' && ( {errorMessage} )}
); }