|
@@ -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>
|