feat: 集成语音识别
This commit is contained in:
@@ -74,6 +74,15 @@
|
||||
<!-- 录音按钮 -->
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<yao-asdRealSpeech
|
||||
ref="appSpeechRef"
|
||||
:options="appSpeechOptions"
|
||||
@result="handleAppSpeechResult"
|
||||
@change="handleAppSpeechChange"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
|
||||
<view class="color-99A0AE font-size-9 text-center text-gray-400">
|
||||
内容由AI大模型生成,请仔细鉴别
|
||||
</view>
|
||||
@@ -84,15 +93,25 @@
|
||||
import { ref, computed, watch, nextTick, onMounted, defineExpose, onUnmounted } from "vue";
|
||||
import RecordingWaveBtn from "@/components/Speech/RecordingWaveBtn.vue";
|
||||
import { getCurrentConfig } from "@/constant/base";
|
||||
import { appSpeechRecognitionOptions } from "@/constant/speech";
|
||||
|
||||
let manager = null;
|
||||
let speechProvider = "";
|
||||
const isSpeechRecognitionSupported = ref(false);
|
||||
const appSpeechOptions = appSpeechRecognitionOptions;
|
||||
|
||||
// WechatSI 是微信小程序插件,App 原生基座没有 requirePlugin。
|
||||
// #ifdef MP-WEIXIN
|
||||
const plugin = requirePlugin("WechatSI");
|
||||
manager = plugin.getRecordRecognitionManager();
|
||||
isSpeechRecognitionSupported.value = !!manager;
|
||||
speechProvider = manager ? "wechat" : "";
|
||||
// #endif
|
||||
|
||||
// App 端使用 yao-asdRealSpeech;apikey 请在 src/constant/speech.js 中配置。
|
||||
// #ifdef APP-PLUS
|
||||
isSpeechRecognitionSupported.value = true;
|
||||
speechProvider = "app";
|
||||
// #endif
|
||||
|
||||
const props = defineProps({
|
||||
@@ -112,6 +131,7 @@ const emit = defineEmits([
|
||||
|
||||
const textareaRef = ref(null);
|
||||
const recordingWaveBtnRef = ref(null);
|
||||
const appSpeechRef = ref(null);
|
||||
const placeholder = computed(() => {
|
||||
const config = getCurrentConfig();
|
||||
return `快告诉${config.name}您在想什么~`;
|
||||
@@ -122,7 +142,10 @@ const keyboardHeight = ref(0);
|
||||
const isVoiceMode = ref(false);
|
||||
const visibleWaveBtn = ref(false);
|
||||
const isRecording = ref(false);
|
||||
const appRecognizedText = ref("");
|
||||
let watchDogTimer = null;
|
||||
let appStopFallbackTimer = null;
|
||||
let hasSentAppRecognition = false;
|
||||
|
||||
const resetUI = () => {
|
||||
isRecording.value = false;
|
||||
@@ -148,6 +171,13 @@ const startWatchDog = (timeout = 10000) => {
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
const clearAppStopFallback = () => {
|
||||
if (appStopFallbackTimer) {
|
||||
clearTimeout(appStopFallbackTimer);
|
||||
appStopFallbackTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 保持和父组件同步
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
@@ -172,9 +202,38 @@ const toggleVoiceMode = () => {
|
||||
|
||||
// 处理语音按钮长按开始
|
||||
const handleVoiceTouchStart = () => {
|
||||
if (!manager) return;
|
||||
if (!isSpeechRecognitionSupported.value) return;
|
||||
|
||||
try {
|
||||
manager.start({ lang: "zh_CN" });
|
||||
if (speechProvider === "wechat") {
|
||||
if (!manager) return;
|
||||
manager.start({ lang: "zh_CN" });
|
||||
} else if (speechProvider === "app") {
|
||||
if (!appSpeechOptions.apikey) {
|
||||
uni.showToast({
|
||||
title: "请先配置语音识别API Key",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
appRecognizedText.value = "";
|
||||
hasSentAppRecognition = false;
|
||||
|
||||
const appSpeech = appSpeechRef.value;
|
||||
if (!appSpeech || typeof appSpeech.start !== "function") {
|
||||
uni.showToast({
|
||||
title: "语音组件未初始化",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
appSpeech.start();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
isRecording.value = true;
|
||||
visibleWaveBtn.value = true;
|
||||
|
||||
@@ -194,7 +253,7 @@ const handleVoiceTouchStart = () => {
|
||||
|
||||
// 处理语音按钮长按结束
|
||||
const handleVoiceTouchEnd = () => {
|
||||
if (!manager) {
|
||||
if (!isSpeechRecognitionSupported.value) {
|
||||
resetUI();
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +272,16 @@ const handleVoiceTouchEnd = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
manager.stop();
|
||||
if (speechProvider === "wechat") {
|
||||
manager?.stop?.();
|
||||
} else if (speechProvider === "app") {
|
||||
appSpeechRef.value?.stop?.();
|
||||
clearAppStopFallback();
|
||||
appStopFallbackTimer = setTimeout(() => {
|
||||
appStopFallbackTimer = null;
|
||||
sendAppRecognizedText();
|
||||
}, 600);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("record stop error:", err);
|
||||
} finally {
|
||||
@@ -255,6 +323,60 @@ const initRecord = () => {
|
||||
};
|
||||
};
|
||||
|
||||
const getAppSpeechText = (res) => {
|
||||
return (
|
||||
res?.sentence?.text ||
|
||||
res?.text ||
|
||||
res?.result ||
|
||||
res?.payload?.output?.sentence?.text ||
|
||||
""
|
||||
);
|
||||
};
|
||||
|
||||
const sendAppRecognizedText = () => {
|
||||
if (hasSentAppRecognition) return;
|
||||
|
||||
const text = appRecognizedText.value.trim();
|
||||
if (!text) {
|
||||
console.log("没有说话");
|
||||
return;
|
||||
}
|
||||
|
||||
hasSentAppRecognition = true;
|
||||
clearAppStopFallback();
|
||||
inputMessage.value = text;
|
||||
emit("send", text);
|
||||
appRecognizedText.value = "";
|
||||
};
|
||||
|
||||
const handleAppSpeechResult = (res) => {
|
||||
const text = getAppSpeechText(res);
|
||||
if (!text) return;
|
||||
|
||||
appRecognizedText.value = text;
|
||||
inputMessage.value = text;
|
||||
};
|
||||
|
||||
const handleAppSpeechChange = (msg) => {
|
||||
if (!msg || !msg.status) return;
|
||||
|
||||
if (msg.status === "START") {
|
||||
isRecording.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.status === "STOP") {
|
||||
resetUI();
|
||||
sendAppRecognizedText();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.status === "ERROR") {
|
||||
console.error("app speech recognition error", msg.msg);
|
||||
resetUI();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听键盘高度变化
|
||||
onMounted(() => {
|
||||
// 监听键盘弹起
|
||||
@@ -281,6 +403,14 @@ onUnmounted(() => {
|
||||
manager.onStop = null;
|
||||
manager.onError = null;
|
||||
}
|
||||
if (appSpeechRef.value) {
|
||||
try {
|
||||
appSpeechRef.value.stop && appSpeechRef.value.stop();
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
clearAppStopFallback();
|
||||
resetUI();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user