Your Name c7ffcda87b
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
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

261 lines
7.3 KiB
TypeScript

// 画布服务层
import type { CanvasProject, CanvasState, CanvasNode } from '@/types/canvas';
// API 响应类型
interface ApiResponse<T = any> {
success: boolean;
data?: T;
error?: string;
}
// 画布服务
export const canvasService = {
// 获取画布列表
async getProjects(): Promise<ApiResponse<CanvasProject[]>> {
try {
const response = await fetch('/api/canvas');
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '获取画布列表失败' };
}
},
// 获取单个画布
async getProject(id: string): Promise<ApiResponse<CanvasProject>> {
try {
const response = await fetch(`/api/canvas/${id}`);
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '获取画布失败' };
}
},
// 创建画布
async createProject(project: Partial<CanvasProject>): Promise<ApiResponse<CanvasProject>> {
try {
const response = await fetch('/api/canvas', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(project),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '创建画布失败' };
}
},
// 更新画布
async updateProject(id: string, updates: Partial<CanvasProject>): Promise<ApiResponse<CanvasProject>> {
try {
const response = await fetch(`/api/canvas/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '更新画布失败' };
}
},
// 删除画布
async deleteProject(id: string): Promise<ApiResponse> {
try {
const response = await fetch(`/api/canvas/${id}`, {
method: 'DELETE',
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '删除画布失败' };
}
},
// 保存画布状态
async saveCanvasState(id: string, canvasData: CanvasState): Promise<ApiResponse> {
try {
const response = await fetch(`/api/canvas/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ canvasData }),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '保存画布状态失败' };
}
},
// 自动保存(防抖)
createAutoSave(projectId: string, delay = 2000) {
let timeoutId: NodeJS.Timeout | null = null;
return (canvasData: CanvasState) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(async () => {
await this.saveCanvasState(projectId, canvasData);
}, delay);
};
},
};
// 图片生成服务
export const imageGenerationService = {
// AI 生成图片
async generateImage(params: {
prompt: string;
negativePrompt?: string;
model?: string;
aspectRatio?: string;
imageCount?: number;
referenceImage?: string;
}): Promise<ApiResponse<{ images: Array<{ thumbnailUrl: string; originalUrl: string }>; model?: string; prompt?: string }>> {
try {
const response = await fetch('/api/canvas/generate/image', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '生成图片失败' };
}
},
// 图片抠图
async removeBackground(imageUrl: string): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/rmbg', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl }),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '抠图失败' };
}
},
// 图片增强
async enhanceImage(imageUrl: string): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/enhance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl }),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '图片增强失败' };
}
},
// 图片扩展
async extendImage(imageUrl: string, direction?: string): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/extend', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl, direction }),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '图片扩展失败' };
}
},
// 尺寸调整
async resizeImage(imageUrl: string, width: number, height: number): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/resize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl, width, height }),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '尺寸调整失败' };
}
},
// 角度编辑
async editAngle(params: {
imageUrl: string;
horizontalAngle: number;
verticalAngle: number;
zoom: number;
}): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/angle-edit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '角度编辑失败' };
}
},
// 场景裂变
async sceneFusion(params: {
productImage: string;
backgroundImages: string[];
productName?: string;
}): Promise<ApiResponse<{ urls: string[] }>> {
try {
const response = await fetch('/api/scene-fusion', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '场景裂变失败' };
}
},
};
// 视频生成服务
export const videoGenerationService = {
// 生成视频
async generateVideo(params: {
prompt: string;
model: string;
referenceImage?: string;
duration?: number;
}): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/canvas/generate/video', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
const data = await response.json();
return data;
} catch (error) {
return { success: false, error: '生成视频失败' };
}
},
};
export default {
canvas: canvasService,
image: imageGenerationService,
video: videoGenerationService,
};