- 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
44 KiB
44 KiB
无限画布功能集成开发方案
项目背景
本方案旨在参考 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 - 完整移植 + 深度集成
基于以下考虑:
- lobe-chat-pro 的画布功能已经过验证,稳定可靠
- 完整移植可以快速获得全部功能
- 移植后再深度集成我们现有的图片编辑功能(抠图、增强、场景裂变等)
- 可以复用我们现有的 AI 模型配置和存储服务
- 渐进式适配降低风险
三、完整移植方案
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 关键成功因素
- 完整移植: 先完整移植功能,确保基础能力可用
- 分阶段适配: 逐步适配数据库、认证、存储服务
- 深度集成: 将我们现有的图片编辑能力集成进去
- 保持兼容: 初期保留 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/