front/src/composables/useOperationVoiceBroadcast.ts
zzh 6a7f8f1bcb feat: 用户扩展、Nana双Tab、仓库与客服入口优化
扩展用户管理字段与组织编码生成,顶部改为仓库下拉;Nana 分 AI 与专属顾问 Tab,仓库页移除客服字段,并完善扫描语音与订单相关页面。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 11:23:54 +08:00

67 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ref, watch } from 'vue';
function storageKey(pageKey: string) {
return `oms.operation.voiceBroadcast.${pageKey}`;
}
function pickChineseVoice(): SpeechSynthesisVoice | undefined {
const voices = window.speechSynthesis?.getVoices?.() ?? [];
return (
voices.find((v) => v.lang === 'zh-CN') ||
voices.find((v) => v.lang.startsWith('zh')) ||
voices[0]
);
}
/**
* 操作扫描页语音播报(Web Speech API)。
*/
export function useOperationVoiceBroadcast(pageKey: string) {
const voiceEnabled = ref(true);
try {
const stored = localStorage.getItem(storageKey(pageKey));
if (stored != null) voiceEnabled.value = stored === '1';
} catch {
/* ignore */
}
watch(voiceEnabled, (v) => {
try {
localStorage.setItem(storageKey(pageKey), v ? '1' : '0');
} catch {
/* ignore */
}
if (!v) {
window.speechSynthesis?.cancel?.();
}
});
if (typeof window !== 'undefined' && window.speechSynthesis) {
window.speechSynthesis.onvoiceschanged = () => {
pickChineseVoice();
};
}
function speak(text: string) {
const content = text?.trim();
if (!voiceEnabled.value || !content || typeof window === 'undefined') return;
if (!('speechSynthesis' in window)) return;
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(content);
utterance.lang = 'zh-CN';
utterance.rate = 1.05;
utterance.pitch = 1;
const voice = pickChineseVoice();
if (voice) utterance.voice = voice;
window.speechSynthesis.speak(utterance);
}
function stopSpeak() {
window.speechSynthesis?.cancel?.();
}
return { voiceEnabled, speak, stopSpeak };
}