import React, { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Button } from '@/client/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog'; import { Card, CardContent } from '@/client/components/ui/card'; import { toast } from 'sonner'; import { fileClient } from '@/client/api'; import type { FileType } from '@/server/modules/files/file.schema'; import MinioUploader from '@/client/admin-shadcn/components/MinioUploader'; import { Check, Upload, Eye, X } from 'lucide-react'; import { Avatar, AvatarFallback, AvatarImage } from '@/client/components/ui/avatar'; import { cn } from '@/client/lib/utils'; interface AvatarSelectorProps { value?: number; onChange: (fileId: number) => void; accept?: string; maxSize?: number; uploadPath?: string; uploadButtonText?: string; previewSize?: 'small' | 'medium' | 'large'; showPreview?: boolean; placeholder?: string; } const AvatarSelector: React.FC = ({ value, onChange, accept = 'image/*', maxSize = 2, uploadPath = '/avatars', uploadButtonText = '上传头像', previewSize = 'medium', showPreview = true, placeholder = '选择头像', }) => { const [isOpen, setIsOpen] = useState(false); const [selectedFile, setSelectedFile] = useState(null); // 获取当前选中的文件详情 const { data: currentFile } = useQuery({ queryKey: ['file-detail', value], queryFn: async () => { if (!value) return null; const response = await fileClient[':id']['$get']({ param: { id: value.toString() } }); if (response.status !== 200) throw new Error('获取文件详情失败'); return response.json(); }, enabled: !!value, }); // 当对话框打开时,设置当前选中的头像 useEffect(() => { if (isOpen && value && currentFile) { setSelectedFile(currentFile); } }, [isOpen, value, currentFile]); // 获取头像列表 const { data: filesData, isLoading } = useQuery({ queryKey: ['avatars-for-selection'] as const, queryFn: async () => { const response = await fileClient.$get({ query: { page: 1, pageSize: 50, keyword: 'image' } }); if (response.status !== 200) throw new Error('获取头像列表失败'); return response.json(); }, enabled: isOpen, }); const avatars = filesData?.data?.filter((f: any) => f?.type?.startsWith('image/')) || []; const handleSelectAvatar = (file: FileType) => { setSelectedFile(prevSelected => { // 如果点击的是已选中的头像,则取消选择 if (prevSelected?.id === file.id) { return null; } // 否则选择新的头像 return file; }); }; const handleConfirm = () => { if (!selectedFile) { toast.warning('请选择一个头像'); return; } onChange(selectedFile.id); setIsOpen(false); setSelectedFile(null); }; const handleCancel = () => { setIsOpen(false); setSelectedFile(null); }; const handleUploadSuccess = (fileKey: string, fileUrl: string, file: any) => { toast.success('头像上传成功!请从列表中选择新上传的头像'); }; const getPreviewSize = () => { switch (previewSize) { case 'small': return 'h-16 w-16'; case 'medium': return 'h-24 w-24'; case 'large': return 'h-32 w-32'; default: return 'h-24 w-24'; } }; const handleRemoveAvatar = (e: React.MouseEvent) => { e.stopPropagation(); onChange(0); }; return ( <>
{showPreview && (
setIsOpen(true)} > {currentFile ? ( ) : ( {placeholder.charAt(0).toUpperCase()} )} {currentFile && ( )}
{currentFile && (

当前: {currentFile.name}

)}
)} {!showPreview && ( )}
选择头像 从已有头像中选择,或上传新头像
{/* 上传区域 */}

上传后请从下方列表中选择新上传的头像

{/* 头像列表 */}
{isLoading ? (

加载中...

) : avatars.length > 0 ? (
{avatars.map((file) => (
handleSelectAvatar(file)} >
{file.name} {selectedFile?.id === file.id && (
)}
{ e.stopPropagation(); window.open(file.fullUrl, '_blank'); }} />

{file.name}

))}
) : (

暂无符合条件的头像

请先上传头像文件

)}
); }; export default AvatarSelector;