2024-12-03 01:37:02 +08:00
|
|
|
|
// @ts-nocheck
|
2024-12-04 02:35:06 +08:00
|
|
|
|
import React, { createContext, useState, useContext } from "react";
|
|
|
|
|
import { Button, Flex, Card, Text, Box } from "@radix-ui/themes";
|
|
|
|
|
import {
|
|
|
|
|
CheckCircledIcon,
|
|
|
|
|
CrossCircledIcon,
|
|
|
|
|
InfoCircledIcon,
|
|
|
|
|
} from "@radix-ui/react-icons";
|
2024-12-03 01:37:02 +08:00
|
|
|
|
|
|
|
|
|
// 定义通知类型枚举
|
|
|
|
|
export enum NotificationType {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
SUCCESS = "success",
|
|
|
|
|
ERROR = "error",
|
|
|
|
|
INFO = "info",
|
2024-12-03 01:37:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通知类型定义
|
|
|
|
|
type Notification = {
|
|
|
|
|
id: string;
|
|
|
|
|
type: NotificationType;
|
|
|
|
|
title: string;
|
|
|
|
|
message?: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 通知配置类型定义
|
|
|
|
|
type NotificationConfig = {
|
|
|
|
|
icon: React.ReactNode;
|
|
|
|
|
bgColor: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 通知配置映射
|
|
|
|
|
const notificationConfigs: Record<NotificationType, NotificationConfig> = {
|
|
|
|
|
[NotificationType.SUCCESS]: {
|
|
|
|
|
icon: <CheckCircledIcon className="w-5 h-5 text-white" />,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
bgColor: "bg-[rgba(0,168,91,0.85)]",
|
2024-12-03 01:37:02 +08:00
|
|
|
|
},
|
|
|
|
|
[NotificationType.ERROR]: {
|
|
|
|
|
icon: <CrossCircledIcon className="w-5 h-5 text-white" />,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
bgColor: "bg-[rgba(225,45,57,0.85)]",
|
2024-12-03 01:37:02 +08:00
|
|
|
|
},
|
|
|
|
|
[NotificationType.INFO]: {
|
|
|
|
|
icon: <InfoCircledIcon className="w-5 h-5 text-white" />,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
bgColor: "bg-[rgba(38,131,255,0.85)]",
|
|
|
|
|
},
|
2024-12-03 01:37:02 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 修改通知上下文类型定义
|
|
|
|
|
type NotificationContextType = {
|
|
|
|
|
show: (type: NotificationType, title: string, message?: string) => void;
|
|
|
|
|
success: (title: string, message?: string) => void;
|
|
|
|
|
error: (title: string, message?: string) => void;
|
|
|
|
|
info: (title: string, message?: string) => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const NotificationContext = createContext<NotificationContextType>({
|
|
|
|
|
show: () => {},
|
|
|
|
|
success: () => {},
|
|
|
|
|
error: () => {},
|
|
|
|
|
info: () => {},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 简化全局 toast 对象定义
|
|
|
|
|
export const toast: NotificationContextType = {
|
|
|
|
|
show: () => {},
|
|
|
|
|
success: () => {},
|
|
|
|
|
error: () => {},
|
|
|
|
|
info: () => {},
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
export const NotificationProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
|
|
children,
|
|
|
|
|
}) => {
|
2024-12-03 01:37:02 +08:00
|
|
|
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
|
|
|
|
|
|
|
|
// 统一参数顺序:title 在前,message 在后
|
|
|
|
|
const show = (type: NotificationType, title: string, message?: string) => {
|
|
|
|
|
const id = Math.random().toString(36).substring(2, 9);
|
|
|
|
|
const newNotification = { id, type, title, message };
|
2024-12-04 02:35:06 +08:00
|
|
|
|
|
|
|
|
|
setNotifications((prev) => [...prev, newNotification]);
|
|
|
|
|
|
2024-12-03 01:37:02 +08:00
|
|
|
|
setTimeout(() => {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
setNotifications((prev) =>
|
|
|
|
|
prev.filter((notification) => notification.id !== id),
|
|
|
|
|
);
|
2024-12-03 01:37:02 +08:00
|
|
|
|
}, 3000);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 简化快捷方法定义
|
|
|
|
|
const contextValue = {
|
|
|
|
|
show,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
success: (title: string, message?: string) =>
|
|
|
|
|
show(NotificationType.SUCCESS, title, message),
|
|
|
|
|
error: (title: string, message?: string) =>
|
|
|
|
|
show(NotificationType.ERROR, title, message),
|
|
|
|
|
info: (title: string, message?: string) =>
|
|
|
|
|
show(NotificationType.INFO, title, message),
|
2024-12-03 01:37:02 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 初始化全局方法
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
Object.assign(toast, contextValue);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const closeNotification = (id: string) => {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
setNotifications((prev) =>
|
|
|
|
|
prev.filter((notification) => notification.id !== id),
|
|
|
|
|
);
|
2024-12-03 01:37:02 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<NotificationContext.Provider value={contextValue}>
|
|
|
|
|
{notifications.length > 0 && (
|
|
|
|
|
<Box
|
|
|
|
|
position="fixed"
|
|
|
|
|
top="4"
|
|
|
|
|
className="fixed top-4 right-4 z-[1000] flex flex-col gap-2 w-full max-w-[360px] px-4 md:px-0 md:right-6"
|
|
|
|
|
>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
{notifications.map((notification) => (
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<Card
|
|
|
|
|
key={notification.id}
|
|
|
|
|
className="p-0 overflow-hidden shadow-lg w-full"
|
|
|
|
|
>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Flex
|
|
|
|
|
direction="column"
|
2024-12-03 01:37:02 +08:00
|
|
|
|
gap="2"
|
|
|
|
|
className={`relative min-h-[52px] p-4 ${notificationConfigs[notification.type].bgColor}`}
|
|
|
|
|
>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
2024-12-03 01:37:02 +08:00
|
|
|
|
onClick={() => closeNotification(notification.id)}
|
|
|
|
|
className="absolute right-2 top-2 p-1 min-w-0 h-auto text-white opacity-70 cursor-pointer bg-transparent border-none text-sm hover:opacity-100 transition-opacity"
|
|
|
|
|
>
|
|
|
|
|
✕
|
|
|
|
|
</Button>
|
|
|
|
|
<Flex direction="column" gap="1.5" className="pr-6">
|
2024-12-20 00:34:54 +08:00
|
|
|
|
<Flex className="items-center space-x-3">
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<span className="flex items-center justify-center">
|
|
|
|
|
{notificationConfigs[notification.type].icon}
|
|
|
|
|
</span>
|
|
|
|
|
{notification.title && (
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Text
|
|
|
|
|
weight="bold"
|
|
|
|
|
size="2"
|
2024-12-20 00:34:54 +08:00
|
|
|
|
className="text-white leading-tight truncate max-w-[250px]"
|
2024-12-03 01:37:02 +08:00
|
|
|
|
>
|
|
|
|
|
{notification.title}
|
|
|
|
|
</Text>
|
|
|
|
|
)}
|
|
|
|
|
</Flex>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Text size="2" className="text-white/80 leading-normal">
|
2024-12-03 01:37:02 +08:00
|
|
|
|
{notification.message}
|
|
|
|
|
</Text>
|
|
|
|
|
</Flex>
|
|
|
|
|
<div className="h-0.5 w-full bg-white/10 mt-1">
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<div
|
2024-12-03 01:37:02 +08:00
|
|
|
|
className="h-full bg-white/20 animate-[progress_3s_linear]"
|
2024-12-04 02:35:06 +08:00
|
|
|
|
style={{
|
|
|
|
|
transformOrigin: "left",
|
2024-12-03 01:37:02 +08:00
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Card>
|
|
|
|
|
))}
|
|
|
|
|
</Box>
|
|
|
|
|
)}
|
|
|
|
|
{children}
|
|
|
|
|
</NotificationContext.Provider>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 导出hook
|
|
|
|
|
export const useNotification = () => {
|
|
|
|
|
const context = useContext(NotificationContext);
|
|
|
|
|
if (!context) {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
throw new Error(
|
|
|
|
|
"useNotification must be used within a NotificationProvider",
|
|
|
|
|
);
|
2024-12-03 01:37:02 +08:00
|
|
|
|
}
|
|
|
|
|
return context;
|
2024-12-04 02:35:06 +08:00
|
|
|
|
};
|