video-flow-b/components/pages/work-flow/editing-notification.tsx
2025-09-05 01:25:17 +08:00

200 lines
5.8 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',
display: 'flex',
alignItems: 'center'
};
interface EditingNotificationProps {
/** 编辑是否完成 */
isCompleted?: boolean;
/** 初始描述文案 */
description?: string;
/** 编辑成功时的描述文案 */
successDescription?: string;
/** 编辑超时失败时的描述文案 */
timeoutDescription?: string;
/** key */
key?: string;
/** 完成时的回调 */
onComplete?: () => void;
/** 失败时的回调 */
onFail?: () => void;
/** 超时时间毫秒默认10分钟 */
timeout?: number;
/** 是否显示关闭按钮默认false */
showCloseIcon?: boolean;
}
/**
* 显示视频编辑进度通知
* @param props EditingNotificationProps
*/
export const showEditingNotification = (props: EditingNotificationProps) => {
const {
isCompleted = false,
description = 'The intelligent editing platform is currently editing the videos.',
successDescription = 'The editing is complete, and the updated video has been displayed on the page.',
timeoutDescription = 'The editing timed out, please try again.',
key,
onComplete,
onFail,
timeout = 8 * 60 * 1000, // 默认8分钟
showCloseIcon = false,
} = 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 resetProgress = () => {
setProgress(0);
setStatus('active');
setCurrentDescription(description);
startTimeRef.current = Date.now();
};
// 将重置方法暴露给外部
if (props.key && typeof window !== 'undefined') {
(window as any)[`resetProgress_${props.key}`] = resetProgress;
}
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 = timeout; // 使用传入的超时时间
if (isCompleted) {
// 如果完成了快速增加到100%
setProgress(prev => {
const next = prev + (100 - prev) / 10;
if (next >= 99.9) {
setStatus('success');
setCurrentDescription(successDescription);
onComplete?.();
clearInterval(timerRef.current);
return 100;
}
return next;
});
} else if (elapsed >= timeLimit) {
// 超时失败
setStatus('exception');
setCurrentDescription(timeoutDescription);
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: showCloseIcon ? undefined : null
});
// 返回key以便外部可以手动关闭通知
return key;
};