15c635b553
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 6m8s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 7m9s
CI/CD Pipeline / Unit Tests (push) Failing after 7m50s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 8m13s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 8m19s
CI/CD Pipeline / Integration Tests (push) Successful in 2m15s
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
169 lines
4.8 KiB
TypeScript
169 lines
4.8 KiB
TypeScript
/**
|
||
* 模板相关 API
|
||
* 对接后端模板管理接口:
|
||
* - GET /api/v1/templates — 模板列表(分页/筛选)
|
||
* - GET /api/v1/templates/{id} — 模板详情
|
||
* - POST /api/v1/templates/{id}/copy — 复制模板
|
||
* - POST /api/v1/templates/{id}/generate — 从模板生成
|
||
* - POST /api/v1/templates/{id}/toggle-favorite — 收藏/取消收藏
|
||
*/
|
||
import apiClient from "./client"
|
||
import type { TitleConfig, SubtitleConfig, BgmConfig } from "./editingPlanner"
|
||
import type { EditPlanConfig } from "./templateEditor"
|
||
|
||
/* ──────────── 类型定义 ──────────── */
|
||
|
||
/** 模板条目(后端 TemplateResponse) */
|
||
export interface TemplateItem {
|
||
id: string
|
||
user_id?: string
|
||
name: string
|
||
description?: string
|
||
mode?: string
|
||
category: string
|
||
tags?: string[]
|
||
/** 预估时长(后端字段名 estimated_duration) */
|
||
estimated_duration?: number
|
||
/** @deprecated 后端已改名为 estimated_duration,保留兼容 */
|
||
target_duration?: number
|
||
clip_count?: number
|
||
/** 使用次数 */
|
||
usage_count?: number
|
||
thumbnail_url?: string
|
||
preview_url?: string
|
||
is_active?: boolean
|
||
is_favorite?: boolean
|
||
/** 素材规则(片段配置) */
|
||
segments?: TemplateSegment[]
|
||
/** 字幕样式 */
|
||
subtitle_config?: SubtitleConfig
|
||
/** BGM 配置 */
|
||
bgm_config?: BgmConfig
|
||
/** 标题配置 */
|
||
title_config?: TitleConfig
|
||
/** 视频比例 */
|
||
aspect_ratio?: string
|
||
created_at?: string
|
||
updated_at?: string
|
||
}
|
||
|
||
/** 模板片段(素材规则) */
|
||
export interface TemplateSegment {
|
||
id?: string
|
||
segment_order: number
|
||
duration_min: number
|
||
duration_max: number
|
||
material_type: string | null
|
||
description?: string
|
||
}
|
||
|
||
/** 模板列表查询参数 */
|
||
export interface TemplateListParams {
|
||
page?: number
|
||
page_size?: number
|
||
category?: string
|
||
tags?: string
|
||
keyword?: string
|
||
/** 时长筛选(秒):short < 30, medium 30-120, long > 120 */
|
||
duration_range?: "short" | "medium" | "long"
|
||
}
|
||
|
||
/** 模板列表分页响应 */
|
||
export interface TemplateListResponse {
|
||
items: TemplateItem[]
|
||
total: number
|
||
page: number
|
||
page_size: number
|
||
}
|
||
|
||
/** 从模板生成请求 */
|
||
export interface GenerateFromTemplateRequest {
|
||
asset_ids?: string[]
|
||
name?: string
|
||
config?: EditPlanConfig
|
||
}
|
||
|
||
/** 从模板生成响应 */
|
||
export interface GenerateFromTemplateResponse {
|
||
plan_id: string
|
||
template_id: string
|
||
status: string
|
||
name: string
|
||
}
|
||
|
||
/** 复制模板响应 */
|
||
export interface CopyTemplateResponse {
|
||
id: string
|
||
name: string
|
||
source_template_id: string
|
||
}
|
||
|
||
/* ──────────── API 函数 ──────────── */
|
||
|
||
/** 获取模板列表(支持分页和筛选) */
|
||
export const getTemplates = async (params?: TemplateListParams): Promise<TemplateListResponse> => {
|
||
const { data } = await apiClient.get<TemplateListResponse>("/templates", {
|
||
params,
|
||
})
|
||
return data
|
||
}
|
||
|
||
/** 获取模板列表(兼容旧接口,返回数组) */
|
||
export const getTemplatesList = async (): Promise<TemplateItem[]> => {
|
||
const response = await apiClient.get("/templates")
|
||
return response.data.items || response.data || []
|
||
}
|
||
|
||
/** 获取单个模板详情 */
|
||
export const getTemplate = async (templateId: string): Promise<TemplateItem> => {
|
||
const response = await apiClient.get(`/templates/${templateId}`)
|
||
return response.data
|
||
}
|
||
|
||
/** 收藏 / 取消收藏模板 */
|
||
export const toggleFavoriteTemplate = async (
|
||
templateId: string,
|
||
): Promise<{ is_favorite: boolean }> => {
|
||
const response = await apiClient.post(`/templates/${templateId}/toggle-favorite`)
|
||
return response.data
|
||
}
|
||
|
||
/** 复制模板(创建副本到我的模板) */
|
||
export const copyTemplate = async (templateId: string): Promise<CopyTemplateResponse> => {
|
||
const response = await apiClient.post<CopyTemplateResponse>(`/templates/${templateId}/copy`)
|
||
return response.data
|
||
}
|
||
|
||
/** 从模板生成 */
|
||
export const generateFromTemplate = async (
|
||
templateId: string,
|
||
data?: GenerateFromTemplateRequest,
|
||
): Promise<GenerateFromTemplateResponse> => {
|
||
const response = await apiClient.post<GenerateFromTemplateResponse>(
|
||
`/templates/${templateId}/generate`,
|
||
data,
|
||
)
|
||
return response.data
|
||
}
|
||
|
||
/* ──────────── 常量 ──────────── */
|
||
|
||
/** 模板分类选项 */
|
||
export const TEMPLATE_CATEGORY_OPTIONS = [
|
||
{ value: "", label: "全部分类" },
|
||
{ value: "口播", label: "口播" },
|
||
{ value: "种草", label: "种草" },
|
||
{ value: "产品", label: "产品" },
|
||
{ value: "品牌", label: "品牌" },
|
||
{ value: "混剪", label: "混剪" },
|
||
{ value: "Vlog", label: "Vlog" },
|
||
]
|
||
|
||
/** 时长筛选选项 */
|
||
export const TEMPLATE_DURATION_OPTIONS = [
|
||
{ value: "", label: "全部时长" },
|
||
{ value: "short", label: "30秒以内" },
|
||
{ value: "medium", label: "30秒-2分钟" },
|
||
{ value: "long", label: "2分钟以上" },
|
||
]
|