|
|
@ -1,10 +1,6 @@ |
|
|
|
<template> |
|
|
|
<div class="ai-emotion-container"> |
|
|
|
<!-- 居中的图片 --> |
|
|
|
<img src="@/assets/img/AiEmotion/round01.jpg" alt="AI情绪图片" class="center-image" |
|
|
|
:class="{ rotating: isRotating }" /> |
|
|
|
|
|
|
|
<!-- 显示消息内容 --> |
|
|
|
<!-- 消息显示区域 --> |
|
|
|
<div class="user-input-display"> |
|
|
|
<div v-for="(message, index) in messages" :key="index" class="message-container"> |
|
|
|
<!-- 用户输入内容 --> |
|
|
@ -13,10 +9,7 @@ |
|
|
|
</div> |
|
|
|
<!-- AI返回结果 --> |
|
|
|
<div v-if="message.sender === 'ai'" class="message-bubble ai-message"> |
|
|
|
<div v-if="message.chartData" id="kline-chart" style="width: 100%; height: 300px;"></div> |
|
|
|
<div v-else> |
|
|
|
{{ message.response }} |
|
|
|
</div> |
|
|
|
{{ message.text }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -27,100 +20,298 @@ |
|
|
|
<button @click="sendMessage" class="send-button">发送</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 渲染整个页面 --> |
|
|
|
<div class="class01"> |
|
|
|
<!-- 四维矩阵图 --> |
|
|
|
<div class="class02"> |
|
|
|
<span class="span01">房间卡方法啊付了款公交卡阿济格</span> |
|
|
|
<span class="span02"> 2025/06/07</span> |
|
|
|
</div> |
|
|
|
<img src="@/assets/img/AiEmotion/L1.png" alt="情绪监控"> |
|
|
|
<!-- 温度计图表 --> |
|
|
|
<div class="class03"> |
|
|
|
|
|
|
|
</div> |
|
|
|
<img src="@/assets/img/AiEmotion/L2.png" alt="情绪解码"> |
|
|
|
<!-- 情绪解码器图表 --> |
|
|
|
<div class="class04"> |
|
|
|
<emotionDecod ref="emotionDecodRef"></emotionDecod> |
|
|
|
</div> |
|
|
|
<img src="@/assets/img/AiEmotion/L3.png" alt="情绪推演"> |
|
|
|
<!-- 情绪探底雷达图表 --> |
|
|
|
<div class="class05"> |
|
|
|
<emotionalBottomRadar ref="emotionalBottomRadarRef"></emotionalBottomRadar> |
|
|
|
</div> |
|
|
|
<img src="@/assets/img/AiEmotion/L4.png" alt="情绪套利"> |
|
|
|
<!-- 情绪能量转化器图表 --> |
|
|
|
<div class="class06"> |
|
|
|
<emoEnergyConverter ref="emoEnergyConverterRef"></emoEnergyConverter> |
|
|
|
</div> |
|
|
|
<!-- 核心看点 --> |
|
|
|
<div class="class07"> |
|
|
|
|
|
|
|
</div> |
|
|
|
<!-- 核心逻辑 --> |
|
|
|
<img src="@/assets/img/AiEmotion/量子神经决策树.png" alt=""> |
|
|
|
<div class="class08"> |
|
|
|
|
|
|
|
</div> |
|
|
|
<div class="class09"></div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup> |
|
|
|
import { ref, reactive, nextTick } from 'vue'; |
|
|
|
import { ref, reactive } from 'vue'; |
|
|
|
import { getReplyAPI } from '@/api/AiEmotionApi.js'; // 导入工作流接口方法 |
|
|
|
import * as echarts from 'echarts'; // 引入 ECharts |
|
|
|
import axios from 'axios'; |
|
|
|
import emotionDecod from '@/views/components/emotionDecod.vue'; // 导入情绪解码组件 |
|
|
|
import emotionalBottomRadar from '@/views/components/emotionalBottomRadar.vue'; // 导入情绪探底雷达图组件 |
|
|
|
import emoEnergyConverter from '@/views/components/emotionalBottomRadar.vue'; // 导入情绪能量转化器组件 |
|
|
|
|
|
|
|
|
|
|
|
const emoEnergyConverterRef = ref(null) |
|
|
|
const emotionDecodRef = ref(null) |
|
|
|
const emotionalBottomRadarRef = ref(null) |
|
|
|
|
|
|
|
const userInput = ref(''); |
|
|
|
const isRotating = ref(false); |
|
|
|
const messages = ref([]); |
|
|
|
|
|
|
|
// 请求工作流接口 |
|
|
|
async function sendMessage() { |
|
|
|
console.log('发送内容:', userInput.value); |
|
|
|
|
|
|
|
// 图片旋转逻辑 |
|
|
|
isRotating.value = true; |
|
|
|
setTimeout(() => { |
|
|
|
isRotating.value = false; // 停止旋转 |
|
|
|
}, 2000); // 旋转持续2秒 |
|
|
|
|
|
|
|
// 准备调用接口的参数 |
|
|
|
const params = { |
|
|
|
content: userInput.value, |
|
|
|
userData: { |
|
|
|
token: localStorage.getItem('localToken'), |
|
|
|
language: 'cn', |
|
|
|
brainPrivilegeState: "1", |
|
|
|
swordPrivilegeState: "1", |
|
|
|
stockForecastPrivile: "1", |
|
|
|
spaceForecastPrivile: "1", |
|
|
|
aibullPrivilegeState: "1", |
|
|
|
aigoldBullPrivilegeS: "1", |
|
|
|
airadarPrivilegeStat: "1", |
|
|
|
marketList: 'hk,cn,usa,my,sg,vi,in,gb', |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
// 调用工作流接口 |
|
|
|
// 检查用户输入内容是否为空 |
|
|
|
if (userInput.value.trim()) { |
|
|
|
const userMessage = reactive({ sender: 'user', text: userInput.value }); |
|
|
|
messages.value.push(userMessage); |
|
|
|
userInput.value = ''; // 清空输入框 |
|
|
|
|
|
|
|
try { |
|
|
|
// 调用工作流接口 |
|
|
|
const params = { |
|
|
|
content: userMessage.text, |
|
|
|
userData: { |
|
|
|
token: '9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs', |
|
|
|
language: 'cn', |
|
|
|
brainPrivilegeState: '1', |
|
|
|
swordPrivilegeState: '1', |
|
|
|
stockForecastPrivile: '1', |
|
|
|
spaceForecastPrivile: '1', |
|
|
|
aibullPrivilegeState: '1', |
|
|
|
aigoldBullPrivilegeS: '1', |
|
|
|
airadarPrivilegeStat: '1', |
|
|
|
marketList: 'hk,cn,usa,my,sg,vi,in,gb', |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
const result = await getReplyAPI(params); |
|
|
|
const response = await result.json(); // 解析返回的 JSON 数据 |
|
|
|
console.log('工作流接口返回数据:', response); |
|
|
|
|
|
|
|
// 解析 data 字段 |
|
|
|
const parsedData = JSON.parse(response.data); // 将字符串形式的 JSON 转换为对象 |
|
|
|
console.log('解析后的数据:', parsedData); |
|
|
|
|
|
|
|
if (parsedData && parsedData.market && parsedData.code) { |
|
|
|
console.log('工作流接口返回股票信息:', parsedData); |
|
|
|
|
|
|
|
// 请求数据接口 |
|
|
|
fetchData(parsedData.code, parsedData.market); |
|
|
|
} else { |
|
|
|
console.error('工作流接口返回非股票信息:', parsedData.refuse); |
|
|
|
messages.value.push({ |
|
|
|
sender: 'ai', |
|
|
|
text: `工作流接口返回信息: ${parsedData.refuse || '未知错误'}`, |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('请求工作流接口失败:', error); |
|
|
|
messages.value.push({ |
|
|
|
sender: 'ai', |
|
|
|
text: '工作流接口请求失败,请稍后再试。', |
|
|
|
}); |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.warn('用户输入内容为空,无法发送请求'); |
|
|
|
messages.value.push({ |
|
|
|
sender: 'ai', |
|
|
|
text: '请输入内容后再发送。', |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 请求数据接口 |
|
|
|
async function fetchData(code, market) { |
|
|
|
try { |
|
|
|
const result = await getReplyAPI(params); |
|
|
|
const response = await result.json(); // 解析返回的 JSON 数据 |
|
|
|
console.log('工作流接口返回数据:', response); |
|
|
|
const stockDataParams = { |
|
|
|
token: '9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs', |
|
|
|
market: market, |
|
|
|
code: code, |
|
|
|
language: 'cn', |
|
|
|
}; |
|
|
|
|
|
|
|
const stockDataResult = await axios.post( |
|
|
|
'http://39.101.133.168:8828/link/api/aiEmotion/client/getAiEmotionData', |
|
|
|
stockDataParams, |
|
|
|
{ |
|
|
|
headers: { |
|
|
|
'Content-Type': 'application/json', |
|
|
|
}, |
|
|
|
} |
|
|
|
); |
|
|
|
|
|
|
|
if (response && response.chartData) { |
|
|
|
const aiMessage = reactive({ sender: 'ai', chartData: response.chartData }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
const stockDataResponse = stockDataResult.data; // 获取返回的数据 |
|
|
|
console.log('图表数据接口返回数据:', stockDataResponse); |
|
|
|
|
|
|
|
// 渲染 K 线图 |
|
|
|
nextTick(() => { |
|
|
|
renderKLineChart(response.chartData); |
|
|
|
if (stockDataResponse.data) { |
|
|
|
messages.value.push({ |
|
|
|
sender: 'ai', |
|
|
|
text: `股票数据已成功获取: ${JSON.stringify(stockDataResponse.data)}`, |
|
|
|
}); |
|
|
|
} else { |
|
|
|
const aiMessage = reactive({ sender: 'ai', response: 'AI思考失败,请稍后再试...' }); |
|
|
|
messages.value.push(aiMessage); |
|
|
|
console.error('图表数据接口返回数据不完整'); |
|
|
|
messages.value.push({ |
|
|
|
sender: 'ai', |
|
|
|
text: '图表数据接口返回数据不完整,请稍后再试。', |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('请求失败:', error); |
|
|
|
const errorMessage = reactive({ sender: 'ai', response: '请求失败,请稍后重试' }); |
|
|
|
messages.value.push(errorMessage); |
|
|
|
console.error('请求图表数据接口失败:', error); |
|
|
|
messages.value.push({ |
|
|
|
sender: 'ai', |
|
|
|
text: '图表数据接口请求失败,请稍后再试。', |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function renderKLineChart(chartData) { |
|
|
|
const chartDom = document.getElementById('kline-chart'); |
|
|
|
const myChart = echarts.init(chartDom); |
|
|
|
|
|
|
|
const option = { |
|
|
|
title: { |
|
|
|
text: 'K线图', |
|
|
|
left: 'center', |
|
|
|
}, |
|
|
|
tooltip: { |
|
|
|
trigger: 'axis', |
|
|
|
}, |
|
|
|
xAxis: { |
|
|
|
type: 'category', |
|
|
|
data: chartData.dates, |
|
|
|
}, |
|
|
|
yAxis: { |
|
|
|
type: 'value', |
|
|
|
}, |
|
|
|
series: [ |
|
|
|
{ |
|
|
|
type: 'candlestick', |
|
|
|
data: chartData.values, |
|
|
|
}, |
|
|
|
], |
|
|
|
}; |
|
|
|
// 渲染组件图表的方法 |
|
|
|
function renderCharts(data) { |
|
|
|
nextTick(() => { |
|
|
|
// 渲染情绪能量转化器图表 |
|
|
|
if (emoEnergyConverterRef.value && data.QXNLZHQ) { |
|
|
|
emoEnergyConverterRef.value.initQXNLZHEcharts(data.KLine20, data.QXNLZHQ); |
|
|
|
} |
|
|
|
|
|
|
|
// 渲染情绪解码器图表 |
|
|
|
if (emotionDecodRef.value && data.QXJMQ) { |
|
|
|
emotionDecodRef.value.initQXNLZHEcharts(data.KLine20, data.QXJMQ); |
|
|
|
} |
|
|
|
|
|
|
|
myChart.setOption(option); |
|
|
|
// 渲染情绪探底雷达图表 |
|
|
|
if (emotionalBottomRadarRef.value && data.QXTDLD) { |
|
|
|
emotionalBottomRadarRef.value.initEmotionalBottomRadar(data.KLine20, data.QXTDLD); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.class08 { |
|
|
|
background-image: url('@/assets/img/AiEmotion/tree.png'); |
|
|
|
/* 使用导入的背景图片 */ |
|
|
|
background-size: cover; |
|
|
|
/* 确保背景图片完整显示 */ |
|
|
|
background-repeat: no-repeat; |
|
|
|
/* 防止背景图片重复 */ |
|
|
|
width: 100%; |
|
|
|
/* 设置容器宽度 */ |
|
|
|
height: auto; |
|
|
|
/* 高度根据内容动态变化 */ |
|
|
|
min-height: 400px; |
|
|
|
/* 设置最小高度,确保图片显示 */ |
|
|
|
} |
|
|
|
|
|
|
|
.class06 { |
|
|
|
background-image: url('@/assets/img/AiEmotion/bk03.png'); |
|
|
|
/* 使用导入的背景图片 */ |
|
|
|
background-size: cover; |
|
|
|
/* 确保背景图片完整显示 */ |
|
|
|
background-repeat: no-repeat; |
|
|
|
/* 防止背景图片重复 */ |
|
|
|
width: 100%; |
|
|
|
/* 设置容器宽度 */ |
|
|
|
height: auto; |
|
|
|
/* 高度根据内容动态变化 */ |
|
|
|
min-height: 575px; |
|
|
|
/* 设置最小高度,确保图片显示 */ |
|
|
|
} |
|
|
|
|
|
|
|
.class05 { |
|
|
|
background-image: url('@/assets/img/AiEmotion/bk03.png'); |
|
|
|
/* 使用导入的背景图片 */ |
|
|
|
background-size: cover; |
|
|
|
/* 确保背景图片完整显示 */ |
|
|
|
background-repeat: no-repeat; |
|
|
|
/* 防止背景图片重复 */ |
|
|
|
width: 100%; |
|
|
|
/* 设置容器宽度 */ |
|
|
|
height: auto; |
|
|
|
/* 高度根据内容动态变化 */ |
|
|
|
min-height: 575px; |
|
|
|
/* 设置最小高度,确保图片显示 */ |
|
|
|
} |
|
|
|
|
|
|
|
.class04 { |
|
|
|
background-image: url('@/assets/img/AiEmotion/bk03.png'); |
|
|
|
/* 使用导入的背景图片 */ |
|
|
|
background-size: cover; |
|
|
|
/* 确保背景图片完整显示 */ |
|
|
|
background-repeat: no-repeat; |
|
|
|
/* 防止背景图片重复 */ |
|
|
|
width: 100%; |
|
|
|
/* 设置容器宽度 */ |
|
|
|
height: auto; |
|
|
|
/* 高度根据内容动态变化 */ |
|
|
|
min-height: 575px; |
|
|
|
/* 设置最小高度,确保图片显示 */ |
|
|
|
} |
|
|
|
|
|
|
|
.class03 { |
|
|
|
background-image: url('@/assets/img/AiEmotion/bk02.png'); |
|
|
|
/* 使用导入的背景图片 */ |
|
|
|
background-size: cover; |
|
|
|
/* 确保背景图片完整显示 */ |
|
|
|
background-repeat: no-repeat; |
|
|
|
/* 防止背景图片重复 */ |
|
|
|
width: 100%; |
|
|
|
/* 设置容器宽度 */ |
|
|
|
height: auto; |
|
|
|
/* 高度根据内容动态变化 */ |
|
|
|
min-height: 460px; |
|
|
|
/* 设置最小高度,确保图片显示 */ |
|
|
|
} |
|
|
|
|
|
|
|
.span01 { |
|
|
|
background-image: url('@/assets/img/AiEmotion/bk01.png'); |
|
|
|
/* 使用导入的背景图片 */ |
|
|
|
background-size: cover; |
|
|
|
/* 背景图片覆盖整个容器 */ |
|
|
|
background-repeat: no-repeat; |
|
|
|
/* 防止背景图片重复 */ |
|
|
|
display: inline-block; |
|
|
|
/* 确保容器是块级元素 */ |
|
|
|
padding: 10px; |
|
|
|
/* 添加内边距以显示内容 */ |
|
|
|
color: #fff; |
|
|
|
/* 设置文字颜色以确保可读性 */ |
|
|
|
border-radius: 5px; |
|
|
|
/* 添加圆角 */ |
|
|
|
} |
|
|
|
|
|
|
|
.class01 { |
|
|
|
width: 60%; |
|
|
|
/* 固定容器宽度 */ |
|
|
|
min-height: 100px; |
|
|
|
/* 设置最小高度,确保初始显示 */ |
|
|
|
height: auto; |
|
|
|
/* 高度根据内容动态变化 */ |
|
|
|
padding: 20px; |
|
|
|
/* 添加内边距,确保内容与边界有间距 */ |
|
|
|
box-sizing: border-box; |
|
|
|
/* 包括内边距在宽度和高度计算中 */ |
|
|
|
} |
|
|
|
|
|
|
|
.ai-emotion-container { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
@ -130,25 +321,8 @@ function renderKLineChart(chartData) { |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
.center-image { |
|
|
|
max-width: 20%; |
|
|
|
/* 缩小为原大小的50% */ |
|
|
|
height: auto; |
|
|
|
margin-bottom: 20px; |
|
|
|
border-radius: 100%; |
|
|
|
/* 将图片变成圆角 */ |
|
|
|
transition: transform 2s ease-out; |
|
|
|
/* 添加旋转动画 */ |
|
|
|
} |
|
|
|
|
|
|
|
.center-image.rotating { |
|
|
|
transform: rotate(360deg); |
|
|
|
/* 旋转360度 */ |
|
|
|
} |
|
|
|
|
|
|
|
.user-input-display { |
|
|
|
margin-top: 20px; |
|
|
|
/* 添加与图片的间距 */ |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
width: 100%; |
|
|
@ -168,7 +342,6 @@ function renderKLineChart(chartData) { |
|
|
|
text-align: left; |
|
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
|
|
|
align-self: flex-end; |
|
|
|
/* 用户消息靠右 */ |
|
|
|
} |
|
|
|
|
|
|
|
.ai-message { |
|
|
@ -180,7 +353,6 @@ function renderKLineChart(chartData) { |
|
|
|
text-align: left; |
|
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); |
|
|
|
align-self: flex-start; |
|
|
|
/* AI消息靠左 */ |
|
|
|
} |
|
|
|
|
|
|
|
.input-container { |
|
|
@ -192,7 +364,6 @@ function renderKLineChart(chartData) { |
|
|
|
.fixed-bottom { |
|
|
|
position: fixed; |
|
|
|
bottom: 100px; |
|
|
|
/* 向上移动10px */ |
|
|
|
left: 0; |
|
|
|
width: 100%; |
|
|
|
background-color: #f8f9fa; |
|
|
@ -206,7 +377,6 @@ function renderKLineChart(chartData) { |
|
|
|
border: 1px solid #ccc; |
|
|
|
border-radius: 5px; |
|
|
|
width: calc(100% - 120px); |
|
|
|
/* 适配按钮宽度 */ |
|
|
|
} |
|
|
|
|
|
|
|
.send-button { |
|
|
|