桌面通知(系统级消息提醒
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { ChatEvent } from './useChatSSE'
|
||||
|
||||
export type NotificationPermission = 'default' | 'granted' | 'denied'
|
||||
|
||||
const permission = ref<NotificationPermission>('default')
|
||||
const isSupported = 'Notification' in window
|
||||
|
||||
/**
|
||||
* 桌面通知 composable
|
||||
* 用于在收到新消息时发送系统级通知
|
||||
*/
|
||||
export function useDesktopNotification(scope: 'user' | 'admin') {
|
||||
const router = useRouter()
|
||||
|
||||
// 初始化权限状态
|
||||
if (isSupported) {
|
||||
permission.value = Notification.permission as NotificationPermission
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求通知权限
|
||||
*/
|
||||
async function requestPermission(): Promise<NotificationPermission> {
|
||||
if (!isSupported) return 'denied'
|
||||
|
||||
try {
|
||||
const result = await Notification.requestPermission()
|
||||
permission.value = result as NotificationPermission
|
||||
return permission.value
|
||||
} catch {
|
||||
permission.value = 'denied'
|
||||
return 'denied'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该发送通知
|
||||
*/
|
||||
function shouldNotify(conversationId: number, currentConversationId: number | null): boolean {
|
||||
// 不支持通知
|
||||
if (!isSupported || permission.value !== 'granted') return false
|
||||
|
||||
// 页面在前台且正在查看该会话 - 不需要通知
|
||||
if (!document.hidden && currentConversationId === conversationId) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送桌面通知
|
||||
*/
|
||||
function notify(event: ChatEvent, currentConversationId: number | null) {
|
||||
if (!event.message) return
|
||||
if (!shouldNotify(event.conversation_id, currentConversationId)) return
|
||||
|
||||
const { sender_name, sender_role, content, attachment_urls } = event.message
|
||||
|
||||
// 构建通知内容
|
||||
let body = content
|
||||
if (!body && attachment_urls.length > 0) {
|
||||
body = `[图片消息]`
|
||||
}
|
||||
if (!body) {
|
||||
body = '收到新消息'
|
||||
}
|
||||
|
||||
// 角色标签
|
||||
const roleMap: Record<string, string> = {
|
||||
renter: '租客',
|
||||
owner: '号主',
|
||||
support: '客服',
|
||||
customer: '咨询',
|
||||
admin: '管理员',
|
||||
system: '系统',
|
||||
}
|
||||
const roleLabel = roleMap[sender_role] || '成员'
|
||||
const title = `${roleLabel} · ${sender_name}`
|
||||
|
||||
try {
|
||||
const notification = new Notification(title, {
|
||||
body: body.length > 100 ? body.substring(0, 100) + '...' : body,
|
||||
icon: '/favicon.ico',
|
||||
badge: '/favicon.ico',
|
||||
tag: `chat-${event.conversation_id}`, // 相同会话的通知会被替换
|
||||
requireInteraction: false,
|
||||
silent: false,
|
||||
})
|
||||
|
||||
// 点击通知跳转到对应会话
|
||||
notification.onclick = () => {
|
||||
window.focus()
|
||||
const path = scope === 'admin'
|
||||
? `/admin/chats/${event.conversation_id}`
|
||||
: `/messages/${event.conversation_id}`
|
||||
router.push(path)
|
||||
notification.close()
|
||||
}
|
||||
|
||||
// 5秒后自动关闭
|
||||
setTimeout(() => {
|
||||
notification.close()
|
||||
}, 5000)
|
||||
} catch (error) {
|
||||
console.error('Failed to show notification:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静默请求权限(不强制,用户可以忽略)
|
||||
*/
|
||||
function requestPermissionSilently() {
|
||||
if (!isSupported || permission.value !== 'default') return
|
||||
|
||||
// 仅在用户交互后才请求(浏览器要求)
|
||||
const handleInteraction = () => {
|
||||
requestPermission()
|
||||
document.removeEventListener('click', handleInteraction)
|
||||
document.removeEventListener('keydown', handleInteraction)
|
||||
}
|
||||
|
||||
document.addEventListener('click', handleInteraction, { once: true })
|
||||
document.addEventListener('keydown', handleInteraction, { once: true })
|
||||
}
|
||||
|
||||
return {
|
||||
permission,
|
||||
isSupported,
|
||||
requestPermission,
|
||||
notify,
|
||||
requestPermissionSilently,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user