/** * 模板编辑器 API * 对接后端 /api/v1/templates 路由 */ import apiClient from "./client" import type { WatermarkConfig, IntroOutroConfig, PipConfig, FilterConfig, ChromaKeyConfig, StickerConfig, CoverConfig, } from "@/pages/editing-planner/types" /* ──────────── 类型定义 ──────────── */ /** 模板模式(后端枚举值) */ export type TemplateMode = "pip" | "voice_over" | "one_take" | "voice_pip" /** 模式显示名称映射 */ export const MODE_LABELS: Record = { pip: "混剪", voice_over: "人物口播", one_take: "一镜到底", voice_pip: "口播+混剪", } /** 模式颜色映射 */ export const MODE_COLORS: Record = { pip: "blue", voice_over: "green", one_take: "orange", voice_pip: "purple", } /** 标题配置 */ export interface TitleConfig { ai_auto_select: boolean content: string font_preset: string font_color: string font_size: number position: string } /** 字幕配置 */ export interface SubtitleConfig { enabled: boolean position: string font: string color: string size: number animation: string } /** BGM 配置 */ export interface BgmConfig { enabled: boolean music_id: string } /** 模板片段 */ export interface TemplateSegment { id?: string segment_order: number duration_min: number duration_max: number material_type: string | null } /** 剪辑模板 */ export interface EditingTemplate { id: string name: string mode: TemplateMode category: string tags: string[] title_config: TitleConfig subtitle_config: SubtitleConfig bgm_config: BgmConfig estimated_duration: number segments: TemplateSegment[] /** 水印配置(后端就绪后启用) */ watermark_config?: WatermarkConfig /** 片头片尾配置(后端就绪后启用) */ intro_outro_config?: IntroOutroConfig /** 混剪配置 */ pip_config?: PipConfig /** 滤镜调色配置 */ filter_config?: FilterConfig /** 绿幕抠像配置 */ green_screen_config?: ChromaKeyConfig /** 贴纸配置 */ sticker_config?: StickerConfig /** 封面配置 */ cover_config?: CoverConfig is_active?: boolean created_at: string updated_at: string } /** 模板分类 */ export interface TemplateCategory { id: string name: string created_at?: string } /** 创建/更新模板请求体 */ export interface SaveTemplatePayload { name: string mode: TemplateMode category: string tags: string[] title_config: TitleConfig subtitle_config: SubtitleConfig bgm_config: BgmConfig estimated_duration: number segments: Omit[] /** 水印配置(后端就绪后启用) */ watermark_config?: WatermarkConfig /** 片头片尾配置(后端就绪后启用) */ intro_outro_config?: IntroOutroConfig /** 混剪配置 */ pip_config?: PipConfig /** 滤镜调色配置 */ filter_config?: FilterConfig /** 绿幕抠像配置 */ green_screen_config?: ChromaKeyConfig /** 贴纸配置 */ sticker_config?: StickerConfig /** 封面配置 */ cover_config?: CoverConfig } /** 使用模板生成请求体 */ export interface GenerateFromTemplatePayload { voiceover_duration: number } /** 验证警告详情 */ export interface ValidationWarningDetails { /** 相关字段名 */ field?: string /** 期望值 */ expected?: string | number /** 实际值 */ actual?: string | number /** 建议值 */ suggested?: string | number } /** 验证/生成响应 */ export interface ValidateWarning { code: string message: string details?: ValidationWarningDetails } /** 使用模板生成响应 */ export interface GenerateFromTemplateResponse { template: EditingTemplate warnings: ValidateWarning[] } /** 列表响应(带分页) */ export interface ListTemplatesResponse { items: EditingTemplate[] total: number } /** 分类列表响应 */ export interface ListCategoriesResponse { items: TemplateCategory[] } // ============ API 函数 ============ /** 获取模板列表 */ export const getEditingTemplates = async (params?: { category?: string tag?: string skip?: number limit?: number }): Promise => { const response = await apiClient.get("/templates", { params: { skip: params?.skip ?? 0, limit: params?.limit ?? 50, }, }) let list = response.data.items 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 } /** 获取模板详情 */ export const getEditingTemplate = async (id: string): Promise => { const response = await apiClient.get(`/templates/${id}`) return response.data } /** 创建模板 */ export const createEditingTemplate = async ( data: SaveTemplatePayload, ): Promise => { const response = await apiClient.post("/templates", data) return response.data } /** 更新模板 */ export const updateEditingTemplate = async ( id: string, data: SaveTemplatePayload, ): Promise => { const response = await apiClient.patch(`/templates/${id}`, data) return response.data } /** 删除模板 */ export const deleteEditingTemplate = async (id: string): Promise => { await apiClient.delete(`/templates/${id}`) } /** 获取模板分类列表 */ export const getTemplateCategories = async (): Promise => { const response = await apiClient.get("/templates/categories/list") return response.data.items } /** 使用模板生成视频(调用 validate 端点) */ export const generateFromTemplate = async ( templateId: string, data: GenerateFromTemplatePayload, ): Promise => { const response = await apiClient.post( `/templates/${templateId}/validate`, data, ) return response.data }