video-flow-b/components/pages/work-flow.tsx
2025-07-29 10:57:25 +08:00

239 lines
8.7 KiB
TypeScript

"use client"
import React, { useRef, useEffect } from "react";
import "./style/work-flow.css";
import { Skeleton } from "@/components/ui/skeleton";
import { AISuggestionBar } from "@/components/ai-suggestion-bar";
import { EditModal } from "@/components/ui/edit-modal";
import { ErrorBoundary } from "@/components/ui/error-boundary";
import { TaskInfo } from "./work-flow/task-info";
import { MediaViewer } from "./work-flow/media-viewer";
import { ThumbnailGrid } from "./work-flow/thumbnail-grid";
import { useWorkflowData } from "./work-flow/use-workflow-data";
import { usePlaybackControls } from "./work-flow/use-playback-controls";
import { AlertCircle, RefreshCw } from "lucide-react";
import { motion } from "framer-motion";
export default function WorkFlow() {
const containerRef = useRef<HTMLDivElement>(null);
const [isEditModalOpen, setIsEditModalOpen] = React.useState(false);
const [activeEditTab, setActiveEditTab] = React.useState('1');
// 使用自定义 hooks 管理状态
const {
taskObject,
taskSketch,
taskScenes,
taskShotSketch,
taskVideos,
sketchCount,
isLoading,
currentStep,
currentSketchIndex,
isGeneratingSketch,
isGeneratingVideo,
currentLoadingText,
totalSketchCount,
roles,
music,
final,
dataLoadError,
setCurrentSketchIndex,
retryLoadData,
} = useWorkflowData();
const {
isPlaying,
isVideoPlaying,
showControls,
setShowControls,
setIsPlaying,
togglePlay,
toggleVideoPlay,
playTimerRef,
} = usePlaybackControls(taskSketch, taskVideos, currentStep);
// 跟踪是否已经自动开始播放过,避免重复触发
const hasAutoStartedRef = useRef(false);
// 跟踪循环播放的起始索引,用于判断是否完成一轮循环
const loopStartIndexRef = useRef<number | null>(null);
// 调试:监控关键状态变化
useEffect(() => {
console.log('工作流状态:', {
currentStep,
isGeneratingSketch,
isGeneratingVideo,
isPlaying,
taskSketchLength: taskSketch.length,
sketchCount,
totalSketchCount
});
}, [isGeneratingSketch, taskSketch.length, sketchCount, totalSketchCount, currentStep, isPlaying]);
// 专门监控isPlaying状态变化
useEffect(() => {
console.log('播放状态变化:', isPlaying ? '开始播放' : '停止播放');
}, [isPlaying]);
// 检查分镜数据
useEffect(() => {
if (taskSketch.length > 0) {
console.log('分镜数据:', `${taskSketch.length}个分镜,当前索引:${currentSketchIndex}`);
}
}, [taskSketch.length, currentSketchIndex]);
// 模拟 AI 建议 英文
const mockSuggestions = [
"Refine scene transitions",
"Adjust scene composition",
"Improve character action design",
"Add environmental atmosphere",
"Adjust lens language"
];
const handleEditModalOpen = (tab: string) => {
setActiveEditTab(tab);
setIsEditModalOpen(true);
};
const handleSuggestionClick = (suggestion: string) => {
console.log('Selected suggestion:', suggestion);
};
const handleSubmit = (text: string) => {
console.log('Submitted text:', text);
};
return (
<ErrorBoundary>
<div className="w-full overflow-hidden h-[calc(100vh-6rem)] absolute top-[4rem] left-0 right-0 px-[1rem]">
<div className="flex h-full flex-col p-6 justify-center items-center pt-0">
<div className="container-H2sRZG">
<div className="splashContainer-otuV_A">
<div className="content-vPGYx8">
<div className="info-UUGkPJ">
<ErrorBoundary>
<TaskInfo
isLoading={isLoading}
taskObject={taskObject}
currentLoadingText={currentLoadingText}
dataLoadError={dataLoadError}
roles={roles}
/>
</ErrorBoundary>
</div>
</div>
<div className="media-Ocdu1O">
<div
className="videoContainer-qteKNi"
style={currentStep !== '6' ? { flex: 3 } : {}}
ref={containerRef}
>
{dataLoadError ? (
<motion.div
className="flex flex-col items-center justify-center w-full aspect-video rounded-lg bg-red-50 border-2 border-red-200"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<motion.div
className="flex items-center gap-3 mb-4"
initial={{ scale: 0.8 }}
animate={{ scale: 1 }}
transition={{ duration: 0.3, delay: 0.2 }}
>
<AlertCircle className="w-8 h-8 text-red-500" />
<h3 className="text-lg font-medium text-red-800"></h3>
</motion.div>
<p className="text-red-600 text-center mb-6 max-w-md px-4">
{dataLoadError}
</p>
<motion.button
className="flex items-center gap-2 px-6 py-3 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"
onClick={() => retryLoadData?.()}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<RefreshCw className="w-4 h-4" />
</motion.button>
</motion.div>
) : isLoading ? (
<Skeleton className="w-full aspect-video rounded-lg" />
) : (
<div className="heroVideo-FIzuK1" style={{ aspectRatio: "16 / 9" }}>
<ErrorBoundary>
<MediaViewer
currentStep={currentStep}
currentSketchIndex={currentSketchIndex}
taskSketch={taskSketch}
taskVideos={taskVideos}
isVideoPlaying={isVideoPlaying}
isPlaying={isPlaying}
showControls={showControls}
isGeneratingSketch={isGeneratingSketch}
isGeneratingVideo={isGeneratingVideo}
onControlsChange={setShowControls}
onEditModalOpen={handleEditModalOpen}
onToggleVideoPlay={toggleVideoPlay}
onTogglePlay={togglePlay}
final={final}
/>
</ErrorBoundary>
</div>
)}
</div>
<div className="imageGrid-ymZV9z hide-scrollbar" style={{ display: currentStep === '6' ? 'none' : 'block' }}>
<ErrorBoundary>
<ThumbnailGrid
isLoading={isLoading}
isPlaying={isPlaying}
currentStep={currentStep}
currentSketchIndex={currentSketchIndex}
taskSketch={taskSketch}
taskVideos={taskVideos}
isGeneratingSketch={isGeneratingSketch}
isGeneratingVideo={isGeneratingVideo}
sketchCount={sketchCount}
totalSketchCount={totalSketchCount}
onSketchSelect={setCurrentSketchIndex}
/>
</ErrorBoundary>
</div>
</div>
</div>
</div>
</div>
{/* AI 建议栏 */}
<ErrorBoundary>
<AISuggestionBar
suggestions={mockSuggestions}
onSuggestionClick={handleSuggestionClick}
onSubmit={handleSubmit}
placeholder="Please input your ideas, or click the predefined tags to receive AI advice..."
/>
</ErrorBoundary>
<ErrorBoundary>
<EditModal
isOpen={isEditModalOpen}
activeEditTab={activeEditTab}
onClose={() => setIsEditModalOpen(false)}
taskStatus={taskObject?.taskStatus || '1'}
taskSketch={taskSketch}
sketchVideo={taskVideos}
taskScenes={taskScenes}
currentSketchIndex={currentSketchIndex}
onSketchSelect={setCurrentSketchIndex}
roles={roles}
music={music}
/>
</ErrorBoundary>
</div>
</ErrorBoundary>
)
}