|
|
@ -42,9 +42,6 @@ |
|
|
|
</div> |
|
|
|
<!-- 股票标签页 --> |
|
|
|
<StockTabs /> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 渲染整个页面 --> |
|
|
|
<div v-if="isPageLoaded" class="main"> |
|
|
|
<div class="main-content-wrapper"> |
|
|
@ -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; |
|
|
|