import { App, Dropdown, Input } from "antd"; import { Download, LoaderCircle, MoreHorizontal, Pencil, Trash2 } from "lucide-react"; import type { KeyboardEvent } from "react"; import { ProjectPreview } from "@/components/canvas/canvas-project-card"; import { exportCanvasProjects } from "@/lib/canvas/canvas-export"; import { useCanvasStore } from "@/stores/canvas/use-canvas-store"; import type { CanvasLibrarySummary } from "@/services/api/user-data"; import { loadCanvasProjectForEditing, saveRemoteUserDataNow } from "@/services/user-data-sync"; import { useCanvasUiStore } from "@/stores/canvas/use-canvas-ui-store"; import { cn } from "@/lib/utils"; type CanvasFolderCardProps = { project: CanvasLibrarySummary; projectName?: string; onClick: () => void; onPrefetch?: () => void; opening?: boolean; }; /** 画布库中的文件夹封面:单一卡片表面承载预览和信息,避免相邻卡片互相侵入。 */ export function CanvasFolderCard({ project, projectName, onClick, onPrefetch, opening = false }: CanvasFolderCardProps) { const { message } = App.useApp(); const renameProject = useCanvasStore((state) => state.renameProject); const selectedIds = useCanvasUiStore((state) => state.selectedProjectIds); const editingId = useCanvasUiStore((state) => state.editingProjectId); const editingTitle = useCanvasUiStore((state) => state.editingProjectTitle); const startEditing = useCanvasUiStore((state) => state.startEditingProject); const setEditingTitle = useCanvasUiStore((state) => state.setEditingProjectTitle); const stopEditing = useCanvasUiStore((state) => state.stopEditingProject); const toggleSelected = useCanvasUiStore((state) => state.toggleSelectedProjectId); const setDeleteIds = useCanvasUiStore((state) => state.setDeleteProjectIds); const editing = editingId === project.id; const selected = selectedIds.includes(project.id); const saveTitle = async () => { if (!editing) return; stopEditing(); try { await loadCanvasProjectForEditing(project.id); renameProject(project.id, editingTitle); await saveRemoteUserDataNow(); } catch (error) { message.error(error instanceof Error ? error.message : "重命名失败"); } }; const exportProject = async () => { try { const fullProject = await loadCanvasProjectForEditing(project.id); if (!fullProject) throw new Error("画布不存在"); await exportCanvasProjects([fullProject], project.title || "画布"); } catch (error) { message.error(error instanceof Error ? error.message : "导出失败"); } }; const handleOpenKeyDown = (event: KeyboardEvent) => { if (event.target !== event.currentTarget) return; if (event.key === "Enter" || event.key === " ") { event.preventDefault(); if (!editing) onClick(); } }; return (
!editing && !opening && onClick()} onKeyDown={handleOpenKeyDown}>
{editing ? ( setEditingTitle(event.target.value)} onClick={(event) => event.stopPropagation()} onBlur={saveTitle} onKeyDown={(event) => { if (event.key === "Enter") saveTitle(); if (event.key === "Escape") stopEditing(); }} autoFocus /> ) : ( {project.title} )}
{projectName ? `所属项目:${projectName}` : "自由画布"} {project.nodeCount} 节点
创建时间 最后更新
event.stopPropagation()}> toggleSelected(project.id, event.target.checked)} aria-label={`选择 ${project.title}`} />
event.stopPropagation()}> {!editing ? ( ) : null} domEvent.stopPropagation(), items: [ { key: "export", icon: , label: "导出画布", onClick: () => void exportProject() }, { type: "divider" }, { key: "delete", danger: true, icon: , label: "删除", onClick: () => setDeleteIds([project.id]) }, ], }} >
); } function formatCanvasDate(value: string) { const timestamp = Date.parse(value); return Number.isFinite(timestamp) ? new Date(timestamp).toLocaleString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", }) : "时间不可用"; }