角色和场景一起展示

This commit is contained in:
北枳 2025-08-24 12:45:15 +08:00
parent 93a205135c
commit 628ff70a0c
6 changed files with 72 additions and 27 deletions

View File

@ -625,21 +625,25 @@ export interface Role {
name: string; name: string;
url: string; url: string;
status: number; status: number;
type: string;
} }
interface Scene { interface Scene {
url: string; url: string;
script: string; script: string;
status: number; status: number;
type: string;
} }
interface ShotSketch { interface ShotSketch {
url: string; url: string;
script: string; script: string;
status: number; status: number;
type: string;
} }
export interface ShotVideo { export interface ShotVideo {
video_id: string; video_id: string;
urls: string[]; urls: string[];
video_status: number; video_status: number;
type: string;
} }
// 执行loading文字映射 // 执行loading文字映射

View File

@ -153,7 +153,7 @@
.media-Ocdu1O { .media-Ocdu1O {
flex-direction: column; flex-direction: column;
gap: 12px; gap: 8px;
height: 100%; height: 100%;
flex: 1; flex: 1;
display: flex; display: flex;

View File

@ -77,7 +77,7 @@ const WorkFlow = React.memo(function WorkFlow() {
return ( return (
<ErrorBoundary> <ErrorBoundary>
<div className="w-full overflow-hidden h-[calc(100vh-6rem)] absolute top-[4rem] left-0 right-0 px-[1rem]"> <div className="w-full overflow-hidden h-[calc(100vh-5rem)] absolute top-[4rem] left-0 right-0 px-[1rem]">
<div className="w-full h-full"> <div className="w-full h-full">
<div className="splashContainer-otuV_A"> <div className="splashContainer-otuV_A">
<div className="content-vPGYx8"> <div className="content-vPGYx8">
@ -131,7 +131,7 @@ const WorkFlow = React.memo(function WorkFlow() {
) : isLoading ? ( ) : isLoading ? (
<Skeleton className="w-full aspect-video rounded-lg" /> <Skeleton className="w-full aspect-video rounded-lg" />
) : ( ) : (
<div className={`heroVideo-FIzuK1 ${['final_video', 'script'].includes(taskObject.currentStage) ? 'h-[calc(100vh-6rem)] w-[calc((100vh-6rem)/9*16)]' : 'h-[calc(100vh-6rem-200px)] w-[calc((100vh-6rem-200px)/9*16)]'}`} style={{ aspectRatio: "16 / 9" }} key={currentSketchIndex}> <div className={`heroVideo-FIzuK1 ${['final_video', 'script'].includes(taskObject.currentStage) ? 'h-[calc(100vh-6rem)] w-[calc((100vh-6rem)/9*16)]' : 'h-[calc(100vh-6rem-200px)] w-[calc((100vh-6rem-200px)/9*16)]'}`} style={{ aspectRatio: "16 / 9" }} key={taskObject.currentStage+'_'+currentSketchIndex}>
<ErrorBoundary> <ErrorBoundary>
<MediaViewer <MediaViewer
taskObject={taskObject} taskObject={taskObject}
@ -151,7 +151,7 @@ const WorkFlow = React.memo(function WorkFlow() {
)} )}
</div> </div>
{taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && ( {taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && (
<div className="h-[112px] w-[calc((100vh-6rem-200px)/9*16)]"> <div className="h-[123px] w-[calc((100vh-6rem-200px)/9*16)]">
<ThumbnailGrid <ThumbnailGrid
isDisabledFocus={isEditModalOpen || isPauseWorkFlow || isSmartChatBoxOpen} isDisabledFocus={isEditModalOpen || isPauseWorkFlow || isSmartChatBoxOpen}
taskObject={taskObject} taskObject={taskObject}

View File

@ -546,14 +546,29 @@ export const MediaViewer = React.memo(function MediaViewer({
)} )}
{/* 只在生成过程中或没有分镜图片时使用ProgressiveReveal */} {/* 只在生成过程中或没有分镜图片时使用ProgressiveReveal */}
{currentSketch.status === 1 && ( {currentSketch.status === 1 && (
<img <AnimatePresence mode="wait">
key={currentSketchIndex} <motion.img
key={currentSketch.url}
src={currentSketch.url} src={currentSketch.url}
alt={`NG-Sketch ${currentSketchIndex + 1}`}
className="w-full h-full rounded-lg object-cover" className="w-full h-full rounded-lg object-cover"
// 用 circle clip-path 实现“扩散”
initial={{ clipPath: "circle(0% at 50% 50%)" }}
animate={{ clipPath: "circle(150% at 50% 50%)" }}
exit={{ opacity: 0 }}
transition={{ duration: 1, ease: "easeInOut" }}
/> />
</AnimatePresence>
)} )}
<div className="absolute top-0 left-0 right-0 p-2">
{/* 角色类型 */}
{currentSketch.type === 'role' && (
<div className="inline-flex items-center px-2 py-1 rounded-full bg-purple-500/20 backdrop-blur-sm">
<span className="text-xs text-purple-400">Role: {currentSketch.name}</span>
</div>
)}
</div>
{/* 操作按钮组 */} {/* 操作按钮组 */}
<AnimatePresence> <AnimatePresence>
<motion.div <motion.div
@ -636,13 +651,13 @@ export const MediaViewer = React.memo(function MediaViewer({
return renderScriptContent(); return renderScriptContent();
} }
if (taskObject.currentStage === 'scene') { if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') {
return renderSketchContent(taskObject.scenes.data[currentSketchIndex]); return renderSketchContent([...taskObject.roles.data, ...taskObject.scenes.data][currentSketchIndex]);
} }
if (taskObject.currentStage === 'shot_sketch') { if (taskObject.currentStage === 'shot_sketch') {
return renderSketchContent(taskObject.shot_sketch.data[currentSketchIndex]); return renderSketchContent(taskObject.shot_sketch.data[currentSketchIndex]);
} }
return renderSketchContent(taskObject.scenes.data[currentSketchIndex]); return null;
}); });

View File

@ -4,7 +4,7 @@ import React, { useRef, useEffect, useState, useCallback } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal'; import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal';
import { Loader2, X } from 'lucide-react'; import { Loader2, X, SquareUserRound, MapPinHouse, Clapperboard } from 'lucide-react';
import { TaskObject } from '@/api/DTO/movieEdit'; import { TaskObject } from '@/api/DTO/movieEdit';
interface ThumbnailGridProps { interface ThumbnailGridProps {
@ -45,7 +45,7 @@ export function ThumbnailGrid({
if (taskObject.currentStage === 'video') { if (taskObject.currentStage === 'video') {
return taskObject.videos.data; return taskObject.videos.data;
} else if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') { } else if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') {
return taskObject.scenes.data; return [...taskObject.roles.data, ...taskObject.scenes.data];
} else if (taskObject.currentStage === 'shot_sketch') { } else if (taskObject.currentStage === 'shot_sketch') {
return taskObject.shot_sketch.data; return taskObject.shot_sketch.data;
} }
@ -251,13 +251,32 @@ export function ThumbnailGrid({
<img <img
className="w-full h-full object-cover select-none" className="w-full h-full object-cover select-none"
src={sketch.url} src={sketch.url}
alt={`NG ${index + 1}`}
draggable="false" draggable="false"
/> />
</div> </div>
)} )}
<div className="absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black/60 to-transparent"> <div className='absolute top-0 left-0 right-0 p-2'>
<span className="text-xs text-white/90">Scene {index + 1}</span> {/* 角色类型 */}
{sketch.type === 'role' && (
<div className="inline-flex items-center px-2 py-1 rounded-full bg-purple-500/20 backdrop-blur-sm">
<SquareUserRound className="w-3 h-3 text-purple-400 mr-1" />
<span className="text-xs text-purple-400">Role</span>
</div>
)}
{/* 场景类型 */}
{sketch.type === 'scene' && (
<div className="inline-flex items-center px-2 py-1 rounded-full bg-blue-500/20 backdrop-blur-sm">
<MapPinHouse className="w-3 h-3 text-blue-400 mr-1" />
<span className="text-xs text-blue-400">Scene</span>
</div>
)}
{/* 分镜类型 */}
{(!sketch.type || sketch.type === 'shot_sketch') && (
<div className="inline-flex items-center px-2 py-1 rounded-full bg-amber-500/20 backdrop-blur-sm">
<Clapperboard className="w-3 h-3 text-amber-400 mr-1" />
<span className="text-xs text-amber-400">Shot {index + 1}</span>
</div>
)}
</div> </div>
</div> </div>
); );
@ -269,7 +288,7 @@ export function ThumbnailGrid({
<div <div
ref={thumbnailsRef} ref={thumbnailsRef}
tabIndex={0} tabIndex={0}
className="w-full h-full grid grid-flow-col auto-cols-[20%] gap-4 overflow-x-auto hide-scrollbar px-1 py-1 cursor-grab active:cursor-grabbing focus:outline-none select-none" className="w-full h-full grid grid-flow-col auto-cols-[20%] gap-2 overflow-x-auto hide-scrollbar px-1 py-1 cursor-grab active:cursor-grabbing focus:outline-none select-none"
autoFocus autoFocus
onMouseDown={handleMouseDown} onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove} onMouseMove={handleMouseMove}
@ -279,9 +298,8 @@ export function ThumbnailGrid({
onBlur={() => setIsFocused(false)} onBlur={() => setIsFocused(false)}
> >
{taskObject.currentStage === 'video' && renderVideoThumbnails()} {taskObject.currentStage === 'video' && renderVideoThumbnails()}
{taskObject.currentStage === 'scene' && renderSketchThumbnails(taskObject.scenes.data)} {(taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') && renderSketchThumbnails(getCurrentData())}
{taskObject.currentStage === 'shot_sketch' && renderSketchThumbnails(taskObject.shot_sketch.data)} {taskObject.currentStage === 'shot_sketch' && renderSketchThumbnails(taskObject.shot_sketch.data)}
{taskObject.currentStage === 'character' && renderSketchThumbnails(taskObject.scenes.data)}
</div> </div>
); );
} }

View File

@ -215,7 +215,8 @@ export function useWorkflowData() {
characterList.push({ characterList.push({
name: character.character_name, name: character.character_name,
url: character.image_path, url: character.image_path,
status: character.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0) status: character.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0),
type: 'role'
}); });
} }
taskCurrent.roles.data = characterList; taskCurrent.roles.data = characterList;
@ -242,7 +243,8 @@ export function useWorkflowData() {
sketchList.push({ sketchList.push({
url: sketch.image_path, url: sketch.image_path,
script: sketch.sketch_name, script: sketch.sketch_name,
status: sketch.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0) status: sketch.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0),
type: 'scene'
}); });
} }
taskCurrent.scenes.data = sketchList; taskCurrent.scenes.data = sketchList;
@ -269,7 +271,8 @@ export function useWorkflowData() {
sketchList.push({ sketchList.push({
url: sketch.url, url: sketch.url,
script: sketch.description, script: sketch.description,
status: sketch.url ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0) status: sketch.url ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0),
type: 'shot_sketch'
}); });
} }
taskCurrent.shot_sketch.data = sketchList; taskCurrent.shot_sketch.data = sketchList;
@ -302,6 +305,7 @@ export function useWorkflowData() {
urls: video.urls, urls: video.urls,
video_id: video.video_id, video_id: video.video_id,
video_status: video_status, // 0 生成中 1 生成完成 2 生成失败 video_status: video_status, // 0 生成中 1 生成完成 2 生成失败
type: 'video'
}); });
} }
taskCurrent.videos.data = videoList; taskCurrent.videos.data = videoList;
@ -427,7 +431,8 @@ export function useWorkflowData() {
characterList.push({ characterList.push({
name: character.character_name, name: character.character_name,
url: character.image_path, url: character.image_path,
status: character.image_path ? 1 : (data.character.task_status === 'COMPLETED' ? 2 : 0) status: character.image_path ? 1 : (data.character.task_status === 'COMPLETED' ? 2 : 0),
type: 'role'
}); });
} }
taskCurrent.roles.data = characterList; taskCurrent.roles.data = characterList;
@ -446,7 +451,8 @@ export function useWorkflowData() {
sketchList.push({ sketchList.push({
url: sketch.image_path, url: sketch.image_path,
script: sketch.sketch_name, script: sketch.sketch_name,
status: sketch.image_path ? 1 : (data.sketch.task_status === 'COMPLETED' ? 2 : 0) status: sketch.image_path ? 1 : (data.sketch.task_status === 'COMPLETED' ? 2 : 0),
type: 'scene'
}); });
} }
taskCurrent.scenes.data = sketchList; taskCurrent.scenes.data = sketchList;
@ -466,7 +472,8 @@ export function useWorkflowData() {
sketchList.push({ sketchList.push({
url: sketch.url, url: sketch.url,
script: sketch.description, script: sketch.description,
status: sketch.url ? 1 : (data.shot_sketch.task_status === 'COMPLETED' ? 2 : 0) status: sketch.url ? 1 : (data.shot_sketch.task_status === 'COMPLETED' ? 2 : 0),
type: 'shot_sketch'
}); });
} }
taskCurrent.shot_sketch.data = sketchList; taskCurrent.shot_sketch.data = sketchList;
@ -492,6 +499,7 @@ export function useWorkflowData() {
urls: video.urls, urls: video.urls,
video_id: video.video_id, video_id: video.video_id,
video_status: video_status, // 0 生成中 1 生成完成 2 生成失败 video_status: video_status, // 0 生成中 1 生成完成 2 生成失败
type: 'video'
}); });
} }
taskCurrent.videos.data = videoList; taskCurrent.videos.data = videoList;