工作台: 新订单提醒
This commit is contained in:
1 parent
d6743b94d3
commit
aef417409a
2 files changed
+121
-1
No files matched your search
@@ -109,6 +109,7 @@ import StoreOrderRemark from './StoreOrderRemark.vue'
|
||||
import OrderDetail from './OrderDetail.vue'
|
||||
import OrderRecord from './OrderRecord.vue'
|
||||
import type { TabsPaneContext } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
@@ -140,14 +141,115 @@ const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
}
|
||||
|
||||
|
||||
/** 新订单通知(可开关,由 infra 配置 order.work.notify.* 控制) */
|
||||
const NOTIFY_ENABLED_KEY = 'order.work.notify.enabled'
|
||||
const NOTIFY_SOUND_KEY = 'order.work.notify.sound'
|
||||
let notifyEnabled = true // 通知总开关,默认开
|
||||
let soundEnabled = true // 声音开关,默认开
|
||||
let lastMaxOrderId: number | null = null // 上次轮询的最大订单 id,用于判断新订单
|
||||
const newOrderCount = ref(0) // 未读新订单数(用于标题提示)
|
||||
const originalTitle = typeof document !== 'undefined' ? document.title : ''
|
||||
|
||||
/** 读取通知配置 */
|
||||
const loadNotifyConfig = async () => {
|
||||
try {
|
||||
const [enabled, sound] = await Promise.all([
|
||||
ConfigApi.getConfigKey(NOTIFY_ENABLED_KEY),
|
||||
ConfigApi.getConfigKey(NOTIFY_SOUND_KEY)
|
||||
])
|
||||
// 默认 true,仅显式 'false' 才关闭
|
||||
notifyEnabled = enabled !== 'false'
|
||||
soundEnabled = sound !== 'false'
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 更新浏览器标题显示未读数 */
|
||||
const updateTitle = () => {
|
||||
if (!document) return
|
||||
document.title = newOrderCount.value > 0
|
||||
? `(${newOrderCount.value}) 新订单 - ${originalTitle}`
|
||||
: originalTitle
|
||||
}
|
||||
|
||||
/** 播放提示音(Web Audio API 合成,无需音频文件) */
|
||||
let audioCtx: AudioContext | null = null
|
||||
const playBeep = () => {
|
||||
try {
|
||||
const Ctx = window.AudioContext || (window as any).webkitAudioContext
|
||||
if (!Ctx) return
|
||||
if (!audioCtx) audioCtx = new Ctx()
|
||||
// 连响两声,更易引起注意
|
||||
const beepAt = (startAt: number) => {
|
||||
const osc = audioCtx!.createOscillator()
|
||||
const gain = audioCtx!.createGain()
|
||||
osc.connect(gain)
|
||||
gain.connect(audioCtx!.destination)
|
||||
osc.type = 'sine'
|
||||
osc.frequency.value = 880
|
||||
gain.gain.setValueAtTime(0.3, audioCtx!.currentTime + startAt)
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx!.currentTime + startAt + 0.3)
|
||||
osc.start(audioCtx!.currentTime + startAt)
|
||||
osc.stop(audioCtx!.currentTime + startAt + 0.3)
|
||||
}
|
||||
beepAt(0)
|
||||
beepAt(0.35)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 系统通知(页面隐藏时使用,需已授权) */
|
||||
const notifySystem = (count: number) => {
|
||||
if (!('Notification' in window)) return
|
||||
if (Notification.permission !== 'granted') return
|
||||
new Notification('新订单提醒', { body: `收到 ${count} 个新订单,请及时处理` })
|
||||
}
|
||||
|
||||
/** 首次点击时请求桌面通知授权(浏览器要求用户手势,故挂在 pointerdown 上,一次性) */
|
||||
const requestNotifyPermissionOnce = () => {
|
||||
if (!('Notification' in window)) return
|
||||
if (Notification.permission === 'default') {
|
||||
Notification.requestPermission()
|
||||
}
|
||||
}
|
||||
|
||||
/** 触发新订单通知 */
|
||||
const notifyNewOrders = (count: number) => {
|
||||
newOrderCount.value += count
|
||||
updateTitle()
|
||||
if (!notifyEnabled) return
|
||||
if (soundEnabled) playBeep()
|
||||
if (document.hidden) {
|
||||
notifySystem(count)
|
||||
} else {
|
||||
ElNotification({
|
||||
title: '新订单提醒',
|
||||
message: `收到 ${count} 个新订单,请及时处理`,
|
||||
type: 'success',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 检测新订单:对比本次与上次的订单 id */
|
||||
const checkNewOrders = (orders: any[]) => {
|
||||
if (!orders || orders.length === 0) return
|
||||
const ids = orders.map((o) => Number(o.id))
|
||||
const currentMaxId = Math.max(...ids)
|
||||
if (lastMaxOrderId !== null && currentMaxId > lastMaxOrderId) {
|
||||
const newCount = ids.filter((id) => id > lastMaxOrderId!).length
|
||||
if (newCount > 0) notifyNewOrders(newCount)
|
||||
}
|
||||
lastMaxOrderId = currentMaxId
|
||||
}
|
||||
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await StoreOrderApi.getStoreOrderPage(queryParams)
|
||||
list.value = data.list
|
||||
//console.log("aa:",list.value)
|
||||
total.value = data.total
|
||||
checkNewOrders(data.list)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -260,6 +362,12 @@ const handleVisibilityChange = async () => {
|
||||
} else {
|
||||
getList()
|
||||
await loadPollInterval()
|
||||
await loadNotifyConfig()
|
||||
// 回到页面,清零未读新订单计数
|
||||
if (newOrderCount.value > 0) {
|
||||
newOrderCount.value = 0
|
||||
updateTitle()
|
||||
}
|
||||
startPolling()
|
||||
}
|
||||
}
|
||||
@@ -268,13 +376,22 @@ const handleVisibilityChange = async () => {
|
||||
onMounted(async () => {
|
||||
getList()
|
||||
await loadPollInterval()
|
||||
await loadNotifyConfig()
|
||||
startPolling()
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
// 首次点击请求桌面通知授权(浏览器要求用户手势)
|
||||
document.addEventListener('pointerdown', requestNotifyPermissionOnce, { once: true })
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopPolling()
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
document.removeEventListener('pointerdown', requestNotifyPermissionOnce)
|
||||
// 离开页面恢复标题
|
||||
if (newOrderCount.value > 0) {
|
||||
newOrderCount.value = 0
|
||||
document.title = originalTitle
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in new issue
Block a user