diff --git a/apps/web/src/api/editPlans.ts b/apps/web/src/api/editPlans.ts deleted file mode 100644 index 50f98674d..000000000 --- a/apps/web/src/api/editPlans.ts +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 编辑计划 API - * Phase 1 重构:去掉 projectId,编辑计划直接归属用户 - */ -import apiClient from './client'; - -/** 编辑模式 */ -export type EditingMode = 'one-take' | 'pip' | 'voiceover' | 'voice_pip'; - -/** 编辑模板 */ -export interface EditTemplateItem { - id: string; - name: string; - description: string; - target_duration: number; - clip_count: number; - is_active: boolean; - category?: string; - thumbnail_url?: string; -} - -/** 编辑计划片段 */ -export interface EditPlanClipItem { - id: string; - asset_id: string; - asset_name: string; - sequence: number; - start_time: number; - duration: number; - reason: string; - layer?: 'main' | 'pip' | 'broll'; - thumbnail_url?: string; -} - -/** 编辑计划 */ -export interface EditPlanItem { - id: string; - template_id: string; - asset_library_id: string; - title_id: string; - status: string; - editing_mode?: EditingMode; - summary: string; - clips: EditPlanClipItem[]; - created_at?: string; - updated_at?: string; -} - -// ─── 编辑计划 ────────────────────────────────────────────── - -/** 获取当前用户的编辑计划列表 */ -export const getEditPlans = async (): Promise => { - const response = await apiClient.get('/edit-plans'); - return response.data.items || []; -}; - -/** 获取单个编辑计划 */ -export const getEditPlan = async (planId: string): Promise => { - const response = await apiClient.get(`/edit-plans/${planId}`); - return response.data; -}; - -/** 创建编辑计划 */ -export const createEditPlan = async (data: { - asset_library_id: string; - template_id?: string; - title_id?: string; -}): Promise => { - const response = await apiClient.post('/edit-plans', data); - return response.data; -}; - -/** 智能编排 - 自动生成编辑计划 */ -export const autoGenerateEditPlan = async (params: { - template_id: string; - asset_ids?: string[]; - title_ids?: string[]; - voice_ids?: string[]; - editing_mode?: EditingMode; - target_duration?: number; -}): Promise => { - const response = await apiClient.post('/edit-plans/auto-generate', params); - return response.data; -}; - -/** 更新编辑计划 */ -export const updateEditPlan = async ( - planId: string, - data: Partial -): Promise => { - const response = await apiClient.patch(`/edit-plans/${planId}`, data); - return response.data; -}; - -/** 删除编辑计划 */ -export const deleteEditPlan = async (planId: string): Promise => { - await apiClient.delete(`/edit-plans/${planId}`); -}; diff --git a/apps/web/src/api/editingPlanner.ts b/apps/web/src/api/editingPlanner.ts index 063acbc9f..9965dab21 100644 --- a/apps/web/src/api/editingPlanner.ts +++ b/apps/web/src/api/editingPlanner.ts @@ -1,8 +1,8 @@ /** - * 剪辑计划编辑器 API - * 当前使用 mock 数据,后端 API 就绪后替换 + * 剪辑模板 API 模块 + * 对接后端 /api/v1/templates RESTful 端点 */ -// import apiClient from './client'; // TODO: 后端 API 就绪后启用 +import apiClient from './client'; /* ──────────── 类型定义 ──────────── */ @@ -72,14 +72,14 @@ export interface EditingTemplate { bgm_config: BgmConfig; estimated_duration: number; segments: TemplateSegment[]; - created_at: string; - updated_at: string; + created_at?: string; + updated_at?: string; } /** 模板分类 */ export interface TemplateCategory { - id: string; name: string; + count?: number; } /** 创建/更新模板请求体 */ @@ -106,209 +106,56 @@ export interface GenerateFromTemplateResponse { warning?: string; } -/* ──────────── Mock 数据 ──────────── */ +/* ──────────── API 函数 ──────────── */ -let _nextId = 100; -const nextId = () => String(++_nextId); - -const MOCK_CATEGORIES: TemplateCategory[] = [ - { id: 'cat-1', name: '生活' }, - { id: 'cat-2', name: '美食' }, - { id: 'cat-3', name: '旅行' }, - { id: 'cat-4', name: '知识' }, -]; - -const MOCK_TEMPLATES: EditingTemplate[] = [ - { - id: 'tpl-1', - name: '生活 Vlog 模板', - mode: 'pip', - category: '生活', - tags: ['vlog', '日常'], - title_config: { - ai_auto_select: true, - content: '', - font_preset: '思源黑体', - font_color: '#ffffff', - font_size: 32, - position: 'top', - }, - subtitle_config: { - enabled: true, - position: 'bottom', - font: '思源黑体', - color: '#ffffff', - size: 24, - animation: 'fade', - }, - bgm_config: { enabled: true, music_id: 'bgm-1' }, - estimated_duration: 30, - segments: [ - { id: 'seg-1', segment_order: 1, duration_min: 5, duration_max: 15, material_type: null }, - { id: 'seg-2', segment_order: 2, duration_min: 10, duration_max: 20, material_type: null }, - ], - created_at: '2026-06-20T10:00:00Z', - updated_at: '2026-06-20T10:00:00Z', - }, - { - id: 'tpl-2', - name: '知识分享口播', - mode: 'voice_over', - category: '知识', - tags: ['口播', '分享'], - title_config: { - ai_auto_select: false, - content: '每日知识分享', - font_preset: '站酷快乐体', - font_color: '#ffdd00', - font_size: 36, - position: 'top', - }, - subtitle_config: { - enabled: true, - position: 'bottom', - font: '思源黑体', - color: '#ffffff', - size: 28, - animation: 'typewriter', - }, - bgm_config: { enabled: false, music_id: '' }, - estimated_duration: 60, - segments: [ - { id: 'seg-3', segment_order: 1, duration_min: 10, duration_max: 30, material_type: null }, - { id: 'seg-4', segment_order: 2, duration_min: 20, duration_max: 40, material_type: null }, - { id: 'seg-5', segment_order: 3, duration_min: 10, duration_max: 20, material_type: null }, - ], - created_at: '2026-06-21T10:00:00Z', - updated_at: '2026-06-21T10:00:00Z', - }, - { - id: 'tpl-3', - name: '一镜到底展示', - mode: 'one_take', - category: '生活', - tags: ['一镜到底'], - title_config: { - ai_auto_select: true, - content: '', - font_preset: '思源黑体', - font_color: '#ffffff', - font_size: 32, - position: 'center', - }, - subtitle_config: { enabled: false, position: 'bottom', font: '思源黑体', color: '#ffffff', size: 24, animation: 'fade' }, - bgm_config: { enabled: true, music_id: 'bgm-2' }, - estimated_duration: 15, - segments: [ - { id: 'seg-6', segment_order: 1, duration_min: 10, duration_max: 20, material_type: null }, - ], - created_at: '2026-06-22T10:00:00Z', - updated_at: '2026-06-22T10:00:00Z', - }, -]; - -/* ──────────── Mock API 函数 ──────────── */ - -const delay = (ms = 200) => new Promise((r) => setTimeout(r, ms)); - -/** 获取模板列表 */ -export const getEditingTemplates = async (params?: { +/** 获取模板列表(支持分类/标签筛选) */ +export async function getEditingTemplates(params?: { category?: string; tag?: string; -}): Promise => { - await delay(); - let list = [...MOCK_TEMPLATES]; - if (params?.category) list = list.filter((t) => t.category === params.category); - if (params?.tag) list = list.filter((t) => t.tags.includes(params.tag!)); - return list; -}; +}): Promise { + const { data } = await apiClient.get('/templates', { params }); + return data; +} /** 获取模板详情 */ -export const getEditingTemplate = async (id: string): Promise => { - await delay(); - const tpl = MOCK_TEMPLATES.find((t) => t.id === id); - if (!tpl) throw new Error('模板不存在'); - return { ...tpl }; -}; +export async function getEditingTemplate(id: string): Promise { + const { data } = await apiClient.get(`/templates/${id}`); + return data; +} /** 创建模板 */ -export const createEditingTemplate = async ( - data: SaveTemplatePayload, -): Promise => { - await delay(300); - const now = new Date().toISOString(); - const tpl: EditingTemplate = { - id: nextId(), - name: data.name, - mode: data.mode, - category: data.category, - tags: data.tags, - title_config: data.title_config, - subtitle_config: data.subtitle_config, - bgm_config: data.bgm_config, - estimated_duration: data.estimated_duration, - segments: data.segments.map((s, i) => ({ - ...s, - id: nextId(), - segment_order: i + 1, - })), - created_at: now, - updated_at: now, - }; - MOCK_TEMPLATES.push(tpl); - return tpl; -}; +export async function createEditingTemplate( + payload: SaveTemplatePayload, +): Promise { + const { data } = await apiClient.post('/templates', payload); + return data; +} /** 更新模板 */ -export const updateEditingTemplate = async ( +export async function updateEditingTemplate( id: string, - data: SaveTemplatePayload, -): Promise => { - await delay(300); - const idx = MOCK_TEMPLATES.findIndex((t) => t.id === id); - if (idx === -1) throw new Error('模板不存在'); - const updated: EditingTemplate = { - ...MOCK_TEMPLATES[idx], - name: data.name, - mode: data.mode, - category: data.category, - tags: data.tags, - title_config: data.title_config, - subtitle_config: data.subtitle_config, - bgm_config: data.bgm_config, - estimated_duration: data.estimated_duration, - segments: data.segments.map((s, i) => ({ - ...s, - id: nextId(), - segment_order: i + 1, - })), - updated_at: new Date().toISOString(), - }; - MOCK_TEMPLATES[idx] = updated; - return updated; -}; + payload: SaveTemplatePayload, +): Promise { + const { data } = await apiClient.put(`/templates/${id}`, payload); + return data; +} /** 删除模板 */ -export const deleteEditingTemplate = async (id: string): Promise => { - await delay(); - const idx = MOCK_TEMPLATES.findIndex((t) => t.id === id); - if (idx !== -1) MOCK_TEMPLATES.splice(idx, 1); -}; +export async function deleteEditingTemplate(id: string): Promise { + await apiClient.delete(`/templates/${id}`); +} /** 获取模板分类列表 */ -export const getTemplateCategories = async (): Promise => { - await delay(); - return [...MOCK_CATEGORIES]; -}; +export async function getTemplateCategories(): Promise { + const { data } = await apiClient.get('/templates/categories'); + return data; +} /** 使用模板生成视频 */ -export const generateFromTemplate = async ( - _templateId: string, - _data: GenerateFromTemplatePayload, -): Promise => { - await delay(500); - return { - task_id: nextId(), - warning: undefined, - }; -}; +export async function generateFromTemplate( + templateId: string, + payload: GenerateFromTemplatePayload, +): Promise { + const { data } = await apiClient.post(`/templates/${templateId}/generate`, payload); + return data; +}