"use client"; import { useMemo, useState } from "react"; import { Images, Copy, ExternalLink, Eye, Check } from "lucide-react"; import Modal from "@/components/Modal"; import AdminEmpty from "@/components/admin/AdminEmpty"; import type { MediaLibraryItem } from "@/lib/api"; import { isOwnImage, variantURL, widthSrcSet } from "@/lib/responsiveImage"; function formatBytes(n: number): string { if (n < 1024) return `${n} B`; if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`; return `${(n / 1024 / 1024).toFixed(2)} MB`; } function formatTime(iso?: string): string { if (!iso) return "—"; const d = new Date(iso); if (Number.isNaN(d.getTime())) return "—"; return d.toLocaleString("zh-CN", { hour12: false }); } function formatDims(item: MediaLibraryItem): string { if (item.width > 0 && item.height > 0) return `${item.width} × ${item.height}`; return "—"; } /** 网格缩略图:本站图走统一变体端点(磁盘缓存,256 档);非本站图原样返回 */ function thumbSrc(item: MediaLibraryItem): string { // 网格缩略走统一变体端点(256 档);非本站图原样返回 if (isOwnImage(item.url)) { return variantURL(item.url, 256); } return item.url; } /** 管理后台媒体库:全站图片网格 + 分类筛选 + 详情弹窗(WebP 优先,可查看原图) */ export default function MediaLibrary({ items, counts, total, initError, }: { items: MediaLibraryItem[]; counts: Record; total: number; initError?: string; }) { const [category, setCategory] = useState("all"); const [active, setActive] = useState(null); const [showOriginal, setShowOriginal] = useState(false); const [copied, setCopied] = useState(false); const filtered = useMemo( () => (category === "all" ? items : items.filter((i) => i.category === category)), [items, category] ); const tabs = useMemo(() => { const list: { key: string; label: string; count: number }[] = [ { key: "all", label: "全部", count: total }, ]; // 按现有条目实际分类生成 tab,保持稳定顺序 const seen = new Map(); for (const i of items) seen.set(i.category, i.category_name); for (const [key, label] of seen) { list.push({ key, label, count: counts[key] ?? 0 }); } return list; }, [items, counts, total]); const openItem = (item: MediaLibraryItem) => { setActive(item); setShowOriginal(false); setCopied(false); }; const copyLink = async (item: MediaLibraryItem) => { try { await navigator.clipboard.writeText(`${window.location.origin}${item.url}`); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch { /* 剪贴板不可用静默 */ } }; if (initError) { return ( } title="媒体库加载失败" description={initError} /> ); } if (total === 0) { return ( } title="暂无图片资源" description="站点内还没有任何上传图片" /> ); } return (
{/* 分类 tab */}
{tabs.map((t) => { const on = category === t.key; return ( ); })}
{/* 图片网格 */}
{filtered.map((item) => ( ))}
{/* 详情弹窗 */} setActive(null)} title={{active?.name ?? ""}} ariaLabel={active?.name ?? ""} description={active ? `${active.category_name}${active.uploader ? ` · 上传者 ${active.uploader}` : ""}` : undefined} maxWidthClass="max-w-2xl" dismissOnOverlay bodyClassName="" > {active && (
{active.name}
{active.original_url && (

{showOriginal ? "当前显示原图(原格式,体积更大)" : "当前显示 WebP 压缩版;站点内容引用的均为 WebP"}

)}
{[ ["分类", active.category_name], ["尺寸", formatDims(active)], ["大小", formatBytes(active.size)], ["格式", active.mime], ["上传者", active.uploader || "—"], ["时间", formatTime(active.uploaded_at)], ...(active.ref_count && active.ref_count > 1 ? [ [ "引用", `${active.ref_count} 次上传 · 去重节省 ${formatBytes(active.size * (active.ref_count - 1))}`, ], ] : []), ].map(([k, v]) => (
{k}
{v}
))} {active.source_url && ( )}
{active.original_url && ( )} 新窗口打开
)}
); }