lovart3/docs/无限画布集成开发方案.md
Your Name c7ffcda87b feat: add infinite canvas feature with AI image generation
- Add canvas project and node database tables
- Create canvas workspace with drag, resize, zoom and pan
- Add ImageNode with background removal, enhance, extend, angle edit, scene fusion
- Add VideoNode with playback controls
- Add TextNode for text editing
- Integrate AI image generation API (Gemini/Alibaba)
- Implement viewport culling for performance optimization
- Add canvas entry in top navigation bar
2026-01-24 19:48:13 +08:00

44 KiB
Raw Blame History

无限画布功能集成开发方案

项目背景

本方案旨在参考 lobe-chat-pro 项目的无限画布功能,将其核心能力集成到我们的 AI Chatbot 项目中。


一、lobe-chat-pro 无限画布功能分析

1.1 核心功能特性

功能模块 描述 支持的模型
连续对话画布 在对话中设计、修改图片,用图片生成视频 Google、OpenAI、Midjourney、Kling、Doubao
画图生成 文本转图片、参考图生成 Google、OpenAI、Midjourney、Kling、Doubao
视频生成 文本转视频、图片转视频 Google、OpenAI、Kling、Luma、Runway
图片修改 高清放大、移除背景、提取图层、局部修改、图片扩展 各类图像处理 API
联网搜索 根据需求搜索网络上的最新内容 搜索引擎 API

1.2 技术栈对比

技术点 lobe-chat-pro 我们的项目 兼容性
框架 Next.js 15.3.6 Next.js 16 ✅ 高度兼容
React 19.2.1 19.0.0-rc ✅ 兼容
状态管理 Zustand 5.0.4 React Hooks + SWR ⚠️ 需评估
UI 库 Tailwind + antd-style Tailwind + Radix UI ✅ 可共存
虚拟列表 react-virtuoso 无 🆕 需引入
布局 react-layout-kit 无 🆕 需引入
数据库 PostgreSQL PostgreSQL + Drizzle ✅ 兼容

1.3 关键依赖库

{
  "@lobehub/editor": "^1.20.2",
  "@tanstack/react-query": "^5.90.2",
  "react-virtuoso": "^4.14.1",
  "react-layout-kit": "^2.0.0",
  "react-rnd": "^10.5.2",
  "framer-motion": "^12.23.24",
  "zustand": "^5.0.4"
}

二、集成方案设计

2.1 方案选择

方案 A:完整移植 + 深度集成(推荐度:⭐⭐⭐⭐⭐)

描述: 完整移植 lobe-chat-pro 的无限画布模块,然后修改代码深度集成我们的功能

优点:

  • 功能完整,体验一致
  • 快速获得成熟稳定的功能
  • 减少从零开发的风险和工作量
  • 移植后可逐步优化和定制
  • 可复用已验证的代码逻辑

缺点:

  • 需要引入相关依赖
  • 需要花时间适配现有架构
  • 需要理解原项目代码结构

方案 B:参考实现,自主开发(推荐度:⭐⭐⭐)

描述: 参考 lobe-chat-pro 的设计思路,基于现有架构自主开发

优点:

  • 与现有架构深度融合
  • 可控性高

缺点:

  • 开发工作量大
  • 需要从零实现所有功能
  • 开发周期长

方案 C:仅移植核心组件(推荐度:⭐⭐)

描述: 只移植部分核心组件,其余自主开发

优点:

  • 灵活性高

缺点:

  • 可能导致功能不完整
  • 集成难度大

2.2 推荐方案:方案 A - 完整移植 + 深度集成

基于以下考虑:

  1. lobe-chat-pro 的画布功能已经过验证,稳定可靠
  2. 完整移植可以快速获得全部功能
  3. 移植后再深度集成我们现有的图片编辑功能(抠图、增强、场景裂变等)
  4. 可以复用我们现有的 AI 模型配置和存储服务
  5. 渐进式适配降低风险

三、完整移植方案

3.1 需要移植的文件清单

3.1.1 核心状态管理(必须移植)

src/store/painting/
├── action.ts          # 核心操作逻辑(623行)- 包含 Midjourney 等 API 调用
├── index.ts           # 导出入口
├── initialState.ts    # 初始状态定义
├── selectors.ts       # 状态选择器
├── store.ts           # Zustand store 创建
└── utils.ts           # 工具函数

src/store/image/
├── index.ts           # 图片状态 store
├── slices/
│   ├── createImage/   # 图片创建状态
│   ├── generationConfig/ # 生成配置状态
│   ├── generationBatch/  # 批次管理
│   └── generationTopic/  # 主题管理
└── selectors.ts       # 选择器

3.1.2 服务层(必须移植)

src/services/painting/
├── client.ts          # 客户端服务实现
├── index.ts           # 服务导出
├── server.ts          # 服务端实现
└── type.ts            # 服务接口定义

src/services/textToImage.ts    # 文本生图服务
src/services/image.ts          # 图片服务

3.1.3 类型定义(必须移植)

src/types/
├── painting/
│   └── index.ts       # Painting 类型定义
├── image/
│   └── index.ts       # 图片相关类型
└── tool/
    └── index.ts       # 工具类型

3.1.4 页面和组件(必须移植)

src/app/[variants]/(main)/
├── painting/                  # 独立绘图面板
│   ├── page.tsx
│   ├── layout.tsx
│   └── features/
│       ├── ImagesContainer/   # 图片容器
│       ├── PalettePanel/      # 调色板面板
│       └── PromptInput/       # 提示词输入
│
├── image/                     # 图片生成工作区
│   ├── page.tsx
│   ├── layout.tsx
│   └── features/
│       ├── ImageWorkspace/    # 图片工作区
│       ├── GenerationFeed/    # 生成结果展示
│       └── PromptInput/       # 提示词输入

src/features/Conversation/
├── Messages/                  # 消息渲染
│   ├── Assistant/
│   │   ├── MessageContent.tsx
│   │   ├── Tool/             # 工具调用渲染
│   │   └── Extra/
│   └── User/
└── components/
    └── VirtualizedList/       # 虚拟化列表

3.1.5 API 路由(必须移植)

src/app/(backend)/webapi/
├── text-to-image/
│   └── [provider]/
│       └── route.ts           # 图片生成 API
├── text-to-video/
│   └── [provider]/
│       └── route.ts           # 视频生成 API
└── painting/
    └── route.ts               # 绘图管理 API

3.1.6 数据库模型(需适配)

src/database/
├── models/
│   └── painting.ts            # 绘图数据模型
└── schemas/
    └── painting.ts            # 绘图表结构

3.2 需要安装的依赖

# 必须安装的依赖
pnpm add zustand@5.0.4 immer@^10.1.3

# 画布交互相关
pnpm add react-rnd@^10.5.2 react-virtuoso@^4.14.1 react-layout-kit@^2.0.0

# 动画效果
pnpm add framer-motion@^12.23.24

# 数据获取(可选,我们已有 SWR)
pnpm add @tanstack/react-query@^5.90.2

# 编辑器组件(可选)
pnpm add @lobehub/editor@^1.20.2

# UI 组件(可选,用于过渡期)
pnpm add antd@^5.27.4 antd-style@^3.7.1

3.3 移植步骤

步骤 1:准备工作

# 1. 克隆 lobe-chat-pro 仓库到本地参考
git clone https://github.com/vual/lobe-chat-pro.git ~/lobe-chat-pro-ref

# 2. 安装必要依赖
cd ~/Desktop/ai-chatbot-main
pnpm add zustand@5.0.4 immer@^10.1.3 react-rnd@^10.5.2 \
  react-virtuoso@^4.14.1 react-layout-kit@^2.0.0 framer-motion@^12.23.24

步骤 2:创建目录结构

# 在我们项目中创建对应目录
mkdir -p components/canvas
mkdir -p components/canvas/nodes
mkdir -p components/canvas/tools
mkdir -p components/canvas/hooks
mkdir -p store/painting
mkdir -p store/image
mkdir -p services/painting
mkdir -p types/painting
mkdir -p app/canvas
mkdir -p app/canvas/[id]
mkdir -p app/api/canvas
mkdir -p app/api/canvas/generate

步骤 3:移植核心文件

按以下顺序移植(依赖关系从低到高):

顺序 文件/目录 来源路径 目标路径 说明
1 类型定义 src/types/painting/ types/painting/ 基础类型
2 服务接口 src/services/painting/type.ts services/painting/type.ts 接口定义
3 初始状态 src/store/painting/initialState.ts store/painting/initialState.ts 状态定义
4 选择器 src/store/painting/selectors.ts store/painting/selectors.ts 状态选择
5 工具函数 src/store/painting/utils.ts store/painting/utils.ts 辅助函数
6 Store src/store/painting/store.ts store/painting/store.ts 状态管理
7 Actions src/store/painting/action.ts store/painting/action.ts 核心逻辑
8 服务实现 src/services/painting/client.ts services/painting/client.ts 客户端服务
9 页面组件 src/app/[variants]/(main)/painting/ app/canvas/ 画布页面

四、深度集成方案

移植完成后,需要深度集成我们现有的功能。

4.1 整体架构设计

┌─────────────────────────────────────────────────────────────────────────┐
│                           无限画布工作区                                 │
├─────────────────────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌───────────────────────┐  ┌─────────────────────┐   │
│  │  工具栏     │  │      画布区域         │  │     属性面板        │   │
│  │  - 选择     │  │  - 消息节点           │  │  - 节点配置         │   │
│  │  - 画笔     │  │  - 图片节点           │  │  - 生成参数         │   │
│  │  - 文本     │  │  - 视频节点           │  │  - 历史记录         │   │
│  │  - 形状     │  │  - 连接线             │  │                     │   │
│  │             │  │                       │  │  【深度集成功能】   │   │
│  │  【集成】   │  │  【集成我们的功能】   │  │  - 抠图             │   │
│  │  - 抠图     │  │  - 场景裂变结果       │  │  - 图片增强         │   │
│  │  - 增强     │  │  - 角度编辑结果       │  │  - 图片扩展         │   │
│  │  - 扩展     │  │  - 抠图结果           │  │  - 角度编辑         │   │
│  │  - 角度     │  │                       │  │  - 场景裂变         │   │
│  └─────────────┘  └───────────────────────┘  └─────────────────────┘   │
├─────────────────────────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                          输入区域                                │   │
│  │  - 文本输入 / 图片上传 / 语音输入 / 【复用 multimodal-input】   │   │
│  └─────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────┘

4.2 需要适配的部分

4.2.1 数据库适配(Drizzle ORM)

将 lobe-chat-pro 的数据库模型适配到我们的 Drizzle Schema:

// lib/db/schema.ts (新增)
import { pgTable, uuid, text, jsonb, timestamp, integer } from 'drizzle-orm/pg-core';

// 画布项目表
export const canvasProject = pgTable('canvas_project', {
  id: uuid('id').primaryKey().defaultRandom(),
  userId: uuid('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
  title: text('title').notNull().default('未命名画布'),
  description: text('description'),
  thumbnail: text('thumbnail'),
  canvasData: jsonb('canvas_data'), // 存储完整画布状态
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
});

// 绘图记录表(对应 lobe-chat-pro 的 Painting)
export const painting = pgTable('painting', {
  id: uuid('id').primaryKey().defaultRandom(),
  userId: uuid('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
  projectId: uuid('project_id').references(() => canvasProject.id, { onDelete: 'set null' }),
  
  // 平台和动作
  platform: text('platform').notNull(), // 'Midjourney' | 'Google' | 'OpenAI' | 'Kling' | 'Doubao' | 'Alibaba'
  action: text('action').notNull(),     // 'IMAGINE' | 'BLEND' | 'DESCRIBE' | 'UPSCALE' | 'VARIATION' 等
  
  // 任务状态
  taskId: text('task_id'),
  status: text('status').notNull().default('SUBMITTED'), // 'SUBMITTED' | 'IN_PROGRESS' | 'SUCCESS' | 'FAILURE'
  progress: integer('progress').default(0),
  failReason: text('fail_reason'),
  
  // 内容
  prompt: text('prompt'),
  negativePrompt: text('negative_prompt'),
  images: jsonb('images').$type<Array<{ fileId: string; url: string }>>(),
  
  // 配置
  config: jsonb('config'),
  extra: jsonb('extra'),
  
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
});

4.2.2 认证适配(NextAuth)

// services/painting/server.ts (适配)
import { auth } from '@/app/(auth)/auth';

export class ServerPaintingService {
  async queryPainting(params?: { current?: number; pageSize?: number }) {
    const session = await auth();
    if (!session?.user?.id) {
      throw new Error('Unauthorized');
    }
    
    // 使用我们的 Drizzle 查询
    const paintings = await db.query.painting.findMany({
      where: eq(painting.userId, session.user.id),
      limit: params?.pageSize || 20,
      offset: ((params?.current || 1) - 1) * (params?.pageSize || 20),
      orderBy: desc(painting.createdAt),
    });
    
    return paintings;
  }
  
  // ... 其他方法
}

4.2.3 AI 提供商适配

将 lobe-chat-pro 的 AI 调用适配到我们的动态提供商系统:

// lib/ai/canvas-providers.ts
import { getActiveAIProvider } from '@/lib/db/queries';

export async function getCanvasImageProvider(providerType: string) {
  // 从数据库获取配置的提供商
  const provider = await getActiveAIProvider(providerType);
  
  switch (providerType) {
    case 'alibaba':
      return createAlibabaImageProvider(provider);
    case 'gemini':
      return createGeminiImageProvider(provider);
    case 'openai':
      return createOpenAIImageProvider(provider);
    // ... 其他提供商
  }
}

4.2.4 存储服务适配

// services/painting/storage.ts
import { uploadToStorage } from '@/lib/storage/service';

export async function savePaintingImage(imageUrl: string, userId: string) {
  // 下载图片
  const response = await fetch(imageUrl);
  const blob = await response.blob();
  
  // 上传到我们的存储桶
  const file = new File([blob], `painting-${Date.now()}.png`, { type: 'image/png' });
  const storedUrl = await uploadToStorage(file, userId, 'painting');
  
  return storedUrl;
}

4.3 深度集成我们的现有功能

4.3.1 集成图片编辑功能

在画布的图片节点中集成我们现有的图片编辑能力:

// components/canvas/nodes/ImageNode.tsx
import { ImageAnnotator } from '@/components/image-annotator';
import { AngleEditDialog } from '@/components/angle-edit-dialog';

export function ImageNode({ data }: { data: ImageNodeData }) {
  const [editMode, setEditMode] = useState<'view' | 'annotate' | 'angle' | null>('view');
  
  // 图片操作菜单 - 集成我们现有的所有功能
  const imageActions = [
    // 图片处理
    { label: '抠图', action: 'rmbg', api: '/api/rmbg', icon: Scissors },
    { label: '变清晰', action: 'enhance', api: '/api/enhance', icon: Sparkles },
    { label: '图片扩展', action: 'extend', api: '/api/extend', icon: Expand },
    { label: '尺寸调整', action: 'resize', api: '/api/resize', icon: Maximize2 },
    { label: '角度编辑', action: 'angle', component: AngleEditDialog, icon: Move3D },
    { label: '场景裂变', action: 'scene-fusion', component: SceneFusionView, icon: Layers },
    { label: '物体检测', action: 'detect', api: '/api/detect-object', icon: Search },
    
    // 标注工具
    { label: '画笔标注', action: 'pen', tool: 'pen', icon: Pencil },
    { label: '箭头标注', action: 'arrow', tool: 'arrow', icon: ArrowRight },
    { label: '圆圈标注', action: 'circle', tool: 'circle', icon: Circle },
    { label: '文字标注', action: 'text', tool: 'text', icon: Type },
    
    // 其他
    { label: '下载图片', action: 'download', icon: Download },
    { label: '上传到相册', action: 'upload-gallery', api: '/api/gallery', icon: Upload },
  ];
  
  const handleAction = async (action: string) => {
    // 调用对应的 API 或打开编辑组件
  };
  
  return (
    <div className="image-node">
      <img src={data.src} alt={data.prompt} />
      <ImageActionsMenu actions={imageActions} onAction={handleAction} />
      
      {editMode === 'annotate' && (
        <ImageAnnotator 
          imageUrl={data.src}
          onSave={handleAnnotationSave}
        />
      )}
      
      {editMode === 'angle' && (
        <AngleEditDialog
          imageUrl={data.src}
          onComplete={handleAngleEditComplete}
        />
      )}
    </div>
  );
}

4.3.2 集成 AI 模型选择

使用我们数据库中配置的 AI 模型:

// components/canvas/panels/ModelSelector.tsx
import useSWR from 'swr';

export function ModelSelector({ type }: { type: 'image' | 'video' }) {
  // 从数据库获取可用的模型
  const { data: models } = useSWR(`/api/admin/ai-models?type=${type}`);
  
  return (
    <Select onValueChange={setSelectedModel}>
      <SelectTrigger>
        <SelectValue placeholder="选择模型" />
      </SelectTrigger>
      <SelectContent>
        {models?.map((model) => (
          <SelectItem key={model.id} value={model.modelId}>
            {model.displayName}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

4.3.3 集成场景裂变功能

将场景裂变作为画布的一个特殊节点类型:

// components/canvas/nodes/SceneFusionNode.tsx
export function SceneFusionNode({ data }: { data: SceneFusionNodeData }) {
  const [results, setResults] = useState<string[]>([]);
  const [isGenerating, setIsGenerating] = useState(false);
  
  const handleGenerate = async () => {
    setIsGenerating(true);
    
    const response = await fetch('/api/scene-fusion', {
      method: 'POST',
      body: JSON.stringify({
        productImage: data.productImage,
        backgroundImages: data.backgroundImages,
        productName: data.productName,
      }),
    });
    
    const result = await response.json();
    setResults(result.images);
    setIsGenerating(false);
  };
  
  return (
    <div className="scene-fusion-node">
      <div className="inputs">
        <ImageUpload label="产品图" value={data.productImage} />
        <ImageUpload label="背景图" value={data.backgroundImages} multiple />
      </div>
      
      <Button onClick={handleGenerate} disabled={isGenerating}>
        {isGenerating ? '生成中...' : '开始场景裂变'}
      </Button>
      
      <div className="results grid grid-cols-4 gap-2">
        {results.map((url, index) => (
          <img key={index} src={url} alt={`结果 ${index + 1}`} />
        ))}
      </div>
    </div>
  );
}

4.4 UI 适配策略

方式一:保留 antd 组件(快速上线)

初期可以保留 lobe-chat-pro 使用的 antd 组件,后续再逐步替换:

pnpm add antd@^5.27.4 antd-style@^3.7.1

方式二:替换为 Radix UI(推荐长期)

将 antd 组件逐步替换为我们的 Radix UI 组件:

antd 组件 替换为 说明
Button @/components/ui/button 直接替换
Input @/components/ui/input 直接替换
Select @/components/ui/select 直接替换
Modal @/components/ui/dialog 直接替换
Dropdown @/components/ui/dropdown-menu 直接替换
Tabs @/components/ui/tabs 直接替换
Slider @/components/ui/slider 直接替换
Tooltip @/components/ui/tooltip 直接替换

五、开发计划(移植 + 集成)

5.1 阶段划分

第一阶段:环境准备与依赖安装

任务清单:

  • 克隆 lobe-chat-pro 仓库到本地作为参考
  • 安装所有必要依赖
  • 创建目录结构
  • 配置 TypeScript 和 ESLint

命令:

# 克隆参考仓库
git clone https://github.com/vual/lobe-chat-pro.git ~/lobe-chat-pro-ref

# 安装依赖
pnpm add zustand@5.0.4 immer@^10.1.3 react-rnd@^10.5.2 \
  react-virtuoso@^4.14.1 react-layout-kit@^2.0.0 \
  framer-motion@^12.23.24 antd@^5.27.4 antd-style@^3.7.1

第二阶段:核心模块移植

任务清单:

  • 移植类型定义 (types/painting/)
  • 移植状态管理 (store/painting/)
  • 移植服务层 (services/painting/)
  • 移植 API 路由
  • 适配数据库 Schema

交付物:

  • 可运行的状态管理系统
  • 数据库表结构
  • 基础 API 接口

第三阶段:页面组件移植

任务清单:

  • 移植画布页面组件
  • 移植绘图面板组件
  • 移植图片工作区组件
  • 配置路由

交付物:

  • 可访问的画布页面 /canvas
  • 基础绘图功能

第四阶段:深度集成我们的功能

任务清单:

  • 集成图片抠图功能
  • 集成图片增强功能
  • 集成图片扩展功能
  • 集成角度编辑功能
  • 集成场景裂变功能
  • 集成我们的 AI 模型配置

交付物:

  • 在画布中可使用我们所有的图片编辑功能
  • 支持从数据库选择 AI 模型

第五阶段:UI 适配与优化

任务清单:

  • 将 antd 组件替换为 Radix UI(可选)
  • 统一样式风格
  • 性能优化
  • 测试和修复 Bug

交付物:

  • 风格统一的 UI
  • 稳定可用的无限画布功能

5.2 详细任务清单

序号 任务 优先级 状态
1 安装 Zustand 和相关依赖 P0 ⬜
2 创建 store/painting/ 目录结构 P0 ⬜
3 移植 painting 类型定义 P0 ⬜
4 移植 paintingStore 状态管理 P0 ⬜
5 创建 painting 数据库表 P0 ⬜
6 移植 PaintingService 服务层 P0 ⬜
7 适配认证系统(NextAuth) P0 ⬜
8 移植画布主页面组件 P1 ⬜
9 移植绘图面板组件 P1 ⬜
10 配置画布路由 /canvas P1 ⬜
11 适配存储服务 P1 ⬜
12 集成抠图功能 P2 ⬜
13 集成图片增强功能 P2 ⬜
14 集成场景裂变功能 P2 ⬜
15 集成角度编辑功能 P2 ⬜
16 集成 AI 模型选择 P2 ⬜
17 UI 风格统一 P3 ⬜
18 性能优化 P3 ⬜

六、技术参考

6.1 核心模块设计

模块 1:画布引擎(Canvas Engine)

文件结构:

components/canvas/
├── index.tsx                 # 主组件入口
├── Canvas.tsx               # 画布容器
├── CanvasViewport.tsx       # 视口控制(缩放、平移)
├── CanvasNode.tsx           # 节点基类
├── nodes/
│   ├── MessageNode.tsx      # 消息节点
│   ├── ImageNode.tsx        # 图片节点
│   ├── VideoNode.tsx        # 视频节点
│   └── ConnectionLine.tsx   # 连接线
├── tools/
│   ├── SelectTool.tsx       # 选择工具
│   ├── PanTool.tsx          # 平移工具
│   └── ZoomControl.tsx      # 缩放控制
├── hooks/
│   ├── useCanvasState.ts    # 画布状态管理
│   ├── useNodeDrag.ts       # 节点拖拽
│   ├── useViewport.ts       # 视口控制
│   └── useCanvasHistory.ts  # 撤销/重做
└── types.ts                 # 类型定义

核心接口定义:

// types.ts
export interface CanvasNode {
  id: string;
  type: 'message' | 'image' | 'video' | 'text';
  position: { x: number; y: number };
  size: { width: number; height: number };
  data: MessageNodeData | ImageNodeData | VideoNodeData;
  connections: string[]; // 连接的节点 ID
  createdAt: Date;
  updatedAt: Date;
}

export interface MessageNodeData {
  role: 'user' | 'assistant';
  content: string;
  attachments?: Attachment[];
}

export interface ImageNodeData {
  src: string;
  prompt?: string;
  model?: string;
  status: 'pending' | 'generating' | 'completed' | 'failed';
  metadata?: Record<string, any>;
}

export interface VideoNodeData {
  src: string;
  thumbnail?: string;
  prompt?: string;
  model?: string;
  status: 'pending' | 'generating' | 'completed' | 'failed';
  duration?: number;
}

export interface CanvasState {
  nodes: CanvasNode[];
  selectedNodeIds: string[];
  viewport: {
    x: number;
    y: number;
    zoom: number;
  };
  tool: 'select' | 'pan' | 'draw';
}

模块 2:状态管理(Canvas Store)

推荐引入 Zustand 进行状态管理:

// store/canvas.ts
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

interface CanvasStore {
  // 状态
  nodes: CanvasNode[];
  selectedNodeIds: string[];
  viewport: Viewport;
  tool: ToolType;
  history: CanvasState[];
  historyIndex: number;
  
  // 节点操作
  addNode: (node: CanvasNode) => void;
  updateNode: (id: string, updates: Partial<CanvasNode>) => void;
  deleteNode: (id: string) => void;
  selectNode: (id: string, multi?: boolean) => void;
  
  // 视口操作
  setViewport: (viewport: Partial<Viewport>) => void;
  zoomIn: () => void;
  zoomOut: () => void;
  fitToScreen: () => void;
  
  // 历史操作
  undo: () => void;
  redo: () => void;
  pushHistory: () => void;
  
  // 工具
  setTool: (tool: ToolType) => void;
}

export const useCanvasStore = create<CanvasStore>()(
  immer((set, get) => ({
    // ... 实现
  }))
);

模块 3:AI 生成集成

扩展现有 AI 服务:

// lib/ai/canvas-ai.ts
export interface CanvasAIService {
  // 图片生成
  generateImage(params: {
    prompt: string;
    model: string;
    referenceImage?: string;
    size?: string;
    style?: string;
  }): Promise<GeneratedImage>;
  
  // 视频生成
  generateVideo(params: {
    prompt: string;
    model: string;
    referenceImage?: string;
    duration?: number;
  }): Promise<GeneratedVideo>;
  
  // 图片编辑
  editImage(params: {
    image: string;
    action: 'upscale' | 'remove-bg' | 'extend' | 'inpaint';
    options?: Record<string, any>;
  }): Promise<EditedImage>;
  
  // 对话生成
  chat(params: {
    messages: Message[];
    model: string;
    attachments?: Attachment[];
  }): AsyncIterable<StreamChunk>;
}

模块 4:持久化存储

数据库 Schema 扩展:

// lib/db/schema.ts (新增)
export const canvasProject = pgTable('canvas_project', {
  id: uuid('id').primaryKey().defaultRandom(),
  userId: uuid('user_id').notNull().references(() => user.id),
  title: text('title').notNull(),
  description: text('description'),
  thumbnail: text('thumbnail'),
  canvasData: jsonb('canvas_data').$type<CanvasState>(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
});

export const canvasNode = pgTable('canvas_node', {
  id: uuid('id').primaryKey().defaultRandom(),
  projectId: uuid('project_id').notNull().references(() => canvasProject.id),
  type: text('type').notNull(),
  position: jsonb('position').$type<Position>(),
  size: jsonb('size').$type<Size>(),
  data: jsonb('data'),
  connections: jsonb('connections').$type<string[]>(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow(),
});

6.2 功能入口位置

现有页面结构

app/
├── (chat)/                        # 聊天模块(带侧边栏和顶部导航)
│   ├── layout.tsx                 # 包含 TopNavbar + AppSidebar
│   ├── page.tsx                   # 首页
│   ├── chat/[id]/page.tsx         # 聊天详情页
│   ├── gallery/page.tsx           # 图片相册页
│   └── featured/page.tsx          # 精选作品页
├── (admin)/                       # 管理后台模块
│   └── settings/page.tsx          # 系统设置页
└── (auth)/                        # 认证模块
    ├── login/page.tsx
    └── register/page.tsx

入口位置方案

方案 1:放在 (chat) 路由组内(推荐)

app/(chat)/
├── canvas/                        # 画布列表页
│   └── page.tsx
├── canvas/[id]/                   # 画布编辑页
│   └── page.tsx
└── painting/                      # 独立绘图面板(可选)
    └── page.tsx

优点:

  • 复用现有的 TopNavbar 和 AppSidebar
  • 与现有页面(gallery、featured)风格一致
  • 用户可以在侧边栏快速切换

方案 2:独立路由组

app/(canvas)/
├── layout.tsx                     # 独立布局(可能需要全屏画布)
├── page.tsx                       # 画布列表
└── [id]/
    └── page.tsx                   # 画布编辑

优点:

  • 画布编辑页可以使用全屏布局
  • 布局更灵活

方案 3:混合方案(推荐)

app/
├── (chat)/
│   └── canvas/                    # 画布列表页(带侧边栏)
│       └── page.tsx
└── canvas/                        # 画布编辑页(独立全屏布局)
    └── [id]/
        ├── layout.tsx             # 全屏画布布局
        └── page.tsx

优点:

  • 列表页保持与其他页面一致的布局
  • 编辑页使用全屏布局,最大化画布空间

入口位置:顶部导航栏

当前顶部导航栏已有按钮:

  • 工具箱(自定义链接)
  • AI精选
  • 精选榜
  • 图片编辑 ← 现有功能
  • 我的图册
  • 系统设置(管理员)

添加"无限画布"入口,放在"图片编辑"旁边:

工具箱 | AI精选 | 精选榜 | 图片编辑 | 【无限画布】 | 我的图册 | ⚙️

需要修改的文件

// components/top-navbar.tsx

// 1. 添加图标导入
import { LayoutGrid } from "lucide-react";

// 2. 在"图片编辑"按钮后面添加"无限画布"按钮(约第255行后)
{/* 无限画布 */}
<Button
  variant="ghost"
  size="sm"
  className="flex items-center gap-1 h-8 px-3 text-white hover:bg-white/15 hover:text-white rounded-full transition-colors"
  onClick={() => router.push("/canvas")}
  type="button"
  title="无限画布"
>
  <LayoutGrid className="h-4 w-4 text-purple-400" />
  <span className="text-sm font-medium">无限画布</span>
</Button>

完整的导航栏按钮顺序

顺序 按钮 图标 颜色 路由
1 工具箱 Briefcase 默认 下拉菜单
2 AI精选 Sparkles 黄色 /featured
3 精选榜 Trophy 橙色 下拉菜单
4 图片编辑 Edit 默认 弹窗
5 无限画布 LayoutGrid 紫色 /canvas
6 我的图册 Images 默认 /gallery
7 系统设置 Settings 默认 /settings

6.3 页面路由设计

app/
├── (chat)/
│   ├── canvas/
│   │   └── page.tsx              # 画布列表页(带侧边栏)
│   └── ...
├── canvas/
│   └── [id]/
│       ├── layout.tsx            # 全屏画布布局
│       └── page.tsx              # 画布编辑页
└── api/
    └── canvas/
        ├── route.ts              # 画布 CRUD
        ├── [id]/
        │   ├── route.ts          # 单个画布操作
        │   └── nodes/
        │       └── route.ts      # 节点操作
        └── generate/
            ├── image/
            │   └── route.ts      # 图片生成
            └── video/
                └── route.ts      # 视频生成

6.3 UI 组件示例

基于现有 Radix UI 组件扩展:

// components/canvas/ui/CanvasToolbar.tsx
export function CanvasToolbar() {
  return (
    <div className="flex items-center gap-2 p-2 bg-background border-b">
      <ToolGroup>
        <ToolButton icon={<MousePointer />} tool="select" />
        <ToolButton icon={<Hand />} tool="pan" />
        <ToolButton icon={<Pencil />} tool="draw" />
      </ToolGroup>
      
      <Separator orientation="vertical" />
      
      <ToolGroup>
        <ZoomControl />
        <Button variant="ghost" size="icon"><Undo2 /></Button>
        <Button variant="ghost" size="icon"><Redo2 /></Button>
      </ToolGroup>
      
      <Separator orientation="vertical" />
      
      {/* 集成我们的图片编辑功能 - 完整列表 */}
      <ToolGroup>
        <Button variant="outline"><ImagePlus className="mr-2" />生成图片</Button>
        <Button variant="outline"><Video className="mr-2" />生成视频</Button>
      </ToolGroup>
      
      <Separator orientation="vertical" />
      
      {/* 图片处理功能 */}
      <ToolGroup>
        <Button variant="outline"><Scissors className="mr-2" />抠图</Button>
        <Button variant="outline"><Sparkles className="mr-2" />变清晰</Button>
        <Button variant="outline"><Expand className="mr-2" />图片扩展</Button>
        <Button variant="outline"><Maximize2 className="mr-2" />尺寸调整</Button>
        <Button variant="outline"><Move3D className="mr-2" />角度编辑</Button>
        <Button variant="outline"><Layers className="mr-2" />场景裂变</Button>
        <Button variant="outline"><Search className="mr-2" />物体检测</Button>
      </ToolGroup>
      
      <Separator orientation="vertical" />
      
      {/* 标注工具 */}
      <ToolGroup>
        <Button variant="outline"><Pencil className="mr-2" />画笔</Button>
        <Button variant="outline"><ArrowRight className="mr-2" />箭头</Button>
        <Button variant="outline"><Circle className="mr-2" />圆圈</Button>
        <Button variant="outline"><Type className="mr-2" />文字</Button>
      </ToolGroup>
    </div>
  );
}

七、技术风险与应对

风险 影响 应对措施
移植代码兼容性问题 高 分模块移植,逐步测试验证
antd 与 Radix UI 冲突 中 初期保留 antd,后期逐步替换
大量节点性能问题 高 使用虚拟化渲染、节点懒加载
状态同步复杂 中 使用 Zustand + immer 简化状态管理
AI 生成超时 中 实现任务队列、状态轮询
数据库 Schema 差异 中 提前设计好适配层

八、技术选型建议

8.1 必须引入的依赖

{
  "zustand": "^5.0.4",           // 状态管理(lobe-chat-pro 使用)
  "immer": "^10.0.0",            // 不可变数据
  "react-rnd": "^10.5.2",        // 拖拽调整大小
  "framer-motion": "^12.0.0",    // 动画效果
  "antd": "^5.27.4",             // UI 组件(过渡期使用)
  "antd-style": "^3.7.1"         // 样式系统(过渡期使用)
}

8.2 可选依赖

{
  "react-virtuoso": "^4.14.1",   // 虚拟列表(大量节点时)
  "@tanstack/react-query": "^5.0.0", // 数据获取缓存
  "react-layout-kit": "^2.0.0"   // 布局工具
}

九、与现有功能整合清单

9.1 复用现有组件

现有组件 集成方式 功能说明
image-annotator.tsx 作为图片节点的编辑模式 完整的图片编辑器(标注、抠图、增强、扩展、调整等)
angle-edit-dialog.tsx 集成到图片节点操作菜单 3D 角度编辑对话框
scene-fusion-view.tsx 作为特殊节点类型 场景裂变视图组件
qwen-camera-widget.tsx 用于 3D 视角调整 Three.js 3D 相机控件
multimodal-input.tsx 作为画布输入组件 多模态输入(文本+图片+文件)
image-editor.tsx 用于流式图片生成 简单图片编辑器

9.2 复用现有 API

图片处理 API

现有 API 用途 说明
/api/rmbg 图片抠图 移除图片背景
/api/enhance 变清晰 图片高清增强
/api/extend 图片扩展 向外扩展图片内容
/api/resize 尺寸调整 调整图片分辨率/比例
/api/angle-edit 角度编辑 3D 视角调整
/api/scene-fusion 场景裂变 产品图+背景图融合生成
/api/scene-fusion/config 场景裂变配置 获取场景裂变模型配置
/api/detect-object 物体检测 检测图片中的物体

用户数据 API

现有 API 用途 说明
/api/gallery 图片相册 用户生成的图片管理
/api/featured 精选作品 精选作品展示
/api/featured/ranking 排行榜 作品排行榜
/api/messages 消息管理 聊天消息 CRUD
/api/user/image-quota 图片配额 用户图片生成配额
/api/user/image-stats 图片统计 用户图片使用统计

系统配置 API

现有 API 用途 说明
/api/site-settings 网站设置 网站名称、Logo 等
/api/nav-links 导航链接 自定义导航链接
/api/starter-questions 启动问题 聊天启动问题配置
/api/auth-settings 认证设置 登录认证配置

管理后台 API

现有 API 用途 说明
/api/admin/ai-models AI 模型管理 配置可用的 AI 模型
/api/admin/ai-providers AI 提供商管理 配置 AI 服务提供商
/api/admin/images 图片管理 管理用户生成的图片
/api/admin/images/analytics 图片分析 图片使用数据分析
/api/admin/storage 存储配置 S3/MinIO 存储配置
/api/admin/users 用户管理 用户列表和管理
/api/admin/users/batch-limit 批量限额 批量设置用户配额
/api/admin/settings 系统设置 系统级别配置
/api/admin/tools 工具管理 AI 工具配置
/api/admin/volcano-video-models 火山视频模型 火山引擎视频模型配置

定时任务 API

现有 API 用途 说明
/api/jobs/archive-videos 视频归档 定时归档视频任务

9.3 复用现有服务

// 复用现有的存储服务
import { uploadToStorage } from '@/lib/storage/service';

// 复用现有的 AI 提供商
import { getAIProvider } from '@/lib/ai/providers';

// 复用现有的数据库查询
import { saveGeneratedImage } from '@/lib/db/queries';

十、总结

10.1 实施路径

阶段 内容 说明
第一阶段 环境准备 + 依赖安装 克隆参考仓库,安装依赖
第二阶段 核心模块移植 移植 store、services、types
第三阶段 页面组件移植 移植画布页面和组件
第四阶段 深度集成 集成我们的图片编辑功能
第五阶段 UI 适配优化 替换 antd 为 Radix UI(可选)

10.2 关键成功因素

  1. 完整移植: 先完整移植功能,确保基础能力可用
  2. 分阶段适配: 逐步适配数据库、认证、存储服务
  3. 深度集成: 将我们现有的图片编辑能力集成进去
  4. 保持兼容: 初期保留 antd,避免大规模重写

10.3 后续扩展方向

  • 实时协作(多人同时编辑)
  • 模板系统(预设画布模板)
  • 插件系统(自定义节点类型)
  • 工作流自动化(节点触发规则)

附录

A. 参考资源

B. 我们项目中相关的现有文件

components/
├── image-annotator.tsx        # 图片标注器(1775行)
├── angle-edit-dialog.tsx      # 角度编辑对话框
├── qwen-camera-widget.tsx     # 3D 相机控件
├── multimodal-input.tsx       # 多模态输入

lib/
├── ai/providers/              # AI 提供商
│   ├── alibaba-image.ts
│   ├── gemini-image.ts
│   └── ...
├── storage/service.ts         # 存储服务
└── db/queries.ts              # 数据库查询

app/api/
├── rmbg/                      # 抠图 API
├── enhance/                   # 变清晰 API
├── extend/                    # 图片扩展 API
├── resize/                    # 尺寸调整 API
├── angle-edit/                # 角度编辑 API
├── scene-fusion/              # 场景裂变 API
│   └── config/                # 场景裂变配置
├── detect-object/             # 物体检测 API
├── gallery/                   # 图片相册 API
├── featured/                  # 精选作品 API
│   └── ranking/               # 排行榜 API
├── messages/                  # 消息管理 API
├── site-settings/             # 网站设置 API
├── nav-links/                 # 导航链接 API
├── starter-questions/         # 启动问题 API
├── auth-settings/             # 认证设置 API
├── user/
│   ├── image-quota/           # 图片配额 API
│   └── image-stats/           # 图片统计 API
├── jobs/
│   └── archive-videos/        # 视频归档任务
├── auth/
│   ├── dingtalk/              # 钉钉登录
│   └── logout/                # 登出
└── admin/
    ├── ai-models/             # AI 模型管理
    ├── ai-providers/          # AI 提供商管理
    ├── images/                # 图片管理
    │   └── analytics/         # 图片分析
    ├── storage/               # 存储配置
    ├── users/                 # 用户管理
    │   └── batch-limit/       # 批量限额
    ├── settings/              # 系统设置
    ├── tools/                 # 工具管理
    ├── volcano-video-models/  # 火山视频模型
    └── sync-dingtalk/         # 钉钉同步

C. lobe-chat-pro 主要文件位置

src/store/painting/            # 绘图状态管理
src/services/painting/         # 绘图服务
src/types/painting/            # 类型定义
src/app/[variants]/(main)/painting/  # 绘图页面
src/app/[variants]/(main)/image/     # 图片工作区
src/features/Conversation/     # 对话组件

D. 移植命令参考

# 1. 克隆参考仓库
git clone https://github.com/vual/lobe-chat-pro.git ~/lobe-chat-pro-ref

# 2. 安装依赖
pnpm add zustand@5.0.4 immer@^10.1.3 react-rnd@^10.5.2 \
  react-virtuoso@^4.14.1 react-layout-kit@^2.0.0 \
  framer-motion@^12.23.24 antd@^5.27.4 antd-style@^3.7.1

# 3. 创建目录结构
mkdir -p store/painting
mkdir -p services/painting
mkdir -p types/painting
mkdir -p components/canvas
mkdir -p app/canvas/[id]
mkdir -p app/api/canvas/generate

# 4. 复制文件(示例)
cp ~/lobe-chat-pro-ref/src/store/painting/* store/painting/
cp ~/lobe-chat-pro-ref/src/services/painting/* services/painting/
cp ~/lobe-chat-pro-ref/src/types/painting/* types/painting/