696 lines
21 KiB
TypeScript
696 lines
21 KiB
TypeScript
|
import { ref, computed } from 'vue'
|
||
|
|
||
|
export interface NotificationItem {
|
||
|
id: string
|
||
|
type: 'APPROVAL' | 'PENDING' | 'SYSTEM' | 'PROCUREMENT' | 'EQUIPMENT_BORROW' | 'EQUIPMENT_RETURN' | 'EQUIPMENT_MAINTENANCE' | 'EQUIPMENT_ALERT' | 'WORKFLOW'
|
||
|
title: string
|
||
|
content: string
|
||
|
createTime: string
|
||
|
read: boolean
|
||
|
targetUrl?: string
|
||
|
priority?: 'LOW' | 'NORMAL' | 'HIGH' | 'URGENT'
|
||
|
category?: string
|
||
|
metadata?: Record<string, any>
|
||
|
// 新增字段
|
||
|
source?: string // 消息来源
|
||
|
actionRequired?: boolean // 是否需要操作
|
||
|
expiresAt?: string // 过期时间
|
||
|
relatedId?: string // 关联的业务ID
|
||
|
// 新增智能提醒字段
|
||
|
reminderTime?: string // 提醒时间
|
||
|
reminderType?: 'IMMEDIATE' | 'DELAYED' | 'RECURRING' // 提醒类型
|
||
|
reminderInterval?: number // 重复提醒间隔(分钟)
|
||
|
lastReminderTime?: string // 上次提醒时间
|
||
|
}
|
||
|
|
||
|
class NotificationService {
|
||
|
private notifications = ref<NotificationItem[]>([])
|
||
|
|
||
|
// 计算属性
|
||
|
public readonly unreadCount = computed(() =>
|
||
|
this.notifications.value.filter(n => !n.read).length
|
||
|
)
|
||
|
|
||
|
public readonly pendingCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'PENDING' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
public readonly approvalCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'APPROVAL' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
public readonly procurementCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'PROCUREMENT' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:设备借用通知数量
|
||
|
public readonly equipmentBorrowCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'EQUIPMENT_BORROW' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:设备归还通知数量
|
||
|
public readonly equipmentReturnCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'EQUIPMENT_RETURN' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:设备维护通知数量
|
||
|
public readonly equipmentMaintenanceCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'EQUIPMENT_MAINTENANCE' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:设备告警通知数量
|
||
|
public readonly equipmentAlertCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'EQUIPMENT_ALERT' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:工作流通知数量
|
||
|
public readonly workflowCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.type === 'WORKFLOW' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:需要操作的通知数量
|
||
|
public readonly actionRequiredCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.actionRequired && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:紧急通知数量
|
||
|
public readonly urgentCount = computed(() =>
|
||
|
this.notifications.value.filter(n =>
|
||
|
n.priority === 'URGENT' && !n.read
|
||
|
).length
|
||
|
)
|
||
|
|
||
|
// 新增:今日通知数量
|
||
|
public readonly todayCount = computed(() => {
|
||
|
const today = new Date()
|
||
|
today.setHours(0, 0, 0, 0)
|
||
|
return this.notifications.value.filter(n => {
|
||
|
const createDate = new Date(n.createTime)
|
||
|
return createDate >= today
|
||
|
}).length
|
||
|
})
|
||
|
|
||
|
// 获取所有通知
|
||
|
public getAllNotifications() {
|
||
|
return this.notifications.value
|
||
|
}
|
||
|
|
||
|
// 获取未读通知
|
||
|
public getUnreadNotifications() {
|
||
|
return this.notifications.value.filter(n => !n.read)
|
||
|
}
|
||
|
|
||
|
// 获取特定类型的通知
|
||
|
public getNotificationsByType(type: NotificationItem['type']) {
|
||
|
return this.notifications.value.filter(n => n.type === type)
|
||
|
}
|
||
|
|
||
|
// 新增:获取需要操作的通知
|
||
|
public getActionRequiredNotifications() {
|
||
|
return this.notifications.value.filter(n => n.actionRequired && !n.read)
|
||
|
}
|
||
|
|
||
|
// 新增:获取紧急通知
|
||
|
public getUrgentNotifications() {
|
||
|
return this.notifications.value.filter(n => n.priority === 'URGENT' && !n.read)
|
||
|
}
|
||
|
|
||
|
// 新增:获取今日通知
|
||
|
public getTodayNotifications() {
|
||
|
const today = new Date()
|
||
|
today.setHours(0, 0, 0, 0)
|
||
|
return this.notifications.value.filter(n => {
|
||
|
const createDate = new Date(n.createTime)
|
||
|
return createDate >= today
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:获取相关通知
|
||
|
public getRelatedNotifications(relatedId: string) {
|
||
|
return this.notifications.value.filter(n => n.relatedId === relatedId)
|
||
|
}
|
||
|
|
||
|
// 新增:获取需要提醒的通知
|
||
|
public getReminderNotifications() {
|
||
|
const now = new Date()
|
||
|
return this.notifications.value.filter(n => {
|
||
|
if (!n.reminderTime) return false
|
||
|
|
||
|
const reminderDate = new Date(n.reminderTime)
|
||
|
if (reminderDate <= now) {
|
||
|
// 检查重复提醒
|
||
|
if (n.reminderType === 'RECURRING' && n.reminderInterval) {
|
||
|
const lastReminder = n.lastReminderTime ? new Date(n.lastReminderTime) : reminderDate
|
||
|
const nextReminder = new Date(lastReminder.getTime() + n.reminderInterval * 60 * 1000)
|
||
|
return nextReminder <= now
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:设置通知提醒
|
||
|
public setNotificationReminder(notificationId: string, reminderTime: string, reminderType: 'IMMEDIATE' | 'DELAYED' | 'RECURRING' = 'IMMEDIATE', reminderInterval?: number) {
|
||
|
const notification = this.notifications.value.find(n => n.id === notificationId)
|
||
|
if (notification) {
|
||
|
notification.reminderTime = reminderTime
|
||
|
notification.reminderType = reminderType
|
||
|
notification.reminderInterval = reminderInterval
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 新增:取消通知提醒
|
||
|
public cancelNotificationReminder(notificationId: string) {
|
||
|
const notification = this.notifications.value.find(n => n.id === notificationId)
|
||
|
if (notification) {
|
||
|
notification.reminderTime = undefined
|
||
|
notification.reminderType = undefined
|
||
|
notification.reminderInterval = undefined
|
||
|
notification.lastReminderTime = undefined
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 新增:标记提醒已处理
|
||
|
public markReminderProcessed(notificationId: string) {
|
||
|
const notification = this.notifications.value.find(n => n.id === notificationId)
|
||
|
if (notification && notification.reminderType === 'RECURRING') {
|
||
|
notification.lastReminderTime = new Date().toISOString()
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 添加通知
|
||
|
public addNotification(notification: Omit<NotificationItem, 'id' | 'createTime' | 'read'>) {
|
||
|
const newNotification: NotificationItem = {
|
||
|
...notification,
|
||
|
id: this.generateId(),
|
||
|
createTime: new Date().toISOString(),
|
||
|
read: false,
|
||
|
// 设置默认值
|
||
|
priority: notification.priority || 'NORMAL',
|
||
|
actionRequired: notification.actionRequired || false,
|
||
|
source: notification.source || 'SYSTEM'
|
||
|
}
|
||
|
|
||
|
this.notifications.value.unshift(newNotification)
|
||
|
|
||
|
// 限制通知数量,避免内存泄漏
|
||
|
if (this.notifications.value.length > 200) {
|
||
|
this.notifications.value = this.notifications.value.slice(0, 200)
|
||
|
}
|
||
|
|
||
|
// 触发事件
|
||
|
this.emitNotificationEvent('add', newNotification)
|
||
|
|
||
|
// 自动保存
|
||
|
this.saveToStorage()
|
||
|
|
||
|
// 检查是否需要播放提示音
|
||
|
if (newNotification.priority === 'URGENT' || newNotification.priority === 'HIGH') {
|
||
|
this.playNotificationSound()
|
||
|
}
|
||
|
|
||
|
return newNotification
|
||
|
}
|
||
|
|
||
|
// 标记通知为已读
|
||
|
public markAsRead(id: string) {
|
||
|
const notification = this.notifications.value.find(n => n.id === id)
|
||
|
if (notification) {
|
||
|
notification.read = true
|
||
|
this.emitNotificationEvent('read', notification)
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 标记所有通知为已读
|
||
|
public markAllAsRead() {
|
||
|
this.notifications.value.forEach(n => n.read = true)
|
||
|
this.emitNotificationEvent('readAll', null)
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
|
||
|
// 标记特定类型通知为已读
|
||
|
public markTypeAsRead(type: NotificationItem['type']) {
|
||
|
this.notifications.value
|
||
|
.filter(n => n.type === type)
|
||
|
.forEach(n => n.read = true)
|
||
|
this.emitNotificationEvent('readType', { type })
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
|
||
|
// 新增:标记相关通知为已读
|
||
|
public markRelatedAsRead(relatedId: string) {
|
||
|
this.notifications.value
|
||
|
.filter(n => n.relatedId === relatedId)
|
||
|
.forEach(n => n.read = true)
|
||
|
this.emitNotificationEvent('readRelated', { relatedId })
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
|
||
|
// 删除通知
|
||
|
public removeNotification(id: string) {
|
||
|
const index = this.notifications.value.findIndex(n => n.id === id)
|
||
|
if (index > -1) {
|
||
|
const notification = this.notifications.value[index]
|
||
|
this.notifications.value.splice(index, 1)
|
||
|
this.emitNotificationEvent('remove', notification)
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 清空所有通知
|
||
|
public clearAll() {
|
||
|
this.notifications.value = []
|
||
|
this.emitNotificationEvent('clear', null)
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
|
||
|
// 清空已读通知
|
||
|
public clearRead() {
|
||
|
this.notifications.value = this.notifications.value.filter(n => !n.read)
|
||
|
this.emitNotificationEvent('clearRead', null)
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
|
||
|
// 新增:清空过期通知
|
||
|
public clearExpired() {
|
||
|
const now = new Date()
|
||
|
const beforeCount = this.notifications.value.length
|
||
|
|
||
|
this.notifications.value = this.notifications.value.filter(n => {
|
||
|
if (!n.expiresAt) return true
|
||
|
const expireDate = new Date(n.expiresAt)
|
||
|
return expireDate > now
|
||
|
})
|
||
|
|
||
|
const afterCount = this.notifications.value.length
|
||
|
if (beforeCount !== afterCount) {
|
||
|
this.emitNotificationEvent('clearExpired', { clearedCount: beforeCount - afterCount })
|
||
|
this.saveToStorage()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 获取通知统计
|
||
|
public getStats() {
|
||
|
const total = this.notifications.value.length
|
||
|
const unread = this.unreadCount.value
|
||
|
const pending = this.pendingCount.value
|
||
|
const approval = this.approvalCount.value
|
||
|
const procurement = this.procurementCount.value
|
||
|
const actionRequired = this.actionRequiredCount.value
|
||
|
const urgent = this.urgentCount.value
|
||
|
const today = this.todayCount.value
|
||
|
|
||
|
return {
|
||
|
total,
|
||
|
unread,
|
||
|
pending,
|
||
|
approval,
|
||
|
procurement,
|
||
|
actionRequired,
|
||
|
urgent,
|
||
|
today,
|
||
|
read: total - unread
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 搜索通知
|
||
|
public searchNotifications(keyword: string) {
|
||
|
if (!keyword.trim()) {
|
||
|
return this.notifications.value
|
||
|
}
|
||
|
|
||
|
const lowerKeyword = keyword.toLowerCase()
|
||
|
return this.notifications.value.filter(n =>
|
||
|
n.title.toLowerCase().includes(lowerKeyword) ||
|
||
|
n.content.toLowerCase().includes(lowerKeyword) ||
|
||
|
n.category?.toLowerCase().includes(lowerKeyword) ||
|
||
|
n.source?.toLowerCase().includes(lowerKeyword)
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// 按优先级排序
|
||
|
public sortByPriority() {
|
||
|
const priorityOrder = { 'URGENT': 4, 'HIGH': 3, 'NORMAL': 2, 'LOW': 1 }
|
||
|
|
||
|
this.notifications.value.sort((a, b) => {
|
||
|
const aPriority = priorityOrder[a.priority || 'NORMAL'] || 2
|
||
|
const bPriority = priorityOrder[b.priority || 'NORMAL'] || 2
|
||
|
|
||
|
if (aPriority !== bPriority) {
|
||
|
return bPriority - aPriority
|
||
|
}
|
||
|
|
||
|
// 优先级相同时,按时间倒序
|
||
|
return new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:按时间排序
|
||
|
public sortByTime() {
|
||
|
this.notifications.value.sort((a, b) =>
|
||
|
new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// 新增:按类型分组
|
||
|
public groupByType() {
|
||
|
const groups: Record<string, NotificationItem[]> = {}
|
||
|
|
||
|
this.notifications.value.forEach(notification => {
|
||
|
const type = notification.type
|
||
|
if (!groups[type]) {
|
||
|
groups[type] = []
|
||
|
}
|
||
|
groups[type].push(notification)
|
||
|
})
|
||
|
|
||
|
return groups
|
||
|
}
|
||
|
|
||
|
// 添加采购申请通知
|
||
|
public addProcurementNotification(equipmentName: string, applicantName: string) {
|
||
|
return this.addNotification({
|
||
|
type: 'PROCUREMENT',
|
||
|
title: '新的设备采购申请',
|
||
|
content: `收到来自 ${applicantName} 的设备采购申请:${equipmentName}`,
|
||
|
targetUrl: '/asset-management/device-management/approval',
|
||
|
priority: 'HIGH',
|
||
|
category: '设备采购',
|
||
|
actionRequired: true,
|
||
|
source: 'PROCUREMENT_SYSTEM',
|
||
|
metadata: {
|
||
|
equipmentName,
|
||
|
applicantName,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 添加审批结果通知
|
||
|
public addApprovalNotification(equipmentName: string, result: 'APPROVED' | 'REJECTED', approverName: string) {
|
||
|
const isApproved = result === 'APPROVED'
|
||
|
return this.addNotification({
|
||
|
type: 'APPROVAL',
|
||
|
title: `采购申请${isApproved ? '已通过' : '已拒绝'}`,
|
||
|
content: `您的设备采购申请"${equipmentName}"${isApproved ? '已通过' : '被拒绝'},审批人:${approverName}`,
|
||
|
targetUrl: '/asset-management/device-management/procurement',
|
||
|
priority: isApproved ? 'NORMAL' : 'HIGH',
|
||
|
category: '审批结果',
|
||
|
actionRequired: !isApproved, // 被拒绝时需要操作
|
||
|
source: 'APPROVAL_SYSTEM',
|
||
|
metadata: {
|
||
|
equipmentName,
|
||
|
result,
|
||
|
approverName,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 添加系统通知
|
||
|
public addSystemNotification(title: string, content: string, priority: NotificationItem['priority'] = 'NORMAL') {
|
||
|
return this.addNotification({
|
||
|
type: 'SYSTEM',
|
||
|
title,
|
||
|
content,
|
||
|
priority,
|
||
|
category: '系统通知',
|
||
|
source: 'SYSTEM',
|
||
|
actionRequired: false
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:添加待审批通知
|
||
|
public addPendingApprovalNotification(equipmentName: string, applicantName: string, businessType: string) {
|
||
|
return this.addNotification({
|
||
|
type: 'PENDING',
|
||
|
title: '新的审批申请',
|
||
|
content: `收到来自 ${applicantName} 的${businessType}申请:${equipmentName}`,
|
||
|
targetUrl: '/asset-management/device-management/approval',
|
||
|
priority: 'HIGH',
|
||
|
category: '审批申请',
|
||
|
actionRequired: true,
|
||
|
source: 'APPROVAL_SYSTEM',
|
||
|
metadata: {
|
||
|
equipmentName,
|
||
|
applicantName,
|
||
|
businessType,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:添加设备借用通知
|
||
|
public addEquipmentBorrowNotification(equipmentName: string, borrowerName: string, borrowReason: string) {
|
||
|
return this.addNotification({
|
||
|
type: 'EQUIPMENT_BORROW',
|
||
|
title: '新的设备借用申请',
|
||
|
content: `收到来自 ${borrowerName} 的设备借用申请:${equipmentName},借用原因:${borrowReason}`,
|
||
|
targetUrl: '/asset-management/device-management/device-center',
|
||
|
priority: 'HIGH',
|
||
|
category: '设备借用',
|
||
|
actionRequired: true,
|
||
|
source: 'EQUIPMENT_SYSTEM',
|
||
|
metadata: {
|
||
|
equipmentName,
|
||
|
borrowerName,
|
||
|
borrowReason,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:添加设备归还通知
|
||
|
public addEquipmentReturnNotification(equipmentName: string, returnerName: string, returnCondition: string) {
|
||
|
return this.addNotification({
|
||
|
type: 'EQUIPMENT_RETURN',
|
||
|
title: '设备归还申请',
|
||
|
content: `收到来自 ${returnerName} 的设备归还申请:${equipmentName},归还状态:${returnCondition}`,
|
||
|
targetUrl: '/asset-management/device-management/device-center',
|
||
|
priority: 'NORMAL',
|
||
|
category: '设备归还',
|
||
|
actionRequired: false,
|
||
|
source: 'EQUIPMENT_SYSTEM',
|
||
|
metadata: {
|
||
|
equipmentName,
|
||
|
returnerName,
|
||
|
returnCondition,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:添加设备维护通知
|
||
|
public addEquipmentMaintenanceNotification(equipmentName: string, maintenanceType: string, estimatedDuration: string) {
|
||
|
return this.addNotification({
|
||
|
type: 'EQUIPMENT_MAINTENANCE',
|
||
|
title: '设备维护申请',
|
||
|
content: `设备 ${equipmentName} 申请${maintenanceType}维护,预计时长:${estimatedDuration}`,
|
||
|
targetUrl: '/asset-management/device-management/device-center',
|
||
|
priority: 'NORMAL',
|
||
|
category: '设备维护',
|
||
|
actionRequired: true,
|
||
|
source: 'EQUIPMENT_SYSTEM',
|
||
|
metadata: {
|
||
|
equipmentName,
|
||
|
maintenanceType,
|
||
|
estimatedDuration,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:添加工作流通知
|
||
|
public addWorkflowNotification(workflowName: string, currentNode: string, actionRequired: string) {
|
||
|
return this.addNotification({
|
||
|
type: 'WORKFLOW',
|
||
|
title: '工作流待处理',
|
||
|
content: `工作流"${workflowName}"当前节点:${currentNode},需要操作:${actionRequired}`,
|
||
|
targetUrl: '/asset-management/device-management/approval',
|
||
|
priority: 'HIGH',
|
||
|
category: '工作流',
|
||
|
actionRequired: true,
|
||
|
source: 'WORKFLOW_SYSTEM',
|
||
|
metadata: {
|
||
|
workflowName,
|
||
|
currentNode,
|
||
|
actionRequired,
|
||
|
timestamp: Date.now()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 新增:播放通知提示音
|
||
|
private playNotificationSound() {
|
||
|
try {
|
||
|
// 创建音频上下文
|
||
|
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
|
||
|
const oscillator = audioContext.createOscillator()
|
||
|
const gainNode = audioContext.createGain()
|
||
|
|
||
|
oscillator.connect(gainNode)
|
||
|
gainNode.connect(audioContext.destination)
|
||
|
|
||
|
oscillator.frequency.setValueAtTime(800, audioContext.currentTime)
|
||
|
oscillator.frequency.setValueAtTime(600, audioContext.currentTime + 0.1)
|
||
|
|
||
|
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime)
|
||
|
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3)
|
||
|
|
||
|
oscillator.start(audioContext.currentTime)
|
||
|
oscillator.stop(audioContext.currentTime + 0.3)
|
||
|
} catch (error) {
|
||
|
console.warn('无法播放通知提示音:', error)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 事件系统
|
||
|
private eventListeners: Map<string, Function[]> = new Map()
|
||
|
|
||
|
public on(event: string, callback: Function) {
|
||
|
if (!this.eventListeners.has(event)) {
|
||
|
this.eventListeners.set(event, [])
|
||
|
}
|
||
|
this.eventListeners.get(event)!.push(callback)
|
||
|
}
|
||
|
|
||
|
public off(event: string, callback: Function) {
|
||
|
const listeners = this.eventListeners.get(event)
|
||
|
if (listeners) {
|
||
|
const index = listeners.indexOf(callback)
|
||
|
if (index > -1) {
|
||
|
listeners.splice(index, 1)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private emitNotificationEvent(event: string, data: any) {
|
||
|
const listeners = this.eventListeners.get(event)
|
||
|
if (listeners) {
|
||
|
listeners.forEach(callback => {
|
||
|
try {
|
||
|
callback(data)
|
||
|
} catch (error) {
|
||
|
console.error('通知事件回调执行失败:', error)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 生成唯一ID
|
||
|
private generateId(): string {
|
||
|
return `notification_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||
|
}
|
||
|
|
||
|
// 持久化存储
|
||
|
public saveToStorage() {
|
||
|
try {
|
||
|
localStorage.setItem('notifications', JSON.stringify(this.notifications.value))
|
||
|
} catch (error) {
|
||
|
console.error('保存通知到本地存储失败:', error)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public loadFromStorage() {
|
||
|
try {
|
||
|
const stored = localStorage.getItem('notifications')
|
||
|
if (stored) {
|
||
|
this.notifications.value = JSON.parse(stored)
|
||
|
|
||
|
// 清理过期通知
|
||
|
this.clearExpired()
|
||
|
|
||
|
// 重新排序
|
||
|
this.sortByPriority()
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('从本地存储加载通知失败:', error)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 自动保存
|
||
|
public enableAutoSave() {
|
||
|
// 监听通知变化,自动保存
|
||
|
this.on('add', () => this.saveToStorage())
|
||
|
this.on('read', () => this.saveToStorage())
|
||
|
this.on('remove', () => this.saveToStorage())
|
||
|
this.on('clear', () => this.saveToStorage())
|
||
|
}
|
||
|
|
||
|
// 新增:定期清理过期通知
|
||
|
public enableAutoCleanup() {
|
||
|
// 每小时清理一次过期通知
|
||
|
setInterval(() => {
|
||
|
this.clearExpired()
|
||
|
}, 60 * 60 * 1000)
|
||
|
}
|
||
|
|
||
|
// 新增:导出通知数据
|
||
|
public exportNotifications() {
|
||
|
try {
|
||
|
const data = JSON.stringify(this.notifications.value, null, 2)
|
||
|
const blob = new Blob([data], { type: 'application/json' })
|
||
|
const url = URL.createObjectURL(blob)
|
||
|
const a = document.createElement('a')
|
||
|
a.href = url
|
||
|
a.download = `notifications_${new Date().toISOString().split('T')[0]}.json`
|
||
|
a.click()
|
||
|
URL.revokeObjectURL(url)
|
||
|
} catch (error) {
|
||
|
console.error('导出通知失败:', error)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 新增:导入通知数据
|
||
|
public importNotifications(data: string) {
|
||
|
try {
|
||
|
const notifications = JSON.parse(data)
|
||
|
if (Array.isArray(notifications)) {
|
||
|
this.notifications.value = [...this.notifications.value, ...notifications]
|
||
|
this.saveToStorage()
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
} catch (error) {
|
||
|
console.error('导入通知失败:', error)
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 创建单例实例
|
||
|
const notificationService = new NotificationService()
|
||
|
|
||
|
// 启用自动保存
|
||
|
notificationService.enableAutoSave()
|
||
|
|
||
|
// 启用自动清理
|
||
|
notificationService.enableAutoCleanup()
|
||
|
|
||
|
// 从本地存储加载通知
|
||
|
notificationService.loadFromStorage()
|
||
|
|
||
|
export default notificationService
|