From aa2554e4ed5f23b59f517b4e73671955799f6b17 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 17 Feb 2026 13:00:39 +0800 Subject: [PATCH] fix(canvas): unify title, improve multi-select drag and immediate delete save --- app/canvas/[id]/layout.tsx | 1 - app/canvas/[id]/page.tsx | 30 +++++++++++++++++++++++++-- components/canvas/CanvasChatPanel.tsx | 2 +- components/canvas/CanvasWorkspace.tsx | 6 ++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/app/canvas/[id]/layout.tsx b/app/canvas/[id]/layout.tsx index c7ce48b48..ec2caaff6 100644 --- a/app/canvas/[id]/layout.tsx +++ b/app/canvas/[id]/layout.tsx @@ -2,7 +2,6 @@ import { Metadata } from "next"; import { Suspense } from "react"; export const metadata: Metadata = { - title: "无限画布", description: "AI 驱动的无限画布创作工具", }; diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index 656037de1..8986bddeb 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState, useCallback, use } from "react"; +import { useEffect, useState, useCallback, use, useRef } from "react"; import { useRouter, useParams } from "next/navigation"; import useSWR from "swr"; import { @@ -133,6 +133,7 @@ export default function CanvasEditorPage({ params }: PageProps) { const [negativePrompt, setNegativePrompt] = useState(""); const [aspectRatio, setAspectRatio] = useState("1:1"); const [imageCount, setImageCount] = useState(1); + const previousNodeCountRef = useRef(0); // 获取选中的图片节点 const selectedImageNode = selectedNodeIds.length === 1 @@ -191,6 +192,31 @@ export default function CanvasEditorPage({ params }: PageProps) { return () => clearTimeout(saveTimer); }, [nodes, id, isLoading, getCanvasState]); + // 删除节点时立即保存,避免用户立刻刷新后看到被删除节点又出现 + useEffect(() => { + if (isLoading) { + previousNodeCountRef.current = nodes.length; + return; + } + + const previousCount = previousNodeCountRef.current; + const currentCount = nodes.length; + previousNodeCountRef.current = currentCount; + + if (previousCount > currentCount) { + const persistAfterDelete = async () => { + setIsSaving(true); + try { + await canvasService.saveCanvasState(id, getCanvasState()); + } catch (error) { + console.error("Delete immediate save failed:", error); + } + setIsSaving(false); + }; + void persistAfterDelete(); + } + }, [nodes.length, id, isLoading, getCanvasState]); + // 智能命名与缩略图逻辑 useEffect(() => { const autoTitleAndThumbnail = async () => { @@ -558,7 +584,7 @@ export default function CanvasEditorPage({ params }: PageProps) { {/* 4. Floating Sidebar (Right) */}
diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 46e79cc77..f0edadc0f 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -3705,7 +3705,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF {/* Bottom Toolbar */}
-
+
({ x: 0, y: 0 }); + const suppressCanvasClickRef = useRef(false); const groupDragRef = useRef<{ nodeId: string; startX: number; @@ -179,6 +180,10 @@ export function CanvasWorkspace() { // 处理画布点击(取消选择) const handleCanvasClick = useCallback((e: React.MouseEvent) => { if (!isEventFromCanvasContainer(e.target)) return; + if (suppressCanvasClickRef.current) { + suppressCanvasClickRef.current = false; + return; + } if (!isPointOnNodeElement(e.target) && !isMarqueeSelecting) { clearSelection(); } @@ -247,6 +252,7 @@ export function CanvasWorkspace() { .map((node) => node.id); if (selectedIds.length > 0) { selectNodes(selectedIds); + suppressCanvasClickRef.current = true; } else { clearSelection(); }