|
@@ -112,16 +112,11 @@
|
|
|
@keyup.enter="sendMessage"
|
|
@keyup.enter="sendMessage"
|
|
|
/>
|
|
/>
|
|
|
<!-- 语音输入按钮 -->
|
|
<!-- 语音输入按钮 -->
|
|
|
- <button
|
|
|
|
|
- @click="toggleSpeechInput"
|
|
|
|
|
- class="speech-btn"
|
|
|
|
|
- :class="{ 'recording': isRecording }"
|
|
|
|
|
- >
|
|
|
|
|
- <el-icon v-if="!isRecording"><Microphone /></el-icon>
|
|
|
|
|
- <el-icon v-else><Mute /></el-icon>
|
|
|
|
|
- <!-- 显示倒计时(仅录音时显示) -->
|
|
|
|
|
- <span v-if="isRecording" class="countdown-text">{{ countdown }}s</span>
|
|
|
|
|
- </button>
|
|
|
|
|
|
|
+ <VoiceInput
|
|
|
|
|
+ @voiceRecognized="handleVoiceRecognized"
|
|
|
|
|
+ lang="zh-CN"
|
|
|
|
|
+ maxDuration="10"
|
|
|
|
|
+ />
|
|
|
<!-- 终止按钮 -->
|
|
<!-- 终止按钮 -->
|
|
|
<div
|
|
<div
|
|
|
v-if="conversationInProgress"
|
|
v-if="conversationInProgress"
|
|
@@ -168,6 +163,7 @@ import { saveRecord } from '@/api/personalized/index.js'
|
|
|
import { globalState } from '@/utils/globalState.js'
|
|
import { globalState } from '@/utils/globalState.js'
|
|
|
// 语音图标
|
|
// 语音图标
|
|
|
import { Microphone, Mute } from "@element-plus/icons-vue";
|
|
import { Microphone, Mute } from "@element-plus/icons-vue";
|
|
|
|
|
+import VoiceInput from '../voice/VoiceInput.vue'
|
|
|
// 终止按钮
|
|
// 终止按钮
|
|
|
import stopicon from "@/assets/icon/stopicon.png";
|
|
import stopicon from "@/assets/icon/stopicon.png";
|
|
|
// 消息组件
|
|
// 消息组件
|
|
@@ -177,11 +173,7 @@ import {Message} from "@/utils/message/Message.js";
|
|
|
import { getModelIdByType } from '@/api/teachers.js'
|
|
import { getModelIdByType } from '@/api/teachers.js'
|
|
|
import { ModelTypeEnum } from '@/api/teachers.js'
|
|
import { ModelTypeEnum } from '@/api/teachers.js'
|
|
|
|
|
|
|
|
-// 语音输入响应式变量
|
|
|
|
|
-const isRecording = ref(false); // 录音状态
|
|
|
|
|
-const recognition = ref(null); // 语音识别实例
|
|
|
|
|
-const countdown = ref(0); // 倒计时剩余秒数
|
|
|
|
|
-const countdownTimer = ref(null); // 倒计时定
|
|
|
|
|
|
|
+
|
|
|
// 对话状态变量
|
|
// 对话状态变量
|
|
|
const conversationInProgress = ref(false); // 对话是否正在进行中
|
|
const conversationInProgress = ref(false); // 对话是否正在进行中
|
|
|
const conversationInAbortController = ref(); // 对话进行中 abort 控制器
|
|
const conversationInAbortController = ref(); // 对话进行中 abort 控制器
|
|
@@ -246,86 +238,12 @@ onMounted(async () => {
|
|
|
const messages = ref([])
|
|
const messages = ref([])
|
|
|
const inputMessage = ref('')
|
|
const inputMessage = ref('')
|
|
|
|
|
|
|
|
-// 初始化语音识别
|
|
|
|
|
-const initSpeechRecognition = () => {
|
|
|
|
|
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
|
|
|
- if (!SpeechRecognition) {
|
|
|
|
|
- alert("当前浏览器不支持语音输入功能");
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const instance = new SpeechRecognition();
|
|
|
|
|
- instance.lang = 'zh-CN';
|
|
|
|
|
- instance.interimResults = false;
|
|
|
|
|
|
|
|
|
|
- instance.onresult = (event) => {
|
|
|
|
|
- if (event.results?.[0]?.[0]) {
|
|
|
|
|
- inputMessage.value += event.results[0][0].transcript;
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 识别器结束时清除定时器
|
|
|
|
|
- instance.onend = () => {
|
|
|
|
|
- clearInterval(countdownTimer.value);
|
|
|
|
|
- isRecording.value = false;
|
|
|
|
|
- countdown.value = 0;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- instance.onerror = (event) => {
|
|
|
|
|
- console.error('语音识别错误:', event.error);
|
|
|
|
|
- clearInterval(countdownTimer.value); // 出错时清除定时器
|
|
|
|
|
- isRecording.value = false;
|
|
|
|
|
- Message().error('语音输入失败,请重试!', true)
|
|
|
|
|
- countdown.value = 0;
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- return instance;
|
|
|
|
|
-};
|
|
|
|
|
|
|
|
|
|
-// 切换录音状态
|
|
|
|
|
-const toggleSpeechInput = () => {
|
|
|
|
|
- // 清除可能存在的旧定时器
|
|
|
|
|
- clearInterval(countdownTimer.value);
|
|
|
|
|
- countdownTimer.value = null;
|
|
|
|
|
-
|
|
|
|
|
- if (isRecording.value) {
|
|
|
|
|
- // 手动停止时重置状态
|
|
|
|
|
- countdown.value = 0;
|
|
|
|
|
- recognition.value?.stop();
|
|
|
|
|
- isRecording.value = false;
|
|
|
|
|
- } else {
|
|
|
|
|
- // 初始化倒计时前再次清除定时器(防止快速点击)
|
|
|
|
|
- clearInterval(countdownTimer.value);
|
|
|
|
|
- countdown.value = 10; // 重置为10秒
|
|
|
|
|
-
|
|
|
|
|
- recognition.value = initSpeechRecognition();
|
|
|
|
|
- if (!recognition.value) return;
|
|
|
|
|
-
|
|
|
|
|
- navigator.mediaDevices.getUserMedia({ audio: true })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- recognition.value.start();
|
|
|
|
|
- isRecording.value = true;
|
|
|
|
|
-
|
|
|
|
|
- // 启动新的倒计时定时器
|
|
|
|
|
- countdownTimer.value = setInterval(() => {
|
|
|
|
|
- countdown.value--;
|
|
|
|
|
- if (countdown.value <= 0) {
|
|
|
|
|
- clearInterval(countdownTimer.value); // 倒计时结束清除
|
|
|
|
|
- recognition.value.stop();
|
|
|
|
|
- isRecording.value = false;
|
|
|
|
|
- countdown.value = 0;
|
|
|
|
|
- }
|
|
|
|
|
- }, 1000);
|
|
|
|
|
- })
|
|
|
|
|
- .catch((err) => {
|
|
|
|
|
- console.error("麦克风权限获取失败:", err);
|
|
|
|
|
- alert("请允许麦克风权限以使用语音输入");
|
|
|
|
|
- // 出错时重置状态
|
|
|
|
|
- isRecording.value = false;
|
|
|
|
|
- countdown.value = 0;
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
|
|
+// 处理语音识别结果
|
|
|
|
|
+const handleVoiceRecognized = (transcript) => {
|
|
|
|
|
+ inputMessage.value += transcript;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// 停止操作函数
|
|
// 停止操作函数
|
|
|
const stopStream = async () => {
|
|
const stopStream = async () => {
|