From 2cf7f8190ddce5ee9381590bd5c6004435c5b2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E6=9D=B0?= Date: Thu, 7 Aug 2025 19:00:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=83=85=E7=BB=AA=E5=A4=A7=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E3=80=81=E5=A4=BA=E5=AE=9D=E5=A5=87=E5=85=B5=E5=A4=A7=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E6=96=B0=E5=A2=9E=E6=80=9D=E8=80=83=E8=BF=87=E7=A8=8B?= =?UTF-8?q?=EF=BC=9B=E6=83=85=E7=BB=AA=E5=A4=A7=E6=A8=A1=E5=9E=8B=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E7=AD=89=E5=BE=85=E6=A1=86=EF=BC=9B=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=9B=BE=E8=A1=A8=E9=A1=BA=E5=BA=8F=E5=B1=95=E7=A4=BA=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=9B=E8=A7=A3=E7=A0=81=E5=99=A8=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=8B=89=E4=BC=B8=E4=BF=AE=E5=A4=8D=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/AIchat.vue | 122 ++++++++++++++ src/views/AiEmotion.vue | 296 +++++++++++++++++++++++----------- src/views/components/emotionDecod.vue | 8 +- 3 files changed, 326 insertions(+), 100 deletions(-) diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue index d5f50fe..63d9de4 100644 --- a/src/views/AIchat.vue +++ b/src/views/AIchat.vue @@ -682,6 +682,104 @@ const addTypingTask = (message, content, speed) => { processTypingQueue(); }; +// 显示思考过程 +async function showThinkingProcess(stockName = null) { + // 第一步:正在思考 + const thinkingMessage1 = reactive({ + sender: 'ai', + class: 'ing', + type: 'ing', + flag: true, + content: '正在思考......' + }); + chatStore.messages.push(thinkingMessage1); + await new Promise(resolve => setTimeout(resolve, 1500)); + chatStore.messages.pop(); + + // 第二步:正在解析关键数据(持续显示直到获取到股票名称) + const thinkingMessage2 = reactive({ + sender: 'ai', + class: 'ing', + type: 'ing', + flag: true, + content: '正在解析关键数据......' + }); + chatStore.messages.push(thinkingMessage2); + + // 如果没有股票名称,保持第二步显示 + if (!stockName) { + return thinkingMessage2; // 返回消息引用,以便后续更新 + } + + // 有股票名称后,继续后续步骤 + await new Promise(resolve => setTimeout(resolve, 1500)); + chatStore.messages.pop(); + + // 第三步:生成具体股票的全景作战报告 + const thinkingMessage3 = reactive({ + sender: 'ai', + class: 'ing', + type: 'ing', + flag: true, + content: `正在生成${stockName}全景作战报告......` + }); + chatStore.messages.push(thinkingMessage3); + await new Promise(resolve => setTimeout(resolve, 1500)); + chatStore.messages.pop(); + + // 第四步:报告已生成 + const thinkingMessage4 = reactive({ + sender: 'ai', + class: 'ing', + type: 'ing', + flag: true, + content: '报告已生成!' + }); + chatStore.messages.push(thinkingMessage4); + await new Promise(resolve => setTimeout(resolve, 1500)); + chatStore.messages.pop(); + + return null; +} + +// 继续思考过程(当获取到股票名称后调用) +async function continueThinkingProcess(thinkingMessageRef, stockName) { + if (!thinkingMessageRef || !stockName) return; + + // 等待一段时间后继续 + await new Promise(resolve => setTimeout(resolve, 1500)); + + // 移除第二步消息 + const index = chatStore.messages.indexOf(thinkingMessageRef); + if (index > -1) { + chatStore.messages.splice(index, 1); + } + + // 第三步:生成具体股票的全景作战报告 + const thinkingMessage3 = reactive({ + sender: 'ai', + class: 'ing', + type: 'ing', + flag: true, + content: `正在生成${stockName}全景作战报告......` + }); + chatStore.messages.push(thinkingMessage3); + await new Promise(resolve => setTimeout(resolve, 1500)); + chatStore.messages.pop(); + + // 第四步:报告已生成 + const thinkingMessage4 = reactive({ + sender: 'ai', + class: 'ing', + type: 'ing', + flag: true, + content: '报告已生成!' + }); + chatStore.messages.push(thinkingMessage4); + await new Promise(resolve => setTimeout(resolve, 1500)); + chatStore.messages.pop(); +} + const hasValidData = ref(false); // 创建一个非响应式的对象来存储图表实例 @@ -724,6 +822,9 @@ watch( // 标志 let flag = true; const codeData = ref(); + // 开始思考过程(不带股票名称) + const thinkingMessageRef = await showThinkingProcess(); + // 第一阶段,意图识别 try { // 调用工作流获取回复 @@ -732,6 +833,11 @@ watch( console.log(codeData.value, "codeData"); // 根据意图识别结果判断 if (result.code == 200) { + // 获取到股票名称后,继续思考过程 + if (thinkingMessageRef && codeData.value.name) { + await continueThinkingProcess(thinkingMessageRef, codeData.value.name); + } + chatStore.messages.push({ class: "ing", type: "ing", @@ -739,6 +845,14 @@ watch( content: result.data.kaishi, }); } else { + // 意图识别失败,先清理思考过程消息 + if (thinkingMessageRef) { + const index = chatStore.messages.indexOf(thinkingMessageRef); + if (index > -1) { + chatStore.messages.splice(index, 1); + } + } + flag = false; console.log("执行回绝话术"); const AIcontent = ref(result.msg); @@ -770,6 +884,14 @@ watch( emit('enableInput'); } } catch (e) { + // 意图识别异常,先清理思考过程消息 + if (thinkingMessageRef) { + const index = chatStore.messages.indexOf(thinkingMessageRef); + if (index > -1) { + chatStore.messages.splice(index, 1); + } + } + console.log(e, "意图识别失败"); chatStore.messages.push({ class: "ing", diff --git a/src/views/AiEmotion.vue b/src/views/AiEmotion.vue index 3123a91..b4354a2 100644 --- a/src/views/AiEmotion.vue +++ b/src/views/AiEmotion.vue @@ -42,9 +42,6 @@ - - -
@@ -299,7 +296,7 @@ defineExpose({ clearConversations }); const isPageLoaded = ref(false); // 控制页面是否显示 -const isLoading = ref(false); // 控制加载状态 +// const isLoading = ref(false); // 控制加载状态 const isRotating = ref(false);//控制旋转 const version1 = ref(1); // 版本号 const conclusionData = ref(''); // 存储第二个工作流接口返回的结论数据 @@ -970,6 +967,83 @@ function startImageRotation() { } +// 显示思考过程 +async function showThinkingProcess(stockName = null) { + // 第一步:正在思考 + const thinkingMessage1 = reactive({ sender: 'ai', text: '正在思考......' }); + messages.value.push(thinkingMessage1); + await new Promise(resolve => setTimeout(resolve, 1500)); + messages.value.pop(); + + // 第二步:正在解析关键数据(持续显示直到获取到股票名称) + const thinkingMessage2 = reactive({ sender: 'ai', text: '正在解析关键数据......' }); + messages.value.push(thinkingMessage2); + + // 如果没有股票名称,保持第二步显示 + if (!stockName) { + return thinkingMessage2; // 返回消息引用,以便后续更新 + } + + // 有股票名称后,继续后续步骤 + await new Promise(resolve => setTimeout(resolve, 1500)); + messages.value.pop(); + + // 第三步:生成具体股票的量子四维矩阵图 + const thinkingMessage3 = reactive({ sender: 'ai', text: `正在生成${stockName}量子四维矩阵图......` }); + messages.value.push(thinkingMessage3); + await new Promise(resolve => setTimeout(resolve, 1500)); + messages.value.pop(); + + // 第四步:报告已生成 + const thinkingMessage4 = reactive({ sender: 'ai', text: '报告已生成!' }); + messages.value.push(thinkingMessage4); + await new Promise(resolve => setTimeout(resolve, 1500)); + messages.value.pop(); + + return null; +} + +// 继续思考过程(当获取到股票名称后调用) +async function continueThinkingProcess(thinkingMessageRef, stockName) { + if (!thinkingMessageRef || !stockName) return; + + // 等待一段时间后继续 + await new Promise(resolve => setTimeout(resolve, 1500)); + + // 移除第二步消息 + const index = messages.value.indexOf(thinkingMessageRef); + if (index > -1) { + messages.value.splice(index, 1); + } + + // 第三步:生成具体股票的量子四维矩阵图 + const thinkingMessage3 = reactive({ sender: 'ai', text: `正在生成${stockName}量子四维矩阵图......` }); + messages.value.push(thinkingMessage3); + + // 返回第三步消息的引用,以便后续处理 + return thinkingMessage3; +} + +// 完成思考过程(当第二个工作流接口成功后调用) +async function finishThinkingProcess(thinkingMessage3Ref) { + if (!thinkingMessage3Ref) return; + + // 等待一段时间 + await new Promise(resolve => setTimeout(resolve, 1500)); + + // 移除第三步消息 + const index = messages.value.indexOf(thinkingMessage3Ref); + if (index > -1) { + messages.value.splice(index, 1); + } + + // 第四步:报告已生成 + const thinkingMessage4 = reactive({ sender: 'ai', text: '报告已生成!' }); + messages.value.push(thinkingMessage4); + await new Promise(resolve => setTimeout(resolve, 1500)); + messages.value.pop(); +} + // 发送消息方法 async function handleSendMessage(input, onComplete) { console.log("发送内容:", input); @@ -1052,6 +1126,9 @@ async function handleSendMessage(input, onComplete) { timestamp: new Date().toISOString() }); + // 开始思考过程(不带股票名称) + const thinkingMessageRef = await showThinkingProcess(); + try { // 第一步:调用第一个接口验证用户输入内容是否合法 const params = { @@ -1084,8 +1161,16 @@ async function handleSendMessage(input, onComplete) { // 检查用户输入是否合法 if (!parsedData || !parsedData.market || !parsedData.code) { - // 输入不合法,关闭加载状态和等待提示,返回refuse信息,停止图片旋转,恢复历史数据 - isLoading.value = false; + // 输入不合法,先清理思考过程消息 + if (thinkingMessageRef) { + const index = messages.value.indexOf(thinkingMessageRef); + if (index > -1) { + messages.value.splice(index, 1); + } + } + + // 关闭加载状态和等待提示,返回refuse信息,停止图片旋转,恢复历史数据 + // isLoading.value = false; isPageLoaded.value = false; const aiMessage = reactive({ sender: 'ai', text: processRefuseMessage(parsedData.refuse) }); messages.value.push(aiMessage); @@ -1108,8 +1193,14 @@ async function handleSendMessage(input, onComplete) { } // 输入合法,继续执行后续处理 + // 获取到股票名称后,继续思考过程 + let thinkingMessage3Ref = null; + if (thinkingMessageRef && parsedData.name) { + thinkingMessage3Ref = await continueThinkingProcess(thinkingMessageRef, parsedData.name); + } + // 设置加载状态,隐藏图表页面 - isLoading.value = true; + // isLoading.value = true; isPageLoaded.value = false; @@ -1135,13 +1226,18 @@ async function handleSendMessage(input, onComplete) { // 检查所有数据是否都加载成功 if (conclusionResponse && conclusionResponse.data && fetchDataResult) { + // 第二个工作流接口成功,完成思考过程 + if (thinkingMessage3Ref) { + await finishThinkingProcess(thinkingMessage3Ref); + } + // 将结论数据存储到响应式变量和store中 conclusionData.value = conclusionResponse.data; // 将结论数据存储到store中的当前激活股票 emotionStore.updateActiveStockConclusion(conclusionResponse.data); // 所有数据加载完成,关闭加载状态,显示页面 - isLoading.value = false; + // isLoading.value = false; isPageLoaded.value = true; // 数据获取成功后,重新获取用户次数以实现实时更新 @@ -1184,8 +1280,16 @@ async function handleSendMessage(input, onComplete) { } }); } else { + // 数据加载失败,清理第三步思考过程消息 + if (thinkingMessage3Ref) { + const index = messages.value.indexOf(thinkingMessage3Ref); + if (index > -1) { + messages.value.splice(index, 1); + } + } + // 数据加载失败,停止图片旋转,恢复历史数据 - isLoading.value = false; + // isLoading.value = false; // 如果 fetchDataResult 为 false,说明数据不完整的错误信息已经在 fetchData 中添加到 messages // 只有在 conclusionResponse 有问题时才添加通用错误信息 if (!conclusionResponse || !conclusionResponse.data) { @@ -1212,8 +1316,16 @@ async function handleSendMessage(input, onComplete) { return; } } catch (error) { + // 请求失败,清理第三步思考过程消息 + if (thinkingMessage3Ref) { + const index = messages.value.indexOf(thinkingMessage3Ref); + if (index > -1) { + messages.value.splice(index, 1); + } + } + // 请求失败时关闭加载状态 - isLoading.value = false; + // isLoading.value = false; // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { @@ -1298,7 +1410,7 @@ async function fetchData(code, market, stockName, queryText) { if (!validation.isValid) { console.log('API返回数据不完整,缺失字段:', validation.missingFields); // 关闭加载状态 - isLoading.value = false; + // isLoading.value = false; // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { @@ -1345,7 +1457,7 @@ async function fetchData(code, market, stockName, queryText) { return true; // 返回成功标识 } else { // 关闭加载状态 - isLoading.value = false; + // isLoading.value = false; // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { @@ -1373,7 +1485,7 @@ async function fetchData(code, market, stockName, queryText) { } } catch (error) { // 关闭加载状态 - isLoading.value = false; + // isLoading.value = false; // 如果有之前的股票数据,恢复显示状态;否则设置为false if (emotionStore.stockList.length > 0 && emotionStore.activeStock) { @@ -1468,6 +1580,69 @@ function hasValidData(obj) { return false; } +// 依次渲染图表的方法 +async function renderChartsSequentially(clonedData) { + console.log('开始依次渲染图表'); + + // 定义图表渲染顺序和配置 + const chartConfigs = [ + { + name: '股市温度计', + ref: marketTemperatureRef, + visibility: chartVisibility.value.marketTemperature, + method: 'initChart', + params: [clonedData.GSWDJ, clonedData.KLine20, clonedData.WDRL] + }, + { + name: '情绪解码器', + ref: emotionDecodRef, + visibility: chartVisibility.value.emotionDecod, + method: 'initQXNLZHEcharts', + params: [clonedData.KLine20, clonedData.QXJMQ] + }, + { + name: '情绪探底雷达', + ref: emotionalBottomRadarRef, + visibility: chartVisibility.value.emotionalBottomRadar, + method: 'initEmotionalBottomRadar', + params: [clonedData.KLine20, clonedData.QXTDLD] + }, + { + name: '情绪能量转化器', + ref: emoEnergyConverterRef, + visibility: chartVisibility.value.emoEnergyConverter, + method: 'initQXNLZHEcharts', + params: [clonedData.KLine20, clonedData.QXNLZHQ] + } + ]; + + // 依次渲染每个图表 + for (const config of chartConfigs) { + if (config.ref.value && config.visibility) { + console.log(`开始渲染${config.name}图表`); + console.log(`${config.name}Ref方法:`, typeof config.ref.value[config.method]); + + if (typeof config.ref.value[config.method] === 'function') { + try { + config.ref.value[config.method](...config.params); + console.log(`${config.name}图表渲染成功`); + + // 每个图表渲染完成后等待一段时间再渲染下一个 + await new Promise(resolve => setTimeout(resolve, 800)); + } catch (error) { + console.error(`${config.name}图表渲染失败:`, error); + } + } else { + console.error(`${config.name}Ref.${config.method} 方法不存在`); + } + } else { + console.log(`${config.name}图表未渲染,ref存在:`, !!config.ref.value, '数据存在:', config.visibility); + } + } + + console.log('所有图表依次渲染完成'); +} + // 渲染组件图表的方法 function renderCharts(data) { console.log('开始渲染图表,数据:', data); @@ -1496,7 +1671,7 @@ function renderCharts(data) { // 隐藏页面内容 isPageLoaded.value = false; - isLoading.value = false; + // isLoading.value = false; return; // 直接返回,不进行后续渲染 } @@ -1549,80 +1724,8 @@ function renderCharts(data) { } }, 1000); - // 渲染股市温度计图表 - if (marketTemperatureRef.value && chartVisibility.value.marketTemperature) { - console.log('开始渲染股市温度计图表'); - console.log('marketTemperatureRef方法:', typeof marketTemperatureRef.value.initChart); - if (typeof marketTemperatureRef.value.initChart === 'function') { - try { - marketTemperatureRef.value.initChart(clonedData.GSWDJ, clonedData.KLine20, clonedData.WDRL); - console.log('股市温度计图表渲染成功'); - } catch (error) { - console.error('股市温度计图表渲染失败:', error); - } - } else { - console.error('marketTemperatureRef.initChart 方法不存在'); - } - } else { - console.log('股市温度计图表未渲染,ref存在:', !!marketTemperatureRef.value, '数据存在:', chartVisibility.value.marketTemperature); - } - - // 渲染情绪解码器图表 - if (emotionDecodRef.value && chartVisibility.value.emotionDecod) { - console.log('开始渲染情绪解码器图表'); - console.log('emotionDecodRef方法:', typeof emotionDecodRef.value.initQXNLZHEcharts); - if (typeof emotionDecodRef.value.initQXNLZHEcharts === 'function') { - try { - emotionDecodRef.value.initQXNLZHEcharts(clonedData.KLine20, clonedData.QXJMQ); - console.log('情绪解码器图表渲染成功'); - } catch (error) { - console.error('情绪解码器图表渲染失败:', error); - } - } else { - console.error('emotionDecodRef.initQXNLZHEcharts 方法不存在'); - } - } else { - console.log('情绪解码器图表未渲染,ref存在:', !!emotionDecodRef.value, '数据存在:', chartVisibility.value.emotionDecod); - } - - // 渲染情绪探底雷达图表 - if (emotionalBottomRadarRef.value && chartVisibility.value.emotionalBottomRadar) { - console.log('开始渲染情绪探底雷达图表'); - console.log('emotionalBottomRadarRef方法:', typeof emotionalBottomRadarRef.value.initEmotionalBottomRadar); - if (typeof emotionalBottomRadarRef.value.initEmotionalBottomRadar === 'function') { - try { - emotionalBottomRadarRef.value.initEmotionalBottomRadar( - clonedData.KLine20, - clonedData.QXTDLD - ); - console.log('情绪探底雷达图表渲染成功'); - } catch (error) { - console.error('情绪探底雷达图表渲染失败:', error); - } - } else { - console.error('emotionalBottomRadarRef.initEmotionalBottomRadar 方法不存在'); - } - } else { - console.log('情绪探底雷达图表未渲染,ref存在:', !!emotionalBottomRadarRef.value, '数据存在:', chartVisibility.value.emotionalBottomRadar); - } - - // 渲染情绪能量转化器图表 - if (emoEnergyConverterRef.value && chartVisibility.value.emoEnergyConverter) { - console.log('开始渲染情绪能量转化器图表'); - console.log('emoEnergyConverterRef方法:', typeof emoEnergyConverterRef.value.initQXNLZHEcharts); - if (typeof emoEnergyConverterRef.value.initQXNLZHEcharts === 'function') { - try { - emoEnergyConverterRef.value.initQXNLZHEcharts(clonedData.KLine20, clonedData.QXNLZHQ); - console.log('情绪能量转化器图表渲染成功'); - } catch (error) { - console.error('情绪能量转化器图表渲染失败:', error); - } - } else { - console.error('emoEnergyConverterRef.initQXNLZHEcharts 方法不存在'); - } - } else { - console.log('情绪能量转化器图表未渲染,ref存在:', !!emoEnergyConverterRef.value, '数据存在:', chartVisibility.value.emoEnergyConverter); - } + // 开始依次渲染图表 + renderChartsSequentially(clonedData); console.log('图表渲染完成'); } catch (error) { @@ -3212,24 +3315,24 @@ const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']); } /* 加载提示样式 */ -.loading-container { +/* .loading-container { display: flex; justify-content: center; align-items: center; min-height: 20vh; padding: 20px 10px; -} +} */ -.loading-content { +/* .loading-content { text-align: center; background: linear-gradient(135deg, rgba(0, 212, 255, 0.15) 0%, rgba(0, 100, 200, 0.15) 100%); border: 2px solid rgba(0, 212, 255, 0.4); border-radius: 20px; padding: 40px; box-shadow: 0 8px 25px rgba(0, 212, 255, 0.3); -} +} */ -.loading-spinner { +/* .loading-spinner { width: 60px; height: 60px; border: 4px solid rgba(0, 212, 255, 0.3); @@ -3237,7 +3340,7 @@ const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']); border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 20px; -} +} */ @keyframes spin { 0% { @@ -3249,13 +3352,13 @@ const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']); } } -.loading-text { +/* .loading-text { color: #00d4ff; font-size: 18px; font-weight: bold; text-shadow: 0 2px 8px rgba(0, 212, 255, 0.5); letter-spacing: 1px; -} +} */ /* 顶部锚点样式 */ .top-anchor { @@ -3283,7 +3386,6 @@ const emit = defineEmits(['updateMessage', 'sendMessage', 'ensureAIchat']); align-items: center !important; justify-content: center !important; cursor: pointer !important; - box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3) !important; transition: all 0.3s ease !important; z-index: 100 !important; color: white !important; diff --git a/src/views/components/emotionDecod.vue b/src/views/components/emotionDecod.vue index ddbc56f..e23347d 100644 --- a/src/views/components/emotionDecod.vue +++ b/src/views/components/emotionDecod.vue @@ -93,8 +93,10 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { top: "5%", height: window.innerWidth <= 768 ? "30%" : "40%" }, - { top: "45%", height: "35%" }, - { top: "80%", height: "2%" }, + { top: window.innerWidth <= 768 ? "35%" :"45%", + height: "35%" }, + { top: window.innerWidth <= 768 ? "70%" : "80%", + height: "2%" }, ], visualMap: [ @@ -278,7 +280,7 @@ function initQXNLZHEcharts(kline, qxnlzhqData) { shadowOffsetX: 2, shadowOffsetY: 2, }, - bottom: window.innerWidth > 768 ? "8%" : "5%", // 下移数据缩放滑块 + bottom: "8%", // 下移数据缩放滑块 }, { show: !1,