forked from 77media/video-flow
383 lines
12 KiB
TypeScript
383 lines
12 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
import {
|
|
Image,
|
|
Video,
|
|
CheckCircle,
|
|
Music,
|
|
Loader2,
|
|
User,
|
|
Scissors,
|
|
Tv
|
|
} from 'lucide-react';
|
|
|
|
interface TaskInfoProps {
|
|
isLoading: boolean;
|
|
taskObject: any;
|
|
currentLoadingText: string;
|
|
dataLoadError?: string | null;
|
|
}
|
|
|
|
// 根据加载文本返回对应的图标
|
|
const getStageIcon = (loadingText: string) => {
|
|
const text = loadingText.toLowerCase();
|
|
|
|
if (text.includes('sketch') || text.includes('草图')) {
|
|
return Image;
|
|
} else if (text.includes('video') || text.includes('视频')) {
|
|
return Video;
|
|
} else if (text.includes('character') || text.includes('角色')) {
|
|
return User;
|
|
} else if (text.includes('audio') || text.includes('音频')) {
|
|
return Music;
|
|
} else if (text.includes('post') || text.includes('后期')) {
|
|
return Scissors;
|
|
} else if (text.includes('final') || text.includes('最终')) {
|
|
return Tv;
|
|
} else if (text.includes('complete') || text.includes('完成')) {
|
|
return CheckCircle;
|
|
} else {
|
|
return Loader2;
|
|
}
|
|
};
|
|
|
|
export function TaskInfo({ isLoading, taskObject, currentLoadingText, dataLoadError }: TaskInfoProps) {
|
|
const StageIcon = getStageIcon(currentLoadingText);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<>
|
|
<motion.div
|
|
className="title-JtMejk text-center"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
{taskObject?.title || '正在加载项目信息...'}
|
|
</motion.div>
|
|
|
|
{/* 加载状态显示 */}
|
|
<motion.div
|
|
className="flex items-center gap-2 justify-center mt-2"
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<motion.div
|
|
className="w-1.5 h-1.5 rounded-full bg-blue-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2
|
|
}}
|
|
/>
|
|
|
|
{/* 阶段图标 */}
|
|
<motion.div
|
|
className="flex items-center gap-2"
|
|
key={currentLoadingText}
|
|
initial={{ opacity: 0, x: -10 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: 10 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<motion.div
|
|
className="text-blue-500"
|
|
animate={{
|
|
rotate: [0, 360],
|
|
scale: [1, 1.1, 1]
|
|
}}
|
|
transition={{
|
|
rotate: { duration: 2, repeat: Infinity, ease: "linear" },
|
|
scale: { duration: 1.5, repeat: Infinity, ease: "easeInOut" }
|
|
}}
|
|
>
|
|
<StageIcon className="w-5 h-5" />
|
|
</motion.div>
|
|
|
|
<motion.span
|
|
className="normalS400 subtitle-had8uE text-transparent bg-clip-text bg-gradient-to-r from-blue-600 via-cyan-500 to-purple-600"
|
|
animate={{
|
|
backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"],
|
|
}}
|
|
transition={{
|
|
duration: 3,
|
|
repeat: Infinity,
|
|
ease: "linear"
|
|
}}
|
|
style={{
|
|
backgroundSize: "300% 300%",
|
|
}}
|
|
>
|
|
{currentLoadingText}
|
|
</motion.span>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="w-1.5 h-1.5 rounded-full bg-blue-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2,
|
|
delay: 0.3
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
|
|
{/* 错误提示 */}
|
|
{dataLoadError && (
|
|
<motion.div
|
|
className="mt-3 text-orange-600 text-sm text-center flex items-center justify-center gap-2"
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<span className="w-2 h-2 bg-orange-500 rounded-full animate-pulse"></span>
|
|
{dataLoadError}
|
|
</motion.div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="title-JtMejk">
|
|
{taskObject?.title || '正在加载项目信息...'}
|
|
</div>
|
|
|
|
{currentLoadingText === 'Task completed' ? (
|
|
<motion.div
|
|
className="flex items-center gap-3 justify-center"
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
<motion.div
|
|
className="w-2 h-2 rounded-full bg-emerald-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2
|
|
}}
|
|
/>
|
|
<motion.div
|
|
className="flex items-center gap-2"
|
|
initial="hidden"
|
|
animate="visible"
|
|
variants={{
|
|
hidden: { opacity: 0 },
|
|
visible: { opacity: 1 }
|
|
}}
|
|
>
|
|
<motion.div
|
|
className="text-emerald-500"
|
|
variants={{
|
|
hidden: { opacity: 0, scale: 0.8 },
|
|
visible: { opacity: 1, scale: 1 }
|
|
}}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
<CheckCircle className="w-5 h-5" />
|
|
</motion.div>
|
|
<motion.span
|
|
className="text-emerald-500 font-medium"
|
|
variants={{
|
|
hidden: { opacity: 0, y: 20 },
|
|
visible: { opacity: 1, y: 0 }
|
|
}}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
{currentLoadingText}
|
|
</motion.span>
|
|
</motion.div>
|
|
<motion.div
|
|
className="w-2 h-2 rounded-full bg-emerald-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2,
|
|
delay: 0.3
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
) : (
|
|
<motion.div
|
|
className="flex items-center gap-2 justify-center"
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<motion.div
|
|
className="w-1.5 h-1.5 rounded-full bg-blue-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2
|
|
}}
|
|
/>
|
|
|
|
{/* 阶段图标 */}
|
|
<motion.div
|
|
className="flex items-center gap-2"
|
|
key={currentLoadingText}
|
|
initial={{ opacity: 0, x: -10 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: 10 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
<motion.div
|
|
className="text-blue-500"
|
|
animate={{
|
|
rotate: [0, 360],
|
|
scale: [1, 1.1, 1]
|
|
}}
|
|
transition={{
|
|
scale: { duration: 1.5, repeat: Infinity, ease: "easeInOut" }
|
|
}}
|
|
>
|
|
<StageIcon className="w-5 h-5" />
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="relative"
|
|
initial={{ opacity: 0, x: -10 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
exit={{ opacity: 0, x: 10 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
{/* 背景发光效果 */}
|
|
<motion.div
|
|
className="absolute inset-0 text-transparent bg-clip-text bg-gradient-to-r from-blue-400 via-cyan-400 to-purple-400 blur-sm"
|
|
animate={{
|
|
backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"],
|
|
}}
|
|
transition={{
|
|
duration: 2,
|
|
repeat: Infinity,
|
|
ease: "linear"
|
|
}}
|
|
style={{
|
|
backgroundSize: "200% 200%",
|
|
}}
|
|
>
|
|
<span className="normalS400 subtitle-had8uE">{currentLoadingText}</span>
|
|
</motion.div>
|
|
|
|
{/* 主文字 - 颜色填充动画 */}
|
|
<motion.div
|
|
className="relative z-10"
|
|
animate={{
|
|
scale: [1, 1.02, 1],
|
|
}}
|
|
transition={{
|
|
duration: 1.5,
|
|
repeat: Infinity,
|
|
ease: "easeInOut"
|
|
}}
|
|
>
|
|
<motion.span
|
|
className="normalS400 subtitle-had8uE text-transparent bg-clip-text bg-gradient-to-r from-blue-600 via-cyan-500 to-purple-600"
|
|
animate={{
|
|
backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"],
|
|
}}
|
|
transition={{
|
|
duration: 3,
|
|
repeat: Infinity,
|
|
ease: "linear"
|
|
}}
|
|
style={{
|
|
backgroundSize: "300% 300%",
|
|
}}
|
|
>
|
|
{currentLoadingText}
|
|
</motion.span>
|
|
</motion.div>
|
|
|
|
{/* 动态光点效果 */}
|
|
<motion.div
|
|
className="absolute left-0 top-1/2 transform -translate-y-1/2 w-2 h-2 bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full blur-sm"
|
|
animate={{
|
|
x: [0, 200, 0],
|
|
opacity: [0, 1, 0],
|
|
scale: [0.5, 1, 0.5],
|
|
}}
|
|
transition={{
|
|
duration: 2.5,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
}}
|
|
/>
|
|
|
|
{/* 文字底部装饰线 */}
|
|
<motion.div
|
|
className="absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-blue-500 via-cyan-400 to-purple-500"
|
|
animate={{
|
|
width: ["0%", "100%", "0%"],
|
|
backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"],
|
|
}}
|
|
transition={{
|
|
width: { duration: 2, repeat: Infinity, ease: "easeInOut" },
|
|
backgroundPosition: { duration: 1.5, repeat: Infinity, ease: "linear" }
|
|
}}
|
|
style={{
|
|
backgroundSize: "200% 200%",
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
className="w-1.5 h-1.5 rounded-full bg-blue-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2,
|
|
delay: 0.3
|
|
}}
|
|
/>
|
|
<motion.div
|
|
className="w-1.5 h-1.5 rounded-full bg-blue-500"
|
|
animate={{
|
|
scale: [1, 1.5, 1],
|
|
opacity: [1, 0.5, 1]
|
|
}}
|
|
transition={{
|
|
duration: 1,
|
|
repeat: Infinity,
|
|
repeatDelay: 0.2,
|
|
delay: 0.6
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|