Explorar el Código

1、数字人语音录入功能(10秒倒计时)

liyanbo hace 8 meses
padre
commit
0d5a961670
Se han modificado 1 ficheros con 133 adiciones y 6 borrados
  1. 133 6
      src/views/AIQuestions.vue

+ 133 - 6
src/views/AIQuestions.vue

@@ -65,11 +65,22 @@
             <!-- 输入框和发送按钮 -->
             <div class="input-section">
               <input
-                type="text"
-                v-model="prompt"
-                placeholder="问我任何问题..."
-                @keyup.enter="handleSendByKeydown"
+                  type="text"
+                  v-model="prompt"
+                  placeholder="问我任何问题..."
+                  @keyup.enter="handleSendByKeydown"
               />
+              <!-- 添加语音输入按钮 -->
+              <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>
               <button @click="handleSendByButton">发送</button>
             </div>
           </div>
@@ -111,10 +122,20 @@ import DefaultMessage from "@/components/DefaultMessage/index.vue";
 // import painting from '@/assets/icon/painting.png'
 // import human from '@/assets/icon/human.png'
 
+// 语音图标
+import { Microphone, Mute } from "@element-plus/icons-vue";
+
+
 import LeftPanel from "@/components/LeftPanel.vue";
 const leftPanelRef = ref(null);
 
 
+// 语音输入响应式变量
+const isRecording = ref(false); // 录音状态
+const recognition = ref(null); // 语音识别实例
+const countdown = ref(0); // 倒计时剩余秒数
+const countdownTimer = ref(null); // 倒计时定时器
+
 // 默认消息控制
 const showDefaultMessages = ref(true);
 const handleDefaultMessageSelect = (message) => {
@@ -174,7 +195,7 @@ const textRoleRunning = ref(false); // Typing speed in milliseconds
 const isComposing = ref(false); // 判断用户是否在输入
 const conversationInAbortController = ref(); // 对话进行中 abort 控制器(控制 stream 对话)
 const inputTimeout = ref(); // 处理输入中回车的定时器
-const prompt = ref(); // prompt
+const prompt = ref(''); // prompt
 const enableContext = ref(true); // 是否开启上下文
 // 接收 Stream 消息
 const receiveMessageFullText = ref("");
@@ -199,7 +220,89 @@ const getConversation = async (id) => {
   activeConversationModelPath.value = personImage.value;
 };
 
-// =========== 【发送消息】相关 ===========
+// =========== 【语音录入】相关 ===========
+// 初始化语音识别
+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]) {
+      prompt.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;
+    alert('语音输入失败,请重试');
+    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;
+        });
+  }
+};
+
+// =========== 【聊天对话】相关 ===========
 
 /** 处理来自 keydown 的发送消息 */
 const handleSendByKeydown = async (event) => {
@@ -725,6 +828,30 @@ onMounted(async () => {
   display: flex;
   padding: rpx(10);
   gap: rpx(10);
+
+  .speech-btn {
+    padding: rpx(5) rpx(10);
+    background: #fff;
+    border: 1px solid #ffce1b;
+    border-radius: rpx(5);
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+
+    &.recording {
+      background: #ffeeba;
+      border-color: #ffc107;
+
+      .el-icon {
+        color: #dc3545;
+      }
+    }
+
+    .el-icon {
+      font-size: rpx(8);
+      color: #666;
+    }
+  }
 }
 .input-section input {
   flex: 1;