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 // 新增字段 source?: string // 消息来源 actionRequired?: boolean // 是否需要操作 expiresAt?: string // 过期时间 relatedId?: string // 关联的业务ID // 新增智能提醒字段 reminderTime?: string // 提醒时间 reminderType?: 'IMMEDIATE' | 'DELAYED' | 'RECURRING' // 提醒类型 reminderInterval?: number // 重复提醒间隔(分钟) lastReminderTime?: string // 上次提醒时间 } class NotificationService { private notifications = ref([]) // 计算属性 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) { 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 = {} 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 = 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