"use client" import React, { useEffect, useState, useRef, useMemo, useCallback } from "react"; import { Play, ChevronUp, Loader2, Edit3, FileText, Pause } from "lucide-react"; import "./style/work-flow.css"; import LiquidGlass from '@/plugins/liquid-glass'; import { Skeleton } from "@/components/ui/skeleton"; import { AISuggestionBar } from "@/components/ai-suggestion-bar"; import { motion, AnimatePresence } from "framer-motion"; import { debounce } from "lodash"; import { GlassIconButton } from "@/components/ui/glass-icon-button"; import { EditModal } from "@/components/ui/edit-modal"; const MOCK_SKETCH_URLS = [ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg', 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg', 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg', 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg', ]; const MOCK_SKETCH_SCRIPT = [ 'script-123', 'script-123', 'script-123', 'script-123', ]; const MOCK_VIDEO_URLS = [ 'https://cdn.qikongjian.com/videos/1750385931_99a8fb42-af89-4ae9-841a-a49869f026bd_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750389908_37d4fffa-8516-43a3-a423-fc0274f40e8a_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750384661_d8e30b79-828e-48cd-9025-ab62a996717c_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750320040_4b47996e-7c70-490e-8433-80c7df990fdd_text_to_video_0.mp4', ]; const MOCK_SKETCH_COUNT = 8; export default function WorkFlow() { const [taskObject, setTaskObject] = useState(null); const [projectObject, setProjectObject] = useState(null); const [taskSketch, setTaskSketch] = useState([]); const [sketchCount, setSketchCount] = useState(0); const containerRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const [isAIBarVisible, setIsAIBarVisible] = useState(true); const [currentStep, setCurrentStep] = useState('0'); const [currentSketchIndex, setCurrentSketchIndex] = useState(0); const [isGeneratingSketch, setIsGeneratingSketch] = useState(false); const thumbnailsRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [startX, setStartX] = useState(0); const [scrollLeft, setScrollLeft] = useState(0); const [showControls, setShowControls] = useState(false); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [taskVideos, setTaskVideos] = useState([]); const [isGeneratingVideo, setIsGeneratingVideo] = useState(false); const mainVideoRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const playTimerRef = useRef(null); const [isVideoPlaying, setIsVideoPlaying] = useState(true); const videoPlayTimerRef = useRef(null); const [currentLoadingText, setCurrentLoadingText] = useState('加载中...'); // 模拟 AI 建议 const mockSuggestions = [ "优化场景转场效果", "调整画面构图", "改进角色动作设计", "增加环境氛围", "调整镜头语言" ]; useEffect(() => { const taskId = localStorage.getItem("taskId") || "taskId-123"; getTaskDetail(taskId).then(async (data) => { setTaskObject(data); setIsLoading(false); setCurrentStep('1'); // 只在任务详情加载完成后获取分镜草图 await getTaskSketch(taskId); await new Promise(resolve => setTimeout(resolve, 2000)); // 首先修改 taskObject 下的 taskStatus 为 '2' setTaskObject((prev: any) => ({ ...prev, taskStatus: '2' })); setCurrentStep('2'); // 获取分镜草图后,开始绘制角色 await getTaskRole(taskId); await new Promise(resolve => setTimeout(resolve, 2000)); // 首先修改 taskObject 下的 taskStatus 为 '3' setTaskObject((prev: any) => ({ ...prev, taskStatus: '3' })); setCurrentStep('3'); // 获取绘制角色后,开始获取分镜视频 await getTaskVideo(taskId); }); }, []); // 监听当前选中索引变化,自动滚动到对应位置 useEffect(() => { if (thumbnailsRef.current && taskSketch.length > 0) { const container = thumbnailsRef.current; const thumbnailWidth = container.offsetWidth / 4; // 每个缩略图宽度(包含间距) const scrollPosition = currentSketchIndex * thumbnailWidth; container.scrollTo({ left: scrollPosition, behavior: 'smooth' }); } }, [currentSketchIndex, taskSketch.length]); // 处理鼠标/触摸拖动事件 const handleMouseDown = (e: React.MouseEvent) => { setIsDragging(true); setStartX(e.pageX - thumbnailsRef.current!.offsetLeft); setScrollLeft(thumbnailsRef.current!.scrollLeft); }; const handleMouseMove = (e: React.MouseEvent) => { if (!isDragging) return; e.preventDefault(); const x = e.pageX - thumbnailsRef.current!.offsetLeft; const walk = (x - startX) * 2; thumbnailsRef.current!.scrollLeft = scrollLeft - walk; }; const handleMouseUp = (e: React.MouseEvent) => { setIsDragging(false); if (!isDragging) return; const container = thumbnailsRef.current!; const thumbnailWidth = container.offsetWidth / 4; const currentScroll = container.scrollLeft; const nearestIndex = Math.round(currentScroll / thumbnailWidth); // 只有在拖动距离较小时才触发选中 const x = e.pageX - container.offsetLeft; const walk = Math.abs(x - startX); if (walk < 10) { return; // 如果拖动距离太小,保持原有的点击选中逻辑 } setCurrentSketchIndex(Math.min(Math.max(0, nearestIndex), taskSketch.length - 1)); }; // 模拟接口请求 获取任务详情 const getTaskDetail = async (taskId: string) => { // const response = await fetch(`/api/task/${taskId}`); // const data = await response.json(); // mock data const data = { projectId: 'projectId-123', projectName: "Project 1", taskId: taskId, taskName: "Task 1", taskDescription: "Task 1 Description", taskStatus: "1", // '1' 绘制分镜、'2' 绘制角色、'3' 生成分镜视频、'4' 视频后期制作、'5' 最终成品 taskProgress: 0, mode: 'auto', // 托管模式、人工干预模式 resolution: '1080p', // 1080p、2160p taskCreatedAt: new Date().toISOString(), taskUpdatedAt: new Date().toISOString(), }; return data; } // 模拟接口请求 每次获取一个分镜草图 轮询获取 const getTaskSketch = async (taskId: string) => { // 避免重复调用 if (isGeneratingSketch || taskSketch.length > 0) return; setIsGeneratingSketch(true); setTaskSketch([]); // 模拟分批获取分镜草图 for (let i = 0; i < MOCK_SKETCH_COUNT; i++) { await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟2秒延迟 const newSketch = { id: `sketch-${i}`, url: MOCK_SKETCH_URLS[i % MOCK_SKETCH_URLS.length], script: MOCK_SKETCH_SCRIPT[i % MOCK_SKETCH_SCRIPT.length], status: 'done' }; setTaskSketch(prev => { // 避免重复添加相同id的sketch if (prev.find(sketch => sketch.id === newSketch.id)) { return prev; } return [...prev, newSketch]; }); setCurrentSketchIndex(i); setSketchCount(i + 1); } setIsGeneratingSketch(false); } // 模拟接口请求 每次获取一个角色 轮询获取 const getTaskRole = async (taskId: string) => { await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟2秒延迟 } // 模拟接口请求 每次获取一个分镜视频 轮询获取 const getTaskVideo = async (taskId: string) => { setIsGeneratingVideo(true); setTaskVideos([]); // 模拟分批获取分镜视频 for (let i = 0; i < MOCK_SKETCH_COUNT; i++) { await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟2秒延迟 const newVideo = { id: `video-${i}`, url: MOCK_VIDEO_URLS[i % MOCK_VIDEO_URLS.length], script: MOCK_SKETCH_SCRIPT[i % MOCK_SKETCH_SCRIPT.length], status: 'done' }; setTaskVideos(prev => { // 避免重复添加相同id的video if (prev.find(video => video.id === newVideo.id)) { return prev; } return [...prev, newVideo]; }); setCurrentSketchIndex(i); } setIsGeneratingVideo(false); }; const handleSuggestionClick = (suggestion: string) => { console.log('Selected suggestion:', suggestion); }; const handleSubmit = (text: string) => { console.log('Submitted text:', text); }; // 缓存渲染的缩略图列表 const renderedSketches = useMemo(() => taskSketch.map((sketch, index) => ( !isDragging && setCurrentSketchIndex(index)} initial={false} animate={{ scale: currentSketchIndex === index ? 1.05 : 1, rotateY: currentSketchIndex === index ? 5 : 0, rotateX: currentSketchIndex === index ? -5 : 0, translateZ: currentSketchIndex === index ? '20px' : '0px', transition: { type: "spring", stiffness: 300, damping: 20 } }} style={{ transformStyle: 'preserve-3d', perspective: '1000px' }} >
场景 {index + 1}
)), [taskSketch, currentSketchIndex, isDragging] ); // 缓存渲染的视频缩略图列表 const renderedVideos = useMemo(() => taskVideos.map((video, index) => ( !isDragging && setCurrentSketchIndex(index)} initial={false} animate={{ scale: currentSketchIndex === index ? 1.05 : 1, rotateY: currentSketchIndex === index ? 5 : 0, rotateX: currentSketchIndex === index ? -5 : 0, translateZ: currentSketchIndex === index ? '20px' : '0px', transition: { type: "spring", stiffness: 300, damping: 20 } }} style={{ transformStyle: 'preserve-3d', perspective: '1000px' }} > )), [taskVideos, currentSketchIndex, isDragging, taskSketch] ); // 处理播放/暂停 const togglePlay = useCallback(() => { setIsPlaying(prev => !prev); }, []); // 自动播放逻辑 useEffect(() => { if (isPlaying && taskSketch.length > 0) { playTimerRef.current = setInterval(() => { setCurrentSketchIndex(prev => { const nextIndex = (prev + 1) % taskSketch.length; return nextIndex; }); }, 2000); // 每2秒切换一次 } else if (playTimerRef.current) { clearInterval(playTimerRef.current); } return () => { if (playTimerRef.current) { clearInterval(playTimerRef.current); } }; }, [isPlaying, taskSketch.length]); // 当切换到视频模式时,停止播放 useEffect(() => { if (currentStep === '3') { setIsPlaying(false); } }, [currentStep]); // 处理视频播放/暂停 const toggleVideoPlay = useCallback(() => { setIsVideoPlaying(prev => !prev); }, []); // 视频自动播放逻辑 useEffect(() => { if (isVideoPlaying && taskVideos.length > 0) { // 确保当前视频开始播放 if (mainVideoRef.current) { mainVideoRef.current.play(); } } else { // 暂停当前视频 if (mainVideoRef.current) { mainVideoRef.current.pause(); } // 清除定时器 if (videoPlayTimerRef.current) { clearInterval(videoPlayTimerRef.current); } } return () => { if (videoPlayTimerRef.current) { clearInterval(videoPlayTimerRef.current); } }; }, [isVideoPlaying, taskVideos.length]); // 当切换视频时重置视频播放 useEffect(() => { if (mainVideoRef.current) { mainVideoRef.current.currentTime = 0; if (isVideoPlaying) { mainVideoRef.current.play(); } } }, [currentSketchIndex, isVideoPlaying]); // 当切换到分镜草图模式时,停止视频播放 useEffect(() => { if (currentStep !== '3') { setIsVideoPlaying(false); } }, [currentStep]); // 更新加载文字 useEffect(() => { if (isLoading) { setCurrentLoadingText('正在加载任务信息...'); return; } if (currentStep === '1') { if (isGeneratingSketch) { setCurrentLoadingText(`正在生成分镜草图 ${sketchCount + 1}/${MOCK_SKETCH_COUNT}...`); } else { setCurrentLoadingText('分镜草图生成完成'); } } else if (currentStep === '2') { setCurrentLoadingText('正在绘制角色...'); } else if (currentStep === '3') { if (isGeneratingVideo) { setCurrentLoadingText(`正在生成分镜视频 ${taskVideos.length + 1}/${taskSketch.length}...`); } else { setCurrentLoadingText('分镜视频生成完成'); } } }, [isLoading, currentStep, isGeneratingSketch, sketchCount, isGeneratingVideo, taskVideos.length, taskSketch.length]); const renderSketchContent = () => { if (!taskObject) { return (
加载中...
); } if (currentStep === '3') { return (
setShowControls(true)} onMouseLeave={() => setShowControls(false)} > {taskVideos[currentSketchIndex] ? ( { if (isVideoPlaying) { // 当前视频播放完成后,自动切换到下一个 setCurrentSketchIndex(prev => (prev + 1) % taskVideos.length); } }} /> {/* 播放进度指示器 */} {isVideoPlaying && ( )} ) : (
正在生成分镜视频 {taskVideos.length + 1}/{taskSketch.length}
)} {/* 操作按钮组 */} {showControls && ( <> {/* 顶部按钮组 */} setIsEditModalOpen(true)} /> {/* console.log('显示脚本')} /> */} )} {/* 底部播放按钮 */}
); } return (
setShowControls(true)} onMouseLeave={() => setShowControls(false)} > {taskSketch[currentSketchIndex] ? ( ) : (
正在生成分镜草图 {sketchCount + 1}/{MOCK_SKETCH_COUNT}
)} {/* 操作按钮组 */} {showControls && ( <> {/* 顶部按钮组 */} setIsEditModalOpen(true)} /> {/* console.log('显示脚本')} /> */} )} {/* 底部播放按钮 */} {/* 播放进度指示器 */} {isPlaying && ( )}
); }; return (
{isLoading ? ( <> ) : ( <>
{taskObject?.projectName}:{taskObject?.taskName}
{/* 实时反馈当前 currentLoadingText */} {currentLoadingText} )}
{isLoading ? ( ) : ( <>
{renderSketchContent()}
)}
{isLoading ? ( <> ) : (
setIsDragging(false)} > {currentStep === '3' ? ( <> {renderedVideos} {isGeneratingVideo && taskVideos.length < taskSketch.length && (
场景 {taskVideos.length + 1}
)} ) : ( <> {renderedSketches} {isGeneratingSketch && sketchCount < MOCK_SKETCH_COUNT && (
场景 {sketchCount + 1}
)} )}
)}
{/* AI 建议栏 */} setIsEditModalOpen(false)} taskStatus={taskObject?.taskStatus || '1'} taskSketch={taskSketch} currentSketchIndex={currentSketchIndex} onSketchSelect={setCurrentSketchIndex} />
) }