"use client"; import { useRef } from "react"; import { useRouter } from "next/navigation"; import { App, Button } from "antd"; import { Download, FileUp, Plus } from "lucide-react"; import { readZip } from "@/lib/zip"; import { setMediaBlob } from "@/services/file-storage"; import { setImageBlob } from "@/services/image-storage"; import { CanvasDeleteProjectsDialog } from "./components/canvas-delete-projects-dialog"; import { CanvasProjectCard } from "./components/canvas-project-card"; import type { CanvasExportFile } from "./export-types"; import { useCanvasStore } from "./stores/use-canvas-store"; import { useCanvasUiStore } from "./stores/use-canvas-ui-store"; import { exportCanvasProjects } from "./utils/canvas-export"; export default function CanvasPage() { const { message } = App.useApp(); const router = useRouter(); const inputRef = useRef(null); const hydrated = useCanvasStore((state) => state.hydrated); const projects = useCanvasStore((state) => state.projects); const createProject = useCanvasStore((state) => state.createProject); const importProject = useCanvasStore((state) => state.importProject); const selectedIds = useCanvasUiStore((state) => state.selectedProjectIds); const setDeleteIds = useCanvasUiStore((state) => state.setDeleteProjectIds); const enterProject = (id: string) => { router.push(`/canvas/${id}`); }; const createAndEnter = () => enterProject(createProject(`无限画布 ${projects.length + 1}`)); const importCanvas = async (file?: File) => { if (!file) return; try { const zip = await readZip(file); const projectFile = zip.get("projects.json"); if (!projectFile) throw new Error("missing projects.json"); const data = JSON.parse(await projectFile.text()) as CanvasExportFile; await Promise.all( data.projects.flatMap((project) => project.files.map(async (item) => { const blob = zip.get(item.path); if (!blob) return; const typedBlob = blob.type ? blob : blob.slice(0, blob.size, item.mimeType); await (item.storageKey.startsWith("image:") ? setImageBlob(item.storageKey, typedBlob) : setMediaBlob(item.storageKey, typedBlob)); }), ), ); data.projects.forEach((item) => importProject(item.project)); message.success(`已导入 ${data.projects.length} 个画布`); } catch { message.error("导入失败,请选择有效的画布压缩包"); } finally { if (inputRef.current) inputRef.current.value = ""; } }; return (

画布库

无限画布

{selectedIds.length ? ( <> ) : null} {projects.length ? ( ) : null}
{!hydrated ? (
正在加载画布...
) : projects.length ? (
{projects.map((project) => ( ))}
) : (

还没有画布

新建一个画布后,就可以独立保存节点、连线和画布外观。

)}
void importCanvas(event.target.files?.[0])} />
); }