|
@ -28,7 +28,7 @@ |
|
|
<div v-if="isLoading" class="loading-container"> |
|
|
<div v-if="isLoading" class="loading-container"> |
|
|
<div class="loading-content"> |
|
|
<div class="loading-content"> |
|
|
<div class="loading-spinner"></div> |
|
|
<div class="loading-spinner"></div> |
|
|
<div class="loading-text">AI小财神正在分析中,请稍候...</div> |
|
|
|
|
|
|
|
|
<div class="loading-text">AI小财神正在加载图表数据和音频内容,请稍候...</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
@ -320,8 +320,7 @@ const parsedConclusion = computed(() => { |
|
|
// 监听当前股票变化,重新渲染图表 |
|
|
// 监听当前股票变化,重新渲染图表 |
|
|
watch(currentStock, (newStock) => { |
|
|
watch(currentStock, (newStock) => { |
|
|
if (newStock && newStock.apiData) { |
|
|
if (newStock && newStock.apiData) { |
|
|
isPageLoaded.value = true; |
|
|
|
|
|
isLoading.value = false; // 数据加载完成,关闭加载状态 |
|
|
|
|
|
|
|
|
// 页面加载状态现在由 handleSendMessage 统一控制 |
|
|
// 停止当前播放的音频 |
|
|
// 停止当前播放的音频 |
|
|
stopAudio(); |
|
|
stopAudio(); |
|
|
// 清理正在进行的打字机效果定时器 |
|
|
// 清理正在进行的打字机效果定时器 |
|
@ -390,9 +389,11 @@ watch(currentStock, (newStock) => { |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 只有在页面已加载的情况下才渲染图表 |
|
|
|
|
|
if (isPageLoaded.value) { |
|
|
nextTick(() => { |
|
|
nextTick(() => { |
|
|
renderCharts(newStock.apiData); |
|
|
renderCharts(newStock.apiData); |
|
|
console.log('0000000000000000000000000', newStock.apiData) |
|
|
|
|
|
|
|
|
console.log('图表数据已准备完成,开始渲染:', newStock.apiData) |
|
|
// 检查场景应用部分是否已经在视口中,如果是则立即触发效果 |
|
|
// 检查场景应用部分是否已经在视口中,如果是则立即触发效果 |
|
|
setTimeout(() => { |
|
|
setTimeout(() => { |
|
|
if (scenarioApplicationRef.value && parsedConclusion.value) { |
|
|
if (scenarioApplicationRef.value && parsedConclusion.value) { |
|
@ -465,7 +466,10 @@ watch(currentStock, (newStock) => { |
|
|
}, 500); // 延迟500ms确保数据完全加载 |
|
|
}, 500); // 延迟500ms确保数据完全加载 |
|
|
}); |
|
|
}); |
|
|
} else { |
|
|
} else { |
|
|
isPageLoaded.value = false; |
|
|
|
|
|
|
|
|
console.log('页面尚未加载完成,等待数据加载完成后再渲染图表'); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
console.log('股票数据不存在或API数据未加载'); |
|
|
} |
|
|
} |
|
|
}, { immediate: true }); |
|
|
}, { immediate: true }); |
|
|
|
|
|
|
|
@ -495,22 +499,20 @@ watch(parsedConclusion, (newConclusion) => { |
|
|
audioUrl.value = voiceUrl; |
|
|
audioUrl.value = voiceUrl; |
|
|
console.log('音频URL已准备,检查是否需要立即触发效果'); |
|
|
console.log('音频URL已准备,检查是否需要立即触发效果'); |
|
|
|
|
|
|
|
|
// 音频准备好后,检查场景应用部分是否已经在视口中 |
|
|
|
|
|
|
|
|
// 音频准备好后,如果页面已加载则立即触发效果 |
|
|
nextTick(() => { |
|
|
nextTick(() => { |
|
|
setTimeout(() => { |
|
|
setTimeout(() => { |
|
|
if (scenarioApplicationRef.value && currentStock.value?.stockInfo) { |
|
|
|
|
|
|
|
|
if (currentStock.value?.stockInfo && isPageLoaded.value) { |
|
|
const stockCode = currentStock.value.stockInfo.code || currentStock.value.stockInfo.symbol; |
|
|
const stockCode = currentStock.value.stockInfo.code || currentStock.value.stockInfo.symbol; |
|
|
const rect = scenarioApplicationRef.value.getBoundingClientRect(); |
|
|
|
|
|
const isInViewport = rect.top < window.innerHeight && rect.bottom > 0; |
|
|
|
|
|
|
|
|
|
|
|
if (isInViewport && parsedConclusion.value && stockCode) { |
|
|
|
|
|
|
|
|
if (parsedConclusion.value && stockCode) { |
|
|
// 如果该股票已经显示过,不需要再处理 |
|
|
// 如果该股票已经显示过,不需要再处理 |
|
|
if (stockTypewriterShown.value.has(stockCode)) { |
|
|
if (stockTypewriterShown.value.has(stockCode)) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 该股票第一次:播放音频和打字机效果 |
|
|
// 该股票第一次:播放音频和打字机效果 |
|
|
console.log('该股票第一次音频准备完成且场景应用部分在视口中,立即触发效果'); |
|
|
|
|
|
|
|
|
console.log('音频准备完成且页面已加载,立即触发效果'); |
|
|
hasTriggeredTypewriter.value = true; |
|
|
hasTriggeredTypewriter.value = true; |
|
|
hasTriggeredAudio.value = true; |
|
|
hasTriggeredAudio.value = true; |
|
|
|
|
|
|
|
@ -785,6 +787,11 @@ async function handleSendMessage(input) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 用户输入不为空,立即触发图片旋转逻辑,隐藏历史数据 |
|
|
|
|
|
isRotating.value = true; |
|
|
|
|
|
const previousMessages = [...messages.value]; // 保存历史消息 |
|
|
|
|
|
messages.value = []; // 清空历史数据 |
|
|
|
|
|
|
|
|
// 检查用户是否有使用次数(检查是否有任何权限) |
|
|
// 检查用户是否有使用次数(检查是否有任何权限) |
|
|
const hasPermission = userStore.brainPerssion || userStore.swordPerssion || |
|
|
const hasPermission = userStore.brainPerssion || userStore.swordPerssion || |
|
|
userStore.pricePerssion || userStore.timePerssion || |
|
|
userStore.pricePerssion || userStore.timePerssion || |
|
@ -796,6 +803,9 @@ async function handleSendMessage(input) { |
|
|
messages.value.push(userMessage); |
|
|
messages.value.push(userMessage); |
|
|
const aiMessage = reactive({ sender: 'ai', text: '您当前没有可用次数,请联系客服或购买服务包。' }); |
|
|
const aiMessage = reactive({ sender: 'ai', text: '您当前没有可用次数,请联系客服或购买服务包。' }); |
|
|
messages.value.push(aiMessage); |
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
// 停止图片旋转,恢复历史数据 |
|
|
|
|
|
isRotating.value = false; |
|
|
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -827,15 +837,15 @@ async function handleSendMessage(input) { |
|
|
|
|
|
|
|
|
// 检查用户输入是否合法 |
|
|
// 检查用户输入是否合法 |
|
|
if (!parsedData || !parsedData.market || !parsedData.code) { |
|
|
if (!parsedData || !parsedData.market || !parsedData.code) { |
|
|
// 输入不合法,返回refuse信息,不进行后续处理 |
|
|
|
|
|
|
|
|
// 输入不合法,返回refuse信息,停止图片旋转,恢复历史数据 |
|
|
const aiMessage = reactive({ sender: 'ai', text: processRefuseMessage(parsedData.refuse) }); |
|
|
const aiMessage = reactive({ sender: 'ai', text: processRefuseMessage(parsedData.refuse) }); |
|
|
messages.value.push(aiMessage); |
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
isRotating.value = false; |
|
|
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 输入合法,开始显示等待提示和后续处理 |
|
|
|
|
|
// 触发图片旋转 |
|
|
|
|
|
isRotating.value = true; |
|
|
|
|
|
|
|
|
// 输入合法,继续执行后续处理 |
|
|
// 设置加载状态,隐藏图表页面 |
|
|
// 设置加载状态,隐藏图表页面 |
|
|
isLoading.value = true; |
|
|
isLoading.value = true; |
|
|
|
|
|
|
|
@ -863,16 +873,57 @@ async function handleSendMessage(input) { |
|
|
const conclusionResponse = await conclusionResult.json(); |
|
|
const conclusionResponse = await conclusionResult.json(); |
|
|
console.log("第二个工作流接口返回数据:", conclusionResponse); |
|
|
console.log("第二个工作流接口返回数据:", conclusionResponse); |
|
|
|
|
|
|
|
|
|
|
|
// 检查所有数据是否都加载成功 |
|
|
|
|
|
if (conclusionResponse && conclusionResponse.data && fetchDataResult) { |
|
|
// 将结论数据存储到响应式变量和store中 |
|
|
// 将结论数据存储到响应式变量和store中 |
|
|
if (conclusionResponse && conclusionResponse.data) { |
|
|
|
|
|
conclusionData.value = conclusionResponse.data; |
|
|
conclusionData.value = conclusionResponse.data; |
|
|
// 将结论数据存储到store中的当前激活股票 |
|
|
// 将结论数据存储到store中的当前激活股票 |
|
|
emotionStore.updateActiveStockConclusion(conclusionResponse.data); |
|
|
emotionStore.updateActiveStockConclusion(conclusionResponse.data); |
|
|
|
|
|
|
|
|
|
|
|
// 所有数据加载完成,关闭加载状态,显示页面 |
|
|
|
|
|
isLoading.value = false; |
|
|
|
|
|
isPageLoaded.value = true; |
|
|
|
|
|
|
|
|
|
|
|
console.log('所有数据加载完成,开始渲染页面'); |
|
|
|
|
|
|
|
|
|
|
|
// 确保页面状态更新后触发图表渲染和音频文本 |
|
|
|
|
|
nextTick(() => { |
|
|
|
|
|
if (currentStock.value && currentStock.value.apiData) { |
|
|
|
|
|
renderCharts(currentStock.value.apiData); |
|
|
|
|
|
console.log('数据加载完成后开始渲染图表'); |
|
|
|
|
|
|
|
|
|
|
|
// 数据加载完成后立即触发音频和文本,不等待滚动到视口 |
|
|
|
|
|
if (parsedConclusion.value && audioUrl.value) { |
|
|
|
|
|
const stockCode = currentStock.value.stockInfo?.code || currentStock.value.stockInfo?.symbol; |
|
|
|
|
|
if (stockCode && !stockTypewriterShown.value.has(stockCode)) { |
|
|
|
|
|
console.log('数据加载完成,立即触发音频和打字机效果'); |
|
|
|
|
|
startTypewriterEffect(parsedConclusion.value); |
|
|
|
|
|
|
|
|
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
|
|
playAudio(audioUrl.value); |
|
|
|
|
|
} |
|
|
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
|
|
|
// 数据加载失败,停止图片旋转,恢复历史数据 |
|
|
|
|
|
isLoading.value = false; |
|
|
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '数据加载失败,请重试' }); |
|
|
|
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
isRotating.value = false; |
|
|
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
|
|
return; |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
const aiMessage = reactive({ sender: 'ai', text: '请求工作流接口失败,请检查网络连接' }); |
|
|
const aiMessage = reactive({ sender: 'ai', text: '请求工作流接口失败,请检查网络连接' }); |
|
|
messages.value.push(aiMessage); |
|
|
messages.value.push(aiMessage); |
|
|
return; // 请求失败时直接返回 |
|
|
|
|
|
|
|
|
// 请求失败时停止图片旋转,恢复历史数据 |
|
|
|
|
|
isRotating.value = false; |
|
|
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
|
|
return; |
|
|
} finally { |
|
|
} finally { |
|
|
// 停止图片旋转(只有在设置了旋转状态时才需要停止) |
|
|
// 停止图片旋转(只有在设置了旋转状态时才需要停止) |
|
|
if (isRotating.value) { |
|
|
if (isRotating.value) { |
|
@ -925,13 +976,17 @@ async function fetchData(code, market, stockName, queryText) { |
|
|
}; |
|
|
}; |
|
|
// 将股票数据添加到store中 |
|
|
// 将股票数据添加到store中 |
|
|
emotionStore.addStock(stockData); |
|
|
emotionStore.addStock(stockData); |
|
|
|
|
|
return true; // 返回成功标识 |
|
|
} else { |
|
|
} else { |
|
|
const aiMessage = reactive({ sender: 'ai', text: '请求失败,请检查网络连接' }); |
|
|
|
|
|
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); |
|
|
messages.value.push(aiMessage); |
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
return false; // 返回失败标识 |
|
|
} |
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
const aiMessage = reactive({ sender: 'ai', text: '请求失败,请检查网络连接' }); |
|
|
|
|
|
|
|
|
console.error('fetchData error:', error); |
|
|
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); |
|
|
messages.value.push(aiMessage); |
|
|
messages.value.push(aiMessage); |
|
|
|
|
|
return false; // 返回失败标识 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|