From 1ba3d7ec830ddd49999638498b3a0fb5fa9ce64c Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 3 Feb 2026 10:38:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BE=E7=89=87=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=99=A8=E6=B7=BB=E5=8A=A0=E5=8E=BB=E6=B0=B4=E5=8D=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=8E=BB=E6=B0=B4=E5=8D=B0=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=88=B0=E7=9B=B8=E5=86=8C=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=8A=A8=E6=80=81=E5=8D=A0=E4=BD=8D=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/watermark-remove/route.ts | 17 ++++- components/image-annotator.tsx | 94 ++++++++++++++++++++++--- components/image-processing-loading.tsx | 18 ++++- components/message.tsx | 6 +- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/app/api/watermark-remove/route.ts b/app/api/watermark-remove/route.ts index 0f632c2..ac95c67 100644 --- a/app/api/watermark-remove/route.ts +++ b/app/api/watermark-remove/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { db } from "@/lib/db"; -import { systemSettings } from "@/lib/db/schema"; +import { systemSettings, generatedImage } from "@/lib/db/schema"; import { eq } from "drizzle-orm"; import { uploadFile } from "@/lib/storage/service"; @@ -192,6 +192,21 @@ export async function POST(request: NextRequest) { const processingTime = (Date.now() - startTime) / 1000; console.log(`[WatermarkRemove] 处理完成,耗时: ${processingTime}s`); + // 保存到相册/历史出图记录 + try { + await db.insert(generatedImage).values({ + userId: session.user.id, + thumbnailUrl: uploadResult.url, + originalUrl: uploadResult.url, + imageSize: "2K", + prompt: "去水印处理", + modelId: "watermark-remove", + }); + console.log(`[WatermarkRemove] 已保存到相册: ${uploadResult.url.substring(0, 80)}...`); + } catch (saveError) { + console.error("[WatermarkRemove] 保存到相册失败:", saveError); + } + return NextResponse.json({ success: true, original_url: uploadResult.url, diff --git a/components/image-annotator.tsx b/components/image-annotator.tsx index cc7d885..e31c8f8 100644 --- a/components/image-annotator.tsx +++ b/components/image-annotator.tsx @@ -4,7 +4,7 @@ import React, { useRef, useState, useEffect, useCallback } from "react"; import { createPortal } from "react-dom"; import Link from "next/link"; import { useTheme } from "next-themes"; -import { X, Pencil, Circle, ArrowRight, Type, Check, Undo, Trash2, Scissors, Sparkles, Download, Expand, Move, Maximize2, ZoomIn, ZoomOut, Loader2, Move3D, Layers, Upload as UploadIcon, Image as ImageIcon } from "lucide-react"; +import { X, Pencil, Circle, ArrowRight, Type, Check, Undo, Trash2, Scissors, Sparkles, Download, Expand, Move, Maximize2, ZoomIn, ZoomOut, Loader2, Move3D, Layers, Upload as UploadIcon, Image as ImageIcon, Droplet } from "lucide-react"; import { Button } from "./ui/button"; import { Input } from "./ui/input"; import { toast } from "sonner"; @@ -67,12 +67,13 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod const [isRemovingBg, setIsRemovingBg] = useState(false); const [isEnhancing, setIsEnhancing] = useState(false); const [isExtending, setIsExtending] = useState(false); + const [isRemovingWatermark, setIsRemovingWatermark] = useState(false); const [showAngleEditDialog, setShowAngleEditDialog] = useState(false); const [showSceneFusion, setShowSceneFusion] = useState(false); const [originalImageUrl, setOriginalImageUrl] = useState(imageUrl); const [processedImageUrl, setProcessedImageUrl] = useState(null); const [processedImageBase64, setProcessedImageBase64] = useState(null); - const [processType, setProcessType] = useState<"rmbg" | "enhance" | "extend" | "resize" | null>(null); + const [processType, setProcessType] = useState<"rmbg" | "enhance" | "extend" | "resize" | "watermark" | null>(null); const [mounted, setMounted] = useState(false); const fileInputRef = useRef(null); @@ -642,6 +643,66 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod } }; + // 去水印功能 + const handleWatermarkRemove = async () => { + if (!image || !originalImageUrl) return; + + setIsRemovingWatermark(true); + try { + console.log("[去水印] 使用原图URL:", originalImageUrl); + + // 调用去水印API + const response = await fetch("/api/watermark-remove", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + image_url: originalImageUrl, + }), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || "去水印失败"); + } + + const result = await response.json(); + + if (result.success && result.original_url) { + console.log("[去水印] API返回结果:", { + originalUrl: result.original_url?.substring(0, 100), + thumbnailUrl: result.thumbnail_url?.substring(0, 100) + }); + + setProcessedImageUrl(result.original_url); + // 下载图片获取base64用于显示 + try { + const imgResponse = await fetch(result.original_url); + const blob = await imgResponse.blob(); + const reader = new FileReader(); + reader.onloadend = () => { + const base64 = (reader.result as string).split(',')[1]; + setProcessedImageBase64(base64); + }; + reader.readAsDataURL(blob); + } catch { + console.warn("[去水印] 无法获取Base64,仅使用URL"); + } + setProcessType("watermark"); + + toast.success(`去水印完成!图片已保存到相册。处理时间: ${result.processing_time?.toFixed(2) || "未知"}秒`); + } else { + throw new Error(result.error || "去水印处理失败"); + } + } catch (error) { + console.error("[去水印] 错误:", error); + toast.error(error instanceof Error ? error.message : "去水印失败,请稍后重试"); + } finally { + setIsRemovingWatermark(false); + } + }; + // 变清晰处理 const handleEnhance = async () => { if (!image || !originalImageUrl) return; @@ -1044,6 +1105,8 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod ? `变清晰-${Date.now()}.${fileExtension}` : processType === "resize" ? `尺寸调整-${resizeCanvasWidth}x${resizeCanvasHeight}-${Date.now()}.${fileExtension}` + : processType === "watermark" + ? `去水印-${Date.now()}.${fileExtension}` : `图像拓展-${Date.now()}.${fileExtension}`; // 如果尺寸调整使用Base64,直接下载(优先使用URL,如果没有URL则使用Base64) @@ -1241,7 +1304,7 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod variant="ghost" size="sm" onClick={handleRemoveBackground} - disabled={isRemovingBg || isEnhancing || isExtending || isResizeMode || showAngleEditDialog || showSceneFusion || !image} + disabled={isRemovingBg || isEnhancing || isExtending || isResizeMode || showAngleEditDialog || showSceneFusion || isRemovingWatermark || !image} title="移除背景(抠图)" className="text-zinc-300 hover:text-white hover:bg-zinc-800" > @@ -1252,7 +1315,7 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod variant="ghost" size="sm" onClick={handleEnhance} - disabled={isEnhancing || isRemovingBg || isExtending || isResizeMode || showAngleEditDialog || showSceneFusion || !image} + disabled={isEnhancing || isRemovingBg || isExtending || isResizeMode || showAngleEditDialog || showSceneFusion || isRemovingWatermark || !image} title="图片变清晰" className="text-zinc-300 hover:text-white hover:bg-zinc-800" > @@ -1263,7 +1326,7 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod variant="ghost" size="sm" onClick={handleExtend} - disabled={isExtending || isRemovingBg || isEnhancing || isResizeMode || showAngleEditDialog || showSceneFusion || !image} + disabled={isExtending || isRemovingBg || isEnhancing || isResizeMode || showAngleEditDialog || showSceneFusion || isRemovingWatermark || !image} title="图像拓展" className="text-zinc-300 hover:text-white hover:bg-zinc-800" > @@ -1274,7 +1337,7 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod variant="ghost" size="sm" onClick={handleResize} - disabled={isResizeMode || isRemovingBg || isEnhancing || isExtending || showAngleEditDialog || showSceneFusion || !image} + disabled={isResizeMode || isRemovingBg || isEnhancing || isExtending || showAngleEditDialog || showSceneFusion || isRemovingWatermark || !image} className="text-zinc-300 hover:text-white hover:bg-zinc-800" title="尺寸调整" > @@ -1285,13 +1348,24 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod variant="ghost" size="sm" onClick={handleAngleEdit} - disabled={showAngleEditDialog || isRemovingBg || isEnhancing || isExtending || isResizeMode || showSceneFusion || !image} + disabled={showAngleEditDialog || isRemovingBg || isEnhancing || isExtending || isResizeMode || showSceneFusion || isRemovingWatermark || !image} title="角度调整" className="text-zinc-300 hover:text-white hover:bg-zinc-800" > 角度调整 +