diff --git a/apps/web/src/api/bgm.ts b/apps/web/src/api/bgm.ts index 6debddbaa..f56af3a54 100644 --- a/apps/web/src/api/bgm.ts +++ b/apps/web/src/api/bgm.ts @@ -30,7 +30,7 @@ export interface BgmPresetsQuery { keyword?: string } -/** BGM 混音配置(嵌入剪辑计划) */ +/** BGM 混音配置(嵌入模板) */ export interface BgmMixConfig { /** 是否启用 BGM */ enabled: boolean diff --git a/apps/web/src/api/editPlans.ts b/apps/web/src/api/editPlans.ts index f7e3f1021..f47760720 100644 --- a/apps/web/src/api/editPlans.ts +++ b/apps/web/src/api/editPlans.ts @@ -1,5 +1,5 @@ /** - * 剪辑计划 API — 对接后端 Edit Plans Schema + * 模板草稿 API — 对接后端 Template Editor Schema * 字段名严格匹配后端 API 响应 */ import apiClient from "./client" @@ -18,7 +18,7 @@ import type { * 后端 API 类型(严格匹配后端 Schema) * ============================================================ */ -/** 剪辑计划状态枚举 */ +/** 模板草稿状态枚举 */ export type EditPlanStatus = "draft" | "editing" | "rendering" | "completed" | "failed" | "cancelled" @@ -71,7 +71,7 @@ export interface SegmentTransitionConfig { duration: number } -/** 剪辑计划中的单个片段(config 内部 segments 项) */ +/** 模板草稿中的单个片段(config 内部 segments 项) */ export interface EditPlanSegment { segment_order: number duration_min: number @@ -83,7 +83,7 @@ export interface EditPlanSegment { trim_config?: SegmentTrimConfig } -/** 剪辑计划 config 完整类型(对齐后端 config JSON 结构) */ +/** 模板草稿 config 完整类型(对齐后端 config JSON 结构) */ export interface EditPlanConfig { title_config?: TitleConfig subtitle_config?: SubtitleConfig @@ -123,7 +123,7 @@ export interface EditPlanConfig { material_mode?: string } -/** 剪辑计划(后端响应) */ +/** 模板草稿(后端响应) */ export interface EditPlan { id: string template_id: string @@ -137,17 +137,17 @@ export interface EditPlan { updated_at: string } -/** 创建剪辑计划请求(后端要求 template_id + name 必填) */ +/** 创建模板草稿请求(后端要求 template_id + name 必填) */ export interface CreateEditPlanRequest { template_id: string name: string config?: EditPlanConfig total_duration?: number - /** 来源剪辑计划 ID(从剪辑计划跳转到智能剪辑时关联) */ + /** 来源模板草稿 ID(从模板编辑器跳转到智能剪辑时关联) */ source_edit_plan_id?: string } -/** 更新剪辑计划请求 */ +/** 更新模板草稿请求 */ export interface UpdateEditPlanRequest { name?: string config?: EditPlanConfig @@ -163,7 +163,7 @@ export interface GenerateResponse { clip_count: number } -/** 剪辑计划关联的生成记录(实际是 GenerationTask 对象) */ +/** 模板草稿关联的生成记录(实际是 GenerationTask 对象) */ export interface EditPlanGeneration { id: string // 即 generation_task_id source_edit_plan_id: string @@ -323,7 +323,7 @@ export interface MediaAsset { * API 函数 — 严格对接后端 * ============================================================ */ -/** 剪辑计划列表查询参数 */ +/** 模板草稿列表查询参数 */ export interface EditPlanListParams { page?: number page_size?: number @@ -331,7 +331,7 @@ export interface EditPlanListParams { status?: string } -/** 剪辑计划列表分页响应 */ +/** 模板草稿列表分页响应 */ export interface EditPlanListResponse { items: EditPlan[] total: number @@ -339,7 +339,7 @@ export interface EditPlanListResponse { page_size: number } -/** 获取剪辑计划列表(支持分页和筛选) */ +/** 获取模板草稿列表(支持分页和筛选) */ export async function getEditPlans(params?: EditPlanListParams): Promise { const response = await apiClient.get("/edit-plans", { params, @@ -347,19 +347,19 @@ export async function getEditPlans(params?: EditPlanListParams): Promise { const response = await apiClient.get(`/edit-plans/${planId}`) return response.data } -/** 创建剪辑计划 */ +/** 创建模板草稿 */ export async function createEditPlan(data: CreateEditPlanRequest): Promise { const response = await apiClient.post("/edit-plans", data) return response.data } -/** 更新剪辑计划 */ +/** 更新模板草稿 */ export async function updateEditPlan( planId: string, data: UpdateEditPlanRequest, @@ -368,18 +368,18 @@ export async function updateEditPlan( return response.data } -/** 删除剪辑计划 */ +/** 删除模板草稿 */ export async function deleteEditPlan(planId: string): Promise { await apiClient.delete(`/edit-plans/${planId}`) } -/** 触发剪辑计划生成 */ +/** 触发生成 */ export async function generateEditPlan(planId: string): Promise { const response = await apiClient.post(`/edit-plans/${planId}/generate`) return response.data } -/** 获取剪辑计划生成状态(轮询用) */ +/** 获取生成状态(轮询用) */ export async function getGenerationStatus(planId: string): Promise { const response = await apiClient.get(`/edit-plans/${planId}/generation-status`) return response.data @@ -403,7 +403,7 @@ export async function generateCover( return response.data } -/** 获取剪辑计划关联的生成记录 */ +/** 获取模板草稿关联的生成记录 */ export async function getEditPlanGenerations(planId: string): Promise { const response = await apiClient.get(`/edit-plans/${planId}/generations`) return response.data.items || [] @@ -607,7 +607,7 @@ export interface CopyEditPlanRequest { project_id?: string } -/** 复制剪辑计划(含所有片段配置) */ +/** 复制模板草稿(含所有片段配置) */ export async function copyEditPlan(planId: string, data?: CopyEditPlanRequest): Promise { const response = await apiClient.post(`/edit-plans/${planId}/copy`, data || {}) return response.data diff --git a/apps/web/src/api/editingPlanner.ts b/apps/web/src/api/editingPlanner.ts old mode 100755 new mode 100644 index da0d9e789..6794bed24 --- a/apps/web/src/api/editingPlanner.ts +++ b/apps/web/src/api/editingPlanner.ts @@ -1,5 +1,5 @@ /** - * 剪辑计划编辑器 API + * 模板编辑器 API * 对接后端 /api/v1/templates 路由 */ import apiClient from "./client" diff --git a/apps/web/src/api/templates.ts b/apps/web/src/api/templates.ts index c04641077..5bcd3e69b 100644 --- a/apps/web/src/api/templates.ts +++ b/apps/web/src/api/templates.ts @@ -4,7 +4,7 @@ * - 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}/generate — 从模板生成 * - POST /api/v1/templates/{id}/toggle-favorite — 收藏/取消收藏 */ import apiClient from "./client" @@ -76,14 +76,14 @@ export interface TemplateListResponse { page_size: number } -/** 从模板生成剪辑计划请求 */ +/** 从模板生成请求 */ export interface GenerateFromTemplateRequest { asset_ids?: string[] name?: string config?: EditPlanConfig } -/** 从模板生成剪辑计划响应 */ +/** 从模板生成响应 */ export interface GenerateFromTemplateResponse { plan_id: string template_id: string @@ -134,7 +134,7 @@ export const copyTemplate = async (templateId: string): Promise = { - draft: { - label: "草稿", - color: "default", - icon: , - }, - editing: { - label: "编辑中", - color: "processing", - icon: , - }, - rendering: { - label: "渲染中", - color: "warning", - icon: , - }, - completed: { - label: "已完成", - color: "success", - icon: , - }, - failed: { - label: "失败", - color: "error", - icon: , - }, - cancelled: { - label: "已取消", - color: "default", - icon: , - }, -} - -/* ──────────── 工具函数 ──────────── */ - -/** 格式化时长 */ -const formatDuration = (seconds: number): string => { - if (seconds <= 0) return "-" - const totalSec = Math.round(seconds) - const m = Math.floor(totalSec / 60) - const s = totalSec % 60 - if (m === 0) return `${s}秒` - return `${m}分${s > 0 ? `${s}秒` : ""}` -} - -/** 格式化时间 */ -const formatTime = (dateStr?: string | null): string => { - if (!dateStr) return "-" - const date = new Date(dateStr) - return date.toLocaleString("zh-CN", { - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - }) -} - -/* ──────────── 主组件 ──────────── */ - -export default function EditPlans() { - const navigate = useNavigate() - const queryClient = useQueryClient() - - // 筛选状态 - const [statusFilter, setStatusFilter] = useState("all") - const [templateFilter, setTemplateFilter] = useState("all") - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(20) - - // 查询参数 - const queryParams: EditPlanListParams = { - page, - page_size: pageSize, - ...(statusFilter !== "all" && { status: statusFilter }), - ...(templateFilter !== "all" && { template_id: templateFilter }), - } - - // 获取剪辑计划列表 - const { - data: planData, - isLoading, - error, - } = useQuery({ - queryKey: ["edit-plans", queryParams], - queryFn: () => getEditPlans(queryParams), - refetchInterval: (query) => { - // 有进行中的计划时自动刷新 - const plans = query.state.data?.items ?? [] - const hasRunning = plans.some((p) => p.status === "rendering" || p.status === "editing") - return hasRunning ? 5000 : false - }, - }) - - // 获取模板列表(用于筛选下拉) - const { data: templates } = useQuery({ - queryKey: ["templates-list-simple"], - queryFn: getTemplatesList, - }) - - const plans = planData?.items ?? [] - const total = planData?.total ?? 0 - - // 模板名称映射 - const templateNameMap = new Map() - ;(templates ?? []).forEach((t: TemplateItem) => { - templateNameMap.set(t.id, t.name) - }) - - // 删除计划 - const deleteMutation = useMutation({ - mutationFn: deleteEditPlan, - onSuccess: () => { - message.success("剪辑计划已删除") - queryClient.invalidateQueries({ queryKey: ["edit-plans"] }) - }, - onError: () => { - message.error("删除失败,请稍后重试") - }, - }) - - // 重新生成 - const regenerateMutation = useMutation({ - mutationFn: generateEditPlan, - onSuccess: () => { - message.success("已重新提交生成") - queryClient.invalidateQueries({ queryKey: ["edit-plans"] }) - }, - onError: () => { - message.error("重新生成失败,请稍后重试") - }, - }) - - // 取消生成 - const cancelMutation = useMutation({ - mutationFn: cancelGeneration, - onSuccess: () => { - message.success("已提交取消请求") - queryClient.invalidateQueries({ queryKey: ["edit-plans"] }) - }, - onError: () => { - message.error("取消失败,请稍后重试") - }, - }) - - // 复制计划 - const copyMutation = useMutation({ - mutationFn: ({ planId, name }: { planId: string; name?: string }) => - copyEditPlan(planId, name ? { name } : undefined), - onSuccess: (newPlan) => { - message.success("计划已复制") - queryClient.invalidateQueries({ queryKey: ["edit-plans"] }) - // 自动跳转到新计划的编辑器 - navigate(`/app/editing-planner?planId=${newPlan.id}`) - }, - onError: () => { - message.error("复制失败,请稍后重试") - }, - }) - - // 跳转到剪辑编辑器 - const handleEdit = useCallback( - (plan: EditPlan) => { - navigate(`/app/editing-planner?planId=${plan.id}`) - }, - [navigate], - ) - - // 表格列定义 - const columns: ColumnsType = [ - { - title: "计划名称", - dataIndex: "name", - key: "name", - width: 240, - ellipsis: true, - render: (name: string, record: EditPlan) => ( - - handleEdit(record)}> - {name} - - - ), - }, - { - title: "模板", - dataIndex: "template_id", - key: "template_id", - width: 140, - ellipsis: true, - render: (templateId: string) => { - const name = templateNameMap.get(templateId) - return ( - - {name || templateId.slice(0, 8)} - - ) - }, - }, - { - title: "状态", - dataIndex: "status", - key: "status", - width: 120, - render: (status: EditPlanStatus) => { - const config = STATUS_CONFIG[status] || { - label: status, - color: "default", - icon: null, - } - return ( - - {config.label} - - ) - }, - }, - { - title: "时长", - dataIndex: "total_duration", - key: "total_duration", - width: 100, - render: (seconds: number) => {formatDuration(seconds)}, - }, - { - title: "视频数", - dataIndex: "result_count", - key: "result_count", - width: 80, - align: "center", - render: (count: number) => ( - {count > 0 ? count : "—"} - ), - }, - { - title: "创建时间", - dataIndex: "created_at", - key: "created_at", - width: 130, - render: (time: string) => {formatTime(time)}, - }, - { - title: "更新时间", - dataIndex: "updated_at", - key: "updated_at", - width: 130, - render: (time: string) => {formatTime(time)}, - }, - { - title: "操作", - key: "action", - width: 240, - fixed: "right", - render: (_: unknown, record: EditPlan) => ( -
- - - - - {record.status === "rendering" && ( - cancelMutation.mutate(record.id)} - okText="确定" - cancelText="再等等" - okButtonProps={{ danger: true }} - > - - - )} - {(record.status === "failed" || - record.status === "completed" || - record.status === "cancelled") && ( - regenerateMutation.mutate(record.id)} - okText="确定" - cancelText="取消" - > - - - )} - - copyMutation.mutate({ - planId: record.id, - name: `${record.name} 副本`, - }) - } - okText="确定" - cancelText="取消" - > - - - deleteMutation.mutate(record.id)} - okText="确定" - cancelText="取消" - okButtonProps={{ danger: true }} - > - - -
- ), - }, - ] - - // 错误处理 - if (error) { - return ( -
-
- -

加载剪辑计划失败

- -
-
- ) - } - - return ( -
- {/* 页面标题 */} -
-
-

剪辑计划

-

管理所有剪辑计划,支持重新生成和编辑

-
- -
- - {/* 筛选栏 */} -
- {/* 状态 Tab */} - { - setStatusFilter(key as EditPlanStatus | "all") - setPage(1) - }} - items={STATUS_TABS.map((tab) => ({ - key: tab.key, - label: tab.label, - }))} - className="edit-plans-status-tabs" - /> - - {/* 模板筛选 */} - - - - - - - - -
- - - - - - -
-
- -