From 0392ed3ede3da8eaa4272f1ef69fb05a8921ac31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E6=9D=B0?= Date: Tue, 12 Aug 2025 16:23:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=89=93=E5=8C=85=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E9=85=8D=E7=BD=AE=EF=BC=9B=E6=83=85=E7=BB=AA=E5=A4=A7?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=AF=B9=E6=8E=A5=E5=8F=A3=E5=AE=8C=E6=88=90?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 4 +- .env.production | 2 +- build/vite/plugin/compress.js | 5 + package.json | 3 +- src/api/AiEmotionApi.js | 68 +++-- src/views/AiEmotion.vue | 514 +++++++++++++++++++++++++++------ src/views/components/HistoryRecord.vue | 4 +- 7 files changed, 484 insertions(+), 116 deletions(-) diff --git a/.env.development b/.env.development index d360df7..11ae2c5 100644 --- a/.env.development +++ b/.env.development @@ -5,8 +5,8 @@ VITE_OUTPUT_DIR = 'dev' VITE_PUBLIC_PATH = / #新数据接口 -# VITE_APP_API_BASE_URL = "http://39.101.133.168:8828/link" -VITE_APP_API_BASE_URL = "https://api.homilychart.com/link" +VITE_APP_API_BASE_URL = "http://39.101.133.168:8828/link" +# VITE_APP_API_BASE_URL = "https://api.homilychart.com/link" # VITE_APP_API_BASE_CAZE_URL = "http://39.101.133.168:8828/link" VITE_APP_API_BASE_CAZE_URL = "https://api.homilychart.com/link" diff --git a/.env.production b/.env.production index 126d0a1..230ac6d 100644 --- a/.env.production +++ b/.env.production @@ -1,6 +1,6 @@ # must start with VITE_ VITE_ENV = 'production' -VITE_OUTPUT_DIR = 'dist' +VITE_OUTPUT_DIR = 'prod' # public path VITE_PUBLIC_PATH = /aixiaocaishen # VITE_PUBLIC_PATH = / diff --git a/build/vite/plugin/compress.js b/build/vite/plugin/compress.js index 87f7e8c..76f0e34 100644 --- a/build/vite/plugin/compress.js +++ b/build/vite/plugin/compress.js @@ -8,6 +8,11 @@ export function configCompressPlugin( compress, deleteOriginFile = false ) { + // 如果compress为undefined或空字符串,返回空数组 + if (!compress || compress === 'none') { + return [] + } + const compressList = compress.split(',') const plugins = [] diff --git a/package.json b/package.json index 14651ed..442d34d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "test": "vite --mode test", "prod": "vite --mode production", "build": "vite build", - "build:test": "vite build --mode test", + "build:test": "vite build --mode development", + "build:prod": "vite build --mode production", "typecheck": "vue-tsc --noEmit", "preview": "npm run build && vite preview --port 5050", "preview:dist": "vite preview", diff --git a/src/api/AiEmotionApi.js b/src/api/AiEmotionApi.js index 6d640d0..acd89c3 100644 --- a/src/api/AiEmotionApi.js +++ b/src/api/AiEmotionApi.js @@ -5,33 +5,57 @@ const MJAPIurl = import.meta.env.VITE_APP_MJ_API_BASE_URL; // 工作流获取用户意图接口 +// export const getReplyAPI = function (params) { +// return fetch("https://api.coze.cn/v1/workflow/run", { +// method: "POST", +// body: JSON.stringify({ +// workflow_id: "7512677411467001910", +// parameters: params, +// }), +// headers: { +// "Content-Type": "application/json", +// Authorization: +// "Bearer pat_D6vYu7FNXfkSJn8GpmA9oC4DY8LkcbhzrSZuJwuTHT0aYn2uqZ6P5cBfRD0kwZYR", +// }, +// }); +// }; export const getReplyAPI = function (params) { - return fetch("https://api.coze.cn/v1/workflow/run", { - method: "POST", - body: JSON.stringify({ - workflow_id: "7512677411467001910", - parameters: params, - }), - headers: { - "Content-Type": "application/json", - Authorization: - "Bearer pat_D6vYu7FNXfkSJn8GpmA9oC4DY8LkcbhzrSZuJwuTHT0aYn2uqZ6P5cBfRD0kwZYR", + return request({ + url: `${APIurl}/api/workflow/aiGodFirst`, + method: "post", + data: { + "token": localStorage.getItem("localToken"), + "language": "cn", + "marketList": "hk,cn,usa,my,sg,vi,in,gb", + "content": params.content }, }); }; - // 获取第二个工作流给出的结论 +// export const getConclusionAPI = function (params) { +// return fetch("https://api.coze.cn/v1/workflow/run", { +// method: "POST", +// body: JSON.stringify({ +// workflow_id: "7509384582975897650", +// parameters: params, +// }), +// headers: { +// "Content-Type": "application/json", +// Authorization: +// "Bearer pat_D6vYu7FNXfkSJn8GpmA9oC4DY8LkcbhzrSZuJwuTHT0aYn2uqZ6P5cBfRD0kwZYR", +// }, +// }); +// }; export const getConclusionAPI = function (params) { - return fetch("https://api.coze.cn/v1/workflow/run", { - method: "POST", - body: JSON.stringify({ - workflow_id: "7509384582975897650", - parameters: params, - }), - headers: { - "Content-Type": "application/json", - Authorization: - "Bearer pat_D6vYu7FNXfkSJn8GpmA9oC4DY8LkcbhzrSZuJwuTHT0aYn2uqZ6P5cBfRD0kwZYR", + return request({ + url: `${APIurl}/api/workflow/aiGodSecond`, + method: "post", + data: { + "language": "cn", + "parentId": params.parentId, + "recordId": params.recordId, + "stockId": params.stockId, + "token": localStorage.getItem("localToken") }, }); -}; +}; \ No newline at end of file diff --git a/src/views/AiEmotion.vue b/src/views/AiEmotion.vue index 733be89..367574c 100644 --- a/src/views/AiEmotion.vue +++ b/src/views/AiEmotion.vue @@ -24,6 +24,7 @@ />
{{ message.text }} +
@@ -152,26 +153,27 @@ 场景应用标题
-
-

L1: 情绪监控

-

{{ getStockConclusion(stock).one1 }}

-

{{ getStockConclusion(stock).one2 }}

+ +
+

{{ displayedTitles.one }}

+

{{ displayedTexts.one1 }}

+

{{ displayedTexts.one2 }}

-
-

L2: 情绪解码

-

{{ getStockConclusion(stock).two }}

+
+

{{ displayedTitles.two }}

+

{{ displayedTexts.two }}

-
-

L3: 情绪推演

-

{{ getStockConclusion(stock).three }}

+
+

{{ displayedTitles.three }}

+

{{ displayedTexts.three }}

-
-

L4: 情绪套利

-

{{ getStockConclusion(stock).four }}

+
+

{{ displayedTitles.four }}

+

{{ displayedTexts.four }}

-
-

该内容由AI生成,请注意甄别

+
+

{{ displayedTexts.disclaimer }}

@@ -212,6 +214,7 @@ import { Howl, Howler } from 'howler'; // 导入音频播放库 import { reactive } from 'vue'; import { marked } from 'marked'; // 引入marked库 import { useUserStore } from "../store/userPessionCode"; +const APIurl = import.meta.env.VITE_APP_API_BASE_URL; // 使用Pinia store const emotionStore = useEmotionStore(); @@ -224,12 +227,26 @@ const toggleVoiceForUser = () => { emotionAudioStore.toggleVoice(); } else { // 如果语音功能开启,则切换播放/暂停状态 - if (emotionAudioStore.currentAudioUrl || emotionAudioStore.ttsUrl) { - // 有音频时切换播放/暂停 + if (emotionAudioStore.isPlaying) { + // 如果正在播放,则暂停 emotionAudioStore.togglePlayPause(); } else { - // 没有音频时关闭语音功能 - emotionAudioStore.toggleVoice(); + // 如果没有在播放,先检查是否处于暂停状态 + if (emotionAudioStore.isPaused && (emotionAudioStore.currentAudioUrl || emotionAudioStore.ttsUrl)) { + // 如果处于暂停状态且有音频,则继续播放 + console.log('从暂停状态继续播放'); + emotionAudioStore.togglePlayPause(); + } else if (parsedConclusion.value && (parsedConclusion.value.one1_url || parsedConclusion.value.two_url || parsedConclusion.value.three_url || parsedConclusion.value.four_url)) { + // 有结论数据时,重新播放整个音频队列 + console.log('用户点击播放,重新播放音频队列'); + playAudioQueue(parsedConclusion.value, false); // 不启动打字机效果,因为内容已经显示 + } else if (emotionAudioStore.currentAudioUrl || emotionAudioStore.ttsUrl) { + // 有单个音频URL时切换播放/暂停 + emotionAudioStore.togglePlayPause(); + } else { + // 没有音频时关闭语音功能 + emotionAudioStore.toggleVoice(); + } } } }; @@ -444,6 +461,13 @@ const currentConclusion = computed(() => { }); const parsedConclusion = computed(() => { if (!currentConclusion.value) return null; + + // 如果conclusionData已经是对象,直接返回 + if (typeof currentConclusion.value === 'object') { + return currentConclusion.value; + } + + // 如果是字符串,尝试解析JSON try { return JSON.parse(currentConclusion.value); } catch (error) { @@ -488,6 +512,13 @@ const getStockData2 = (stock) => { // 辅助函数:获取股票的结论数据 const getStockConclusion = (stock) => { if (!stock?.conclusionData) return null; + + // 如果conclusionData已经是对象,直接返回 + if (typeof stock.conclusionData === 'object') { + return stock.conclusionData; + } + + // 如果是字符串,尝试解析JSON try { return JSON.parse(stock.conclusionData); } catch (error) { @@ -578,7 +609,10 @@ watch(currentStock, (newStock) => { // 如果已经显示过,直接显示完整文本和标题 if (newStock.conclusionData) { try { - const conclusion = JSON.parse(newStock.conclusionData); + // 如果conclusionData已经是对象,直接使用;否则解析JSON + const conclusion = typeof newStock.conclusionData === 'object' + ? newStock.conclusionData + : JSON.parse(newStock.conclusionData); displayedTexts.value = { one1: conclusion.one1 || '', one2: conclusion.one2 || '', @@ -604,7 +638,18 @@ watch(currentStock, (newStock) => { // 提取音频URL但不自动播放,等待用户手动点击 let voiceUrl = null; - if (conclusion.url) { + // 优先使用one1_url,如果没有则尝试其他音频URL + if (conclusion.one1_url) { + voiceUrl = conclusion.one1_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.one2_url) { + voiceUrl = conclusion.one2_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.two_url) { + voiceUrl = conclusion.two_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.three_url) { + voiceUrl = conclusion.three_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.four_url) { + voiceUrl = conclusion.four_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.url) { voiceUrl = conclusion.url.toString().trim().replace(/[`\s]/g, ''); } else if (conclusion.audioUrl) { voiceUrl = conclusion.audioUrl.toString().trim().replace(/[`\s]/g, ''); @@ -624,7 +669,7 @@ watch(currentStock, (newStock) => { // 不自动播放,等待用户手动点击 } } catch (error) { - console.error('解析结论数据失败:', error); + console.error('解析股票结论数据失败:', error); } } } else { @@ -654,9 +699,23 @@ watch(currentStock, (newStock) => { // 即使没有显示过,也需要设置音频URL以便用户手动播放 if (newStock.conclusionData) { try { - const conclusion = JSON.parse(newStock.conclusionData); + // 如果conclusionData已经是对象,直接使用;否则解析JSON + const conclusion = typeof newStock.conclusionData === 'object' + ? newStock.conclusionData + : JSON.parse(newStock.conclusionData); let voiceUrl = null; - if (conclusion.url) { + // 优先使用one1_url,如果没有则尝试其他音频URL + if (conclusion.one1_url) { + voiceUrl = conclusion.one1_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.one2_url) { + voiceUrl = conclusion.one2_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.two_url) { + voiceUrl = conclusion.two_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.three_url) { + voiceUrl = conclusion.three_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.four_url) { + voiceUrl = conclusion.four_url.toString().trim().replace(/[`\s]/g, ''); + } else if (conclusion.url) { voiceUrl = conclusion.url.toString().trim().replace(/[`\s]/g, ''); } else if (conclusion.audioUrl) { voiceUrl = conclusion.audioUrl.toString().trim().replace(/[`\s]/g, ''); @@ -675,7 +734,7 @@ watch(currentStock, (newStock) => { emotionAudioStore.setCurrentAudioUrl(voiceUrl); } } catch (error) { - console.error('解析结论数据失败:', error); + console.error('解析股票结论数据失败:', error); } } } @@ -710,12 +769,13 @@ watch(currentStock, (newStock) => { hasTriggeredTypewriter.value = true; hasTriggeredAudio.value = true; - startTypewriterEffect(parsedConclusion.value); - if (!stockAudioPlayed.value.has(stockCode)) { - console.log('开始音频播放'); + console.log('开始音频播放和打字机效果'); stockAudioPlayed.value.set(stockCode, true); - playAudio(audioUrl.value); + playAudioQueue(parsedConclusion.value, true); + } else { + // 如果音频已播放过,只启动打字机效果 + startTypewriterEffect(parsedConclusion.value); } stockTypewriterShown.value.set(stockCode, true); @@ -777,9 +837,19 @@ watch(parsedConclusion, (newConclusion) => { console.log('场景应用结论数据:', newConclusion); // 不再立即开始打字机效果,等待滚动到场景应用部分时触发 - // 尝试多种可能的语音URL字段名 + // 尝试多种可能的语音URL字段名,优先使用新的数据结构 let voiceUrl = null; - if (newConclusion.url) { + if (newConclusion.one1_url) { + voiceUrl = newConclusion.one1_url.toString().trim().replace(/[`\s]/g, ''); + } else if (newConclusion.one2_url) { + voiceUrl = newConclusion.one2_url.toString().trim().replace(/[`\s]/g, ''); + } else if (newConclusion.two_url) { + voiceUrl = newConclusion.two_url.toString().trim().replace(/[`\s]/g, ''); + } else if (newConclusion.three_url) { + voiceUrl = newConclusion.three_url.toString().trim().replace(/[`\s]/g, ''); + } else if (newConclusion.four_url) { + voiceUrl = newConclusion.four_url.toString().trim().replace(/[`\s]/g, ''); + } else if (newConclusion.url) { // 清理URL字符串,去除空格、反引号等特殊字符 voiceUrl = newConclusion.url.toString().trim().replace(/[`\s]/g, ''); } else if (newConclusion.audioUrl) { @@ -966,7 +1036,258 @@ function clearTypewriterTimers() { typewriterTimers.value = []; } -// 音频播放函数 +// 音频队列管理 +const audioQueue = ref([]); +const isPlayingQueueAudio = ref(false); +let currentPlayIndex = 0; +let isCallingPlayNext = false; + +// 音频队列顺序管理 +const audioQueueOrder = { + "one1_url": 1, + "one2_url": 2, + "two_url": 3, + "three_url": 4, + "four_url": 5, + "url": 6, + "audioUrl": 7, + "voice_url": 8, + "audio": 9, + "tts_url": 10 +}; + +// 播放音频队列中的下一个音频 +const playNextAudio = () => { + console.log("=== playNextAudio 被调用 ==="); + console.log("当前队列状态:", { + queueLength: audioQueue.value.length, + queueItems: audioQueue.value.map((item) => item.name), + currentPlayIndex: currentPlayIndex, + isPlayingQueueAudio: isPlayingQueueAudio.value, + isCallingPlayNext: isCallingPlayNext, + audioStoreIsPlaying: emotionAudioStore.isPlaying, + }); + + if ( + audioQueue.value.length === 0 || + isPlayingQueueAudio.value || + isCallingPlayNext + ) { + console.log("❌ 播放条件不满足 - 队列长度:", audioQueue.value.length, "正在播放:", isPlayingQueueAudio.value, "正在调用:", isCallingPlayNext); + return; + } + + // 检查是否已播放完所有音频 + if (currentPlayIndex >= audioQueue.value.length && audioQueue.value.length > 0) { + console.log("🔄 所有音频播放完成,重置索引从第一个开始"); + currentPlayIndex = 0; + isCallingPlayNext = false; // 重置调用标志 + } + + isCallingPlayNext = true; + isPlayingQueueAudio.value = true; + const audioInfo = audioQueue.value[currentPlayIndex]; + + console.log(`✅ 开始播放${audioInfo.name}音频 (索引:${currentPlayIndex}),队列总长度:`, audioQueue.value.length); + + // 停止之前的音频 + if (emotionAudioStore.nowSound && emotionAudioStore.nowSound.playing()) { + emotionAudioStore.nowSound.stop(); + } + + // 创建新的音频实例 + const audio = new Howl({ + src: [audioInfo.url], + html5: true, + format: ['mp3', 'wav'], + onplay: () => { + isAudioPlaying.value = true; + isPlayingQueueAudio.value = true; + emotionAudioStore.isPlaying = true; + console.log(`开始播放${audioInfo.name}音频`); + + // 如果是第一个音频且需要启动打字机效果,则启动 + if (currentPlayIndex === 0 && audioInfo.shouldStartTypewriter && parsedConclusion.value) { + console.log('🎬 第一个音频开始播放,同时启动打字机效果'); + startTypewriterEffect(parsedConclusion.value, audioInfo.onComplete); + } + }, + onpause: () => { + emotionAudioStore.isPaused = true; + console.log(`${audioInfo.name}音频暂停播放`); + }, + onresume: () => { + emotionAudioStore.isPaused = false; + console.log(`${audioInfo.name}音频继续播放`); + }, + onend: () => { + console.log(`${audioInfo.name}音频播放完成,准备播放下一个`); + emotionAudioStore.isPlaying = false; + emotionAudioStore.isPaused = false; + emotionAudioStore.playbackPosition = 0; + isAudioPlaying.value = false; + isPlayingQueueAudio.value = false; + + // 移动到下一个音频索引 + currentPlayIndex++; + + // 确保只有在音频真正播放完成时才播放下一个 + if (currentPlayIndex < audioQueue.value.length) { + console.log(`队列中还有音频,500ms后播放下一个 (索引:${currentPlayIndex})`); + setTimeout(() => { + isCallingPlayNext = false; + playNextAudio(); + }, 500); + } else { + console.log("🎉 所有音频播放完成"); + emotionAudioStore.nowSound = null; + isCallingPlayNext = false; + } + }, + onstop: () => { + console.log(`${audioInfo.name}音频被停止`); + emotionAudioStore.isPlaying = false; + emotionAudioStore.isPaused = false; + emotionAudioStore.playbackPosition = 0; + isAudioPlaying.value = false; + isPlayingQueueAudio.value = false; + }, + onerror: (error) => { + console.error(`${audioInfo.name}音频播放失败:`, error); + isAudioPlaying.value = false; + isPlayingQueueAudio.value = false; + isCallingPlayNext = false; + // 播放下一个音频 + setTimeout(() => { + playNextAudio(); + }, 100); + }, + onload: () => { + emotionAudioStore.duration = audio.duration(); + console.log(`${audioInfo.name}音频加载完成,时长:`, emotionAudioStore.duration); + } + }); + + // 设置当前音频URL到store + emotionAudioStore.setCurrentAudioUrl(audioInfo.url); + emotionAudioStore.nowSound = audio; + emotionAudioStore.setAudioInstance(audio); + + console.log(`尝试播放${audioInfo.name}音频`); + audio.play(); +}; + +// 添加音频到播放队列 +const addToAudioQueue = (url, name, shouldStartTypewriter = false, onComplete = null) => { + console.log(`=== 添加音频到队列 ===`); + console.log("URL:", url); + console.log("Name:", name); + console.log("是否启动打字机效果:", shouldStartTypewriter); + console.log("音频启用状态:", emotionAudioStore.isVoiceEnabled); + + if (url && emotionAudioStore.isVoiceEnabled) { + const audioItem = { + url, + name, + order: audioQueueOrder[name] || 999, + shouldStartTypewriter, // 添加打字机效果标志 + onComplete, // 添加完成回调 + }; + audioQueue.value.push(audioItem); + + // 按顺序排序队列 + audioQueue.value.sort((a, b) => a.order - b.order); + + console.log(`音频${name}已添加到播放队列,顺序:${audioItem.order}`); + console.log("当前队列顺序:", audioQueue.value.map((item) => `${item.name}(${item.order})`)); + + // 只有在确实没有音频在播放且这是第一个音频时才开始播放 + if (!isPlayingQueueAudio.value && !emotionAudioStore.isPlaying && audioQueue.value.length === 1) { + console.log("✅ 条件满足:没有音频在播放且这是第一个音频,立即开始播放"); + playNextAudio(); + } else { + console.log("⏳ 等待条件:", { + isPlayingQueueAudio: isPlayingQueueAudio.value, + audioStoreIsPlaying: emotionAudioStore.isPlaying, + queueLength: audioQueue.value.length, + reason: audioQueue.value.length > 1 ? "队列中已有其他音频" : "有音频正在播放", + }); + } + } else { + console.log("❌ 跳过添加音频:", { + hasUrl: !!url, + voiceEnabled: emotionAudioStore.isVoiceEnabled, + }); + } +}; + +// 修改后的音频播放函数 - 支持多音频播放和同步打字机效果 +function playAudioQueue(conclusionData, shouldStartTypewriter = false, onComplete = null) { + if (!conclusionData) { + console.log('没有结论数据,跳过播放'); + return; + } + + // 检查是否启用了语音功能 + console.log('语音功能状态:', emotionAudioStore.isVoiceEnabled); + if (!emotionAudioStore.isVoiceEnabled) { + console.log('语音功能已关闭,跳过播放'); + return; + } + + console.log('开始处理多音频播放...', shouldStartTypewriter ? '同时启动打字机效果' : ''); + + try { + // 解析结论数据 + const conclusion = typeof conclusionData === 'object' ? conclusionData : JSON.parse(conclusionData); + + // 清空之前的音频队列 + audioQueue.value = []; + currentPlayIndex = 0; + isCallingPlayNext = false; + isPlayingQueueAudio.value = false; + + // 按优先级顺序检查并添加所有可用的音频URL + const audioSources = [ + { key: 'one1_url', name: 'one1_url' }, + { key: 'one2_url', name: 'one2_url' }, + { key: 'two_url', name: 'two_url' }, + { key: 'three_url', name: 'three_url' }, + { key: 'four_url', name: 'four_url' }, + { key: 'url', name: 'url' }, + { key: 'audioUrl', name: 'audioUrl' }, + { key: 'voice_url', name: 'voice_url' }, + { key: 'audio', name: 'audio' }, + { key: 'tts_url', name: 'tts_url' } + ]; + + audioSources.forEach(source => { + if (conclusion[source.key]) { + const voiceUrl = conclusion[source.key].toString().trim().replace(/[`\s]/g, ''); + if (voiceUrl && voiceUrl.startsWith('http')) { + console.log(`找到音频URL: ${source.name} = ${voiceUrl}`); + addToAudioQueue(voiceUrl, source.name, shouldStartTypewriter && audioQueue.value.length === 0, onComplete); + } + } + }); + + if (audioQueue.value.length === 0) { + console.log('未找到有效的音频URL'); + // 如果没有音频但需要启动打字机效果,直接启动 + if (shouldStartTypewriter) { + console.log('没有音频但需要启动打字机效果'); + startTypewriterEffect(conclusion, onComplete); + } + } else { + console.log(`总共找到 ${audioQueue.value.length} 个音频,准备播放`); + } + + } catch (error) { + console.error('处理音频播放失败:', error); + } +} + +// 原有的单音频播放函数(保持兼容性) function playAudio(url) { console.log('尝试播放音频:', url); @@ -1217,37 +1538,35 @@ async function handleSendMessage(input, onComplete) { // 开始思考过程(不带股票名称) const thinkingMessageRef = await showThinkingProcess(); + let thinkingMessage3Ref = null; try { // 第一步:调用第一个接口验证用户输入内容是否合法 - const params = { - content: userMessage.text, - userData: { - token: localStorage.getItem('localToken'), - language: "cn", - // brainPrivilegeState: userStore.brainPerssion, - // swordPrivilegeState: userStore.swordPerssion, - // stockForecastPrivilegeState: userStore.pricePerssion, - // spaceForecastPrivilegeState: userStore.timePerssion, - // aibullPrivilegeState: userStore.aibullPerssion, - // aigoldBullPrivilegeState: userStore.aiGnbullPerssion, - // airadarPrivilegeState: userStore.airadarPerssion, - // marketList: userStore.aiGoldMarketList, - brainPrivilegeState: '1', - swordPrivilegeState: '1', - stockForecastPrivilegeState: '1', - spaceForecastPrivilegeState: '1', - aibullPrivilegeState: '1', - aigoldBullPrivilegeState: '1', - airadarPrivilegeState: '1', - marketList: "hk,cn,usa,my,sg,vi,in,gb", - }, - }; - - const result = await getReplyAPI(params); - const response = await result.json(); - const parsedData = JSON.parse(response.data); - + // const params = { + // content: userMessage.text, + // userData: { + // token: localStorage.getItem('localToken'), + // language: "cn", + // brainPrivilegeState: '1', + // swordPrivilegeState: '1', + // stockForecastPrivilegeState: '1', + // spaceForecastPrivilegeState: '1', + // aibullPrivilegeState: '1', + // aigoldBullPrivilegeState: '1', + // airadarPrivilegeState: '1', + // marketList: "hk,cn,usa,my,sg,vi,in,gb", + // }, + // }; + + const result = await getReplyAPI({ + "token": localStorage.getItem("localToken"), + "language": "cn", + "marketList": "hk,cn,usa,my,sg,vi,in,gb", + "content": userMessage.text + }); + const response = result; + const parsedData = response.data; + console.log('第一个接口返回的完整数据:', parsedData); // 检查用户输入是否合法 if (!parsedData || !parsedData.market || !parsedData.code) { // 输入不合法,先清理思考过程消息 @@ -1283,11 +1602,9 @@ async function handleSendMessage(input, onComplete) { // 输入合法,继续执行后续处理 // 获取到股票名称后,继续思考过程 - let thinkingMessage3Ref = null; if (thinkingMessageRef && parsedData.name) { thinkingMessage3Ref = await continueThinkingProcess(thinkingMessageRef, parsedData.name); } - // 设置加载状态,隐藏图表页面 // isLoading.value = true; @@ -1295,24 +1612,22 @@ async function handleSendMessage(input, onComplete) { // 调用第二个工作流接口 const conclusionParams = { - content: input.trim(), - userData: { - token: localStorage.getItem('localToken'), - language: "cn", - marketList: "hk,cn,usa,my,sg,vi,in,gb", - }, - code: parsedData.code, - market: parsedData.market, + recordId: parsedData.recordId, + parentId: parsedData.parentId, + stockId: parsedData.stockId, + token: localStorage.getItem('localToken'), + language: "cn", }; + + console.log('第二个接口参数:', conclusionParams); // 同时调用第二个数据流接口和fetchData方法 const [conclusionResult, fetchDataResult] = await Promise.all([ getConclusionAPI(conclusionParams), - fetchData(parsedData.code, parsedData.market, parsedData.name || "未知股票", input.trim()) + fetchData(parsedData.code, parsedData.market, parsedData.name || "未知股票", input.trim(), parsedData.stockId) ]); // 处理结论接口返回的数据 - const conclusionResponse = await conclusionResult.json(); - + const conclusionResponse = conclusionResult; // 检查所有数据是否都加载成功 if (conclusionResponse && conclusionResponse.data && fetchDataResult) { // 第二个工作流接口成功,完成思考过程 @@ -1322,6 +1637,7 @@ async function handleSendMessage(input, onComplete) { // 将结论数据存储到响应式变量和store中 conclusionData.value = conclusionResponse.data; + console.log('第二个接口返回的完整数据结构:', conclusionResponse.data); // 将结论数据存储到store中的当前激活股票 emotionStore.updateActiveStockConclusion(conclusionResponse.data); @@ -1347,11 +1663,12 @@ async function handleSendMessage(input, onComplete) { if (isUserInitiated.value && parsedConclusion.value && audioUrl.value) { const stockCode = currentStock.value.stockInfo?.code || currentStock.value.stockInfo?.symbol; if (stockCode && !stockTypewriterShown.value.has(stockCode)) { - startTypewriterEffect(parsedConclusion.value, onComplete); - if (!stockAudioPlayed.value.has(stockCode)) { stockAudioPlayed.value.set(stockCode, true); - playAudio(audioUrl.value); + playAudioQueue(parsedConclusion.value, true, onComplete); + } else { + // 如果音频已播放过,只启动打字机效果 + startTypewriterEffect(parsedConclusion.value, onComplete); } stockTypewriterShown.value.set(stockCode, true); } else { @@ -1382,7 +1699,7 @@ async function handleSendMessage(input, onComplete) { // 如果 fetchDataResult 为 false,说明数据不完整的错误信息已经在 fetchData 中添加到 messages // 只有在 conclusionResponse 有问题时才添加通用错误信息 if (!conclusionResponse || !conclusionResponse.data) { - const aiMessage = reactive({ sender: 'ai', text: '数据加载失败,请重试' }); + const aiMessage = reactive({ sender: 'ai', text: '网络加载失败,请重试' }); messages.value.push(aiMessage); } isRotating.value = false; @@ -1467,23 +1784,17 @@ async function handleSendMessage(input, onComplete) { // 请求数据接口 -async function fetchData(code, market, stockName, queryText) { +async function fetchData(code, market, stockName, queryText, stockId) { try { const stockDataParams = { - // token: '+XgqsgdW0RLIbIG2pxnnbZi0+fEeMx8pywnIlrmTxtkSaPZ9xjSOWrxq+s0rL3RrfNhXPvGtz9srFfjwu8A', - token: localStorage.getItem('localToken'), - market: market, - code: code, - language: 'cn', - version: version1.value + "stockId": stockId }; - const stockDataResult = await axios.post( // "http://39.101.133.168:8828/link/api/aiEmotion/client/getAiEmotionData", - 'https://api.homilychart.com/link/api/aiEmotion/client/getAiEmotionData', + `${APIurl}/api/workflow/getStockData`, stockDataParams, { - headers: { + headers: { "Content-Type": "application/json", }, } @@ -2150,9 +2461,36 @@ onMounted(async () => { console.log('恢复图表数据:', currentStockData.stockInfo.name); renderCharts(currentStockData.apiData); - // 恢复结论数据但不触发音频和打字机效果 + // 恢复结论数据并显示内容 if (currentStockData.conclusionData) { conclusionData.value = currentStockData.conclusionData; + + // 直接显示所有内容,不使用打字机效果 + const conclusion = currentStockData.conclusionData; + displayedTexts.value = { + one1: conclusion.one1 || '', + one2: conclusion.one2 || '', + two: conclusion.two || '', + three: conclusion.three || '', + four: conclusion.four || '', + disclaimer: '该内容由AI生成,请注意甄别' + }; + + displayedTitles.value = { + one: conclusion.one1 || conclusion.one2 ? 'L1: 情绪监控' : '', + two: conclusion.two ? 'L2: 情绪解码' : '', + three: conclusion.three ? 'L3: 情绪推演' : '', + four: conclusion.four ? 'L4: 情绪套利' : '' + }; + + moduleVisibility.value = { + one: !!(conclusion.one1 || conclusion.one2), + two: !!conclusion.two, + three: !!conclusion.three, + four: !!conclusion.four, + disclaimer: true + }; + // 标记该股票的打字机效果和音频已经显示过,避免后续自动触发 const stockCode = currentStockData.stockInfo?.code || currentStockData.stockInfo?.symbol; if (stockCode) { diff --git a/src/views/components/HistoryRecord.vue b/src/views/components/HistoryRecord.vue index 0a9e227..f98fd13 100644 --- a/src/views/components/HistoryRecord.vue +++ b/src/views/components/HistoryRecord.vue @@ -16,7 +16,7 @@ icon
@@ -54,7 +54,7 @@ icon