134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
/**
|
||
* 全局通知/Toast 状态 Store (Zustand)
|
||
*
|
||
* 管理应用全局的 toast / 通知队列:
|
||
* - 添加通知(支持 success / error / warning / info)
|
||
* - 自动过期清除
|
||
* - 手动移除
|
||
*/
|
||
|
||
import { create } from "zustand";
|
||
|
||
// ── 类型定义 ────────────────────────────────────────────────────────────
|
||
|
||
export type NotificationType = "success" | "error" | "warning" | "info";
|
||
|
||
export interface Notification {
|
||
/** 通知唯一 ID */
|
||
id: string;
|
||
/** 通知类型 */
|
||
type: NotificationType;
|
||
/** 通知消息 */
|
||
message: string;
|
||
/** 通知标题(可选) */
|
||
title?: string;
|
||
/** 创建时间戳 */
|
||
createdAt: number;
|
||
/** 自动过期时间(毫秒),null 表示不自动过期 */
|
||
duration: number | null;
|
||
}
|
||
|
||
export interface NotificationState {
|
||
/** 当前通知队列 */
|
||
notifications: Notification[];
|
||
}
|
||
|
||
export interface NotificationActions {
|
||
/** 添加一条通知 */
|
||
addNotification: (payload: {
|
||
type: NotificationType;
|
||
message: string;
|
||
title?: string;
|
||
/** 自定义过期时间(毫秒),默认按 type 自动设置 */
|
||
duration?: number | null;
|
||
}) => string;
|
||
|
||
/** 移除一条通知 */
|
||
removeNotification: (id: string) => void;
|
||
|
||
/** 清空所有通知 */
|
||
clearAll: () => void;
|
||
}
|
||
|
||
// ── 默认过期时间 ────────────────────────────────────────────────────────
|
||
|
||
const DEFAULT_DURATION_BY_TYPE: Record<NotificationType, number> = {
|
||
success: 3000,
|
||
error: 5000,
|
||
warning: 4000,
|
||
info: 3000,
|
||
};
|
||
|
||
// ── 辅助 ────────────────────────────────────────────────────────────────────
|
||
|
||
/** 生成通知 ID */
|
||
function generateNotificationId(): string {
|
||
return `notif-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
||
}
|
||
|
||
/** 定时器映射,避免重复 */
|
||
const timers = new Map<string, ReturnType<typeof setTimeout>>();
|
||
|
||
// ── 默认值 ──────────────────────────────────────────────────────────────────
|
||
|
||
const INITIAL_STATE: NotificationState = {
|
||
notifications: [],
|
||
};
|
||
|
||
// ── Store ───────────────────────────────────────────────────────────────────
|
||
|
||
export const useNotificationStore = create<NotificationState & NotificationActions>()(
|
||
(set, get) => ({
|
||
...INITIAL_STATE,
|
||
|
||
addNotification: ({ type, message, title, duration }) => {
|
||
const id = generateNotificationId();
|
||
const effectiveDuration = duration ?? DEFAULT_DURATION_BY_TYPE[type];
|
||
|
||
const notification: Notification = {
|
||
id,
|
||
type,
|
||
message,
|
||
title,
|
||
createdAt: Date.now(),
|
||
duration: effectiveDuration,
|
||
};
|
||
|
||
set((state) => ({
|
||
notifications: [...state.notifications, notification],
|
||
}));
|
||
|
||
// 设置自动过期
|
||
if (effectiveDuration !== null) {
|
||
const timer = setTimeout(() => {
|
||
get().removeNotification(id);
|
||
timers.delete(id);
|
||
}, effectiveDuration);
|
||
timers.set(id, timer);
|
||
}
|
||
|
||
return id;
|
||
},
|
||
|
||
removeNotification: (id) => {
|
||
// 清除定时器
|
||
const timer = timers.get(id);
|
||
if (timer) {
|
||
clearTimeout(timer);
|
||
timers.delete(id);
|
||
}
|
||
|
||
set((state) => ({
|
||
notifications: state.notifications.filter((n) => n.id !== id),
|
||
}));
|
||
},
|
||
|
||
clearAll: () => {
|
||
// 清除所有定时器
|
||
timers.forEach((timer) => clearTimeout(timer));
|
||
timers.clear();
|
||
|
||
set({ notifications: [] });
|
||
},
|
||
})
|
||
); |