|
|
@ -625,7 +625,7 @@ watch(parsedConclusion, (newConclusion) => { |
|
|
|
}, { immediate: true }); |
|
|
|
|
|
|
|
// 打字机效果函数 |
|
|
|
function startTypewriterEffect(conclusion) { |
|
|
|
function startTypewriterEffect(conclusion, onComplete) { |
|
|
|
console.log('开始打字机效果,结论数据:', conclusion); |
|
|
|
|
|
|
|
// 详细调试各个字段 |
|
|
@ -758,10 +758,14 @@ function startTypewriterEffect(conclusion) { |
|
|
|
for (let i = 0; i <= disclaimerText.length; i++) { |
|
|
|
const timer = setTimeout(() => { |
|
|
|
displayedTexts.value.disclaimer = disclaimerText.substring(0, i); |
|
|
|
// 在打字机效果的最后一个字符显示完成后,滚动到底部 |
|
|
|
// 在打字机效果的最后一个字符显示完成后,滚动到底部并调用完成回调 |
|
|
|
if (i === disclaimerText.length) { |
|
|
|
setTimeout(() => { |
|
|
|
scrollToBottom(); |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
}, 100); |
|
|
|
} |
|
|
|
}, totalDelay + i * typeSpeed); |
|
|
@ -877,7 +881,7 @@ function startImageRotation() { |
|
|
|
|
|
|
|
|
|
|
|
// 发送消息方法 |
|
|
|
async function handleSendMessage(input) { |
|
|
|
async function handleSendMessage(input, onComplete) { |
|
|
|
console.log("发送内容:", input); |
|
|
|
// 标记为用户主动搜索 |
|
|
|
isUserInitiated.value = true; |
|
|
@ -885,6 +889,10 @@ async function handleSendMessage(input) { |
|
|
|
// 检查用户输入内容是否为空 |
|
|
|
if (!input || !input.trim()) { |
|
|
|
ElMessage.warning("输入内容不能为空"); |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -903,6 +911,10 @@ async function handleSendMessage(input) { |
|
|
|
// 停止图片旋转,恢复历史数据 |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -920,6 +932,10 @@ async function handleSendMessage(input) { |
|
|
|
// 停止图片旋转,恢复历史数据 |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -947,7 +963,6 @@ async function handleSendMessage(input) { |
|
|
|
const result = await getReplyAPI(params); |
|
|
|
const response = await result.json(); |
|
|
|
const parsedData = JSON.parse(response.data); |
|
|
|
console.log("第一个接口解析后的数据:", parsedData); |
|
|
|
|
|
|
|
// 检查用户输入是否合法 |
|
|
|
if (!parsedData || !parsedData.market || !parsedData.code) { |
|
|
@ -956,6 +971,10 @@ async function handleSendMessage(input) { |
|
|
|
messages.value.push(aiMessage); |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@ -963,7 +982,6 @@ async function handleSendMessage(input) { |
|
|
|
// 设置加载状态,隐藏图表页面 |
|
|
|
isLoading.value = true; |
|
|
|
|
|
|
|
console.log("工作流接口返回股票信息:", parsedData); |
|
|
|
isPageLoaded.value = false; |
|
|
|
|
|
|
|
// 调用第二个工作流接口 |
|
|
@ -985,7 +1003,6 @@ async function handleSendMessage(input) { |
|
|
|
|
|
|
|
// 处理结论接口返回的数据 |
|
|
|
const conclusionResponse = await conclusionResult.json(); |
|
|
|
console.log("第二个工作流接口返回数据:", conclusionResponse); |
|
|
|
|
|
|
|
// 检查所有数据是否都加载成功 |
|
|
|
if (conclusionResponse && conclusionResponse.data && fetchDataResult) { |
|
|
@ -1006,26 +1023,28 @@ async function handleSendMessage(input) { |
|
|
|
console.error('更新用户次数失败:', error); |
|
|
|
} |
|
|
|
|
|
|
|
console.log('所有数据加载完成,开始渲染页面'); |
|
|
|
|
|
|
|
// 确保页面状态更新后触发图表渲染和音频文本 |
|
|
|
nextTick(() => { |
|
|
|
if (currentStock.value && currentStock.value.apiData) { |
|
|
|
renderCharts(currentStock.value.apiData); |
|
|
|
console.log('数据加载完成后开始渲染图表'); |
|
|
|
|
|
|
|
// 只有在用户主动搜索时才自动触发音频和文本 |
|
|
|
if (isUserInitiated.value && 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); |
|
|
|
startTypewriterEffect(parsedConclusion.value, onComplete); |
|
|
|
|
|
|
|
if (!stockAudioPlayed.value.has(stockCode)) { |
|
|
|
stockAudioPlayed.value.set(stockCode, true); |
|
|
|
playAudio(audioUrl.value); |
|
|
|
} |
|
|
|
stockTypewriterShown.value.set(stockCode, true); |
|
|
|
} else { |
|
|
|
// 如果不需要打字机效果,直接调用完成回调 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1040,6 +1059,10 @@ async function handleSendMessage(input) { |
|
|
|
messages.value.push(aiMessage); |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
@ -1048,6 +1071,10 @@ async function handleSendMessage(input) { |
|
|
|
// 请求失败时停止图片旋转,恢复历史数据 |
|
|
|
isRotating.value = false; |
|
|
|
messages.value = [...previousMessages, ...messages.value]; |
|
|
|
// 调用完成回调,重新启用输入框 |
|
|
|
if (onComplete && typeof onComplete === 'function') { |
|
|
|
onComplete(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} finally { |
|
|
|
// 停止图片旋转(只有在设置了旋转状态时才需要停止) |
|
|
@ -1082,11 +1109,8 @@ async function fetchData(code, market, stockName, queryText) { |
|
|
|
); |
|
|
|
|
|
|
|
const stockDataResponse = stockDataResult.data; // 获取返回所有的数据 |
|
|
|
console.log('图表数据接口返回数据:', stockDataResponse.data); |
|
|
|
|
|
|
|
if (stockDataResponse.code === 200 && stockDataResponse.data) { |
|
|
|
console.log(stockDataResponse.code) |
|
|
|
|
|
|
|
// 创建股票数据对象 |
|
|
|
const stockData = { |
|
|
|
queryText: queryText, |
|
|
@ -1108,7 +1132,6 @@ async function fetchData(code, market, stockName, queryText) { |
|
|
|
return false; // 返回失败标识 |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('fetchData error:', error); |
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表数据请求失败,请检查网络连接' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
return false; // 返回失败标识 |
|
|
@ -1123,42 +1146,26 @@ function renderCharts(data) { |
|
|
|
try { |
|
|
|
// 深拷贝数据避免污染原始数据 |
|
|
|
const clonedData = JSON.parse(JSON.stringify(data)); |
|
|
|
console.log('已深拷贝数据,避免污染原始数据'); |
|
|
|
console.log('所有数据1111111111111:', clonedData) |
|
|
|
// 渲染股市温度计图表 |
|
|
|
if (marketTemperatureRef.value && clonedData.GSWDJ) { |
|
|
|
console.log("开始渲染股市温度计图表"); |
|
|
|
console.log("股市温度计数据", clonedData.GSWDJ); |
|
|
|
marketTemperatureRef.value.initChart(clonedData.GSWDJ, clonedData.KLine20, clonedData.WDRL); |
|
|
|
console.log("股市温度计图表已渲染"); |
|
|
|
} |
|
|
|
// 渲染情绪解码器图表 |
|
|
|
if (emotionDecodRef.value && clonedData.QXJMQ) { |
|
|
|
console.log("开始渲染情绪解码器图表"); |
|
|
|
console.log("情绪解码器数据", clonedData.QXJMQ); |
|
|
|
emotionDecodRef.value.initQXNLZHEcharts(clonedData.KLine20, clonedData.QXJMQ); |
|
|
|
console.log("情绪解码器图表已渲染"); |
|
|
|
} |
|
|
|
// 渲染情绪探底雷达图表 |
|
|
|
if (emotionalBottomRadarRef.value && clonedData.QXTDLD) { |
|
|
|
console.log("开始渲染情绪探底雷达图表"); |
|
|
|
console.log("探底雷达数据", clonedData.QXTDLD); |
|
|
|
emotionalBottomRadarRef.value.initEmotionalBottomRadar( |
|
|
|
clonedData.KLine20, |
|
|
|
clonedData.QXTDLD |
|
|
|
); |
|
|
|
console.log("情绪探底雷达图表已渲染"); |
|
|
|
} |
|
|
|
// 渲染情绪能量转化器图表 |
|
|
|
if (emoEnergyConverterRef.value && clonedData.QXNLZHQ) { |
|
|
|
console.log("开始渲染情绪能量转化器图表"); |
|
|
|
console.log("KLine20:", clonedData.KLine20); |
|
|
|
console.log("QXNLZHQ:", clonedData.QXNLZHQ); |
|
|
|
emoEnergyConverterRef.value.initQXNLZHEcharts(clonedData.KLine20, clonedData.QXNLZHQ); |
|
|
|
console.log("情绪能量转化器图表已渲染"); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('图表渲染过程中发生错误:', error); |
|
|
|
const aiMessage = reactive({ sender: 'ai', text: '图表渲染失败,请重试' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
} |
|
|
@ -1518,6 +1525,8 @@ defineExpose({ |
|
|
|
.disclaimer-item { |
|
|
|
p { |
|
|
|
color: #ffffff !important; |
|
|
|
font-size: 20px; |
|
|
|
font-weight: bold; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2657,17 +2666,15 @@ defineExpose({ |
|
|
|
} |
|
|
|
|
|
|
|
.disclaimer-item { |
|
|
|
margin-top: 30px; |
|
|
|
/* margin-top: 30px; */ |
|
|
|
padding: 20px; |
|
|
|
border-top: 1px solid rgba(153, 153, 153, 0.2); |
|
|
|
text-align: center; |
|
|
|
|
|
|
|
p.disclaimer-text { |
|
|
|
p { |
|
|
|
color: #ffffff !important; |
|
|
|
font-size: 14px; |
|
|
|
font-size: 16px; |
|
|
|
margin: 0; |
|
|
|
font-style: italic; |
|
|
|
opacity: 0.8; |
|
|
|
letter-spacing: 1px; |
|
|
|
} |
|
|
|
} |
|
|
|