feat: 预览改为前端实时播放,确认生成时一次服务器渲染 #1490
@@ -1,21 +1,18 @@
|
||||
/**
|
||||
* 智能剪辑页面 — 服务器渲染预览架构
|
||||
* 智能剪辑页面 — 前端实时预览架构
|
||||
* 7 步向导:选择模板 → 素材 → 配音 → 标题 → 预览 → 封面 → 确认生成
|
||||
* 左右布局:左侧 generate-form + 右侧 generate-preview
|
||||
*
|
||||
* 架构:
|
||||
* - Step4+ 右侧预览面板自动创建服务器预览渲染任务(POST /generation/preview)
|
||||
* - 轮询完成后播放服务器渲染的真实视频(<video> 标签)
|
||||
* - 标题样式编辑时 CSS 层实时叠加预览
|
||||
* - 素材/配音/BGM 变更自动重新渲染;标题变更标记 stale
|
||||
* - 点"确认生成"时走 confirm 路径,成品就是预览视频本身,100% 一致
|
||||
* - Step4+ 右侧预览面板使用 FrontendPreviewPlayer 实时播放素材片段
|
||||
* - 标题样式编辑时 CSS 层实时叠加预览,所见即所得
|
||||
* - 点"确认生成"时调用 createGenerationTask 创建一次服务器渲染任务
|
||||
*/
|
||||
import React, { useMemo, useCallback } from "react"
|
||||
import React, { useMemo } from "react"
|
||||
import { Modal, message } from "antd"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import type { VoiceClone } from "@/api/voice-clone"
|
||||
import { useCloneProgress } from "@/hooks/useCloneProgress"
|
||||
import { calculateResolution } from "./utils/calculateResolution"
|
||||
import CloneModal from "@/components/voice/CloneModal"
|
||||
import GenerateHeader from "./components/GenerateHeader"
|
||||
import {
|
||||
@@ -24,14 +21,13 @@ import {
|
||||
} from "./utils/calculateTotalVideoDuration"
|
||||
import GenerateStepsBar from "./components/GenerateStepsBar"
|
||||
import GenerateResultPanel from "./components/GenerateResultPanel"
|
||||
import PreviewVideoPanel from "./components/PreviewVideoPanel"
|
||||
import FrontendPreviewPlayer from "./components/FrontendPreviewPlayer"
|
||||
import GenerateStepContent from "./components/GenerateStepContent"
|
||||
import GenerateStepActions from "./components/GenerateStepActions"
|
||||
import { useGenerateFormState } from "./hooks/useGenerateFormState"
|
||||
import { useStepNavigation } from "./hooks/useStepNavigation"
|
||||
import { useGenerateVideo } from "./hooks/useGenerateVideo"
|
||||
import { usePreviewAssets } from "./hooks/usePreviewAssets"
|
||||
import { useServerPreview } from "./hooks/useServerPreview"
|
||||
import { useTitleStyleUpdaters } from "./hooks/useStep4Title/useTitleStyleUpdaters"
|
||||
import "./generate.css"
|
||||
|
||||
@@ -120,10 +116,7 @@ const GeneratePage: React.FC = () => {
|
||||
[bgm, currentTemplate],
|
||||
)
|
||||
|
||||
/* ── 输出分辨率(根据视频比例计算) ── */
|
||||
const resolution = useMemo(() => calculateResolution(videoRatio), [videoRatio])
|
||||
|
||||
/* ── 加载素材详情(仅用于配音时长校验,不用于播放) ── */
|
||||
/* ── 加载素材详情(供前端预览播放器使用 + 配音时长校验) ── */
|
||||
const previewAssetsEnabled = previewAssetIds.length > 0
|
||||
const { assets: previewAssets } = usePreviewAssets(previewAssetIds, previewAssetsEnabled)
|
||||
|
||||
@@ -134,74 +127,7 @@ const GeneratePage: React.FC = () => {
|
||||
return estimateTotalVideoDuration(currentTemplate ?? undefined)
|
||||
}, [previewAssets, currentTemplate])
|
||||
|
||||
/* ── 解析配音 ID ── */
|
||||
const voiceLibraryId = useMemo(
|
||||
() => (voiceMode === "clone" ? selectedClonedVoice || "" : selectedVoice || ""),
|
||||
[voiceMode, selectedClonedVoice, selectedVoice],
|
||||
)
|
||||
|
||||
/* ── 构建服务器预览请求参数 ── */
|
||||
const buildPreviewRequest = useCallback(() => {
|
||||
return {
|
||||
template_id: selectedTemplate,
|
||||
asset_ids: previewAssetIds,
|
||||
duration: duration || 30,
|
||||
video_ratio: videoRatio,
|
||||
output_width: resolution.width,
|
||||
output_height: resolution.height,
|
||||
...(voiceLibraryId ? { voice_library_id: voiceLibraryId } : {}),
|
||||
bgm_config: {
|
||||
enabled: bgm !== false,
|
||||
...(bgmConfig.music_id ? { preset_id: bgmConfig.music_id } : {}),
|
||||
},
|
||||
...(titleSettings.title
|
||||
? {
|
||||
title_config: {
|
||||
text: titleSettings.title,
|
||||
font: titleSettings.font,
|
||||
font_size: titleSettings.size,
|
||||
font_color: titleSettings.color,
|
||||
position: titleSettings.position,
|
||||
bold: titleSettings.bold,
|
||||
stroke: titleSettings.stroke,
|
||||
shadow: titleSettings.shadow,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
}, [
|
||||
selectedTemplate,
|
||||
previewAssetIds,
|
||||
duration,
|
||||
videoRatio,
|
||||
resolution,
|
||||
voiceLibraryId,
|
||||
bgm,
|
||||
bgmConfig,
|
||||
titleSettings,
|
||||
])
|
||||
|
||||
/* ── 服务器预览 ── */
|
||||
const serverPreviewEnabled = currentStep >= 4 && !!selectedTemplate && previewAssetIds.length > 0
|
||||
|
||||
const {
|
||||
status: previewStatus,
|
||||
videoUrl: previewVideoUrl,
|
||||
progress: previewProgress,
|
||||
error: previewError,
|
||||
triggerPreview,
|
||||
} = useServerPreview({
|
||||
enabled: serverPreviewEnabled,
|
||||
buildRequest: buildPreviewRequest,
|
||||
onPreviewTaskCreated: (taskId, planId) => {
|
||||
setPreviewTaskId(taskId)
|
||||
if (planId) setStoredSourceEditPlanId(planId)
|
||||
},
|
||||
})
|
||||
|
||||
/* ── 步骤导航 ── */
|
||||
// Step5 需要服务器预览完成才能前进
|
||||
const previewReady = previewStatus === "ready"
|
||||
const { goNext, goPrev } = useStepNavigation({
|
||||
currentStep,
|
||||
setCurrentStep,
|
||||
@@ -210,7 +136,6 @@ const GeneratePage: React.FC = () => {
|
||||
selectedMaterials,
|
||||
smartSelectedIds,
|
||||
titleSettings,
|
||||
previewReady,
|
||||
})
|
||||
|
||||
/* ── 视频生成核心逻辑 ── */
|
||||
@@ -250,11 +175,6 @@ const GeneratePage: React.FC = () => {
|
||||
},
|
||||
})
|
||||
|
||||
/* ── 手动重新预览(标题变更后或失败重试) ── */
|
||||
const handleRetryPreview = useCallback(() => {
|
||||
triggerPreview()
|
||||
}, [triggerPreview])
|
||||
|
||||
/* ================================================================
|
||||
渲染
|
||||
================================================================ */
|
||||
@@ -320,8 +240,6 @@ const GeneratePage: React.FC = () => {
|
||||
onRetry={handleRetryGenerate}
|
||||
onDismissError={handleDismissError}
|
||||
presetVoices={presetVoices}
|
||||
previewStatus={previewStatus}
|
||||
onRetryPreview={handleRetryPreview}
|
||||
/>
|
||||
|
||||
<GenerateStepActions
|
||||
@@ -337,16 +255,23 @@ const GeneratePage: React.FC = () => {
|
||||
|
||||
{/* ════ 右侧:预览 + 结果 ════ */}
|
||||
<div className="xx-generate-right-col">
|
||||
{currentStep >= 4 && (
|
||||
<PreviewVideoPanel
|
||||
previewStatus={previewStatus}
|
||||
videoUrl={previewVideoUrl}
|
||||
progress={previewProgress}
|
||||
error={previewError}
|
||||
onRetry={handleRetryPreview}
|
||||
{currentStep >= 4 && !!currentTemplate && (
|
||||
<FrontendPreviewPlayer
|
||||
assets={previewAssets}
|
||||
template={currentTemplate}
|
||||
videoRatio={videoRatio}
|
||||
titleSettings={titleSettings}
|
||||
assetCount={previewAssetIds.length}
|
||||
ready={previewAssets.length > 0}
|
||||
titleSettings={{
|
||||
title: titleSettings.title,
|
||||
size: titleSettings.size,
|
||||
font: titleSettings.font,
|
||||
color: titleSettings.color,
|
||||
position: titleSettings.position as "top" | "center" | "bottom",
|
||||
bold: titleSettings.bold,
|
||||
italic: titleSettings.italic,
|
||||
stroke: titleSettings.stroke,
|
||||
shadow: titleSettings.shadow,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{currentStep >= 6 && (
|
||||
|
||||
@@ -84,10 +84,6 @@ export interface GenerateStepContentProps {
|
||||
bgm: boolean
|
||||
/** BGM 配置(来自模板) */
|
||||
bgmConfig?: { enabled: boolean; music_id?: string }
|
||||
/** 服务器预览状态 */
|
||||
previewStatus?: import("../hooks/useServerPreview").ServerPreviewStatus
|
||||
/** 重新预览回调 */
|
||||
onRetryPreview?: () => void
|
||||
}
|
||||
|
||||
export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) => {
|
||||
@@ -137,8 +133,6 @@ export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) =
|
||||
onSourceEditPlanIdExtracted,
|
||||
bgm,
|
||||
bgmConfig,
|
||||
previewStatus,
|
||||
onRetryPreview,
|
||||
} = props
|
||||
|
||||
/* 当前模板的 segments,传给 Step2 构建 clips */
|
||||
@@ -197,8 +191,6 @@ export const GenerateStepContent: React.FC<GenerateStepContentProps> = (props) =
|
||||
onApplyPreset={onApplyPreset}
|
||||
activePreset={activePreset}
|
||||
titlePresets={titlePresets}
|
||||
previewStatus={previewStatus || "idle"}
|
||||
onRetryPreview={onRetryPreview || (() => {})}
|
||||
/>
|
||||
)
|
||||
case 6:
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
/**
|
||||
* Step 5 预览设置组件
|
||||
*
|
||||
* 服务器渲染预览架构:
|
||||
* - 进入此步骤时右侧面板自动播放服务器渲染的真实视频
|
||||
* - 标题样式可实时调整(CSS 层叠加预览)
|
||||
* - 调整标题后点击"重新预览"可刷新服务器渲染结果
|
||||
* - 素材/配音/BGM 变更会自动重新渲染
|
||||
* 前端实时预览架构:
|
||||
* - 右侧面板使用 FrontendPreviewPlayer 实时播放素材片段
|
||||
* - 标题样式可实时调整,CSS 层即时叠加预览
|
||||
* - 点"确认生成"时触发一次服务器渲染
|
||||
*/
|
||||
import React from "react"
|
||||
import { PlayCircleOutlined, ReloadOutlined } from "@ant-design/icons"
|
||||
import { Button } from "antd"
|
||||
import { PlayCircleOutlined } from "@ant-design/icons"
|
||||
import { POSITION_OPTIONS, FONT_OPTIONS } from "../constants"
|
||||
import type { TitleSettings } from "../types"
|
||||
import type { ServerPreviewStatus } from "../hooks/useServerPreview"
|
||||
import TitleStylePanel from "./title/TitleStylePanel"
|
||||
|
||||
interface Step5GeneratePreviewProps {
|
||||
@@ -27,10 +24,6 @@ interface Step5GeneratePreviewProps {
|
||||
onApplyPreset: (presetKey: string) => void
|
||||
activePreset: string | null
|
||||
titlePresets: { key: string; label: string; previewStyle: React.CSSProperties }[]
|
||||
/** 服务器预览状态 */
|
||||
previewStatus: ServerPreviewStatus
|
||||
/** 重新预览回调 */
|
||||
onRetryPreview: () => void
|
||||
}
|
||||
|
||||
const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
|
||||
@@ -45,13 +38,7 @@ const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
|
||||
onApplyPreset,
|
||||
activePreset,
|
||||
titlePresets,
|
||||
previewStatus,
|
||||
onRetryPreview,
|
||||
}) => {
|
||||
const isLoading = previewStatus === "loading"
|
||||
const isStale = previewStatus === "stale"
|
||||
const isFailed = previewStatus === "failed"
|
||||
|
||||
return (
|
||||
<div className="xx-form-section">
|
||||
<h3>🎬 预览设置</h3>
|
||||
@@ -70,71 +57,10 @@ const Step5GeneratePreview: React.FC<Step5GeneratePreviewProps> = ({
|
||||
>
|
||||
<PlayCircleOutlined style={{ fontSize: 18, color: "#3b82f6" }} />
|
||||
<span style={{ fontSize: 13, color: "var(--text-secondary, #666)" }}>
|
||||
右侧为服务器渲染的真实预览视频,最终成片与预览完全一致
|
||||
右侧为实时预览,选完素材即可播放。确认生成后服务器渲染最终视频
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 配置变更提示条 */}
|
||||
{isStale && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: 8,
|
||||
padding: "10px 16px",
|
||||
background: "rgba(250, 173, 20, 0.1)",
|
||||
borderRadius: 8,
|
||||
marginBottom: 16,
|
||||
border: "1px solid rgba(250, 173, 20, 0.2)",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 13, color: "#d48806" }}>
|
||||
标题已修改,点击重新预览刷新服务器渲染
|
||||
</span>
|
||||
<Button size="small" type="primary" icon={<ReloadOutlined />} onClick={onRetryPreview}>
|
||||
重新预览
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isFailed && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: 8,
|
||||
padding: "10px 16px",
|
||||
background: "rgba(255, 77, 79, 0.1)",
|
||||
borderRadius: 8,
|
||||
marginBottom: 16,
|
||||
border: "1px solid rgba(255, 77, 79, 0.2)",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 13, color: "#ff4d4f" }}>预览渲染失败</span>
|
||||
<Button size="small" danger icon={<ReloadOutlined />} onClick={onRetryPreview}>
|
||||
重新预览
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<div
|
||||
style={{
|
||||
padding: "10px 16px",
|
||||
background: "rgba(82, 196, 26, 0.08)",
|
||||
borderRadius: 8,
|
||||
marginBottom: 16,
|
||||
border: "1px solid rgba(82, 196, 26, 0.15)",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 13, color: "#389e0d" }}>
|
||||
⏳ 正在服务器渲染预览视频,请稍候...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<TitleStylePanel
|
||||
settings={titleSettings}
|
||||
onUpdatePosition={onUpdatePosition}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useState, useCallback } from "react"
|
||||
import { message } from "antd"
|
||||
import type { GeneratedVideo } from "@/api/template-editor"
|
||||
import { createGenerationTask } from "@/api/tasks/tasks"
|
||||
import { confirmGeneration } from "@/api/generation/confirm"
|
||||
import type { UseGenerateVideoProps } from "./generate-video/types"
|
||||
import { getGenerationPhase } from "./generate-video/phase"
|
||||
import { useGenerationPolling } from "./generate-video/useGenerationPolling"
|
||||
@@ -61,7 +60,7 @@ export function useGenerateVideo(props: UseGenerateVideoProps) {
|
||||
clearTimer()
|
||||
|
||||
try {
|
||||
// 解析分辨率(共享工具函数,与预览 API 一致)
|
||||
// 解析分辨率(共享工具函数)
|
||||
const { width: outputWidth, height: outputHeight } = calculateResolution(
|
||||
props.videoRatio || "9:16",
|
||||
)
|
||||
@@ -72,78 +71,45 @@ export function useGenerateVideo(props: UseGenerateVideoProps) {
|
||||
// 封面 URL:优先 AI 生成缩略图,兜底用户上传
|
||||
const coverUrl = props.coverSettings?.thumbnail_url || props.coverSettings?.upload_url || ""
|
||||
|
||||
// ── Step7 确认生成:优先复用预览产物(秒出),fallback 到全量渲染 ──
|
||||
let taskId: string | undefined
|
||||
|
||||
// 主路径:如果有预览任务 ID 且只生成 1 个视频,调用 confirmGeneration 复用预览产物
|
||||
// generateCount > 1 时需要走 createGenerationTask 支持批量生成
|
||||
const isSingleGenerate = !props.generateCount || props.generateCount === 1
|
||||
if (props.previewTaskId && isSingleGenerate) {
|
||||
try {
|
||||
console.log(
|
||||
"[handleGenerate] 尝试 confirmGeneration 复用预览产物, previewTaskId:",
|
||||
props.previewTaskId,
|
||||
)
|
||||
const confirmResp = await confirmGeneration(props.previewTaskId, {
|
||||
output_width: outputWidth,
|
||||
output_height: outputHeight,
|
||||
cover_url: coverUrl,
|
||||
custom_title: props.titleSettings?.title || "",
|
||||
})
|
||||
taskId = confirmResp.items?.[0]?.id
|
||||
if (taskId) {
|
||||
console.log("[handleGenerate] confirmGeneration 成功, taskId:", taskId)
|
||||
}
|
||||
} catch (confirmErr) {
|
||||
console.warn(
|
||||
"[handleGenerate] confirmGeneration 失败, fallback 到 createGenerationTask:",
|
||||
confirmErr,
|
||||
)
|
||||
// 继续走 fallback 路径
|
||||
}
|
||||
}
|
||||
|
||||
// 解析配音参数:voiceMode=clone 时用 selectedClonedVoice,否则用 selectedVoice(配音素材库 asset ID)
|
||||
// 解析配音参数:voiceMode=clone 时用 selectedClonedVoice,否则用 selectedVoice
|
||||
const voiceLibraryId =
|
||||
props.voiceMode === "clone" ? props.selectedClonedVoice || "" : props.selectedVoice || ""
|
||||
|
||||
// Fallback 路径:没有预览任务或 confirmGeneration 失败,创建新的生成任务
|
||||
if (!taskId) {
|
||||
const taskResp = await createGenerationTask({
|
||||
template_id: selectedTemplate,
|
||||
asset_ids: assetIds,
|
||||
output_width: outputWidth,
|
||||
output_height: outputHeight,
|
||||
cover_url: coverUrl,
|
||||
custom_title: props.titleSettings?.title || "",
|
||||
duration: props.duration || undefined,
|
||||
video_ratio: props.videoRatio,
|
||||
// 配音:优先用 voice_library_id(配音素材库 asset),兜底 voice_ids
|
||||
...(voiceLibraryId ? { voice_library_id: voiceLibraryId } : {}),
|
||||
...(props.selectedVoice && !voiceLibraryId ? { voice_ids: [props.selectedVoice] } : {}),
|
||||
// BGM 配置:受 bgm 开关控制,enabled=false 时也显式传覆盖模板 BGM
|
||||
bgm_config: {
|
||||
enabled: props.bgm !== false,
|
||||
...(props.bgmConfig?.music_id ? { preset_id: props.bgmConfig.music_id } : {}),
|
||||
},
|
||||
...(props.sourceEditPlanId ? { source_edit_plan_id: props.sourceEditPlanId } : {}),
|
||||
...(props.titleSettings?.title
|
||||
? {
|
||||
title_config: {
|
||||
text: props.titleSettings.title,
|
||||
font: props.titleSettings.font,
|
||||
font_size: props.titleSettings.size,
|
||||
font_color: props.titleSettings.color,
|
||||
position: props.titleSettings.position,
|
||||
bold: props.titleSettings.bold,
|
||||
stroke: props.titleSettings.stroke,
|
||||
shadow: props.titleSettings.shadow,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
taskId = taskResp.items?.[0]?.id
|
||||
}
|
||||
// 创建生成任务(服务器渲染)
|
||||
const taskResp = await createGenerationTask({
|
||||
template_id: selectedTemplate,
|
||||
asset_ids: assetIds,
|
||||
output_width: outputWidth,
|
||||
output_height: outputHeight,
|
||||
cover_url: coverUrl,
|
||||
custom_title: props.titleSettings?.title || "",
|
||||
duration: props.duration || undefined,
|
||||
video_ratio: props.videoRatio,
|
||||
// 配音:优先用 voice_library_id(配音素材库 asset),兜底 voice_ids
|
||||
...(voiceLibraryId ? { voice_library_id: voiceLibraryId } : {}),
|
||||
...(props.selectedVoice && !voiceLibraryId ? { voice_ids: [props.selectedVoice] } : {}),
|
||||
// BGM 配置:受 bgm 开关控制,enabled=false 时也显式传覆盖模板 BGM
|
||||
bgm_config: {
|
||||
enabled: props.bgm !== false,
|
||||
...(props.bgmConfig?.music_id ? { preset_id: props.bgmConfig.music_id } : {}),
|
||||
},
|
||||
...(props.sourceEditPlanId ? { source_edit_plan_id: props.sourceEditPlanId } : {}),
|
||||
...(props.titleSettings?.title
|
||||
? {
|
||||
title_config: {
|
||||
text: props.titleSettings.title,
|
||||
font: props.titleSettings.font,
|
||||
font_size: props.titleSettings.size,
|
||||
font_color: props.titleSettings.color,
|
||||
position: props.titleSettings.position,
|
||||
bold: props.titleSettings.bold,
|
||||
stroke: props.titleSettings.stroke,
|
||||
shadow: props.titleSettings.shadow,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
const taskId = taskResp.items?.[0]?.id
|
||||
|
||||
if (!taskId) {
|
||||
throw new Error("创建任务成功但未返回任务 ID,请稍后在任务列表查看")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 管理步骤切换与各步骤的前置校验
|
||||
* 步骤顺序:模板(1) → 素材(2) → 配音(3) → 标题(4) → 预览(5) → 封面(6) → 确认(7)
|
||||
*
|
||||
* 服务器预览架构:Step5 需要服务器渲染预览完成才能前进
|
||||
* 前端实时预览架构:Step5 无需等待服务器渲染
|
||||
*/
|
||||
import { message } from "antd"
|
||||
import type { TitleSettings } from "../types"
|
||||
@@ -16,8 +16,6 @@ export interface UseStepNavigationOptions {
|
||||
selectedMaterials: string[]
|
||||
smartSelectedIds: string[]
|
||||
titleSettings: TitleSettings
|
||||
/** 服务器预览是否已完成(ready 状态) */
|
||||
previewReady: boolean
|
||||
}
|
||||
|
||||
export interface UseStepNavigationReturn {
|
||||
@@ -34,7 +32,6 @@ export const useStepNavigation = (options: UseStepNavigationOptions): UseStepNav
|
||||
selectedMaterials,
|
||||
smartSelectedIds,
|
||||
titleSettings,
|
||||
previewReady,
|
||||
} = options
|
||||
|
||||
const goNext = () => {
|
||||
@@ -54,10 +51,6 @@ export const useStepNavigation = (options: UseStepNavigationOptions): UseStepNav
|
||||
message.warning("请选择或输入标题")
|
||||
return
|
||||
}
|
||||
if (currentStep === 5 && !previewReady) {
|
||||
message.warning("请等待预览视频渲染完成后再继续")
|
||||
return
|
||||
}
|
||||
if (currentStep < 7) {
|
||||
setCurrentStep((s) => s + 1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user