"use client" import { useEffect, useMemo, useState } from "react" import { Copy, Loader2, Plus, Search } from "lucide-react" import { apiAssetUrl, copyProductLibraryAsset, listProductLibrary, type ImageRef, type ProductLibraryItem, } from "@/lib/api" import { toast } from "sonner" interface ProductLibraryPickerProps { jobId: string onPick: (ref: ImageRef, item: ProductLibraryItem) => void disabled?: boolean buttonLabel?: string title?: string compact?: boolean maxItems?: number className?: string } export function ProductLibraryPicker({ jobId, onPick, disabled = false, buttonLabel = "加入", title = "内置白底产品库", compact = false, maxItems, className = "", }: ProductLibraryPickerProps) { const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const [query, setQuery] = useState("") const [productType, setProductType] = useState("all") const [addingId, setAddingId] = useState(null) useEffect(() => { let alive = true setLoading(true) listProductLibrary() .then((next) => { if (alive) setItems(next) }) .catch((e) => { if (alive) toast.error("产品库读取失败:" + (e instanceof Error ? e.message : String(e))) }) .finally(() => { if (alive) setLoading(false) }) return () => { alive = false } }, []) const productTypes = useMemo(() => { return Array.from(new Set(items.map((item) => item.product_type).filter(Boolean))).sort() }, [items]) const filteredItems = useMemo(() => { const q = query.trim().toLowerCase() const next = items.filter((item) => { if (productType !== "all" && item.product_type !== productType) return false if (!q) return true const haystack = [ item.title, item.handle, item.product_type, item.source_path, String(item.image_index), ].join(" ").toLowerCase() return haystack.includes(q) }) return typeof maxItems === "number" ? next.slice(0, maxItems) : next }, [items, maxItems, productType, query]) const handlePick = async (item: ProductLibraryItem) => { if (disabled || addingId) return setAddingId(item.id) try { const ref = await copyProductLibraryAsset(jobId, item.id) onPick(ref, item) toast.success(`已${buttonLabel}:${item.title}`) } catch (e) { toast.error("产品图加入失败:" + (e instanceof Error ? e.message : String(e))) } finally { setAddingId(null) } } return (
{title} {filteredItems.length}/{items.length}
{loading && }
{loading ? (
读取产品库
) : filteredItems.length === 0 ? (
没有匹配的白底产品图
) : (
{filteredItems.map((item) => { const busy = addingId === item.id return (
{item.title}
{item.width}×{item.height}
{item.title}
#{item.image_index} · {item.product_type || "SKG"}
) })}
)}
) }