扩展用户管理字段与组织编码生成,顶部改为仓库下拉;Nana 分 AI 与专属顾问 Tab,仓库页移除客服字段,并完善扫描语音与订单相关页面。 Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
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 };
|
||
}
|