优化hooks多次初始化ing

This commit is contained in:
北枳 2025-08-16 15:06:54 +08:00
parent fd7aa0a18e
commit adf4579ee1
3 changed files with 69 additions and 93 deletions

View File

@ -58,14 +58,9 @@ const WorkFlow = React.memo(function WorkFlow() {
} = useWorkflowData(); } = useWorkflowData();
const { const {
isPlaying,
isVideoPlaying, isVideoPlaying,
showControls,
setShowControls,
setIsPlaying,
togglePlay, togglePlay,
toggleVideoPlay, toggleVideoPlay,
playTimerRef,
} = usePlaybackControls(taskSketch, taskVideos, currentStep); } = usePlaybackControls(taskSketch, taskVideos, currentStep);
useEffect(() => { useEffect(() => {
@ -162,10 +157,8 @@ const WorkFlow = React.memo(function WorkFlow() {
taskSketch={taskSketch} taskSketch={taskSketch}
taskVideos={taskVideos} taskVideos={taskVideos}
isVideoPlaying={isVideoPlaying} isVideoPlaying={isVideoPlaying}
showControls={showControls}
isGeneratingSketch={isGeneratingSketch} isGeneratingSketch={isGeneratingSketch}
isGeneratingVideo={isGeneratingVideo} isGeneratingVideo={isGeneratingVideo}
onControlsChange={setShowControls}
onEditModalOpen={handleEditModalOpen} onEditModalOpen={handleEditModalOpen}
onToggleVideoPlay={toggleVideoPlay} onToggleVideoPlay={toggleVideoPlay}
onTogglePlay={togglePlay} onTogglePlay={togglePlay}

View File

@ -17,10 +17,8 @@ interface MediaViewerProps {
taskSketch: any[]; taskSketch: any[];
taskVideos: any[]; taskVideos: any[];
isVideoPlaying: boolean; isVideoPlaying: boolean;
showControls: boolean;
isGeneratingSketch: boolean; isGeneratingSketch: boolean;
isGeneratingVideo: boolean; isGeneratingVideo: boolean;
onControlsChange: (show: boolean) => void;
onEditModalOpen: (tab: string) => void; onEditModalOpen: (tab: string) => void;
onToggleVideoPlay: () => void; onToggleVideoPlay: () => void;
onTogglePlay: () => void; onTogglePlay: () => void;
@ -39,10 +37,8 @@ export const MediaViewer = React.memo(function MediaViewer({
taskSketch, taskSketch,
taskVideos, taskVideos,
isVideoPlaying, isVideoPlaying,
showControls,
isGeneratingSketch, isGeneratingSketch,
isGeneratingVideo, isGeneratingVideo,
onControlsChange,
onEditModalOpen, onEditModalOpen,
onToggleVideoPlay, onToggleVideoPlay,
onTogglePlay, onTogglePlay,
@ -270,7 +266,6 @@ export const MediaViewer = React.memo(function MediaViewer({
{/* 静音按钮 */} {/* 静音按钮 */}
<GlassIconButton <GlassIconButton
icon={isMuted ? VolumeX : Volume2} icon={isMuted ? VolumeX : Volume2}
tooltip={isMuted ? "取消静音" : "静音"}
onClick={toggleMute} onClick={toggleMute}
size="sm" size="sm"
/> />
@ -312,10 +307,8 @@ export const MediaViewer = React.memo(function MediaViewer({
<div <div
className="relative w-full h-full rounded-lg overflow-hidden" className="relative w-full h-full rounded-lg overflow-hidden"
key={`render-video-${taskObject.final.note}`} key={`render-video-${taskObject.final.note}`}
onMouseEnter={() => onControlsChange(true)}
onMouseLeave={() => onControlsChange(false)}
> >
<div className="relative w-full h-full"> <div className="relative w-full h-full group">
{/* 背景模糊的视频 */} {/* 背景模糊的视频 */}
<motion.div <motion.div
className="absolute inset-0 overflow-hidden" className="absolute inset-0 overflow-hidden"
@ -347,9 +340,8 @@ export const MediaViewer = React.memo(function MediaViewer({
{/* 操作按钮组 */} {/* 操作按钮组 */}
<AnimatePresence> <AnimatePresence>
{showControls && (
<motion.div <motion.div
className="absolute top-4 right-4 z-10 flex gap-2" className="absolute top-4 right-4 z-10 gap-2 hidden group-hover:flex"
initial={{ opacity: 0, y: -10 }} initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }} exit={{ opacity: 0, y: -10 }}
@ -357,11 +349,9 @@ export const MediaViewer = React.memo(function MediaViewer({
> >
<GlassIconButton <GlassIconButton
icon={Edit3} icon={Edit3}
tooltip="Edit sketch"
onClick={() => handleEditClick('3', 'final')} onClick={() => handleEditClick('3', 'final')}
/> />
</motion.div> </motion.div>
)}
</AnimatePresence> </AnimatePresence>
{/* 视频信息浮层 */} {/* 视频信息浮层 */}
@ -405,7 +395,6 @@ export const MediaViewer = React.memo(function MediaViewer({
> >
<GlassIconButton <GlassIconButton
icon={isFinalVideoPlaying ? Pause : Play} icon={isFinalVideoPlaying ? Pause : Play}
tooltip={isFinalVideoPlaying ? "Pause video" : "Play video"}
onClick={toggleFinalVideoPlay} onClick={toggleFinalVideoPlay}
size="sm" size="sm"
/> />
@ -417,7 +406,6 @@ export const MediaViewer = React.memo(function MediaViewer({
{/* 全屏按钮 */} {/* 全屏按钮 */}
<GlassIconButton <GlassIconButton
icon={isFullscreen ? Minimize : Maximize} icon={isFullscreen ? Minimize : Maximize}
tooltip={isFullscreen ? "退出全屏" : "全屏"}
onClick={toggleFullscreen} onClick={toggleFullscreen}
size="sm" size="sm"
/> />
@ -442,9 +430,7 @@ export const MediaViewer = React.memo(function MediaViewer({
const renderVideoContent = () => { const renderVideoContent = () => {
return ( return (
<div <div
className="relative w-full h-full rounded-lg" className="relative w-full h-full rounded-lg group"
onMouseEnter={() => onControlsChange(true)}
onMouseLeave={() => onControlsChange(false)}
> >
{/* 只在生成过程中或没有视频时使用ProgressiveReveal */} {/* 只在生成过程中或没有视频时使用ProgressiveReveal */}
<div className="relative w-full h-full"> <div className="relative w-full h-full">
@ -500,9 +486,8 @@ export const MediaViewer = React.memo(function MediaViewer({
{/* 操作按钮组 */} {/* 操作按钮组 */}
<AnimatePresence> <AnimatePresence>
{showControls && (
<motion.div <motion.div
className="absolute top-4 right-4 flex gap-2 z-[11]" className="absolute top-4 right-4 gap-2 z-[11] hidden group-hover:flex"
initial={{ opacity: 0, y: -10 }} initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }} exit={{ opacity: 0, y: -10 }}
@ -510,11 +495,9 @@ export const MediaViewer = React.memo(function MediaViewer({
> >
<GlassIconButton <GlassIconButton
icon={Edit3} icon={Edit3}
tooltip="Edit sketch"
onClick={() => handleEditClick('3')} onClick={() => handleEditClick('3')}
/> />
</motion.div> </motion.div>
)}
</AnimatePresence> </AnimatePresence>
{/* 底部控制区域 */} {/* 底部控制区域 */}
@ -535,7 +518,6 @@ export const MediaViewer = React.memo(function MediaViewer({
> >
<GlassIconButton <GlassIconButton
icon={isVideoPlaying ? Pause : Play} icon={isVideoPlaying ? Pause : Play}
tooltip={isVideoPlaying ? "Pause video" : "Play video"}
onClick={onToggleVideoPlay} onClick={onToggleVideoPlay}
size="sm" size="sm"
/> />
@ -557,9 +539,7 @@ export const MediaViewer = React.memo(function MediaViewer({
return ( return (
<div <div
className="relative w-full h-full rounded-lg" className="relative w-full h-full rounded-lg group"
onMouseEnter={() => onControlsChange(true)}
onMouseLeave={() => onControlsChange(false)}
> >
{/* 状态 */} {/* 状态 */}
{currentSketch.status === 0 && ( {currentSketch.status === 0 && (
@ -590,9 +570,8 @@ export const MediaViewer = React.memo(function MediaViewer({
{/* 操作按钮组 */} {/* 操作按钮组 */}
<AnimatePresence> <AnimatePresence>
{showControls && (
<motion.div <motion.div
className="absolute top-4 right-4 flex gap-2" className="absolute top-4 right-4 gap-2 hidden group-hover:flex"
initial={{ opacity: 0, y: -10 }} initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }} exit={{ opacity: 0, y: -10 }}
@ -600,11 +579,9 @@ export const MediaViewer = React.memo(function MediaViewer({
> >
<GlassIconButton <GlassIconButton
icon={Edit3} icon={Edit3}
tooltip="Edit sketch"
onClick={() => handleEditClick('1')} onClick={() => handleEditClick('1')}
/> />
</motion.div> </motion.div>
)}
</AnimatePresence> </AnimatePresence>
{/* 底部播放按钮 */} {/* 底部播放按钮 */}

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan } from '@/api/video_flow'; import { detailScriptEpisodeNew, getScriptTitle, getRunningStreamData, pauseMovieProjectPlan, resumeMovieProjectPlan } from '@/api/video_flow';
import { useAppDispatch, useAppSelector } from '@/lib/store/hooks'; import { useAppDispatch, useAppSelector } from '@/lib/store/hooks';
@ -42,7 +42,7 @@ type ApiStep = keyof typeof STEP_MAP;
export function useWorkflowData() { export function useWorkflowData() {
console.log('98877766777777888888990') console.log('init-useWorkflowData')
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const episodeId = searchParams.get('episodeId') || ''; const episodeId = searchParams.get('episodeId') || '';
@ -78,13 +78,10 @@ export function useWorkflowData() {
// 更新 taskObject 的类型 // 更新 taskObject 的类型
const [taskObject, setTaskObject] = useState<TaskObject>(tempTaskObject.current); const [taskObject, setTaskObject] = useState<TaskObject>(tempTaskObject.current);
const [originalText, setOriginalText] = useState<string>('');
const [scriptData, setScriptData] = useState<any>(null);
const [taskSketch, setTaskSketch] = useState<any[]>([]); const [taskSketch, setTaskSketch] = useState<any[]>([]);
const [taskScenes, setTaskScenes] = useState<any[]>([]); const [taskScenes, setTaskScenes] = useState<any[]>([]);
const [taskShotSketch, setTaskShotSketch] = useState<any[]>([]); const [taskShotSketch, setTaskShotSketch] = useState<any[]>([]);
const [taskVideos, setTaskVideos] = useState<any[]>([]); const [taskVideos, setTaskVideos] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [currentStep, setCurrentStep] = useState('0'); const [currentStep, setCurrentStep] = useState('0');
const [currentSketchIndex, setCurrentSketchIndex] = useState(0); const [currentSketchIndex, setCurrentSketchIndex] = useState(0);
const [isGeneratingSketch, setIsGeneratingSketch] = useState(false); const [isGeneratingSketch, setIsGeneratingSketch] = useState(false);
@ -97,7 +94,11 @@ export function useWorkflowData() {
const [dataLoadError, setDataLoadError] = useState<string | null>(null); const [dataLoadError, setDataLoadError] = useState<string | null>(null);
const [needStreamData, setNeedStreamData] = useState(false); const [needStreamData, setNeedStreamData] = useState(false);
const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false); const [isPauseWorkFlow, setIsPauseWorkFlow] = useState(false);
const [mode, setMode] = useState<'automatic' | 'manual' | 'auto'>('automatic'); const [state, setState] = useState({
mode: 'automatic' as 'automatic' | 'manual' | 'auto',
originalText: '',
isLoading: true
});
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -110,12 +111,8 @@ export function useWorkflowData() {
applyScript applyScript
} = useScriptService(); } = useScriptService();
// 监听剧本加载完毕 // 监听剧本加载完毕
useEffect(() => { const scriptData = useMemo(() => {
if (scriptBlocksMemo.length > 0) { return scriptBlocksMemo.length > 0 ? scriptBlocksMemo : null;
console.log('scriptBlocksMemo 更新:', scriptBlocksMemo);
setScriptData(scriptBlocksMemo);
// setCurrentLoadingText(LOADING_TEXT_MAP.script);
}
}, [scriptBlocksMemo]); }, [scriptBlocksMemo]);
// 监听继续 请求更新数据 // 监听继续 请求更新数据
useUpdateEffect(() => { useUpdateEffect(() => {
@ -418,7 +415,7 @@ export function useWorkflowData() {
} catch (error) { } catch (error) {
console.error('获取数据失败:', error); console.error('获取数据失败:', error);
} }
}, [episodeId, needStreamData, roles.length, taskShotSketch.length]); }, [episodeId, needStreamData]);
// 轮询获取流式数据 // 轮询获取流式数据
useUpdateEffect(() => { useUpdateEffect(() => {
@ -440,12 +437,15 @@ export function useWorkflowData() {
const initializeWorkflow = async () => { const initializeWorkflow = async () => {
if (!episodeId) { if (!episodeId) {
setDataLoadError('缺少必要的参数'); setDataLoadError('缺少必要的参数');
setIsLoading(false);
return; return;
} }
try { try {
setIsLoading(true); setState({
mode: 'automatic' as 'automatic' | 'manual' | 'auto',
originalText: '',
isLoading: true
});
setCurrentLoadingText('loading project info...'); setCurrentLoadingText('loading project info...');
// 获取剧集详情 // 获取剧集详情
@ -455,9 +455,6 @@ export function useWorkflowData() {
} }
const { name, status, data, tags, mode, original_text } = response.data; const { name, status, data, tags, mode, original_text } = response.data;
setMode(mode as 'automatic' | 'manual' | 'auto');
setOriginalText(original_text);
setIsLoading(false);
const { current: taskCurrent } = tempTaskObject; const { current: taskCurrent } = tempTaskObject;
@ -585,15 +582,20 @@ export function useWorkflowData() {
console.log('---look-taskData', taskCurrent); console.log('---look-taskData', taskCurrent);
if (taskCurrent.currentStage === 'script') { if (taskCurrent.currentStage === 'script') {
console.log('开始初始化剧本', originalText,episodeId); console.log('开始初始化剧本', original_text,episodeId);
// TODO 为什么一开始没项目id // TODO 为什么一开始没项目id
originalText && initializeFromProject(episodeId, originalText).then(() => { original_text && initializeFromProject(episodeId, original_text).then(() => {
console.log('应用剧本'); console.log('应用剧本');
// 自动模式下 应用剧本;手动模式 需要点击 下一步 触发 // 自动模式下 应用剧本;手动模式 需要点击 下一步 触发
mode.includes('auto') && applyScript(); mode.includes('auto') && applyScript();
}); });
} }
setState({
mode: mode as 'automatic' | 'manual' | 'auto',
originalText: original_text,
isLoading: false
});
// 设置步骤 // 设置步骤
setTaskObject(prev => { setTaskObject(prev => {
const newState = JSON.parse(JSON.stringify({...prev, ...taskCurrent})); const newState = JSON.parse(JSON.stringify({...prev, ...taskCurrent}));
@ -606,7 +608,11 @@ export function useWorkflowData() {
} catch (error) { } catch (error) {
console.error('初始化失败:', error); console.error('初始化失败:', error);
setDataLoadError('加载失败,请重试'); setDataLoadError('加载失败,请重试');
setIsLoading(false); setState({
mode: 'automatic' as 'automatic' | 'manual' | 'auto',
originalText: '',
isLoading: false
});
} }
}; };
@ -649,7 +655,7 @@ export function useWorkflowData() {
taskShotSketch, taskShotSketch,
taskVideos, taskVideos,
sketchCount, sketchCount,
isLoading, isLoading: state.isLoading,
currentStep, currentStep,
currentSketchIndex, currentSketchIndex,
isGeneratingSketch, isGeneratingSketch,
@ -664,11 +670,11 @@ export function useWorkflowData() {
retryLoadData, retryLoadData,
handleManualPlay, handleManualPlay,
isPauseWorkFlow, isPauseWorkFlow,
mode, mode: state.mode,
setIsPauseWorkFlow, setIsPauseWorkFlow,
setAnyAttribute, setAnyAttribute,
applyScript, applyScript,
fallbackToStep, fallbackToStep,
originalText originalText: state.originalText
}; };
} }