"use client"; import { useState, useRef, useCallback } from "react"; import { toast } from "sonner"; import { Trash2, Upload, Loader2, GripVertical, X, ChevronLeft, ChevronRight, Eye, } from "lucide-react"; import { Button } from "@/components/ui/button"; interface Screenshot { id: string; imageUrl: string; sortOrder: number; } interface Props { addonId: string; initial: Screenshot[]; } export function AddonScreenshots({ addonId, initial }: Props) { const [items, setItems] = useState(initial); const [uploading, setUploading] = useState(false); const [previewIdx, setPreviewIdx] = useState(null); const [dragId, setDragId] = useState(null); const [dragOverId, setDragOverId] = useState(null); const fileRef = useRef(null); const endpoint = `/api/addons/${addonId}/screenshots`; const handleUpload = async (e: React.ChangeEvent) => { const files = e.target.files; if (!files?.length) return; setUploading(true); try { for (const file of Array.from(files)) { const fd = new FormData(); fd.append("file", file); const uploadRes = await fetch("/api/upload", { method: "POST", body: fd }); if (!uploadRes.ok) throw new Error("上传失败"); const { filePath } = await uploadRes.json(); const maxSort = items.reduce((m, it) => Math.max(m, it.sortOrder), -1); const res = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ imageUrl: filePath, sortOrder: maxSort + 1 }), }); if (!res.ok) throw new Error("保存失败"); const created = await res.json(); setItems((prev) => [...prev, created]); } toast.success("截图上传成功"); } catch { toast.error("上传失败"); } finally { setUploading(false); if (fileRef.current) fileRef.current.value = ""; } }; const handleDelete = async (id: string) => { if (!confirm("确认删除该截图?")) return; const res = await fetch(`${endpoint}/${id}`, { method: "DELETE" }); if (res.ok) { setItems((prev) => prev.filter((it) => it.id !== id)); toast.success("已删除"); } else { toast.error("删除失败"); } }; const persistSortOrder = useCallback( async (reordered: Screenshot[]) => { try { await Promise.all( reordered.map((item, idx) => fetch(`${endpoint}/${item.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sortOrder: idx }), }) ) ); } catch { toast.error("排序保存失败"); } }, [endpoint] ); const handleDragStart = (e: React.DragEvent, id: string) => { setDragId(id); e.dataTransfer.effectAllowed = "move"; }; const handleDragOver = (e: React.DragEvent, id: string) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; if (dragId && dragId !== id) setDragOverId(id); }; const handleDrop = (e: React.DragEvent, targetId: string) => { e.preventDefault(); if (!dragId || dragId === targetId) { setDragId(null); setDragOverId(null); return; } const fromIdx = items.findIndex((it) => it.id === dragId); const toIdx = items.findIndex((it) => it.id === targetId); if (fromIdx === -1 || toIdx === -1) return; const newItems = [...items]; const [moved] = newItems.splice(fromIdx, 1); newItems.splice(toIdx, 0, moved); const updated = newItems.map((item, i) => ({ ...item, sortOrder: i })); setItems(updated); setDragId(null); setDragOverId(null); persistSortOrder(updated); toast.success("排序已更新"); }; const handleDragEnd = () => { setDragId(null); setDragOverId(null); }; return (
共 {items.length} 张 · 拖拽可调整顺序
{items.length === 0 ? (

暂无截图,点击上方按钮上传

) : (
{items.map((item, idx) => (
handleDragStart(e, item.id)} onDragOver={(e) => handleDragOver(e, item.id)} onDrop={(e) => handleDrop(e, item.id)} onDragEnd={handleDragEnd} className={`flex items-center gap-3 rounded-lg border p-2 transition-all ${ dragId === item.id ? "scale-[0.98] bg-muted/50 opacity-40" : dragOverId === item.id ? "bg-primary/5 ring-2 ring-primary" : "bg-card hover:bg-muted/30" }`} >
{idx + 1}
setPreviewIdx(idx)} >
{item.imageUrl}
))}
)} {/* Lightbox */} {previewIdx !== null && items[previewIdx] && (
setPreviewIdx(null)} > {items.length > 1 && ( <> )}
e.stopPropagation()} > {previewIdx + 1} / {items.length}
)}
); }