优化超分,优化弹幕弹窗

This commit is contained in:
mtvpls
2025-12-03 23:49:46 +08:00
parent 820fc972df
commit 6f83ef731a
8 changed files with 304 additions and 539 deletions

View File

@@ -10,12 +10,14 @@ interface DanmakuFilterSettingsProps {
isOpen: boolean;
onClose: () => void;
onConfigUpdate?: (config: DanmakuFilterConfig) => void;
onShowToast?: (message: string, type: 'success' | 'error' | 'info') => void;
}
export default function DanmakuFilterSettings({
isOpen,
onClose,
onConfigUpdate,
onShowToast,
}: DanmakuFilterSettingsProps) {
const [config, setConfig] = useState<DanmakuFilterConfig>({ rules: [] });
const [newKeyword, setNewKeyword] = useState('');
@@ -54,10 +56,18 @@ export default function DanmakuFilterSettings({
if (onConfigUpdate) {
onConfigUpdate(config);
}
alert('保存成功!');
if (onShowToast) {
onShowToast('保存成功!', 'success');
}
// 延迟关闭面板让用户看到toast
setTimeout(() => {
onClose();
}, 300);
} catch (error) {
console.error('保存弹幕过滤配置失败:', error);
alert('保存失败,请重试');
if (onShowToast) {
onShowToast('保存失败,请重试', 'error');
}
} finally {
setSaving(false);
}
@@ -66,7 +76,9 @@ export default function DanmakuFilterSettings({
// 添加规则
const handleAddRule = () => {
if (!newKeyword.trim()) {
alert('请输入关键字');
if (onShowToast) {
onShowToast('请输入关键字', 'info');
}
return;
}
@@ -105,7 +117,7 @@ export default function DanmakuFilterSettings({
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70">
<div className="fixed inset-0 z-[2000] flex items-center justify-center bg-black/70">
<div className="bg-gray-900 rounded-lg shadow-xl w-full max-w-2xl max-h-[80vh] flex flex-col">
{/* 头部 */}
<div className="flex items-center justify-between p-4 border-b border-gray-700">

64
src/components/Toast.tsx Normal file
View File

@@ -0,0 +1,64 @@
'use client';
import { useEffect, useState } from 'react';
import { CheckCircle, XCircle, Info, X } from 'lucide-react';
export interface ToastProps {
message: string;
type?: 'success' | 'error' | 'info';
duration?: number;
onClose?: () => void;
}
export default function Toast({ message, type = 'info', duration = 3000, onClose }: ToastProps) {
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setIsVisible(false);
setTimeout(() => {
onClose?.();
}, 300); // 等待动画完成
}, duration);
return () => clearTimeout(timer);
}, [duration, onClose]);
const handleClose = () => {
setIsVisible(false);
setTimeout(() => {
onClose?.();
}, 300);
};
const icons = {
success: <CheckCircle className="w-5 h-5" />,
error: <XCircle className="w-5 h-5" />,
info: <Info className="w-5 h-5" />,
};
const colors = {
success: 'bg-green-500/90',
error: 'bg-red-500/90',
info: 'bg-blue-500/90',
};
return (
<div
className={`fixed top-20 left-1/2 -translate-x-1/2 z-[9999] transition-all duration-300 ${
isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-4'
}`}
>
<div className={`${colors[type]} text-white px-6 py-3 rounded-lg shadow-lg flex items-center gap-3 min-w-[300px]`}>
<div className="flex-shrink-0">{icons[type]}</div>
<div className="flex-1 text-sm font-medium">{message}</div>
<button
onClick={handleClose}
className="flex-shrink-0 hover:bg-white/20 rounded p-1 transition-colors"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
);
}