From 3552f2388622ae3fbefda1b3cdaf8881e1ee5e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E6=9D=B0?= Date: Wed, 2 Jul 2025 19:03:58 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=BA=E5=AE=9D?= =?UTF-8?q?=E5=A5=87=E5=85=B5=E5=A4=A7=E6=A8=A1=E5=9E=8B=E8=AF=AD=E9=9F=B3?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E6=88=90=E5=90=8C=E6=AD=A5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/AIchat.vue | 255 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 203 insertions(+), 52 deletions(-) diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue index aa03422..8fda2cc 100644 --- a/src/views/AIchat.vue +++ b/src/views/AIchat.vue @@ -612,28 +612,142 @@ watch( four: { completed: false, result: null, error: null }, }; + // 音频预加载状态跟踪 + const audioPreloadStatus = { + one: { loaded: false, url: null }, + two: { loaded: false, url: null }, + three: { loaded: false, url: null }, + four: { loaded: false, url: null } + }; + + // 音频播放队列管理 + const audioQueue = ref([]); + const isPlayingAudio = ref(false); + + // 播放音频队列 + const playNextAudio = () => { + if (audioQueue.value.length === 0 || isPlayingAudio.value) { + return; + } + + isPlayingAudio.value = true; + const audioInfo = audioQueue.value.shift(); + + console.log(`开始播放${audioInfo.name}音频`); + + if (audioStore.nowSound) { + audioStore.nowSound.stop(); + } + + const audio = new Howl({ + src: [audioInfo.url], + html5: true, + format: ["mp3", "acc"], + rate: 1.2, + onplay: () => { + audioStore.isPlaying = true; + audioStore.isPaused = false; + console.log(`${audioInfo.name}音频开始播放`); + }, + onend: () => { + audioStore.isPlaying = false; + audioStore.isPaused = false; + isPlayingAudio.value = false; + console.log(`${audioInfo.name}音频播放完成`); + // 播放下一个音频 + setTimeout(() => { + playNextAudio(); + }, 100); + }, + onloaderror: (id, err) => { + console.error(`${audioInfo.name}音频播放失败:`, err); + isPlayingAudio.value = false; + // 播放下一个音频 + setTimeout(() => { + playNextAudio(); + }, 100); + } + }); + + audioStore.nowSound = audio; + audioStore.setAudioInstance(audio); + audio.play(); + }; + + // 添加音频到播放队列 + const addToAudioQueue = (url, name) => { + if (url && audioStore.isVoiceEnabled) { + audioQueue.value.push({ url, name }); + console.log(`音频${name}已添加到播放队列`); + // 如果当前没有播放音频,立即开始播放 + if (!isPlayingAudio.value) { + playNextAudio(); + } + } + }; + + // 预加载音频函数 + const preloadAudio = (url, apiKey) => { + if (!url || !audioStore.isVoiceEnabled) { + audioPreloadStatus[apiKey].loaded = true; + return Promise.resolve(); + } + + return new Promise((resolve) => { + const audio = new Howl({ + src: [url], + html5: true, + format: ["mp3", "acc"], + rate: 1.2, + preload: true, + onload: () => { + console.log(`音频${apiKey}预加载完成:`, url); + audioPreloadStatus[apiKey].loaded = true; + audioPreloadStatus[apiKey].url = url; + resolve(); + }, + onloaderror: (id, err) => { + console.error(`音频${apiKey}预加载失败:`, err); + audioPreloadStatus[apiKey].loaded = true; // 标记为已处理,避免阻塞 + resolve(); + } + }); + }); + }; + + // 检查第一个接口是否可以开始输出(文本和音频都准备好) + const canStartFirstOutput = () => { + return apiStatus.one.completed && audioPreloadStatus.one.loaded; + }; + // 检查并按顺序执行代码的函数 const checkAndExecuteInOrder = () => { - // 检查OneAPI - if (apiStatus.one.completed && !apiStatus.one.executed) { - apiStatus.one.executed = true; - if (apiStatus.one.result) { - console.log("执行OneAPI代码:", apiStatus.one.result); - // 在这里添加OneAPI成功后需要执行的代码 - // 删除正在为您生成信息 - chatStore.messages.pop(); - // 添加报告头和时间 - addTypingTask( - { - sender: "ai", - class: "title1", - type: "title1", - content: codeData.value.name + "全景作战报告", - date: moment().format("MM/DD/YYYY"), - }, - "", - 50 - ); + // 检查OneAPI - 只有当文本和音频都准备好时才开始输出 + if (canStartFirstOutput() && !apiStatus.one.executed) { + apiStatus.one.executed = true; + if (apiStatus.one.result) { + console.log("执行OneAPI代码(文本和音频同步开始):", apiStatus.one.result); + + // 将第一个音频添加到播放队列 + if (audioPreloadStatus.one.url) { + addToAudioQueue(audioPreloadStatus.one.url, "第一个"); + } + + // 在这里添加OneAPI成功后需要执行的代码 + // 删除正在为您生成信息 + chatStore.messages.pop(); + // 添加报告头和时间 + addTypingTask( + { + sender: "ai", + class: "title1", + type: "title1", + content: codeData.value.name + "全景作战报告", + date: moment().format("MM/DD/YYYY"), + }, + "", + 50 + ); // chatStore.messages.push({ // sender: "ai", // class: "title1", @@ -858,6 +972,12 @@ watch( apiStatus.two.executed = true; if (apiStatus.two.result) { console.log("执行TwoAPI代码:", apiStatus.two.result); + + // 将第二个音频添加到播放队列 + if (audioPreloadStatus.two.url) { + addToAudioQueue(audioPreloadStatus.two.url, "第二个"); + } + // 在这里添加TwoAPI成功后需要执行的代码 // 添加标题2 addTypingTask( @@ -930,6 +1050,12 @@ watch( apiStatus.three.executed = true; if (apiStatus.three.result) { console.log("执行ThreeAPI代码:", apiStatus.three.result); + + // 将第三个音频添加到播放队列 + if (audioPreloadStatus.three.url) { + addToAudioQueue(audioPreloadStatus.three.url, "第三个"); + } + // 在这里添加ThreeAPI成功后需要执行的代码 // 添加标题3-2 addTypingTask( @@ -1073,6 +1199,12 @@ watch( apiStatus.four.executed = true; if (apiStatus.four.result) { console.log("执行FourAPI代码:", apiStatus.four.result); + + // 将第四个音频添加到播放队列 + if (audioPreloadStatus.four.url) { + addToAudioQueue(audioPreloadStatus.four.url, "第四个"); + } + // 在这里添加FourAPI成功后需要执行的代码 // 添加标题3-4 addTypingTask( @@ -1177,47 +1309,34 @@ watch( apiStatus.four.completed && apiStatus.four.executed ) { - console.log("所有API已完成,开始收集音频URL"); - // 收集所有音频URL + console.log("所有API已完成,开始收集预加载的音频URL"); + // 收集所有预加载的音频URL const audioUrls = []; - console.log("API返回结果检查:"); - console.log("result21:", result21); - console.log("result22:", result22); - console.log("result23:", result23); - console.log("result24:", result24); + console.log("预加载音频状态检查:"); + console.log("audioPreloadStatus:", audioPreloadStatus); - if (result21?.data?.url) { - console.log("添加result21音频URL:", result21.data.url); - audioUrls.push(result21.data.url.trim()); + if (audioPreloadStatus.one.url) { + console.log("添加预加载音频URL one:", audioPreloadStatus.one.url); + audioUrls.push(audioPreloadStatus.one.url); } - if (result22?.data?.url) { - console.log("添加result22音频URL:", result22.data.url); - audioUrls.push(result22.data.url.trim()); + if (audioPreloadStatus.two.url) { + console.log("添加预加载音频URL two:", audioPreloadStatus.two.url); + audioUrls.push(audioPreloadStatus.two.url); } - if (result23?.data?.url) { - console.log("添加result23音频URL:", result23.data.url); - audioUrls.push(result23.data.url.trim()); + if (audioPreloadStatus.three.url) { + console.log("添加预加载音频URL three:", audioPreloadStatus.three.url); + audioUrls.push(audioPreloadStatus.three.url); } - if (result24?.data?.url) { - console.log("添加result24音频URL:", result24.data.url); - audioUrls.push(result24.data.url.trim()); + if (audioPreloadStatus.four.url) { + console.log("添加预加载音频URL four:", audioPreloadStatus.four.url); + audioUrls.push(audioPreloadStatus.four.url); } - console.log("收集到的音频URLs:", audioUrls); + console.log("收集到的预加载音频URLs:", audioUrls); console.log("语音是否启用:", audioStore.isVoiceEnabled); - // 开始轮流播放音频 - if (audioUrls.length > 0 && audioStore.isVoiceEnabled) { - console.log("开始播放音频序列"); - playAudioSequence(audioUrls); - } else { - console.log( - "跳过音频播放 - audioUrls长度:", - audioUrls.length, - "语音启用状态:", - audioStore.isVoiceEnabled - ); - } + // 音频播放逻辑已移至各个接口的执行代码中 + console.log("所有接口执行完成,音频已在各接口中单独播放"); } }; @@ -1229,12 +1348,20 @@ watch( apiStatus.one.completed = true; apiStatus.one.result = result21; + // 预加载第一个接口的音频 + if (result21?.data?.url) { + await preloadAudio(result21.data.url.trim(), 'one'); + } else { + audioPreloadStatus.one.loaded = true; + } + // 检查是否可以执行 checkAndExecuteInOrder(); } catch (error) { console.error("OneAPI失败:", error); apiStatus.one.completed = true; apiStatus.one.error = error; + audioPreloadStatus.one.loaded = true; // 失败时也标记为已处理 // 即使失败也要检查后续执行 checkAndExecuteInOrder(); } @@ -1248,12 +1375,20 @@ watch( apiStatus.two.completed = true; apiStatus.two.result = result22; + // 预加载第二个接口的音频 + if (result22?.data?.url) { + await preloadAudio(result22.data.url.trim(), 'two'); + } else { + audioPreloadStatus.two.loaded = true; + } + // 检查是否可以执行 checkAndExecuteInOrder(); } catch (error) { console.error("TwoAPI失败:", error); apiStatus.two.completed = true; apiStatus.two.error = error; + audioPreloadStatus.two.loaded = true; checkAndExecuteInOrder(); } }; @@ -1266,12 +1401,20 @@ watch( apiStatus.three.completed = true; apiStatus.three.result = result23; + // 预加载第三个接口的音频 + if (result23?.data?.url) { + await preloadAudio(result23.data.url.trim(), 'three'); + } else { + audioPreloadStatus.three.loaded = true; + } + // 检查是否可以执行 checkAndExecuteInOrder(); } catch (error) { console.error("ThreeAPI失败:", error); apiStatus.three.completed = true; apiStatus.three.error = error; + audioPreloadStatus.three.loaded = true; checkAndExecuteInOrder(); } }; @@ -1284,12 +1427,20 @@ watch( apiStatus.four.completed = true; apiStatus.four.result = result24; + // 预加载第四个接口的音频 + if (result24?.data?.url) { + await preloadAudio(result24.data.url.trim(), 'four'); + } else { + audioPreloadStatus.four.loaded = true; + } + // 检查是否可以执行 checkAndExecuteInOrder(); } catch (error) { console.error("FourAPI失败:", error); apiStatus.four.completed = true; apiStatus.four.error = error; + audioPreloadStatus.four.loaded = true; checkAndExecuteInOrder(); } }; From aa74c209a63514aafdce9d5985ce2fbc90acfeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E6=9D=B0?= Date: Wed, 2 Jul 2025 19:36:03 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E7=A7=BB=E5=88=B0=E4=B8=8A=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/AiEmotion.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/views/AiEmotion.vue b/src/views/AiEmotion.vue index 23f53da..bec32dd 100644 --- a/src/views/AiEmotion.vue +++ b/src/views/AiEmotion.vue @@ -20,10 +20,6 @@ - - - -
@@ -31,6 +27,10 @@
AI小财神正在加载图表数据和音频内容,请稍候...
+ + + +
From 0dc8c67cf5a7f6f408a82155a7e0c27df5f3e59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E6=9D=B0?= Date: Thu, 3 Jul 2025 13:30:39 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=A4=BA=E5=AE=9D=E5=A5=87=E5=85=B5?= =?UTF-8?q?=E8=AF=AD=E9=9F=B3=E9=98=9F=E5=88=97=E6=92=AD=E6=94=BE=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/AIchat.vue | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue index 3127864..f5a6bbf 100644 --- a/src/views/AIchat.vue +++ b/src/views/AIchat.vue @@ -657,9 +657,21 @@ watch( audioStore.isPaused = false; console.log(`${audioInfo.name}音频开始播放`); }, + onpause: () => { + audioStore.isPlaying = false; + audioStore.isPaused = true; + audioStore.playbackPosition = audio.seek() || 0; + console.log(`${audioInfo.name}音频已暂停,位置:`, audioStore.playbackPosition); + }, + onresume: () => { + audioStore.isPlaying = true; + audioStore.isPaused = false; + console.log(`${audioInfo.name}音频继续播放`); + }, onend: () => { audioStore.isPlaying = false; audioStore.isPaused = false; + audioStore.playbackPosition = 0; isPlayingAudio.value = false; console.log(`${audioInfo.name}音频播放完成`); // 播放下一个音频 @@ -667,6 +679,13 @@ watch( playNextAudio(); }, 100); }, + onstop: () => { + audioStore.isPlaying = false; + audioStore.isPaused = false; + audioStore.playbackPosition = 0; + isPlayingAudio.value = false; + console.log(`${audioInfo.name}音频已停止`); + }, onloaderror: (id, err) => { console.error(`${audioInfo.name}音频播放失败:`, err); isPlayingAudio.value = false; @@ -677,6 +696,8 @@ watch( } }); + // 设置当前音频URL到store + audioStore.setCurrentAudioUrl(audioInfo.url); audioStore.nowSound = audio; audioStore.setAudioInstance(audio); audio.play(); @@ -694,6 +715,37 @@ watch( } }; + // 重写audioStore的togglePlayPause方法以支持队列播放控制 + const originalTogglePlayPause = audioStore.togglePlayPause; + audioStore.togglePlayPause = () => { + console.log('主页音频控制按钮被点击'); + console.log('当前音频状态 - isPlaying:', audioStore.isPlaying, 'isPaused:', audioStore.isPaused); + console.log('当前音频实例:', audioStore.soundInstance); + + if (audioStore.soundInstance) { + if (audioStore.isPlaying) { + // 暂停当前音频 + console.log('暂停当前音频'); + audioStore.soundInstance.pause(); + } else if (audioStore.isPaused && audioStore.playbackPosition > 0) { + // 从暂停位置继续播放 + console.log('从暂停位置继续播放音频,位置:', audioStore.playbackPosition); + audioStore.soundInstance.seek(audioStore.playbackPosition); + audioStore.soundInstance.play(); + } else { + // 重新开始播放 + console.log('重新开始播放音频'); + audioStore.soundInstance.play(); + } + } else { + console.log('没有音频实例,尝试播放队列中的音频'); + // 没有音频实例时,尝试播放队列中的音频 + if (!isPlayingAudio.value && audioQueue.value.length > 0) { + playNextAudio(); + } + } + }; + // 预加载音频函数 const preloadAudio = (url, apiKey) => { if (!url || !audioStore.isVoiceEnabled) { From 682dc4e1ca543f929429d6d7f09796898d2a34d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E6=9D=B0?= Date: Thu, 3 Jul 2025 16:54:05 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=89=93=E5=AD=97?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/AIchat.vue | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue index f5a6bbf..ca4aa47 100644 --- a/src/views/AIchat.vue +++ b/src/views/AIchat.vue @@ -156,6 +156,9 @@ const playAudioSequence = (audioUrls) => { audioStore.isPlaying = false; audioStore.isPaused = false; audioStore.playbackPosition = 0; + // 清除音频实例,确保下次点击从头开始 + audioStore.soundInstance = null; + audioStore.nowSound = null; if (audioSequence.length > 0) { audioStore.setCurrentAudioUrl(audioSequence[0]); } @@ -230,22 +233,33 @@ const playAudioSequence = (audioUrls) => { // 重写togglePlayPause方法以支持音频序列控制 const originalTogglePlayPause = audioStore.togglePlayPause; audioStore.togglePlayPause = () => { + console.log('音频控制按钮被点击'); + console.log('当前播放状态:', audioStore.isPlaying); + console.log('当前暂停状态:', audioStore.isPaused); + console.log('当前音频实例:', audioStore.soundInstance); + console.log('当前索引:', currentIndex, '音频序列长度:', audioSequence.length); + if (audioStore.soundInstance) { if (audioStore.isPlaying) { // 暂停当前音频 + console.log('暂停当前音频'); audioStore.pause(); } else if (audioStore.isPaused) { // 从暂停位置继续播放 + console.log('从暂停位置继续播放'); audioStore.play(); } else { // 重新开始播放当前音频或从头开始播放序列 + console.log('重新开始播放,当前索引:', currentIndex); if (currentIndex >= audioSequence.length) { + console.log('所有音频已播放完成,从头开始'); currentIndex = 0; // 重置到第一个音频 } playNext(); } } else { // 没有音频实例时,从头开始播放 + console.log('没有音频实例,从头开始播放'); currentIndex = 0; playNext(); } @@ -467,7 +481,7 @@ const processTypingQueue = async () => { }; // 添加打字机任务到队列 -const addTypingTask = (message, content, speed = 50) => { +const addTypingTask = (message, content, speed) => { typingQueue.value.push({ message, content, speed }); processTypingQueue(); }; @@ -856,7 +870,7 @@ watch( // } // }, 50); // 调整速度为50ms/字符 - addTypingTask(aiMessage1, ["", ac1], 50); + addTypingTask(aiMessage1, ["", ac1], 130); // chatStore.messages.push({ // sender: "ai", @@ -1090,7 +1104,7 @@ watch( // aiMessage2.isTyping = false; // } // }, 50); // 调整速度为50ms/字符 - addTypingTask(aiMessage2, ["", ac2], 50); + addTypingTask(aiMessage2, ["", ac2], 130); // chatStore.messages.push({ // sender: "ai", @@ -1172,7 +1186,7 @@ watch( // } // }, 50); // 调整速度为50ms/字符 - addTypingTask(aiMessage3, [ac31, ac32], 50); + addTypingTask(aiMessage3, [ac31, ac32], 180); // chatStore.messages.push({ // sender: "ai", @@ -1253,7 +1267,7 @@ watch( addTypingTask( aiMessage4, [ac41, ac42, ac43, ac44, ac45, ac46, ac47, ac48], - 50 + 180 ); // chatStore.messages.push({ @@ -1337,7 +1351,7 @@ watch( // } // }, 50); // 调整速度为50ms/字符 - addTypingTask(aiMessage5, [ac51, ac52, ac53, ac54], 50); + addTypingTask(aiMessage5, [ac51, ac52, ac53, ac54], 180); // chatStore.messages.push({ // sender: "ai", @@ -1368,7 +1382,7 @@ watch( // } // }, 50); // 调整速度为50ms/字符 - addTypingTask(aiMessage6, ["", ac6], 100); + addTypingTask(aiMessage6, ["", ac6], 180); // chatStore.messages.push({ // sender: "ai",