import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { AudioOutlined, CheckOutlined, PictureOutlined, ReloadOutlined, SearchOutlined, VideoCameraOutlined } from '@ant-design/icons'; import { Button, Empty, Input, message, Modal, Pagination, Select, Space, Spin, Tag, Typography } from 'antd'; import { getUploadResourceHistoryItems } from '../../api'; import type { UploadResourceHistoryItem } from '../../types'; const { Text } = Typography; type MediaTypeFilter = '' | 'image' | 'video' | 'audio'; interface UploadResourceHistoryPickerProps { open: boolean; onClose: () => void; onSelect: (items: UploadResourceHistoryItem[]) => void; allowedTypes?: Array<'image' | 'video' | 'audio'>; selectedIds?: string[]; title?: string; } const today = () => new Date().toISOString().slice(0, 10); const buildPreviewUrl = (url: string) => { if (!url) return ''; if (/^(https?:|data:|blob:)/i.test(url)) return url; const base = (import.meta.env.VITE_API_BASE || '').replace(/\/$/, ''); return `${base}${url.startsWith('/') ? '' : '/'}${url}`; }; const typeIcon = (type: string) => { if (type === 'video') return ; if (type === 'audio') return ; return ; }; const typeLabel = (type: string) => { if (type === 'video') return '视频'; if (type === 'audio') return '音频'; return '图片'; }; const UploadResourceHistoryPicker: React.FC = ({ open, onClose, onSelect, allowedTypes, selectedIds = [], title = '选择历史上传素材', }) => { const [date, setDate] = useState(today()); const [resourceType, setResourceType] = useState(''); const [keyword, setKeyword] = useState(''); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [loading, setLoading] = useState(false); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [checkedMap, setCheckedMap] = useState>(new Map()); const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]); const effectiveAllowedTypes = useMemo(() => new Set(allowedTypes || ['image', 'video', 'audio']), [allowedTypes]); const load = useCallback(async () => { if (!open) return; setLoading(true); try { const res = await getUploadResourceHistoryItems(date, { resourceType: resourceType || undefined, keyword: keyword || undefined, page, pageSize, scene: 'picker', }); const filtered = (res.items || []).filter((item) => effectiveAllowedTypes.has(item.resourceType)); setItems(filtered); setTotal(res.total || 0); } catch (error: any) { message.error(error?.message || '加载历史上传素材失败'); setItems([]); setTotal(0); } finally { setLoading(false); } }, [date, effectiveAllowedTypes, keyword, open, page, pageSize, resourceType]); useEffect(() => { if (open) load(); }, [open, load]); useEffect(() => { if (open) setCheckedMap(new Map()); }, [open]); const toggle = (item: UploadResourceHistoryItem) => { if (selectedIdSet.has(item.id)) { message.warning('该素材已经在参考内容中'); return; } setCheckedMap((prev) => { const next = new Map(prev); if (next.has(item.id)) next.delete(item.id); else next.set(item.id, item); return next; }); }; const confirm = () => { const selected = Array.from(checkedMap.values()); if (selected.length === 0) { message.warning('请先选择历史素材'); return; } onSelect(selected); setCheckedMap(new Map()); onClose(); }; const typeOptions = useMemo(() => { const all = [ { label: '全部', value: '' }, { label: '图片', value: 'image' }, { label: '视频', value: 'video' }, { label: '音频', value: 'audio' }, ]; return all.filter((opt) => !opt.value || effectiveAllowedTypes.has(opt.value as any)); }, [effectiveAllowedTypes]); return ( 取消, , ]} > { setDate(e.target.value || today()); setPage(1); }} style={{ width: 160 }} /> } placeholder="搜索文件名" style={{ width: 220 }} onChange={(e) => setKeyword(e.target.value)} onPressEnter={() => { setPage(1); load(); }} /> {items.length === 0 ? ( ) : (
{items.map((item) => { const checked = checkedMap.has(item.id); const disabled = selectedIdSet.has(item.id); const preview = buildPreviewUrl(item.previewUrl || item.displayUrl || item.resourceUrl); return (
!disabled && toggle(item)} style={{ position: 'relative', borderRadius: 14, border: checked ? '2px solid #8b5cf6' : '1px solid #e2e8f0', background: disabled ? '#f8fafc' : '#fff', opacity: disabled ? 0.55 : 1, cursor: disabled ? 'not-allowed' : 'pointer', overflow: 'hidden', boxShadow: checked ? '0 10px 24px rgba(139, 92, 246, 0.18)' : '0 6px 18px rgba(15,23,42,0.06)', }} >
{item.resourceType === 'image' ? ( {item.fileName ) : item.resourceType === 'video' ? (
{typeLabel(item.resourceType)} {item.durationSeconds ? {Number(item.durationSeconds).toFixed(1)}s : null} {item.fileName || item.id} {item.moduleLabel || '普通上传'}
{checked && (
)} {disabled &&
已选
}
); })}
{ setPage(nextPage); setPageSize(nextSize); }} />
)}
); }; export default UploadResourceHistoryPicker;