🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
前言
一个完善的通知系统可以明显提拔用户体验,让用户及时了解:
本日分享怎样实现一个优雅的通知中心!
功能设计
通知类型
- // src/types/notification.ts
- export type NotificationType =
- | 'comment' // 评论通知
- | 'reply' // 回复通知
- | 'like' // 点赞通知
- | 'follow' // 关注通知
- | 'system' // 系统通知
- | 'achievement' // 成就通知
- export interface Notification {
- id: string
- type: NotificationType
- title: string
- content: string
- avatar?: string
- link?: string
- read: boolean
- createTime: number
- }
复制代码 核心实现
1. 通知服务
- // src/services/notification.ts
- import { defineStore } from 'pinia'
- import { ref, computed } from 'vue'
- import type { Notification, NotificationType } from '@/types/notification'
- export const useNotificationStore = defineStore('notification', () => {
- const notifications = ref<Notification[]>([])
-
- // 加载通知
- function loadNotifications() {
- const data = localStorage.getItem('blog_notifications')
- if (data) {
- notifications.value = JSON.parse(data)
- }
- }
-
- // 保存通知
- function saveNotifications() {
- localStorage.setItem('blog_notifications', JSON.stringify(notifications.value))
- }
-
- // 添加通知
- function addNotification(notification: Omit<Notification, 'id' | 'read' | 'createTime'>) {
- const newNotification: Notification = {
- ...notification,
- id: `notif_${Date.now()}_${Math.random().toString(36).slice(2)}`,
- read: false,
- createTime: Date.now()
- }
-
- notifications.value.unshift(newNotification)
- saveNotifications()
-
- // 触发浏览器通知
- if (Notification.permission === 'granted') {
- new Notification(newNotification.title, {
- body: newNotification.content,
- icon: newNotification.avatar
- })
- }
-
- return newNotification
- }
-
- // 标记已读
- function markAsRead(id: string) {
- const notification = notifications.value.find(n => n.id === id)
- if (notification) {
- notification.read = true
- saveNotifications()
- }
- }
-
- // 全部已读
- function markAllAsRead() {
- notifications.value.forEach(n => {
- n.read = true
- })
- saveNotifications()
- }
-
- // 删除通知
- function deleteNotification(id: string) {
- const index = notifications.value.findIndex(n => n.id === id)
- if (index > -1) {
- notifications.value.splice(index, 1)
- saveNotifications()
- }
- }
-
- // 未读数量
- const unreadCount = computed(() => {
- return notifications.value.filter(n => !n.read).length
- })
-
- // 按类型分组
- const groupedNotifications = computed(() => {
- const groups: Record<NotificationType, Notification[]> = {
- comment: [],
- reply: [],
- like: [],
- follow: [],
- system: [],
- achievement: []
- }
-
- notifications.value.forEach(n => {
- groups[n.type].push(n)
- })
-
- return groups
- })
-
- // 请求通知权限
- async function requestPermission() {
- if ('Notification' in window) {
- const permission = await Notification.requestPermission()
- return permission === 'granted'
- }
- return false
- }
-
- loadNotifications()
-
- return {
- notifications,
- unreadCount,
- groupedNotifications,
- addNotification,
- markAsRead,
- markAllAsRead,
- deleteNotification,
- requestPermission
- }
- })
复制代码 2. 通知中心组件
- <template>
- <el-popover
- v-model:visible="visible"
- placement="bottom-end"
- :width="360"
- trigger="click"
- >
- <template #reference>
-
- <el-badge :value="unreadCount" :hidden="unreadCount === 0" :max="99">
- <el-button :icon="Bell" circle />
- </el-badge>
-
- <span v-if="hasNewNotification" />
-
- </template>
-
- <template #default>
-
-
-
- <h3>通知中心</h3>
- <el-button
- v-if="unreadCount > 0"
- text
- size="small"
- @click="handleMarkAllRead"
- >
- 全部已读
- </el-button>
-
-
-
- <el-tabs v-model="activeTab" >
- <el-tab-pane label="全部" name="all" />
- <el-tab-pane label="评论" name="comment" />
- <el-tab-pane label="点赞" name="like" />
- <el-tab-pane label="系统" name="system" />
- </el-tabs>
-
-
-
-
- <el-avatar
- :src="notification.avatar || defaultAvatar"
- :size="40"
- />
-
-
- {{ notification.title }}
- {{ notification.content }}
- {{ formatTime(notification.createTime) }}
-
-
-
- <el-button
- v-if="!notification.read"
- text
- size="small"
- @click.stop="handleMarkRead(notification.id)"
- >
- 标记已读
- </el-button>
- <el-button
- text
- size="small"
- @click.stop="handleDelete(notification.id)"
- >
- 删除
- </el-button>
-
-
-
- <el-empty
- v-if="filteredNotifications.length === 0"
- description="暂无通知"
- />
-
-
- </template>
- </el-popover>
- </template>
复制代码 使用示例
- <template>
- <header>
-
-
- <NotificationCenter />
-
- </header>
- </template>
复制代码 浏览器通知
- // 在需要时请求权限并发送通知
- async function sendBrowserNotification(title: string, options?: NotificationOptions) {
- if ('Notification' in window && Notification.permission === 'granted') {
- new Notification(title, {
- icon: '/logo.png',
- badge: '/badge.png',
- ...options
- })
- }
- }
复制代码💡 进阶功能
- 接入 WebSocket 实现及时推送
- 添加通知免打扰模式
- 支持通知折叠和展开
如果对您有所资助,欢迎您点个关注,我会定时更新技能文档,大家一起讨论学习,一起进步。
|