"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { toast } from "sonner"; import { Download, Pencil, Trash2, Check, X, Upload, Star } from "lucide-react"; interface Release { id: string; version: string; changelog: string; changelogEn: string; wowVersion: string; downloadType: string; filePath: string | null; externalUrl: string | null; gameVersion: string; downloadCount: number; isLatest: boolean; createdAt: string; addon: { name: string; slug: string }; } export function ReleasesTable({ releases: initial }: { releases: Release[] }) { const router = useRouter(); const [editingId, setEditingId] = useState(null); async function handleSetLatest(releaseId: string) { const res = await fetch(`/api/releases/${releaseId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ isLatest: true }), }); if (res.ok) { toast.success("已设为最新版本"); router.refresh(); } else { toast.error("设置失败"); } } async function handleDelete(r: Release) { if (!confirm(`确定要删除 ${r.addon.name} v${r.version} 吗?此操作不可撤销。`)) return; const res = await fetch(`/api/releases/${r.id}`, { method: "DELETE" }); if (res.ok) { toast.success("版本已删除"); router.refresh(); } else { toast.error("删除失败"); } } return (
插件 版本 WoW 游戏版本 下载方式 下载量 发布时间 状态 操作 {initial.length === 0 ? ( 暂无版本发布 ) : ( initial.map((r) => ( {r.addon.name} v{r.version} {r.wowVersion} {r.gameVersion || "-"} {r.downloadType === "local" ? "本地文件" : "外部链接"} {r.downloadCount} {new Date(r.createdAt).toLocaleDateString("zh-CN")} {r.isLatest && 最新}
{!r.isLatest && ( )}
)) )}
{editingId && ( r.id === editingId)!} onClose={() => setEditingId(null)} onSaved={() => { setEditingId(null); router.refresh(); }} /> )}
); } function ReleaseEditPanel({ release: r, onClose, onSaved, }: { release: Release; onClose: () => void; onSaved: () => void; }) { const [saving, setSaving] = useState(false); const [version, setVersion] = useState(r.version); const [changelog, setChangelog] = useState(r.changelog); const [changelogEn, setChangelogEn] = useState(r.changelogEn || ""); const [wowVersion, setWowVersion] = useState<"1.18" | "1.17">( (r.wowVersion as "1.18" | "1.17") || "1.18" ); const [gameVersion, setGameVersion] = useState(r.gameVersion); const [downloadType, setDownloadType] = useState(r.downloadType); const [externalUrl, setExternalUrl] = useState(r.externalUrl || ""); const [filePath, setFilePath] = useState(r.filePath || ""); const [uploading, setUploading] = useState(false); async function handleFileUpload(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; setUploading(true); const fd = new FormData(); fd.append("file", file); const res = await fetch("/api/upload", { method: "POST", body: fd }); if (res.ok) { const data = await res.json(); setFilePath(data.filePath); toast.success(`文件 ${data.originalName} 上传成功`); } else { toast.error("文件上传失败"); } setUploading(false); } async function handleSave() { setSaving(true); if (downloadType === "local" && !filePath) { toast.error("请先上传文件"); setSaving(false); return; } if (downloadType === "url" && !externalUrl) { toast.error("请输入外部链接"); setSaving(false); return; } const res = await fetch(`/api/releases/${r.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ version, changelog, changelogEn, wowVersion, gameVersion, downloadType, filePath: downloadType === "local" ? filePath : null, externalUrl: downloadType === "url" ? externalUrl : null, }), }); if (res.ok) { toast.success("版本更新成功"); onSaved(); } else { const err = await res.json(); toast.error(err.error || "更新失败"); } setSaving(false); } return (

编辑 {r.addon.name} v{r.version}

setVersion(e.target.value)} />
setGameVersion(e.target.value)} placeholder="1.18.1" />
{(["1.18", "1.17"] as const).map((wv) => ( ))}