From 676be1c70cf560001859daf4f48ed59fb8b61c63 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 26 Jan 2026 21:19:52 +0800 Subject: [PATCH] feat: switch file list from dialog to inline panel - Replace dialog-based file list with inline panel in sidebar - Click file list button now switches panel content instead of opening popup - Add CanvasFileListPanel component for embedded file list view - Fix thumbnail property name and type casting issues - Add placeholder handleAction function --- app/canvas/[id]/page.tsx | 62 +++++--- components/canvas/CanvasFileList.tsx | 4 +- components/canvas/CanvasFileListPanel.tsx | 184 ++++++++++++++++++++++ 3 files changed, 227 insertions(+), 23 deletions(-) create mode 100644 components/canvas/CanvasFileListPanel.tsx diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index 74defe8d2..9a032576a 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -62,10 +62,10 @@ import { useCanvasStore, canvasSelectors } from "@/store/canvas"; import { canvasService, imageGenerationService } from "@/services/canvas"; import { CanvasWorkspace } from "@/components/canvas"; import { CanvasChatPanel } from "@/components/canvas/CanvasChatPanel"; -import { CanvasFileList } from "@/components/canvas/CanvasFileList"; +import { CanvasFileListPanel } from "@/components/canvas/CanvasFileListPanel"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; -import { MessageSquare, FolderOpen } from "lucide-react"; +import { MessageSquare, FolderOpen, Grid2X2 } from "lucide-react"; interface PageProps { params: Promise<{ id: string }>; @@ -116,7 +116,8 @@ export default function CanvasEditorPage({ params }: PageProps) { const [isEditingTitle, setIsEditingTitle] = useState(false); const [editTitle, setEditTitle] = useState(""); const [isPanelOpen, setIsPanelOpen] = useState(true); - const [isFileListOpen, setIsFileListOpen] = useState(false); + // 面板模式: 'chat' 聊天面板, 'fileList' 文件列表 + const [panelMode, setPanelMode] = useState<'chat' | 'fileList'>('chat'); // 生成配置 const [prompt, setPrompt] = useState(""); @@ -173,17 +174,18 @@ export default function CanvasEditorPage({ params }: PageProps) { const autoTitleAndThumbnail = async () => { // 如果标题是默认值,且有图片节点 if ((projectTitle === "未命名画布" || projectTitle === "新画布") && nodes.length > 0) { - const imageNode = nodes.find(n => n.type === 'image' && n.data?.src); - if (imageNode?.data?.src) { + const imageNode = nodes.find(n => n.type === 'image' && (n.data as any)?.src); + const imageSrc = (imageNode?.data as any)?.src; + if (imageSrc) { try { // 1. 设置缩略图 - await canvasService.updateProject(id, { thumbnailUrl: imageNode.data.src }); + await canvasService.updateProject(id, { thumbnail: imageSrc }); // 2. 智能命名 const res = await fetch('/api/canvas/auto-title', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ imageUrl: imageNode.data.src }) + body: JSON.stringify({ imageUrl: imageSrc }) }); const data = await res.json(); if (data.title) { @@ -274,6 +276,16 @@ export default function CanvasEditorPage({ params }: PageProps) { }); }; + // 处理图片操作(占位函数,后续实现具体逻辑) + const handleAction = (action: string) => { + if (!selectedImageNode) { + toast.error("请先选择一张图片"); + return; + } + toast.info(`${action} 功能开发中...`); + // TODO: 实现具体的图片处理逻辑 + }; + // AI 生成图片 const handleGenerate = async () => { if (!prompt.trim()) { @@ -442,10 +454,16 @@ export default function CanvasEditorPage({ params }: PageProps) { + + + {/* Content */} + +
+ {isLoading ? ( +
+ +
+ ) : projects.length === 0 ? ( +
+ +

暂无画布历史

+ +
+ ) : ( +
+ {projects.map((project) => ( +
handleSwitch(project.id)} + className={cn( + "group flex items-center gap-3 p-3 rounded-xl cursor-pointer transition-all", + "hover:bg-muted/50", + project.id === currentId + ? "bg-primary/5 ring-1 ring-primary/20" + : "bg-card" + )} + > + {/* 缩略图 */} +
+ {project.thumbnail ? ( + {project.title} + ) : ( +
+ +
+ )} +
+ + {/* 信息 */} +
+

+ {project.title || "未命名画布"} +

+

+ {formatDistanceToNow(new Date(project.updatedAt), { + addSuffix: true, + locale: zhCN + })} +

+
+ + {/* 删除按钮 */} + +
+ ))} + +
+ 到底了 +
+
+ )} +
+
+ + ); +}