import { useState, useEffect, useCallback } from 'react'; import { Modal, Input, Upload, Image, Empty, Spin, message } from 'antd'; import { SearchOutlined, UploadOutlined } from '@ant-design/icons'; import { resolveMediaUrl } from '../../utils/media'; import { mediaApi, type MediaItem } from '../../api/health/media'; import { uploadFile } from '../../api/upload'; interface MediaPickerProps { open: boolean; onClose: () => void; onSelect: (url: string, item?: MediaItem) => void; accept?: string; } const PAGE_SIZE = 20; export default function MediaPicker({ open, onClose, onSelect, accept = 'image/*' }: MediaPickerProps) { const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [keyword, setKeyword] = useState(''); const [loading, setLoading] = useState(false); const [uploading, setUploading] = useState(false); const loadData = useCallback(async () => { setLoading(true); try { const result = await mediaApi.list({ page, page_size: PAGE_SIZE, keyword: keyword || undefined, content_type: accept === 'image/*' ? 'image' : undefined, }); setItems(result.data); setTotal(result.total); } catch { setItems([]); } finally { setLoading(false); } }, [page, keyword, accept]); useEffect(() => { if (open) loadData(); }, [open, loadData]); const handleUpload = async (file: File) => { setUploading(true); try { await uploadFile(file); message.success('上传成功'); await loadData(); } catch { message.error('上传失败'); } finally { setUploading(false); } return false; }; const handleSelect = (item: MediaItem) => { const url = resolveMediaUrl(item.storage_path); onSelect(url, item); onClose(); }; const totalPages = Math.ceil(total / PAGE_SIZE); return (
} placeholder="搜索文件名..." value={keyword} onChange={(e) => { setKeyword(e.target.value); setPage(1); }} allowClear style={{ flex: 1 }} /> { handleUpload(file); return false; }} >
{loading ? (
) : items.length === 0 ? ( ) : ( <>
{items.map((item) => (
handleSelect(item)} style={{ cursor: 'pointer', borderRadius: 8, overflow: 'hidden', border: '1px solid #f0f0f0', background: '#fafafa', transition: 'border-color 0.2s', }} onMouseEnter={(e) => { (e.currentTarget.style.borderColor = '#1677ff'); }} onMouseLeave={(e) => { (e.currentTarget.style.borderColor = '#f0f0f0'); }} >
{item.content_type.startsWith('image/') ? ( {item.alt_text ) : ( {item.content_type.split('/')[1]} )}
{item.filename}
))}
{totalPages > 1 && (
{page} / {totalPages}
)} )}
); }