feat(shop): 实现新订单声音和语音提醒功能
- 新增 useOrderNotify 组合式函数,支持每60秒轮询检测新订单 - 采用 Web Audio API 播放叮声,Speech Synthesis API 进行语音播报 - shopOrder 页面添加提醒开关栏,包含开关、提示和测试按钮 - 开关默认关闭,需用户点击启用后启动声音和轮询功能 - 测试按钮仅在提醒开启时可用,用于验证提醒效果 - 完善样式,提升提醒栏的视觉效果和交互体验 - 统一更新相关环境变量,调整项目名称和接口地址
This commit is contained in:
@@ -48,15 +48,23 @@ let audioCtx: AudioContext | null = null;
|
||||
/** 是否已初始化(首次查询记录基准值,不触发提醒) */
|
||||
let initialized = false;
|
||||
|
||||
/** 用户交互解锁音频的监听器(一次性) */
|
||||
let unlockHandler: (() => void) | null = null;
|
||||
|
||||
// ============ 声音播放 ============
|
||||
|
||||
/**
|
||||
* 初始化 AudioContext(必须在用户交互后调用)
|
||||
* 初始化 AudioContext(必须在用户交互后调用才能生效)
|
||||
*/
|
||||
function initAudioContext() {
|
||||
if (!audioCtx) {
|
||||
audioCtx = new (window.AudioContext ||
|
||||
(window as any).webkitAudioContext)();
|
||||
try {
|
||||
audioCtx = new (window.AudioContext ||
|
||||
(window as any).webkitAudioContext)();
|
||||
} catch (e) {
|
||||
console.warn('[订单提醒] AudioContext 创建失败:', e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 如果上下文被暂停(浏览器策略),尝试恢复
|
||||
if (audioCtx.state === 'suspended') {
|
||||
@@ -64,6 +72,27 @@ function initAudioContext() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册用户交互监听器,首次交互时解锁音频
|
||||
* 浏览器策略:音频播放需要用户交互后才允许,注册一次性监听器自动解锁
|
||||
*/
|
||||
function registerAudioUnlock() {
|
||||
if (unlockHandler) return;
|
||||
|
||||
unlockHandler = () => {
|
||||
initAudioContext();
|
||||
// 解锁后移除监听器
|
||||
if (unlockHandler) {
|
||||
document.removeEventListener('click', unlockHandler);
|
||||
document.removeEventListener('keydown', unlockHandler);
|
||||
unlockHandler = null;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', unlockHandler, { once: true });
|
||||
document.addEventListener('keydown', unlockHandler, { once: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放"叮"声 - 使用 Web Audio API 生成,无需音频文件
|
||||
*/
|
||||
@@ -117,6 +146,8 @@ function speak(text: string) {
|
||||
* 触发提醒:叮声 + 语音播报
|
||||
*/
|
||||
function triggerNotify() {
|
||||
// 尝试初始化音频上下文(如果已解锁则生效,未解锁则静默失败)
|
||||
initAudioContext();
|
||||
playDingSound();
|
||||
speak(SPEECH_TEXT);
|
||||
}
|
||||
@@ -205,9 +236,23 @@ function stopPolling() {
|
||||
* ```
|
||||
*/
|
||||
export function useOrderNotify() {
|
||||
// 组件挂载时自动启动(默认开启)
|
||||
onMounted(() => {
|
||||
// 注册用户交互解锁音频监听器
|
||||
registerAudioUnlock();
|
||||
// 自动开始轮询
|
||||
startPolling();
|
||||
});
|
||||
|
||||
// 组件卸载时自动清理
|
||||
onBeforeUnmount(() => {
|
||||
stopPolling();
|
||||
// 清理交互监听器
|
||||
if (unlockHandler) {
|
||||
document.removeEventListener('click', unlockHandler);
|
||||
document.removeEventListener('keydown', unlockHandler);
|
||||
unlockHandler = null;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user