"use client"; import { Check, ImageIcon, ImagePlus, Loader2, Search, X } from "lucide-react"; import { useCallback, useMemo, useState } from "react"; import { AgentPreviewImage } from "@/components/agents/AgentPreviewImage"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { cn } from "@/lib/utils"; import type { AgentWorkflowMaterialImage, AgentWorkflowMaterialQueryType, AgentWorkflowMaterialSearchResult, AgentWorkflowMaterialSkuOption, } from "@/types/agent"; export type CanvasAddImageDialogImage = AgentWorkflowMaterialImage; interface CanvasAddImageDialogProps { materialSearchEnabled: boolean; onAddImages: (images: CanvasAddImageDialogImage[]) => Promise | void; onCreateBlankImage: () => void; onOpenChange: (open: boolean) => void; open: boolean; } function getMaterialSearchError(payload: any, fallback = "搜索失败") { const error = payload?.error; if (typeof error === "string") return error; if (error?.message) return String(error.message); return fallback; } function normalizeImages(result?: AgentWorkflowMaterialSearchResult | null) { return Array.isArray(result?.images) ? result.images.filter((image) => String(image.imageUrl || "").trim()) : []; } function MaterialImageCard({ image, selected, onToggle, }: { image: AgentWorkflowMaterialImage; selected: boolean; onToggle: (id: string) => void; }) { return ( ); } function SkuOptionCard({ option, onSelect, }: { option: AgentWorkflowMaterialSkuOption; onSelect: (skuName: string) => void; }) { return ( ); } export function CanvasAddImageDialog({ materialSearchEnabled, onAddImages, onCreateBlankImage, onOpenChange, open, }: CanvasAddImageDialogProps) { const [query, setQuery] = useState(""); const [queryMode, setQueryMode] = useState("spu"); const [result, setResult] = useState(null); const [selectedImageIds, setSelectedImageIds] = useState([]); const [isSearching, setIsSearching] = useState(false); const [isAdding, setIsAdding] = useState(false); const [errorText, setErrorText] = useState(null); const images = useMemo(() => normalizeImages(result), [result]); const skuOptions = Array.isArray(result?.skuOptions) ? result.skuOptions : []; const selectedImages = useMemo( () => images.filter((image) => selectedImageIds.includes(image.id)), [images, selectedImageIds] ); const resetDialogState = useCallback(() => { setQuery(""); setQueryMode("spu"); setResult(null); setSelectedImageIds([]); setErrorText(null); }, []); const handleDialogOpenChange = useCallback( (nextOpen: boolean) => { if (!nextOpen) { resetDialogState(); } onOpenChange(nextOpen); }, [onOpenChange, resetDialogState] ); const closeDialog = useCallback(() => { resetDialogState(); onOpenChange(false); }, [onOpenChange, resetDialogState]); const handleSearch = async (nextQuery?: string, nextMode?: AgentWorkflowMaterialQueryType) => { if (!materialSearchEnabled) return; const keyword = String(nextQuery ?? query).trim(); const mode = nextMode || queryMode; if (!keyword) { setErrorText("请输入 SPU 或 SKU"); return; } try { setIsSearching(true); setErrorText(null); const searchParams = new URLSearchParams({ keyword, source: "primary", queryType: mode, appearanceRecognitionEnabled: "false", }); const response = await fetch( `/api/agent-workflow/material-search?${searchParams.toString()}`, { cache: "no-store" } ); const payload = await response.json().catch(() => ({})); if (!response.ok || payload?.success !== true || !payload?.data) { throw new Error(getMaterialSearchError(payload)); } setResult(payload.data); setSelectedImageIds( Array.isArray(payload.data?.recommendedImageIds) ? payload.data.recommendedImageIds.filter((id: string) => payload.data.images?.some((image: AgentWorkflowMaterialImage) => image.id === id) ) : [] ); } catch (error) { setResult(null); setSelectedImageIds([]); setErrorText(error instanceof Error ? error.message : "搜索失败"); } finally { setIsSearching(false); } }; const handleSelectSku = (skuName: string) => { setQuery(skuName); setQueryMode("sku"); void handleSearch(skuName, "sku"); }; const handleToggleImage = (imageId: string) => { setSelectedImageIds((current) => current.includes(imageId) ? current.filter((id) => id !== imageId) : [...current, imageId] ); }; const handleAddSelected = async () => { if (!selectedImages.length) return; try { setIsAdding(true); await onAddImages(selectedImages); closeDialog(); } finally { setIsAdding(false); } }; return ( 添加图片 添加空白图片节点,或从风格化素材库检索 SPU / SKU 白底图。
{materialSearchEnabled ? (
{ setQuery(event.target.value); setErrorText(null); }} onKeyDown={(event) => { if (event.key === "Enter") { void handleSearch(); } }} placeholder={queryMode === "spu" ? "输入 SPU 查询 SKU" : "输入 SKU 查询白底图"} value={query} /> {query ? ( ) : null}
) : null}
{!materialSearchEnabled ? (
风格化功能或素材搜索步骤未开启,当前只显示空白图片入口。
) : null}
{errorText ? (
{errorText}
) : null} {skuOptions.length > 0 ? (
选择 SKU
命中 {skuOptions.length} 个 SKU,选择有白底图的 SKU 继续拉取图片。
{skuOptions.map((option) => ( ))}
) : null} {images.length > 0 ? (
白底图素材 {result?.matchedSku ? ` · ${result.matchedSku}` : ""}
共 {images.length} 张,已选 {selectedImages.length} 张。
{images.map((image) => ( ))}
) : materialSearchEnabled && !isSearching && !skuOptions.length && result ? (
当前没有可添加的白底图。
) : null}
); }