"use client"; import React, { useState, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import { useAuthStore } from "@/store/auth"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { Slider } from "@/components/ui/slider"; import { Image, ImageIcon, Wand2, Download, Share2, Eye, Loader2, Palette, Settings, Sparkles, Type, Edit3, ArrowRight, Upload, X } from "lucide-react"; import LoadingSpinner from "@/components/ui/loading-spinner"; import { imageAPI } from "@/lib/api"; import { toast } from "sonner"; import { motion } from "framer-motion"; interface GeneratedImage { id: string; url: string; prompt: string; model: string; metadata: any; } interface EditResult { id: string; url: string; original_filename?: string; edit_prompt: string; mode: string; metadata: any; } interface VariationResult { id: string; url: string; type: string; metadata: any; } interface ConfigOption { id: string; name: string; description: string; } export default function SpatialPage() { const router = useRouter(); const { isAuthenticated, isLoading: authLoading } = useAuthStore(); // Text-to-image states const [prompt, setPrompt] = useState(""); const [model, setModel] = useState("kolors"); const [template, setTemplate] = useState("custom"); const [style, setStyle] = useState("realistic"); const [size, setSize] = useState("1024x1024"); const [numImages, setNumImages] = useState(1); const [isGenerating, setIsGenerating] = useState(false); const [generatedImages, setGeneratedImages] = useState([]); // Image-to-image states const [selectedImage, setSelectedImage] = useState(null); const [imagePreview, setImagePreview] = useState(""); const [editMode, setEditMode] = useState("optimize"); const [editPrompt, setEditPrompt] = useState(""); const [strength, setStrength] = useState(0.8); const [numVariations, setNumVariations] = useState(3); const [isProcessing, setIsProcessing] = useState(false); const [editResult, setEditResult] = useState(null); const [variations, setVariations] = useState([]); // Canvas states for local editing const [isDrawing, setIsDrawing] = useState(false); const [brushSize, setBrushSize] = useState(20); const [maskData, setMaskData] = useState(""); // Configuration options const [models, setModels] = useState([]); const [templates, setTemplates] = useState([]); const [styles, setStyles] = useState([]); const [sizes, setSizes] = useState([]); const [editModes, setEditModes] = useState([]); const [stylePresets, setStylePresets] = useState([]); const canvasRef = useRef(null); const fileInputRef = useRef(null); useEffect(() => { if (!authLoading && !isAuthenticated) { router.push("/login"); return; } if (isAuthenticated) { loadConfigs(); } }, [isAuthenticated, authLoading, router]); const loadConfigs = async () => { try { const [modelsData, templatesData, stylesData, sizesData, modesData, presetsData] = await Promise.all([ imageAPI.getModels(), imageAPI.getTemplates(), imageAPI.getStyles(), imageAPI.getSizes(), imageAPI.getEditModes(), imageAPI.getStylePresets(), ]); setModels(modelsData); setTemplates(templatesData); setStyles(stylesData); setSizes(sizesData); setEditModes(modesData); setStylePresets(presetsData); } catch (error) { console.error("加载配置失败:", error); toast.error("加载配置失败"); } }; // Text-to-image functions const handleGenerate = async () => { if (!prompt.trim()) { toast.error("请输入图像描述"); return; } setIsGenerating(true); try { const response = await imageAPI.textToImage({ prompt, model, template, style, size, num_images: numImages, }); setGeneratedImages(response.images); toast.success(`成功生成 ${response.total} 张图像`); } catch (error) { console.error("生成图像失败:", error); toast.error("生成图像失败,请重试"); } finally { setIsGenerating(false); } }; // Image-to-image functions const handleImageUpload = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { setSelectedImage(file); const reader = new FileReader(); reader.onload = (e) => { setImagePreview(e.target?.result as string); }; reader.readAsDataURL(file); } }; const handleDrop = (event: React.DragEvent) => { event.preventDefault(); const file = event.dataTransfer.files[0]; if (file && file.type.startsWith("image/")) { setSelectedImage(file); const reader = new FileReader(); reader.onload = (e) => { setImagePreview(e.target?.result as string); }; reader.readAsDataURL(file); } }; const handleDragOver = (event: React.DragEvent) => { event.preventDefault(); }; const handleExecute = () => { switch (editMode) { case "optimize": handleOptimizeImage(); break; case "style_transfer": handleStyleTransfer(); break; case "local_edit": handleImageEdit(); break; case "outpaint": handleOutpaintImage(); break; default: handleImageEdit(); } }; const handleOptimizeImage = async () => { if (!selectedImage) { toast.error("请先上传图像"); return; } setIsProcessing(true); try { const response = await imageAPI.optimizeImage(selectedImage, editPrompt || "优化图像质量,增强细节,提高清晰度", strength); const result = response.result || response; // 兼容包装和非包装的响应 console.log("图像优化结果:", result); setEditResult(result); toast.success("图像优化完成"); } catch (error) { console.error("图像优化失败:", error); toast.error("图像优化失败,请重试"); } finally { setIsProcessing(false); } }; const handleStyleTransfer = async () => { if (!selectedImage) { toast.error("请先上传图像"); return; } if (!editPrompt.trim()) { toast.error("请输入风格描述"); return; } setIsProcessing(true); try { const response = await imageAPI.styleTransfer(selectedImage, editPrompt, strength); const result = response.result || response; // 兼容包装和非包装的响应 console.log("风格转换结果:", result); setEditResult(result); toast.success("风格转换完成"); } catch (error) { console.error("风格转换失败:", error); toast.error("风格转换失败,请重试"); } finally { setIsProcessing(false); } }; const handleImageEdit = async () => { if (!selectedImage) { toast.error("请先上传图像"); return; } if (!editPrompt.trim()) { toast.error("请输入编辑描述"); return; } setIsProcessing(true); try { let maskFile: File | undefined; if (editMode === "local_edit" && maskData) { const response = await fetch(maskData); const blob = await response.blob(); maskFile = new File([blob], "mask.png", { type: "image/png" }); } const response = await imageAPI.imageEdit( selectedImage, editPrompt, editMode, maskFile, strength ); const result = response.result || response; // 兼容包装和非包装的响应 console.log("图像编辑结果:", result); setEditResult(result); toast.success("图像编辑完成"); } catch (error) { console.error("图像编辑失败:", error); toast.error("图像编辑失败,请重试"); } finally { setIsProcessing(false); } }; const handleOutpaintImage = async () => { if (!selectedImage) { toast.error("请先上传图像"); return; } if (!editPrompt.trim()) { toast.error("请输入扩展描述"); return; } setIsProcessing(true); try { const response = await imageAPI.outpaintImage(selectedImage, editPrompt, strength); const result = response.result || response; // 兼容包装和非包装的响应 console.log("图像扩展结果:", result); setEditResult(result); toast.success("图像扩展完成"); } catch (error) { console.error("图像扩展失败:", error); toast.error("图像扩展失败,请重试"); } finally { setIsProcessing(false); } }; const handleGenerateVariations = async () => { if (!selectedImage) { toast.error("请先上传图像"); return; } setIsProcessing(true); try { const result = await imageAPI.imageVariations(selectedImage, numVariations); setVariations(result.variations); toast.success(`成功生成 ${result.total} 个变体`); } catch (error) { console.error("生成变体失败:", error); toast.error("生成变体失败,请重试"); } finally { setIsProcessing(false); } }; const handleDownload = (imageUrl: string, imageId: string) => { const link = document.createElement("a"); link.href = imageUrl; link.download = `image-${imageId}.png`; document.body.appendChild(link); link.click(); document.body.removeChild(link); toast.success("图像下载成功"); }; if (authLoading) { return (
); } if (!isAuthenticated) { return null; } return (
{/* Tab导航 */} 文生图 图生图 {/* 文生图 Tab */}
{/* 左侧:生成配置 */} 生成配置 配置图像生成参数,获得最佳效果 {/* 模型选择 */}
{/* 提示词模板 */}
{/* 图像描述 */}