feat: 本次提交更新内容如下
场景获客列表搞定
This commit is contained in:
182
Cunkebao/app/components/common/NotificationSystem.tsx
Normal file
182
Cunkebao/app/components/common/NotificationSystem.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
"use client"
|
||||
|
||||
import { useState, createContext, useContext, type ReactNode } from "react"
|
||||
import { Card, CardContent } from "@/app/components/ui/card"
|
||||
import { Button } from "@/app/components/ui/button"
|
||||
import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from "lucide-react"
|
||||
import { cn } from "@/app/lib/utils"
|
||||
|
||||
export type NotificationType = "success" | "error" | "warning" | "info"
|
||||
|
||||
export interface Notification {
|
||||
id: string
|
||||
type: NotificationType
|
||||
title: string
|
||||
message?: string
|
||||
duration?: number
|
||||
persistent?: boolean
|
||||
actions?: Array<{
|
||||
label: string
|
||||
onClick: () => void
|
||||
variant?: "default" | "outline"
|
||||
}>
|
||||
}
|
||||
|
||||
interface NotificationContextType {
|
||||
notifications: Notification[]
|
||||
addNotification: (notification: Omit<Notification, "id">) => void
|
||||
removeNotification: (id: string) => void
|
||||
clearAll: () => void
|
||||
}
|
||||
|
||||
const NotificationContext = createContext<NotificationContextType | undefined>(undefined)
|
||||
|
||||
export function useNotifications() {
|
||||
const context = useContext(NotificationContext)
|
||||
if (!context) {
|
||||
throw new Error("useNotifications must be used within a NotificationProvider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
export function NotificationProvider({ children }: { children: ReactNode }) {
|
||||
const [notifications, setNotifications] = useState<Notification[]>([])
|
||||
|
||||
const addNotification = (notification: Omit<Notification, "id">) => {
|
||||
const id = Math.random().toString(36).substr(2, 9)
|
||||
const newNotification: Notification = {
|
||||
...notification,
|
||||
id,
|
||||
duration: notification.duration ?? 5000,
|
||||
}
|
||||
|
||||
setNotifications((prev) => [newNotification, ...prev])
|
||||
|
||||
// 自动移除非持久化通知
|
||||
if (!notification.persistent && newNotification.duration > 0) {
|
||||
setTimeout(() => {
|
||||
removeNotification(id)
|
||||
}, newNotification.duration)
|
||||
}
|
||||
}
|
||||
|
||||
const removeNotification = (id: string) => {
|
||||
setNotifications((prev) => prev.filter((notification) => notification.id !== id))
|
||||
}
|
||||
|
||||
const clearAll = () => {
|
||||
setNotifications([])
|
||||
}
|
||||
|
||||
return (
|
||||
<NotificationContext.Provider value={{ notifications, addNotification, removeNotification, clearAll }}>
|
||||
{children}
|
||||
<NotificationContainer />
|
||||
</NotificationContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function NotificationContainer() {
|
||||
const { notifications, removeNotification } = useNotifications()
|
||||
|
||||
if (notifications.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="fixed top-4 right-4 z-50 space-y-2 max-w-sm w-full">
|
||||
{notifications.map((notification) => (
|
||||
<NotificationItem key={notification.id} notification={notification} onRemove={removeNotification} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NotificationItem({
|
||||
notification,
|
||||
onRemove,
|
||||
}: {
|
||||
notification: Notification
|
||||
onRemove: (id: string) => void
|
||||
}) {
|
||||
const getIcon = () => {
|
||||
switch (notification.type) {
|
||||
case "success":
|
||||
return <CheckCircle className="h-5 w-5 text-green-500" />
|
||||
case "error":
|
||||
return <AlertCircle className="h-5 w-5 text-red-500" />
|
||||
case "warning":
|
||||
return <AlertTriangle className="h-5 w-5 text-yellow-500" />
|
||||
case "info":
|
||||
return <Info className="h-5 w-5 text-blue-500" />
|
||||
}
|
||||
}
|
||||
|
||||
const getBorderColor = () => {
|
||||
switch (notification.type) {
|
||||
case "success":
|
||||
return "border-l-green-500"
|
||||
case "error":
|
||||
return "border-l-red-500"
|
||||
case "warning":
|
||||
return "border-l-yellow-500"
|
||||
case "info":
|
||||
return "border-l-blue-500"
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={cn("border-l-4 shadow-lg animate-in slide-in-from-right", getBorderColor())}>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
{getIcon()}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="font-medium text-sm">{notification.title}</h4>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 text-gray-400 hover:text-gray-600"
|
||||
onClick={() => onRemove(notification.id)}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
{notification.message && <p className="text-sm text-gray-600 mt-1">{notification.message}</p>}
|
||||
{notification.actions && notification.actions.length > 0 && (
|
||||
<div className="flex space-x-2 mt-3">
|
||||
{notification.actions.map((action, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
variant={action.variant || "outline"}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
action.onClick()
|
||||
onRemove(notification.id)
|
||||
}}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
// 便捷的通知钩子
|
||||
export function useNotify() {
|
||||
const { addNotification } = useNotifications()
|
||||
|
||||
return {
|
||||
success: (title: string, message?: string, options?: Partial<Notification>) =>
|
||||
addNotification({ type: "success", title, message, ...options }),
|
||||
error: (title: string, message?: string, options?: Partial<Notification>) =>
|
||||
addNotification({ type: "error", title, message, ...options }),
|
||||
warning: (title: string, message?: string, options?: Partial<Notification>) =>
|
||||
addNotification({ type: "warning", title, message, ...options }),
|
||||
info: (title: string, message?: string, options?: Partial<Notification>) =>
|
||||
addNotification({ type: "info", title, message, ...options }),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user