video-flow-b/components/pages/work-flow/editing-notification.tsx
2025-09-04 22:20:13 +08:00

173 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { notification, Progress } from 'antd';
import { useEffect, useRef, useState, useMemo } from 'react';
import { Scissors } from 'lucide-react';
import { motion } from 'framer-motion';
// 暗色玻璃风格样式
const darkGlassStyle = {
background: 'rgba(30, 32, 40, 0.95)',
backdropFilter: 'blur(10px)',
WebkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.08)',
borderRadius: '8px',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.4)',
padding: '12px 16px',
};
const messageStyle = {
fontSize: '13px',
fontWeight: 500,
color: '#ffffff',
marginBottom: '6px',
display: 'flex',
alignItems: 'center',
gap: '6px',
};
const descriptionStyle = {
fontSize: '12px',
color: 'rgba(255, 255, 255, 0.65)',
marginBottom: '12px',
lineHeight: '1.5',
};
interface EditingNotificationProps {
/** 编辑是否完成 */
isCompleted?: boolean;
/** 自定义描述 */
description?: string;
/** key */
key?: string;
/** 完成时的回调 */
onComplete?: () => void;
/** 失败时的回调 */
onFail?: () => void;
}
/**
* 显示视频编辑进度通知
* @param props EditingNotificationProps
*/
export const showEditingNotification = (props: EditingNotificationProps) => {
const {
isCompleted = false,
description = 'Your video is being edited by AI...',
key,
onComplete,
onFail,
} = props;
const NotificationContent = () => {
const [progress, setProgress] = useState(0);
const [status, setStatus] = useState<'active' | 'success' | 'exception'>('active');
const [currentDescription, setCurrentDescription] = useState(description);
const timerRef = useRef<NodeJS.Timeout>();
const startTimeRef = useRef(Date.now());
const scissorsIcon = useMemo(() => (
<motion.div
style={{ display: 'inline-flex', marginRight: '8px' }}
animate={status === 'active' ? {
rotate: [0, 360],
scale: [1, 1.2, 1]
} : { rotate: 0, scale: 1 }}
transition={status === 'active' ? {
rotate: { duration: 3, repeat: Infinity, ease: "linear" },
scale: { duration: 1.5, repeat: Infinity, ease: "easeInOut" }
} : { duration: 0.3 }}
>
<Scissors className="w-5 h-5 text-[#f59e0b]" />
</motion.div>
), [status]);
// 处理进度更新
useEffect(() => {
const updateProgress = () => {
const elapsed = Date.now() - startTimeRef.current;
const timeLimit = 10 * 60 * 1000; // 10分钟
if (isCompleted) {
// 如果完成了快速增加到100%
setProgress(prev => {
const next = prev + (100 - prev) / 10;
if (next >= 99.9) {
setStatus('success');
setCurrentDescription('编辑完成,已更新到页面中');
onComplete?.();
clearInterval(timerRef.current);
return 100;
}
return next;
});
} else if (elapsed >= timeLimit) {
// 超时失败
setStatus('exception');
setCurrentDescription('编辑超时,请重试');
onFail?.();
clearInterval(timerRef.current);
return;
} else {
// 正常进度缓慢增加到90%
setProgress(prev => {
const targetProgress = (elapsed / timeLimit) * 90;
const next = Math.min(prev + 0.5, targetProgress);
return next;
});
}
};
timerRef.current = setInterval(updateProgress, 100);
return () => clearInterval(timerRef.current);
}, [isCompleted]);
return (
<div data-alt="editing-notification" style={{ minWidth: '300px' }}>
<h3 style={{
...descriptionStyle,
color: status === 'exception' ? '#ff4d4f' :
'rgba(255, 255, 255, 0.65)',
}}>
{scissorsIcon}
{currentDescription}
</h3>
<Progress
percent={Math.round(progress)}
status={status}
strokeColor={{
'0%': '#f59e0b',
'100%': '#a855f7',
}}
trailColor="rgba(255,255,255,0.08)"
size="small"
format={percent => (
<span style={{
color: status === 'exception' ? '#ff4d4f' :
status === 'success' ? '#a855f7' :
'rgba(255,255,255,0.85)',
fontSize: '12px',
fontWeight: 500
}}>
{`${percent}%`}
</span>
)}
className="transition-all duration-300 ease-in-out"
/>
</div>
);
};
notification.open({
key,
message: null,
description: <NotificationContent />,
duration: 0,
placement: 'topRight',
style: darkGlassStyle,
className: 'dark-glass-notification',
closeIcon: null
});
// 返回key以便外部可以手动关闭通知
return key;
};