| 
					
				 | 
			
			
				@@ -17,6 +17,7 @@ const conversationId = computed(() => route.query.taskId); // 会话ID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const round = computed(() => route.query.round); // 轮次 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const sessionId = ref(""); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const taskStatus = ref(false) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const loading = ref(true);  //初始化进入的加载状态 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const currentRate = ref(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const currentRound = computed(() => { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -175,6 +176,7 @@ const playNextAudio = async () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // 如果播放队列为空,停止播放 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   if (audioQueue.length === 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     isPlaying = false;  // 播放完所有音频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    audioQueue = []; // 确保列表为空 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     console.log("所有音频播放完毕."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -228,7 +230,6 @@ const playNextAudio = async () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // 获取下一个应该播放的音频(保证顺序) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const getNextAudioToPlay = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   // 检查音频队列中是否有下一个应该播放的音频 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -246,48 +247,82 @@ const getNextAudioToPlay = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // 播放音频的方法 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const audioContext = new (window.AudioContext || window.webkitAudioContext)(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const playAudio = (audioData) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   return new Promise((resolve, reject) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const audioContext = new (window.AudioContext || window.webkitAudioContext)(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const blob = new Blob([audioData], { type: "audio/wav" }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     const url = URL.createObjectURL(blob); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    const audioBufferSourceNode = audioContext.createBufferSource(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     fetch(url) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .then((response) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          if (!response.ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            console.error("音频文件请求失败: ", response.statusText); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            reject(new Error("音频文件请求失败")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          return response.arrayBuffer(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .then((audioBuffer) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          audioBufferSourceNode.buffer = audioBuffer; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          audioBufferSourceNode.connect(audioContext.destination); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          audioBufferSourceNode.start(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          // 当音频播放完毕时,调用 resolve 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          audioBufferSourceNode.onended = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            URL.revokeObjectURL(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            resolve();  // 播放完成后,继续播放下一个 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          audioBufferSourceNode.onerror = (error) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            console.error("音频播放错误:", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            URL.revokeObjectURL(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            reject(error);  // 播放失败时,返回错误 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .catch((error) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          console.error("音频加载或解码时出错:", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          reject(error);  // 如果解码或播放出错,reject 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .then((res) => res.arrayBuffer()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .then((audioBuffer) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const source = audioContext.createBufferSource(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source.buffer = audioBuffer; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source.connect(audioContext.destination); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source.start(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source.onended = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          URL.revokeObjectURL(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          resolve(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source.onerror = (error) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          console.error("音频播放错误:", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          URL.revokeObjectURL(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          reject(error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      .catch((error) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        console.error("音频加载或解码时出错:", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        reject(error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// const playAudio = (audioData) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//   return new Promise((resolve, reject) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//     const audioContext = new (window.AudioContext || window.webkitAudioContext)(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//     const blob = new Blob([audioData], { type: "audio/wav" }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//     const url = URL.createObjectURL(blob); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//     const audioBufferSourceNode = audioContext.createBufferSource(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//     fetch(url) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         .then((response) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           if (!response.ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             console.error("音频文件请求失败: ", response.statusText); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             reject(new Error("音频文件请求失败")); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           return response.arrayBuffer(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         .then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         .then((audioBuffer) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           audioBufferSourceNode.buffer = audioBuffer; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           audioBufferSourceNode.connect(audioContext.destination); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           audioBufferSourceNode.start(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           // 当音频播放完毕时,调用 resolve 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           audioBufferSourceNode.onended = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             URL.revokeObjectURL(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             resolve();  // 播放完成后,继续播放下一个 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           audioBufferSourceNode.onerror = (error) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             console.error("音频播放错误:", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             URL.revokeObjectURL(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//             reject(error);  // 播放失败时,返回错误 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         .catch((error) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           console.error("音频加载或解码时出错:", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//           reject(error);  // 如果解码或播放出错,reject 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//         }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//   }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // const playTTS1 = async (ttsMessage) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //   try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 //     const res = await axios.post( 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -491,9 +526,35 @@ const handleTaskStatus = async () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async function requestMicrophonePermission() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    hasPermission.value = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // console.log("🎤 录音权限已授予"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return stream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } catch (error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // console.warn("⛔ 录音权限被拒绝", error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    retryPermissionRequest(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+async function retryPermissionRequest() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const permissionStatus = await navigator.permissions.query({ name: "microphone" }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (permissionStatus.state === "denied") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Toast("您已拒绝录音权限,请前往浏览器或系统设置手动开启权限。"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    requestMicrophonePermission(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // 在组件挂载时调用 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 onMounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   loadChatHistory(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  requestMicrophonePermission(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  setTimeout(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    loading.value = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }, 2000); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 </script> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -511,6 +572,10 @@ onMounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     <BG /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <div class="loading" v-show="loading"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <van-loading color="#0094ff" size="26px" vertical>加载中...</van-loading> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     <ChatList :chatList="chatList" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     <BottomArea 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       @startRecord="handleStartRecord" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -554,4 +619,16 @@ onMounted(() => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   box-sizing: border-box; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   padding: 0 20px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.loading { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  position: fixed; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  top: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  left: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  width: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  height: 100%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  background-color: rgba(0, 0, 0, 0.5); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  justify-content: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  align-items: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  z-index: 9999; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 </style> 
			 |