添加查看角色下视频状态

This commit is contained in:
北枳 2025-08-18 20:20:31 +08:00
parent 94cd1c92be
commit 3635e67c0a
4 changed files with 41 additions and 60 deletions

View File

@ -987,7 +987,7 @@ export const batchUpdateVideoSegments = async (request: {
/** 新的视频地址列表 */ /** 新的视频地址列表 */
video_urls: string[]; video_urls: string[];
/** 新的状态 0:视频加载中 1:任务已完成 2:任务失败 */ /** 新的状态 0:视频加载中 1:任务已完成 2:任务失败 */
status: number; status: number | null;
/** 优化后的描述文本 */ /** 优化后的描述文本 */
optimized_description?: string; optimized_description?: string;
/** 关键词列表 */ /** 关键词列表 */
@ -1033,7 +1033,8 @@ export const getCharacterShots = async (request: {
video_id: string; video_id: string;
video_status: number|null; video_status: number|null;
}[]; }[];
/** 视频状态 */
video_status: number|null;
}>; }>;
/** 总数量 */ /** 总数量 */
total_count: number; total_count: number;

View File

@ -98,7 +98,7 @@ export const useRoleShotServiceHook = (projectId: string,selectRole?:RoleEntity,
name: `视频片段_${scene.video_id}`, name: `视频片段_${scene.video_id}`,
sketchUrl: "", sketchUrl: "",
videoUrl: scene.video_urls,// 保持为string[]类型 videoUrl: scene.video_urls,// 保持为string[]类型
status:scene.video_urls.length>0?1:0, // 默认为已完成状态 status: scene.video_status !== null? scene.video_status : scene.video_urls.length>0?1:0, // 默认为已完成状态
lens: [], lens: [],
selected: false, selected: false,
applied: true // 由于是通过角色查询到的,所以都是已应用的 applied: true // 由于是通过角色查询到的,所以都是已应用的

View File

@ -88,7 +88,7 @@ export interface VideoSegmentEntity {
video_status: number | null; video_status: number | null;
}[]; }[];
/**视频片段状态 0:视频加载中 1:任务已完成 2:任务失败 */ /**视频片段状态 0:视频加载中 1:任务已完成 2:任务失败 */
status: number; status: number|null;
/**镜头项 */ /**镜头项 */
lens: LensType[]; lens: LensType[];
} }

View File

@ -1,6 +1,6 @@
import React, { useState, useRef } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Check, X, CircleAlert, ArrowLeft, ArrowRight } from 'lucide-react'; import { Check, X, CircleAlert, ArrowLeft, ArrowRight, Loader2 } from 'lucide-react';
import { cn } from '@/public/lib/utils'; import { cn } from '@/public/lib/utils';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
@ -30,33 +30,12 @@ export function ReplacePanel({
); );
const [addToLibrary, setAddToLibrary] = useState(false); const [addToLibrary, setAddToLibrary] = useState(false);
const [hoveredVideoId, setHoveredVideoId] = useState<string | null>(null); const [hoveredVideoId, setHoveredVideoId] = useState<string | null>(null);
const [isAtStart, setIsAtStart] = useState(true);
const [isAtEnd, setIsAtEnd] = useState(false);
const videoRefs = useRef<{ [key: string]: HTMLVideoElement }>({}); const videoRefs = useRef<{ [key: string]: HTMLVideoElement }>({});
const shotsRef = useRef<HTMLDivElement>(null); const shotsRef = useRef<HTMLDivElement>(null);
// 检查滚动位置 useEffect(() => {
const checkScrollPosition = () => { console.log('replace-panel-shots', shots);
if (!shotsRef.current) return; }, [shots]);
const { scrollLeft, scrollWidth, clientWidth } = shotsRef.current;
setIsAtStart(scrollLeft <= 0);
setIsAtEnd(Math.ceil(scrollLeft + clientWidth) >= scrollWidth);
};
// 添加滚动事件监听
React.useEffect(() => {
const shotsElement = shotsRef.current;
if (!shotsElement) return;
shotsElement.addEventListener('scroll', checkScrollPosition);
// 初始检查
checkScrollPosition();
return () => {
shotsElement.removeEventListener('scroll', checkScrollPosition);
};
}, []);
const handleShotToggle = (shotId: string) => { const handleShotToggle = (shotId: string) => {
// setSelectedShots(prev => // setSelectedShots(prev =>
@ -66,10 +45,6 @@ export function ReplacePanel({
// ); // );
}; };
const handleSelectAllShots = (checked: boolean) => {
setSelectedShots(checked ? shots.map(shot => shot.id) : []);
};
const handleMouseEnter = (shotId: string) => { const handleMouseEnter = (shotId: string) => {
setHoveredVideoId(shotId); setHoveredVideoId(shotId);
if (videoRefs.current[shotId]) { if (videoRefs.current[shotId]) {
@ -173,29 +148,34 @@ export function ReplacePanel({
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
> >
{shot.videoUrl && shot.videoUrl.length > 0 && ( <>
<video {(shot.status === 0 || !shot.videoUrl.length) && (
ref={el => { <div className="w-full h-full absolute inset-0 bg-black/50 flex items-center justify-center">
if (el) videoRefs.current[shot.id] = el; <div className="text-white text-sm">
}} <Loader2 className="w-4 h-4 animate-spin" />
src={shot.videoUrl[0]} </div>
className="w-full h-full object-cover"
loop
muted
playsInline
/>
)}
{(!shot.videoUrl || shot.videoUrl.length === 0) && (
<>
<img
src={shot.sketchUrl}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black/50 flex items-center justify-center">
<div className="text-white text-sm">Generating...</div>
</div> </div>
</> )}
)} {shot.status === 2 && (
<div className="w-full h-full absolute inset-0 bg-black/50 flex items-center justify-center">
<div className="text-white text-sm">
<CircleAlert className="w-4 h-4" />
</div>
</div>
)}
{shot.status === 1 && shot.videoUrl.length && (
<video
ref={el => {
if (el) videoRefs.current[shot.id] = el;
}}
src={shot.videoUrl[0]}
className="w-full h-full object-cover"
loop
muted
playsInline
/>
)}
</>
</motion.div> </motion.div>
))} ))}
</div> </div>
@ -204,18 +184,18 @@ export function ReplacePanel({
<div <div
className={cn( className={cn(
"absolute top-1/2 -translate-y-1/2 left-0 w-10 h-10 rounded-full bg-black/50 flex items-center justify-center transition-opacity duration-200", "absolute top-1/2 -translate-y-1/2 left-0 w-10 h-10 rounded-full bg-black/50 flex items-center justify-center transition-opacity duration-200",
isAtStart ? "opacity-30 cursor-not-allowed" : "opacity-100 cursor-pointer hover:bg-black/70" "opacity-100 cursor-pointer hover:bg-black/70"
)} )}
onClick={() => !isAtStart && handleLeftArrowClick()} onClick={() => handleLeftArrowClick()}
> >
<ArrowLeft className="w-4 h-4 text-white" /> <ArrowLeft className="w-4 h-4 text-white" />
</div> </div>
<div <div
className={cn( className={cn(
"absolute top-1/2 -translate-y-1/2 right-0 w-10 h-10 rounded-full bg-black/50 flex items-center justify-center transition-opacity duration-200", "absolute top-1/2 -translate-y-1/2 right-0 w-10 h-10 rounded-full bg-black/50 flex items-center justify-center transition-opacity duration-200",
isAtEnd ? "opacity-30 cursor-not-allowed" : "opacity-100 cursor-pointer hover:bg-black/70" "opacity-100 cursor-pointer hover:bg-black/70"
)} )}
onClick={() => !isAtEnd && handleRightArrowClick()} onClick={() => handleRightArrowClick()}
> >
<ArrowRight className="w-4 h-4 text-white" /> <ArrowRight className="w-4 h-4 text-white" />
</div> </div>