From 7b7a93832d1fd519930cb722d0ea7e802b6ff551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Fri, 5 Sep 2025 01:25:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/QueueBox/QueueNotification2.tsx | 2 +- components/pages/work-flow.tsx | 11 ++- .../pages/work-flow/editing-notification.tsx | 35 ++++++- components/pages/work-flow/media-viewer.tsx | 3 +- .../pages/work-flow/use-workflow-data.tsx | 91 +++++++++++++++++-- 5 files changed, 123 insertions(+), 19 deletions(-) diff --git a/components/QueueBox/QueueNotification2.tsx b/components/QueueBox/QueueNotification2.tsx index e08bcbb..d8a1067 100644 --- a/components/QueueBox/QueueNotification2.tsx +++ b/components/QueueBox/QueueNotification2.tsx @@ -14,7 +14,7 @@ const darkGlassStyle = { const studioContainerStyle = { position: 'relative' as const, width: '100%', - height: '120px', + height: '100px', marginBottom: '16px', background: 'rgba(26, 27, 30, 0.6)', borderRadius: '8px', diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 5d1ee7e..2b843d6 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -56,9 +56,13 @@ const WorkFlow = React.memo(function WorkFlow() { aiEditingButtonRef.current?.handleAIEditing(); editingNotificationKey.current = `editing-${Date.now()}`; showEditingNotification({ + description: 'Performing intelligent editing...', + successDescription: 'Editing successful', + timeoutDescription: 'Editing failed, please try again', + timeout: 5 * 60 * 1000, key: editingNotificationKey.current, onFail: () => { - console.log('编辑失败'); + console.log('Editing failed'); // 清缓存 生成计划 视频重新分析 localStorage.removeItem(`isLoaded_plan_${episodeId}`); // 3秒后关闭通知 @@ -109,7 +113,10 @@ const WorkFlow = React.memo(function WorkFlow() { if (taskObject.final.url && editingNotificationKey.current && isHandleEdit) { // 更新通知状态为完成 showEditingNotification({ - isCompleted: true, + description: 'Performing intelligent editing...', + successDescription: 'Editing successful', + timeoutDescription: 'Editing failed, please try again', + timeout: 5 * 60 * 1000, key: editingNotificationKey.current, onComplete: () => { console.log('编辑完成'); diff --git a/components/pages/work-flow/editing-notification.tsx b/components/pages/work-flow/editing-notification.tsx index 865e650..2b4e7c2 100644 --- a/components/pages/work-flow/editing-notification.tsx +++ b/components/pages/work-flow/editing-notification.tsx @@ -36,14 +36,22 @@ const descriptionStyle = { interface EditingNotificationProps { /** 编辑是否完成 */ isCompleted?: boolean; - /** 自定义描述 */ + /** 初始描述文案 */ description?: string; + /** 编辑成功时的描述文案 */ + successDescription?: string; + /** 编辑超时失败时的描述文案 */ + timeoutDescription?: string; /** key */ key?: string; /** 完成时的回调 */ onComplete?: () => void; /** 失败时的回调 */ onFail?: () => void; + /** 超时时间(毫秒),默认10分钟 */ + timeout?: number; + /** 是否显示关闭按钮,默认false */ + showCloseIcon?: boolean; } /** @@ -54,9 +62,13 @@ export const showEditingNotification = (props: EditingNotificationProps) => { const { isCompleted = false, description = 'The intelligent editing platform is currently editing the videos.', + successDescription = 'The editing is complete, and the updated video has been displayed on the page.', + timeoutDescription = 'The editing timed out, please try again.', key, onComplete, onFail, + timeout = 8 * 60 * 1000, // 默认8分钟 + showCloseIcon = false, } = props; const NotificationContent = () => { @@ -66,6 +78,19 @@ export const showEditingNotification = (props: EditingNotificationProps) => { const timerRef = useRef(); const startTimeRef = useRef(Date.now()); + // 重置进度条 + const resetProgress = () => { + setProgress(0); + setStatus('active'); + setCurrentDescription(description); + startTimeRef.current = Date.now(); + }; + + // 将重置方法暴露给外部 + if (props.key && typeof window !== 'undefined') { + (window as any)[`resetProgress_${props.key}`] = resetProgress; + } + const scissorsIcon = useMemo(() => ( { useEffect(() => { const updateProgress = () => { const elapsed = Date.now() - startTimeRef.current; - const timeLimit = 10 * 60 * 1000; // 10分钟 + const timeLimit = timeout; // 使用传入的超时时间 if (isCompleted) { // 如果完成了,快速增加到100% @@ -94,7 +119,7 @@ export const showEditingNotification = (props: EditingNotificationProps) => { const next = prev + (100 - prev) / 10; if (next >= 99.9) { setStatus('success'); - setCurrentDescription('The editing is complete, and the updated video has been displayed on the page.'); + setCurrentDescription(successDescription); onComplete?.(); clearInterval(timerRef.current); return 100; @@ -104,7 +129,7 @@ export const showEditingNotification = (props: EditingNotificationProps) => { } else if (elapsed >= timeLimit) { // 超时失败 setStatus('exception'); - setCurrentDescription('The editing timed out, please try again.'); + setCurrentDescription(timeoutDescription); onFail?.(); clearInterval(timerRef.current); return; @@ -166,7 +191,7 @@ export const showEditingNotification = (props: EditingNotificationProps) => { placement: 'topRight', style: darkGlassStyle, className: 'dark-glass-notification', - closeIcon: null + closeIcon: showCloseIcon ? undefined : null }); // 返回key以便外部可以手动关闭通知 diff --git a/components/pages/work-flow/media-viewer.tsx b/components/pages/work-flow/media-viewer.tsx index 4e188d6..d082cc7 100644 --- a/components/pages/work-flow/media-viewer.tsx +++ b/components/pages/work-flow/media-viewer.tsx @@ -582,9 +582,8 @@ export const MediaViewer = React.memo(function MediaViewer({ )} {currentSketch.status === 2 && (
-
+
- Failed
)} diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index 8009a3a..c1fc383 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -2,6 +2,8 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import { useSearchParams } from 'next/navigation'; +import { notification } from 'antd'; +import { showEditingNotification } from '@/components/pages/work-flow/editing-notification'; import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan, getGenerateEditPlan, regenerateVideoNew } from '@/api/video_flow'; import { useScriptService } from "@/app/service/Interaction/ScriptService"; import { useUpdateEffect } from '@/app/hooks/useUpdateEffect'; @@ -13,16 +15,25 @@ interface UseWorkflowDataProps { } export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = {}) { - useEffect(() => { - console.log("init-useWorkflowData"); - return () => console.log("unmount-useWorkflowData"); - }, []); - const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId') || ''; const from = searchParams.get('from') || ''; const token = localStorage.getItem('token') || ''; const useid = JSON.parse(localStorage.getItem("currentUser") || '{}').id || NaN; + const notificationKey = useMemo(() => `video-workflow-${episodeId}`, [episodeId]); + + useEffect(() => { + console.log("init-useWorkflowData"); + return () => { + console.log("unmount-useWorkflowData"); + // 组件卸载时销毁通知 + notification.destroy(notificationKey); + // 清理window上的重置函数 + if (typeof window !== 'undefined') { + delete (window as any)[`resetProgress_${notificationKey}`]; + } + }; + }, [notificationKey]); // 查看缓存中 是否已经 加载过 这个项目的 剪辑计划 let isLoadedRef = useRef(localStorage.getItem(`isLoaded_plan_${episodeId}`)); @@ -61,6 +72,7 @@ export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false); const [canGoToCut, setCanGoToCut] = useState(false); const [isShowError, setIsShowError] = useState(false); + const [isAnalyzing, setIsAnalyzing] = useState(false); const [isGenerateEditPlan, setIsGenerateEditPlan] = useState(false); const [state, setState] = useState({ mode: 'automatic' as 'automatic' | 'manual' | 'auto', @@ -127,6 +139,19 @@ export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = if (isLoadedRef.current) { return; } + // 调用重置方法 + const resetFunc = (window as any)[`resetProgress_${notificationKey}`]; + if (resetFunc) { + resetFunc(); + } + // 更新通知内容 + showEditingNotification({ + key: notificationKey, + description: 'Generating intelligent editing plan...', + successDescription: 'Generating successful', + timeoutDescription: 'Generating failed, please try again', + timeout: 3 * 60 * 1000 + }); // 先停止轮询 await new Promise(resolve => { setNeedStreamData(false); @@ -138,14 +163,38 @@ export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = setIsGenerateEditPlan(true); isLoadedRef.current = 'true'; setNeedStreamData(true); + + // 显示成功通知3秒 + showEditingNotification({ + key: notificationKey, + isCompleted: true, + description: 'Generating intelligent editing plan...', + successDescription: 'Generating successful', + timeout: 3000 + }); + setTimeout(() => { + notification.destroy(notificationKey); + }, 3000); + // 触发回调,通知父组件计划生成完成 onEditPlanGenerated?.(); } catch (error) { console.error('生成剪辑计划失败:', error); setNeedStreamData(true); setIsGenerateEditPlan(false); + + // 显示失败通知3秒 + showEditingNotification({ + key: notificationKey, + description: 'Generating intelligent editing plan...', + timeoutDescription: 'Generating failed, please try again', + timeout: 3000 + }); + setTimeout(() => { + notification.destroy(notificationKey); + }, 3000); } - }, [episodeId, onEditPlanGenerated]); + }, [episodeId, onEditPlanGenerated, notificationKey]); const openEditPlan = useCallback(async () => { window.open(`https://smartcut.movieflow.ai/ai-editor/${episodeId}?token=${token}&user_id=${useid}`, '_target'); @@ -336,11 +385,35 @@ export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = const total_count = taskCurrent.videos.data.length; let analyze_video_completed_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video' && item.task_status !== 'INIT' && item.task_status !== 'RUNNING').length; let analyze_video_total_count = all_task_data.filter((item: any) => item.task_name === 'generate_analyze_video').length; + + // 检查是否需要开始显示视频分析进度 + // 只在第一次检测到视频分析任务时显示通知 + if (analyze_video_total_count > 0 && !isAnalyzing && analyze_video_completed_count !== analyze_video_total_count) { + setIsAnalyzing(true); + // 如果是第一次显示通知,才调用showEditingNotification + const resetFunc = (window as any)[`resetProgress_${notificationKey}`]; + if (!resetFunc) { + showEditingNotification({ + key: notificationKey, + description: 'Preparing intelligent editing plan...', + successDescription: 'Preparing successful', + timeoutDescription: 'Preparing failed, please try again', + timeout: 3 * 60 * 1000 + }); + } + } + if (analyze_video_total_count && analyze_video_completed_count === analyze_video_total_count) { - if(error_totle < total_count * errorConfig) + // 视频分析完成 + if(error_totle < total_count * errorConfig) { setCanGoToCut(true); - else + // 重置进度条,显示生成剪辑计划进度 + setIsAnalyzing(false); + } else { setIsShowError(true); + notification.destroy(notificationKey); + setIsAnalyzing(false); + } } } } @@ -644,7 +717,7 @@ export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = fallbackToStep, originalText: state.originalText, // showGotoCutButton: from && currentLoadingText.includes('Post-production') ? true : false, - showGotoCutButton: canGoToCut && (isGenerateEditPlan || taskObject.currentStage === 'final_video') ? true : false, + showGotoCutButton: (canGoToCut && (isGenerateEditPlan || taskObject.currentStage === 'final_video') || isShowError) ? true : false, generateEditPlan: openEditPlan, handleRetryVideo };