'use client'; import React, { useRef, useEffect, useState, useCallback } from 'react'; import { motion } from 'framer-motion'; import { Skeleton } from '@/components/ui/skeleton'; import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal'; import { Loader2, X, SquareUserRound, MapPinHouse, Clapperboard, Video, RotateCcw } from 'lucide-react'; import { TaskObject } from '@/api/DTO/movieEdit'; interface ThumbnailGridProps { isDisabledFocus: boolean; taskObject: TaskObject; currentSketchIndex: number; onSketchSelect: (index: number) => void; } export function ThumbnailGrid({ isDisabledFocus, taskObject, currentSketchIndex, onSketchSelect }: ThumbnailGridProps) { const thumbnailsRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [startX, setStartX] = useState(0); const [scrollLeft, setScrollLeft] = useState(0); const [isFocused, setIsFocused] = useState(false); // 监听当前选中索引变化,自动滚动到对应位置 useEffect(() => { if (thumbnailsRef.current) { const container = thumbnailsRef.current; const thumbnails = container.children; if (currentSketchIndex >= 0 && currentSketchIndex < thumbnails.length) { const thumbnail = thumbnails[currentSketchIndex] as HTMLElement; const containerLeft = container.getBoundingClientRect().left; const thumbnailLeft = thumbnail.getBoundingClientRect().left; const scrollPosition = container.scrollLeft + (thumbnailLeft - containerLeft); container.scrollTo({ left: scrollPosition, behavior: 'smooth' }); } } }, [currentSketchIndex]); // 获取当前阶段的数据数组 const getCurrentData = useCallback(() => { if (taskObject.currentStage === 'video') { return taskObject.videos.data; } else if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') { // 为 roles 和 scenes 数据添加唯一标识前缀,避免重复 const rolesWithPrefix = taskObject.roles.data.map((role, index) => ({ ...role, uniqueId: `role_${index}` })); const scenesWithPrefix = taskObject.scenes.data.map((scene, index) => ({ ...scene, uniqueId: `scene_${index}` })); return [...rolesWithPrefix, ...scenesWithPrefix]; } return []; }, [taskObject.currentStage, taskObject.videos.data, taskObject.roles.data, taskObject.scenes.data]); // 使用 useRef 存储前一次的数据,避免触发重渲染 const prevDataRef = useRef([]); useEffect(() => { const currentData = getCurrentData(); if (currentData && currentData.length > 0) { const currentDataStr = JSON.stringify(currentData); const prevDataStr = JSON.stringify(prevDataRef.current); // 只有当数据真正发生变化时才进行处理 if (currentDataStr !== prevDataStr) { // 找到最新更新的数据项的索引 const changedIndex = currentData.findIndex((item, index) => { // 检查是否是新增的数据 if (index >= prevDataRef.current.length) return true; // 检查数据是否发生变化(包括状态变化) return JSON.stringify(item) !== JSON.stringify(prevDataRef.current[index]); }); console.log('changedIndex_thumbnail-grid', changedIndex, 'currentData:', currentData, 'prevData:', prevDataRef.current); // 如果找到变化的项,自动选择该项 if (changedIndex !== -1) { onSketchSelect(changedIndex); } // 更新前一次的数据快照 prevDataRef.current = JSON.parse(JSON.stringify(currentData)); } } }, [taskObject, getCurrentData, onSketchSelect]); // 处理键盘左右键事件 const handleKeyDown = useCallback((e: KeyboardEvent) => { const currentData = getCurrentData(); const maxIndex = currentData.length - 1; console.log('handleKeyDown', maxIndex, 'isFocused:', isFocused); if ((e.key === 'ArrowLeft' || e.key === 'ArrowRight') && maxIndex >= 0) { e.preventDefault(); let newIndex = currentSketchIndex; if (e.key === 'ArrowLeft') { // 向左循环 newIndex = currentSketchIndex === 0 ? maxIndex : currentSketchIndex - 1; } else { // 向右循环 newIndex = currentSketchIndex === maxIndex ? 0 : currentSketchIndex + 1; } console.log('切换索引:', currentSketchIndex, '->', newIndex, '最大索引:', maxIndex); onSketchSelect(newIndex); } }, [isFocused, currentSketchIndex, onSketchSelect, getCurrentData]); // 监听键盘事件 useEffect(() => { // 组件挂载时自动聚焦 if (thumbnailsRef.current && !isDisabledFocus) { window.addEventListener('keydown', handleKeyDown); } return () => window.removeEventListener('keydown', handleKeyDown); }, [handleKeyDown, isDisabledFocus]); // 处理鼠标/触摸拖动事件 const handleMouseDown = (e: React.MouseEvent) => { // 阻止默认的拖拽行为 e.preventDefault(); 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; }; // 监听阶段变化 useEffect(() => { console.log('taskObject.currentStage_thumbnail-grid', taskObject.currentStage); }, [taskObject.currentStage]); // 粗剪/精剪最终成片阶段不显示缩略图 if (taskObject.currentStage === 'final_video') { return null; } // 渲染视频阶段的缩略图 const renderVideoThumbnails = () => ( taskObject.videos.data.map((video, index) => { const urls: string = video.urls ? video.urls.join(',') : ''; return (
!isDragging && onSketchSelect(index)} > {/* 视频层 */}
{taskObject.videos.data[index].video_status === 0 && (
)} {taskObject.videos.data[index].video_status === 2 && (
)} {taskObject.videos.data[index].urls ? (
{/*
Scene {index + 1}
*/}
); }) ); // 渲染分镜草图阶段的缩略图 const renderSketchThumbnails = (sketchData: any[]) => ( <> {sketchData.map((sketch, index) => { return (
!isDragging && onSketchSelect(index)} > {/* 状态 */} {sketch.status === 0 && (
)} {sketch.status === 2 && (
)} {/* 只在生成过程中或没有分镜图片时使用ProgressiveReveal */} {(sketch.status === 1) && (
)}
{/* 角色类型 */} {sketch.type === 'role' && (
Role
)} {/* 场景类型 */} {sketch.type === 'scene' && (
Scene
)} {/* 分镜类型 */} {(!sketch.type || sketch.type === 'shot_sketch') && (
Shot {index + 1}
)}
{/*
{sketch.type === 'role' ? 'Role' : (sketch.type === 'scene' ? 'Scene' : 'Shot')} {index + 1}
*/}
); })} ); return (
setIsDragging(false)} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} > {taskObject.currentStage === 'video' && renderVideoThumbnails()} {(taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') && renderSketchThumbnails(getCurrentData())}
); }