import { useEffect, useRef, useState } from 'react' import { Link, Loader2, Trash2, Upload } from 'lucide-react' import { useAssetStore } from '@/components/asset-provider' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { addAsset, getAsset } from '@/lib/asset-store' import { cn } from '@/lib/utils' type BackgroundUploaderProps = { assetId?: string onAssetSelect: (id: string | undefined) => void className?: string } export function BackgroundUploader({ assetId, onAssetSelect, className }: BackgroundUploaderProps) { const { getAssetUrl } = useAssetStore() const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const [dragActive, setDragActive] = useState(false) const [previewUrl, setPreviewUrl] = useState(undefined) const [assetType, setAssetType] = useState<'image' | 'video' | undefined>(undefined) const [urlInput, setUrlInput] = useState('') const fileInputRef = useRef(null) // 加载预览 useEffect(() => { let active = true const loadPreview = async () => { if (!assetId) { setPreviewUrl(undefined) setAssetType(undefined) return } try { const url = await getAssetUrl(assetId) const record = await getAsset(assetId) if (active) { if (url && record) { setPreviewUrl(url) setAssetType(record.type) } else { // 如果找不到资源,可能是被删除了 onAssetSelect(undefined) } } } catch (err) { console.error('Failed to load asset preview:', err) } } loadPreview() return () => { active = false } }, [assetId, getAssetUrl, onAssetSelect]) const handleFile = async (file: File) => { setError(null) setIsLoading(true) try { // 验证文件类型 if (!file.type.startsWith('image/') && !file.type.startsWith('video/')) { throw new Error('不支持的文件类型。请上传图片或视频。') } // 验证文件大小 (例如限制 50MB) if (file.size > 50 * 1024 * 1024) { throw new Error('文件过大。请上传小于 50MB 的文件。') } const id = await addAsset(file) onAssetSelect(id) setUrlInput('') // 清空 URL 输入框 } catch (err) { setError(err instanceof Error ? err.message : '上传失败') } finally { setIsLoading(false) } } const handleUrlUpload = async () => { if (!urlInput) return setError(null) setIsLoading(true) try { const response = await fetch(urlInput) if (!response.ok) { throw new Error(`下载失败: ${response.statusText}`) } const blob = await response.blob() // 尝试从 Content-Type 或 URL 推断文件名和类型 const contentType = response.headers.get('content-type') || '' const urlFilename = urlInput.split('/').pop() || 'downloaded-file' const filename = urlFilename.includes('.') ? urlFilename : `${urlFilename}.${contentType.split('/')[1] || 'bin'}` const file = new File([blob], filename, { type: contentType }) await handleFile(file) } catch (err) { setError(err instanceof Error ? err.message : '从 URL 上传失败') } finally { setIsLoading(false) } } // 拖拽处理 const handleDrag = (e: React.DragEvent) => { e.preventDefault() e.stopPropagation() if (e.type === 'dragenter' || e.type === 'dragover') { setDragActive(true) } else if (e.type === 'dragleave') { setDragActive(false) } } const handleDrop = (e: React.DragEvent) => { e.preventDefault() e.stopPropagation() setDragActive(false) if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFile(e.dataTransfer.files[0]) } } const handleClear = () => { onAssetSelect(undefined) setPreviewUrl(undefined) setAssetType(undefined) setError(null) } return (
{/* 预览区域 / 上传区域 */}
{isLoading ? (

处理中...

) : assetId && previewUrl ? (
{assetType === 'video' ? (
) : (

点击或拖拽上传

支持 JPG, PNG, GIF, MP4, WebM

)} { if (e.target.files?.[0]) { handleFile(e.target.files[0]) } // 重置 value,允许重复选择同一文件 e.target.value = '' }} />
{error && (
{error}
)}
{/* URL 上传 */}
setUrlInput(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault() handleUrlUpload() } }} />
) }