Browse Source
Merge branch 'aiEmotion' of http://39.101.133.168:8807/hongxilin/AIxiaocaishen into ds
ds_hxl
Merge branch 'aiEmotion' of http://39.101.133.168:8807/hongxilin/AIxiaocaishen into ds
ds_hxl
5 changed files with 1406 additions and 0 deletions
-
5src/router/index.js
-
445src/views/components/emoEnergyConverter.vue
-
354src/views/components/emotionDecod.vue
-
560src/views/components/emotionalBottomRadar.vue
-
42src/views/emotionsEcharts.vue
@ -0,0 +1,445 @@ |
|||
<template> |
|||
<div ref="qxnlzhqEchartsRef" id="qxnlzhqEcharts"></div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, onMounted, onBeforeUnmount, toRef, reactive } from "vue"; |
|||
import * as echarts from "echarts"; |
|||
|
|||
defineExpose({ initQXNLZHEcharts }); |
|||
|
|||
let qxnlzhqEchartsRef = ref(null); |
|||
let qxnlzhqEchartsInstance = null; |
|||
|
|||
let regions = reactive([]); |
|||
|
|||
// 设置区域名称 位置 |
|||
function getNameTop(min, max, regionMiidle) { |
|||
// 获取整个图表的高度 |
|||
const chartHeight = qxnlzhqEchartsInstance.getHeight(); |
|||
// 60: 为x轴占的高度 |
|||
return (max - Number(regionMiidle)) / (max - min) * (chartHeight - 60) |
|||
} |
|||
// 设置区域最大值 位置 |
|||
function getNumberTop(min, max, regionMax) { |
|||
// 获取整个图表的高度 |
|||
const chartHeight = qxnlzhqEchartsInstance.getHeight(); |
|||
// 60: 为x轴占的高度 |
|||
return (max - Number(regionMax)) / (max - min) * (chartHeight - 60) |
|||
} |
|||
|
|||
// 生成图形标注(核心逻辑) |
|||
const generateGraphics = (min, max) => { |
|||
return regions.flatMap((region) => { |
|||
const middleY = (Number(region.min) + Number(region.max)) / 2; |
|||
return [ |
|||
// 区域名称(中间位置) |
|||
{ |
|||
type: "text", |
|||
left: '17', // 靠近y轴内侧 |
|||
top: getNameTop(min, max, middleY), |
|||
style: { |
|||
text: region.name, |
|||
fill: region.fontColor, |
|||
fontSize: 14, |
|||
fontWeight: "bold", |
|||
}, |
|||
z: 3, |
|||
}, |
|||
// y轴数值(顶部位置) |
|||
{ |
|||
type: "text", |
|||
left: '25', |
|||
top: getNumberTop(min, max, region.max), |
|||
// top: 100, |
|||
style: { |
|||
text: region.max.toString(), |
|||
fill: region.NumberColor, |
|||
fontSize: 12, |
|||
}, |
|||
z: 3, |
|||
}, |
|||
]; |
|||
}); |
|||
}; |
|||
|
|||
function initQXNLZHEcharts(kline, qxnlzhqData) { |
|||
// 测试数据 !!! 删掉 |
|||
// qxnlzhqData.topxh = ["2025/04/04", "2025/04/15"] |
|||
// qxnlzhqData.lowxh = ["2025/04/08", "2025/04/18"] |
|||
// qxnlzhqData.qixh = ["2025/04/10", "2025/04/21"] |
|||
if (qxnlzhqEchartsInstance) { |
|||
qxnlzhqEchartsInstance.dispose(); |
|||
} |
|||
// 数据 |
|||
let mixData = []; |
|||
kline.forEach((element) => { |
|||
let date = element[0]; |
|||
let value = [element[1], element[2], element[3], element[4]]; |
|||
mixData.push({ |
|||
date, |
|||
value, |
|||
}); |
|||
}); |
|||
|
|||
// 动态区域配置 |
|||
// dd到zc 低吸区------情绪冰点区 ; zc到ht 关注区------认知潜伏区; ht到qs 回调区------多空消化区 ; qs到tp 拉升区------共识加速区; |
|||
// tp到js 突破区------情绪临界区 ; js到yl 警示区-------杠杆失衡区 ; yl到gg 风险区-------情绪熔断区; |
|||
regions = [ |
|||
{ |
|||
min: qxnlzhqData.dd, |
|||
max: qxnlzhqData.zc, |
|||
name: "【情绪冰点区】", |
|||
color: "#e7a5d6", |
|||
fontColor: '#000', |
|||
NumberColor: 'blue', |
|||
}, |
|||
{ |
|||
min: qxnlzhqData.zc, |
|||
max: qxnlzhqData.ht, |
|||
name: "【认知潜伏区】", |
|||
color: "#f36587", |
|||
fontColor: '#000', |
|||
NumberColor: 'blue', |
|||
}, |
|||
{ |
|||
min: qxnlzhqData.ht, |
|||
max: qxnlzhqData.qs, |
|||
name: "【多空消化区】", |
|||
color: "#e99883", |
|||
fontColor: '#000', |
|||
NumberColor: 'blue', |
|||
}, |
|||
{ |
|||
min: qxnlzhqData.qs, |
|||
max: qxnlzhqData.tp, |
|||
name: "【共识加速区】", |
|||
color: "#f0db84", |
|||
fontColor: '#000', |
|||
NumberColor: 'red', |
|||
}, |
|||
{ |
|||
min: qxnlzhqData.tp, |
|||
max: qxnlzhqData.js, |
|||
name: "【情绪临界区】", |
|||
color: "#dbeee3", |
|||
fontColor: 'red', |
|||
NumberColor: 'red', |
|||
}, |
|||
]; |
|||
// gg yl为-1 不绘制部分图表 |
|||
if (Number(qxnlzhqData.yl) != -1) { |
|||
regions.push( |
|||
{ |
|||
min: qxnlzhqData.js, |
|||
max: qxnlzhqData.yl, |
|||
name: "【杠杆失衡区】", |
|||
color: "#9ac2d8", |
|||
fontColor: 'red', |
|||
NumberColor: 'red', |
|||
}, |
|||
) |
|||
} |
|||
if (Number(qxnlzhqData.gg) != -1) { |
|||
regions.push( |
|||
{ |
|||
min: qxnlzhqData.yl, |
|||
max: qxnlzhqData.gg, |
|||
name: "【情绪熔断区】", |
|||
color: "#bce283", |
|||
fontColor: 'red', |
|||
NumberColor: 'red', |
|||
}, |
|||
) |
|||
} |
|||
|
|||
// 计算止盈止损价格 |
|||
const stopProfitPrice = Number(qxnlzhqData.cc) * 1.05; // 止盈价 |
|||
const stopLossPrice = Number(qxnlzhqData.cc) * 0.97; // 止损价 |
|||
// 确定起始和结束位置 |
|||
const startIndex = Math.max(0, mixData.length - 17); |
|||
// 创建完整数据数组 |
|||
const takeProfitData = new Array(mixData.length).fill(null); |
|||
const stopLossData = new Array(mixData.length).fill(null); |
|||
// 填充显示区域的数据 |
|||
for (var i = startIndex; i < mixData.length; i++) { |
|||
takeProfitData[i] = stopProfitPrice; |
|||
stopLossData[i] = stopLossPrice; |
|||
} |
|||
|
|||
// topxh、lowxh、qixh 对应k线染色 |
|||
// 创建中间区域数据 |
|||
const middleRangeData = []; |
|||
const middleRangeData1 = []; |
|||
const markPointData = []; |
|||
mixData.forEach((item, index) => { |
|||
const [open, close, low, high] = item.value; |
|||
const rangeHeight = high - low; |
|||
// const middleThirdStart = low + rangeHeight * (1/3); |
|||
// const middleThirdEnd = low + rangeHeight * (2/3); |
|||
|
|||
let color = null; |
|||
|
|||
if (qxnlzhqData.topxh.includes(item.date)) { |
|||
color = '#000000'; // 黑色 |
|||
} else if (qxnlzhqData.lowxh.includes(item.date)) { |
|||
color = '#1E90FF'; // 蓝色 |
|||
} |
|||
|
|||
|
|||
// 添加中间区域数据 |
|||
if (color) { |
|||
middleRangeData.push({ |
|||
value: [index, close > open ? (close - open) : (open - close)], // 修正数据格式 |
|||
itemStyle: { |
|||
normal: { |
|||
color: color |
|||
} |
|||
}, |
|||
}); |
|||
middleRangeData1.push({ |
|||
value: [index, close > open ? open : close], // 修正数据格式 |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'transparent' |
|||
} |
|||
}, |
|||
}); |
|||
} else { |
|||
middleRangeData.push(null); |
|||
middleRangeData1.push(null); |
|||
} |
|||
|
|||
// 添加文字标记数据 |
|||
if (qxnlzhqData.qixh.includes(item.date)) { |
|||
markPointData.push({ |
|||
name: '起', |
|||
coord: [index, (open + close) / 2], |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'rgba(0,0,0,0)' // 标记点透明 |
|||
} |
|||
}, |
|||
label: { |
|||
normal:{ |
|||
show: true, |
|||
position: 'inside', |
|||
formatter: '起', |
|||
textStyle: { |
|||
color: '#FF0000', |
|||
fontSize: 10, |
|||
// fontWeight: 'bold' |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
// 初始化图表 |
|||
qxnlzhqEchartsInstance = echarts.init(qxnlzhqEchartsRef.value); |
|||
let option; |
|||
// 设置图表配置 |
|||
option = { |
|||
xAxis: { |
|||
type: "category", |
|||
data: mixData.map((item) => item.date), |
|||
axisLabel: { |
|||
rotate: 45, // 日期旋转45度防止重叠 |
|||
}, |
|||
}, |
|||
yAxis: { |
|||
scale: true, |
|||
splitLine: { |
|||
show: false, |
|||
}, |
|||
axisLabel: { // 刻度标签 |
|||
show: false // 不显示刻度标签 |
|||
}, |
|||
axisTick: { // 刻度线 |
|||
show: false // 不显示刻度线 |
|||
}, |
|||
min: |
|||
qxnlzhqData.dd < stopLossPrice * 0.98 |
|||
? Math.floor(qxnlzhqData.dd) |
|||
: Math.floor(stopLossPrice * 0.98), |
|||
max: |
|||
qxnlzhqData.yl > stopProfitPrice * 1.02 |
|||
? Math.ceil(qxnlzhqData.yl) |
|||
: Math.ceil(stopProfitPrice * 1.02), |
|||
}, |
|||
// 自定义区域名称和区域范围值 位置 |
|||
graphic: generateGraphics(qxnlzhqData.dd < stopLossPrice * 0.98 |
|||
? Math.floor(qxnlzhqData.dd) |
|||
: Math.floor(stopLossPrice * 0.98),qxnlzhqData.yl > stopProfitPrice * 1.02 |
|||
? Math.ceil(qxnlzhqData.yl) |
|||
: Math.ceil(stopProfitPrice * 1.02)), |
|||
series: [ |
|||
{ |
|||
type: "candlestick", |
|||
data: mixData.map((item) => item.value), |
|||
z: 1, |
|||
markPoint: { |
|||
symbol: 'circle', |
|||
symbolSize: 10, |
|||
data: markPointData, |
|||
z: 5 // 确保标记显示在最上层 |
|||
}, |
|||
itemStyle: { |
|||
normal: { |
|||
// 阳线样式(收盘 > 开盘) |
|||
color: 'transparent', // 阳线色 |
|||
color0: '#008080', // 阴线色 |
|||
borderColor: '#ff0783', // 阳线边框色(红) |
|||
borderColor0: '#008080', // 阴线边框色(绿) |
|||
} |
|||
}, |
|||
// 实现 分区域背景色 |
|||
markArea: { |
|||
silent: true, |
|||
data: regions.map((region) => [ |
|||
{ |
|||
yAxis: region.min, |
|||
itemStyle: { normal: { color: region.color } }, |
|||
}, |
|||
{ yAxis: region.max }, |
|||
]), |
|||
}, |
|||
}, |
|||
{ |
|||
name: '中间区域', |
|||
type: 'bar', |
|||
stack: 'total', |
|||
data: middleRangeData1, |
|||
barWidth: '20%', |
|||
barCategoryGap: '40%', |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'rgba(0,0,0,0)' // 默认透明 |
|||
} |
|||
}, |
|||
z:2 |
|||
}, |
|||
// 中间区域染色 |
|||
{ |
|||
name: '中间区域', |
|||
type: 'bar', |
|||
stack: 'total', |
|||
data: middleRangeData, |
|||
barWidth: '20%', |
|||
barCategoryGap: '40%', |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'rgba(0,0,0,0)' // 默认透明 |
|||
} |
|||
}, |
|||
z:2 |
|||
}, |
|||
{ |
|||
name: '止盈线', |
|||
type: 'line', |
|||
data: takeProfitData, |
|||
symbol: 'none', |
|||
lineStyle: { |
|||
normal: { |
|||
color: '#ff80ff', // 蓝色 |
|||
width: 2, |
|||
type: 'solid' |
|||
} |
|||
}, |
|||
markPoint: { |
|||
symbol: 'circle', |
|||
symbolSize: 1, |
|||
data: [ |
|||
{ |
|||
coord: [mixData.map((item) => item.value).length - 1, stopProfitPrice], |
|||
itemStyle: { |
|||
color: '#ff80ff' |
|||
}, |
|||
label: { |
|||
normal: { |
|||
show: true, |
|||
position: 'bottom', |
|||
formatter: `{text|止盈: ${stopProfitPrice}}`, |
|||
rich: { |
|||
text: { |
|||
color: '#820a06', |
|||
fontSize: 14, |
|||
fontWeight: 'bold' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
name: '止损线', |
|||
type: 'line', |
|||
data: stopLossData, |
|||
symbol: 'none', |
|||
lineStyle: { |
|||
normal: { |
|||
color: '#080bfd', // 红色 |
|||
width: 2, |
|||
type: 'solid' |
|||
} |
|||
}, |
|||
markPoint: { |
|||
symbol: 'circle', |
|||
symbolSize: 1, |
|||
data: [ |
|||
{ |
|||
coord: [mixData.map((item) => item.value).length - 1, stopLossPrice], |
|||
itemStyle: { |
|||
color: '#080bfd' |
|||
}, |
|||
label: { |
|||
normal: { |
|||
show: true, |
|||
position: 'bottom', |
|||
formatter: `{text|止损: ${stopLossPrice}}`, |
|||
rich: { |
|||
text: { |
|||
color: '#080bfd', |
|||
fontSize: 14, |
|||
fontWeight: 'bold' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
], |
|||
grid: { |
|||
left: "0", |
|||
right: "10", |
|||
top: '10', |
|||
bottom: "0", |
|||
containLabel: true, |
|||
}, |
|||
}; |
|||
// 应用配置 |
|||
qxnlzhqEchartsInstance.setOption(option); |
|||
// 监听窗口大小变化,调整图表尺寸 |
|||
window.addEventListener('resize', () => { |
|||
qxnlzhqEchartsInstance.resize() |
|||
}) |
|||
} |
|||
|
|||
onBeforeUnmount(() => { |
|||
// 组件卸载时销毁图表 |
|||
if (qxnlzhqEchartsInstance) { |
|||
qxnlzhqEchartsInstance.dispose(); |
|||
} |
|||
}); |
|||
</script> |
|||
<style scoped> |
|||
#qxnlzhqEcharts { |
|||
width: 100%; |
|||
height: 700px; |
|||
} |
|||
</style> |
@ -0,0 +1,354 @@ |
|||
<template> |
|||
<!-- <div ref="qxnlzhqEchartsRef" id="qxnlzhqEcharts"></div> --> |
|||
<div class="qxjmqbox"> |
|||
<div ref="KlineCanvs" id="qxjmqEcharts" class="qxjmqbox"></div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, onMounted, onBeforeUnmount, toRef } from "vue"; |
|||
import * as echarts from "echarts"; |
|||
import { color } from "echarts/lib/export"; |
|||
|
|||
defineExpose({ initQXNLZHEcharts }); |
|||
|
|||
const KlineCanvs = ref(null); |
|||
let KlineCanvsChart = null; |
|||
const heatmapData = ref([]); // 存储热力图数据 |
|||
|
|||
// 假数据 |
|||
const rawData2 = ref([]); |
|||
const lineData3 = ref([]); |
|||
function initQXNLZHEcharts(kline, qxnlzhqData) { |
|||
qxnlzhqData[9][9] = 1; |
|||
qxnlzhqData[2][9] = 1; |
|||
rawData2.value = qxnlzhqData; |
|||
// 处理热力图数据 |
|||
const processedHeatmap = []; |
|||
rawData2.value.forEach((item, dateIndex) => { |
|||
const [date, d1, d2, d3, d4, d5, d6, d7, d8, d9] = item; |
|||
processedHeatmap.push([ |
|||
dateIndex, |
|||
0, |
|||
d4, |
|||
d8 == 1 ? "green" : "transparent", |
|||
]); // 数据1 |
|||
processedHeatmap.push([ |
|||
dateIndex, |
|||
1, |
|||
d3, |
|||
d7 == 1 ? "purple" : "transparent", |
|||
]); // 数据2 |
|||
processedHeatmap.push([dateIndex, 2, d2, d6 == 1 ? "red" : "transparent"]); // 数据3 |
|||
processedHeatmap.push([ |
|||
dateIndex, |
|||
3, |
|||
d1, |
|||
d5 == 1 ? "yellow" : "transparent", |
|||
]); // 数据4 |
|||
}); |
|||
heatmapData.value = processedHeatmap; |
|||
// 处理折线图数据 |
|||
lineData3.value = rawData2.value.map((item) => item[9]); |
|||
console.log("热力图数据:", heatmapData.value); |
|||
console.log("折线图数据:", lineData3.value); |
|||
const data = kline; |
|||
// 切割数据方法 |
|||
const spliteDate = (a) => { |
|||
const categoryData = []; |
|||
let value = []; |
|||
for (let i = 0; i < a.length; i++) { |
|||
categoryData.push(a[i].splice(0, 1)[0]); |
|||
value.push(a[i]); |
|||
} |
|||
return { categoryData, value }; |
|||
}; |
|||
const dealData = spliteDate(data); |
|||
// 给配置项 |
|||
const KlineOption = { |
|||
tooltip: { |
|||
trigger: "item", // 触发类型 坐标轴触发 |
|||
// axisPointer: { |
|||
// type: "cross", // 十字准星效果 |
|||
// crossStyle: { |
|||
// color: "#999" |
|||
// } |
|||
// } |
|||
}, |
|||
|
|||
//控制坐标轴 |
|||
grid: [ |
|||
{ top: "5%", height: "40%" }, |
|||
{ top: "45%", height: "35%" }, |
|||
{ top: "80%", height: "2%" }, |
|||
], |
|||
|
|||
visualMap: [ |
|||
{ |
|||
show: false, // 不显示控制条 |
|||
seriesIndex: 1, // 作用于热力图的系列 |
|||
min: 0, |
|||
max: 2000, // 根据您的数据范围调整 |
|||
calculable: true, |
|||
orient: "horizontal", |
|||
left: "center", |
|||
bottom: "15%", |
|||
inRange: { |
|||
color: ["transparent"], |
|||
}, |
|||
}, |
|||
], |
|||
// 横坐标内容 |
|||
xAxis: [ |
|||
{ |
|||
type: "category", |
|||
gridIndex: 0, |
|||
data: dealData.categoryData, |
|||
axisPointer: { |
|||
show: true, |
|||
type: "shadow", |
|||
}, |
|||
axisTick: { show: false }, // 隐藏刻度线 |
|||
axisLabel: { show: false, rotate: 45 }, // 隐藏刻度标签 |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { |
|||
color: "grey", |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
type: "category", |
|||
gridIndex: 1, |
|||
data: dealData.categoryData, |
|||
axisTick: { show: false }, // 隐藏刻度线 |
|||
axisLabel: { show: false, rotate: 45 }, // 隐藏刻度标签 |
|||
splitLine: { |
|||
show: true, |
|||
lineStyle: { |
|||
color: "white", |
|||
// 间隔线类型实线 |
|||
type: "solid", |
|||
}, |
|||
interval: 0, |
|||
}, |
|||
}, |
|||
{ |
|||
type: "category", |
|||
gridIndex: 2, |
|||
data: dealData.categoryData, |
|||
axisLine: { lineStyle: { color: "#8392A5" } }, |
|||
}, |
|||
], |
|||
yAxis: [ |
|||
{ |
|||
scale: true, |
|||
axisLabel: { |
|||
formatter: function (value) { |
|||
return value; |
|||
}, |
|||
}, |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: "#8392A5" }, |
|||
}, |
|||
splitLine: { |
|||
show: false, |
|||
}, |
|||
axisPointer: { |
|||
show: true, |
|||
label: { |
|||
show: true, |
|||
}, |
|||
type: "line", |
|||
}, |
|||
}, |
|||
{ |
|||
gridIndex: 1, |
|||
type: "category", |
|||
data: [0, 1, 2, 3], // 倒序显示 |
|||
axisLine: { lineStyle: { color: "#8392A5" } }, |
|||
axisLabel: { |
|||
show: true, // 显示刻度标签 |
|||
color: "#fff", // 白色文字 |
|||
backgroundColor: "transparent", |
|||
fontSize: 12, |
|||
margin: 8, |
|||
}, |
|||
axisTick: { show: false }, // 隐藏刻度线 |
|||
splitLine: { |
|||
show: true, |
|||
lineStyle: { |
|||
color: "#8392A5", |
|||
width: 1, |
|||
type: "solid", |
|||
}, |
|||
|
|||
interval: 0, // 强制显示间隔 |
|||
}, |
|||
}, |
|||
{ |
|||
gridIndex: 2, |
|||
type: "value", |
|||
axisLine: { |
|||
show: true, |
|||
lineStyle: { color: "#8392A5" }, |
|||
}, |
|||
splitLine: { |
|||
show: !1, |
|||
}, |
|||
axisTick: { show: false }, // 隐藏刻度线 |
|||
axisLabel: { show: false }, // 隐藏刻度标签 |
|||
}, |
|||
], |
|||
// 下拉条 |
|||
dataZoom: [ |
|||
{ |
|||
textStyle: { |
|||
color: "white", // 字体颜色 |
|||
}, |
|||
handleIcon: |
|||
"M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z", |
|||
handleSize: "80%", |
|||
dataBackground: { |
|||
areaStyle: { |
|||
color: "#8392A5", |
|||
}, |
|||
lineStyle: { |
|||
opacity: 0.8, |
|||
color: "#8392A5", |
|||
}, |
|||
}, |
|||
xAxisIndex: [0, 1, 2], |
|||
handleStyle: { |
|||
color: "#fff", |
|||
shadowBlur: 3, |
|||
shadowColor: "rgba(0, 0, 0, 0.6)", |
|||
shadowOffsetX: 2, |
|||
shadowOffsetY: 2, |
|||
}, |
|||
bottom: "0%", // 下移数据缩放滑块 |
|||
}, |
|||
{ |
|||
show: !1, |
|||
type: "slider", |
|||
}, |
|||
{ |
|||
type: "inside", |
|||
}, |
|||
], |
|||
series: [ |
|||
{ |
|||
type: "candlestick", |
|||
name: "\u65e5K", |
|||
// 数据 |
|||
data: dealData.value, |
|||
itemStyle: { |
|||
normal: { |
|||
color0: "red", // 阴线颜色 |
|||
color: "#0CF49B", // 阳线颜色 |
|||
borderColor0: "#FD1050", // 阴线边框颜色 |
|||
borderColor: "#0CF49B", // 阳线边框颜色 |
|||
}, |
|||
}, |
|||
}, |
|||
// 副图热力矩阵 |
|||
{ |
|||
name: "热力矩阵", |
|||
type: "heatmap", |
|||
gridIndex: 1, |
|||
xAxisIndex: 1, |
|||
yAxisIndex: 1, |
|||
data: processedHeatmap, |
|||
coordinateSystem: "cartesian2d", |
|||
// tooltip: { |
|||
// // 覆盖全局 tooltip 配置 |
|||
// formatter: function (params) { |
|||
// const date = params.axisValue; |
|||
// return `${date}:${params.data[2]}`; // 直接取数据中的第3个元素(数值) |
|||
// } |
|||
// }, |
|||
label: { |
|||
normal: { |
|||
show: true, |
|||
color: "#fff", // 文本颜色 |
|||
formatter: function (params) { |
|||
const value = params.value[2]; // 数值 |
|||
const colorType = params.value[3]; // 颜色类型 |
|||
// 使用 rich 富文本格式 |
|||
return `{${colorType}|${value}}`; |
|||
}, |
|||
rich: { |
|||
green: { color: "#27ae60", fontWeight: "bold" }, |
|||
purple: { color: "#8e44ad", fontWeight: "bold" }, |
|||
red: { color: "#FF0000", fontWeight: "bold" }, |
|||
yellow: { color: "#FFFF00", fontWeight: "bold" }, |
|||
normal: { color: "#fff" }, |
|||
}, |
|||
}, |
|||
}, |
|||
itemStyle: { |
|||
normal: { |
|||
color: "transparent", |
|||
borderWidth: 2, |
|||
}, |
|||
}, |
|||
emphasis: { |
|||
itemStyle: { |
|||
shadowBlur: 10, |
|||
shadowColor: "rgba(0, 0, 0, 0.5)", |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
name: "凸起", |
|||
type: "line", |
|||
xAxisIndex: 2, |
|||
yAxisIndex: 2, |
|||
data: lineData3.value, |
|||
color: "black", |
|||
lineStyle: { |
|||
normal: { |
|||
color: "red", |
|||
}, |
|||
}, |
|||
|
|||
symbol: "none", |
|||
emphasis: { |
|||
showSymbol: true, |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
// 创造echarts图 |
|||
KlineCanvsChart = echarts.init(KlineCanvs.value); |
|||
KlineCanvsChart.setOption(KlineOption); |
|||
// 窗口大小变化时重置图表大小 |
|||
window.addEventListener("resize", () => { |
|||
KlineCanvsChart.resize(); |
|||
}); |
|||
} |
|||
const windowHeight = ref(window.innerHeight); |
|||
const updateHeight = () => { |
|||
windowHeight.value = window.innerHeight; // 更新响应式变量 |
|||
}; |
|||
onMounted(() => { |
|||
window.addEventListener("resize", updateHeight); // 监听窗口变化 |
|||
}); |
|||
onBeforeUnmount(() => { |
|||
// 组件卸载时销毁图表 |
|||
if (KlineCanvsChart) { |
|||
KlineCanvsChart.dispose(); |
|||
} |
|||
}); |
|||
</script> |
|||
<style scoped> |
|||
.qxjmqbox { |
|||
background-color: #131a2a; |
|||
} |
|||
#qxjmqEcharts { |
|||
width: 100%; |
|||
height: 600px; |
|||
} |
|||
</style> |
@ -0,0 +1,560 @@ |
|||
<template> |
|||
<div ref="bottomRadarRef" id="bottomRadarChart"></div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref } from 'vue' |
|||
import * as echarts from 'echarts' |
|||
const bottomRadarRef = ref(null) |
|||
let bottomRadarChart = null |
|||
function initEmotionalBottomRadar(KlineData, barAndLineData) { |
|||
let bottomRadarChartDom = document.getElementById('bottomRadarChart') |
|||
bottomRadarChart = echarts.init(bottomRadarChartDom) |
|||
|
|||
// 日期-作为x轴 |
|||
let dateArray = KlineData.map(subArray => subArray[0]) |
|||
// k线,取前四个即可 |
|||
let kLineDataArray = KlineData.map(subArray => subArray.slice(1, 5)) |
|||
// 红线,取第二个值 |
|||
let redLineDataArray = barAndLineData.map(subArray => subArray[1]) |
|||
// 色块数据格式化 |
|||
let barTotalDataArray = barAndLineData.map(subArray => subArray.slice(2, 6)) |
|||
// 删掉 |
|||
// barTotalDataArray[0] = [0, 0, 0, 0] |
|||
// barTotalDataArray[8] = [1, 1, 1, 1] |
|||
// barTotalDataArray[9] = [0, 0, 1, 1] |
|||
// barTotalDataArray[13] = [1, 0, 1, 0] |
|||
// barTotalDataArray[16] = [0, 0, 1, 0] |
|||
// 黄色块、黄色加仓资金、紫色柱子、蓝色柱子 |
|||
let yellowBlockDataArray = [] |
|||
let yellowLineDataArray = [] |
|||
let purpleLineDataArray = [] |
|||
let blueLineDataArray = [] |
|||
let transparentFillingDataArray = [] |
|||
// 黄色块:为1 0-4显示柱体 |
|||
// 黄色加仓资金文字:为1 在4的位置展示文字 |
|||
// 紫色柱子:为1 1-80显示柱体 |
|||
// 蓝色柱子:为1 0-40显示柱体 |
|||
// 因数据要互相叠加展示,所以需要数据处理。base适用于 ECharts 4.x 及以上版本 |
|||
barTotalDataArray.forEach((item) => { |
|||
if (item[0]) { |
|||
yellowBlockDataArray.push(4) |
|||
if (item[3]) { |
|||
// 40-4 |
|||
blueLineDataArray.push(36) |
|||
if (item[2]) { |
|||
// 80-40 |
|||
purpleLineDataArray.push(40) |
|||
transparentFillingDataArray.push(0) |
|||
} else { |
|||
purpleLineDataArray.push(0) |
|||
transparentFillingDataArray.push(0) |
|||
} |
|||
} else { |
|||
blueLineDataArray.push(0) |
|||
if (item[2]) { |
|||
// 80-4 |
|||
purpleLineDataArray.push(76) |
|||
transparentFillingDataArray.push(0) |
|||
} else { |
|||
purpleLineDataArray.push(0) |
|||
transparentFillingDataArray.push(0) |
|||
} |
|||
} |
|||
} else if (!item[0]) { |
|||
yellowBlockDataArray.push(0) |
|||
if (item[3]) { |
|||
blueLineDataArray.push(40) |
|||
if (item[2]) { |
|||
// 80-40 |
|||
purpleLineDataArray.push(40) |
|||
transparentFillingDataArray.push(0) |
|||
} else { |
|||
purpleLineDataArray.push(0) |
|||
transparentFillingDataArray.push(0) |
|||
} |
|||
} else { |
|||
blueLineDataArray.push(0) |
|||
if (item[2]) { |
|||
// 80-1,base为1 |
|||
purpleLineDataArray.push(79) |
|||
transparentFillingDataArray.push(1) |
|||
} else { |
|||
purpleLineDataArray.push(0) |
|||
transparentFillingDataArray.push(0) |
|||
} |
|||
} |
|||
} |
|||
// 加仓资金 |
|||
if (item[1]) { |
|||
yellowLineDataArray.push(1) |
|||
} else if (!item[1]) { |
|||
yellowLineDataArray.push(0) |
|||
} |
|||
}) |
|||
|
|||
// 配置图表选项,很多操作和展示已限制,如果需要需放开 |
|||
let option = { |
|||
// backgroundColor: '#000046', // 设置整个图表的背景色 |
|||
tooltip: { |
|||
show: false, // 不展示tip |
|||
// 当前echart版本十字准星独立渲染了; 4.x+才有showAxisPointer |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'cross', |
|||
crossStyle: { |
|||
color: '#999' |
|||
} |
|||
}, |
|||
backgroundColor: 'rgba(255, 255, 255, 0.8)', |
|||
borderColor: '#ccc', |
|||
borderWidth: 1, |
|||
padding: 10, |
|||
textStyle: { |
|||
color: '#333' |
|||
}, |
|||
formatter: function (params) { |
|||
let result = `<div style="font-weight: bold;">${params[0].name}</div>` |
|||
|
|||
params.forEach(param => { |
|||
let value = param.value |
|||
let color = param.color |
|||
|
|||
if (param.seriesType === 'candlestick') { |
|||
result += `<div style="color: ${color}"><i class="fa fa-square mr-1"></i>${param.seriesName}: 开 ${value[0]}, 收 ${value[1]}, 低 ${value[2]}, 高 ${value[3]}</div>` |
|||
} else { |
|||
result += `<div style="color: ${color}"><i class="fa fa-square mr-1"></i>${param.seriesName}: ${value}</div>` |
|||
} |
|||
}) |
|||
|
|||
return result |
|||
} |
|||
}, |
|||
legend: { |
|||
// data: ['K线', '红线', '色块'], 不要展示图例 |
|||
type: 'scroll', |
|||
pageButtonItemGap: 2, |
|||
pageButtonPosition: 'end', |
|||
textStyle: { |
|||
color: '#666' |
|||
} |
|||
}, |
|||
grid: [ |
|||
{ |
|||
left: '3%', |
|||
right: '3%', |
|||
top: '20px', |
|||
bottom: '50%', |
|||
height: '300px', |
|||
// containLabel: true |
|||
}, |
|||
{ |
|||
left: '3%', |
|||
right: '3%', |
|||
top: '320px', |
|||
bottom: '25%', |
|||
height: '300px', |
|||
// containLabel: true |
|||
}, |
|||
{ |
|||
left: '3%', |
|||
right: '3%', |
|||
top: '620px', |
|||
bottom: '50px', |
|||
height: '300px', |
|||
// containLabel: true |
|||
} |
|||
], |
|||
xAxis: [ |
|||
{ |
|||
type: 'category', |
|||
data: dateArray, |
|||
gridIndex: 0, |
|||
boundaryGap: true, // 保持间距,不要离y轴太近,不然重叠了 |
|||
axisLine: { |
|||
// show: false, |
|||
lineStyle: { |
|||
color: '#837b7b', // x轴线颜色 |
|||
} |
|||
}, |
|||
axisTick: { |
|||
show: false |
|||
}, |
|||
axisLabel: { |
|||
show: false |
|||
}, |
|||
splitLine: { |
|||
show: false // 不要x轴的分割线 |
|||
}, |
|||
axisPointer: { |
|||
link: { |
|||
xAxisIndex: 'all' |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
type: 'category', |
|||
data: dateArray, |
|||
gridIndex: 1, |
|||
boundaryGap: true, |
|||
axisLine: { |
|||
// show: false, |
|||
lineStyle: { |
|||
// color: '#008000' |
|||
color: '#837b7b' |
|||
} |
|||
}, |
|||
axisTick: { |
|||
show: false |
|||
}, |
|||
axisLabel: { |
|||
show: false |
|||
}, |
|||
splitLine: { |
|||
show: false |
|||
}, |
|||
axisPointer: { |
|||
link: { |
|||
xAxisIndex: 'all' |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
type: 'category', |
|||
data: dateArray, |
|||
gridIndex: 2, |
|||
axisLine: { |
|||
lineStyle: { |
|||
color: '#837b7b' |
|||
} |
|||
}, |
|||
axisTick: { |
|||
show: true, // 显示刻度线 |
|||
alignWithLabel: true, |
|||
lineStyle: { |
|||
color: '#999', // 颜色 |
|||
width: 1, // 宽度 |
|||
type: 'solid' // 线样式(solid/dashed/dotted) |
|||
} |
|||
}, |
|||
axisLabel: { |
|||
color: '#000000', |
|||
interval: 'auto', |
|||
rotate: 45 |
|||
}, |
|||
splitLine: { |
|||
show: false |
|||
}, |
|||
axisPointer: { |
|||
link: { |
|||
xAxisIndex: 'all' |
|||
} |
|||
} |
|||
} |
|||
], |
|||
yAxis: [ |
|||
{ |
|||
type: 'value', |
|||
gridIndex: 0, |
|||
splitNumber: 4, |
|||
axisLine: { |
|||
lineStyle: { |
|||
color: '#837b7b' // y轴坐标轴颜色 |
|||
} |
|||
}, |
|||
axisTick: { |
|||
show: true |
|||
}, |
|||
axisLabel: { |
|||
width: 50, // 宽度限制 |
|||
color: '#000000', |
|||
formatter: function (value, index) { |
|||
if (index === 0) { |
|||
return '0' |
|||
} |
|||
return value.toFixed(2) |
|||
} |
|||
}, |
|||
splitLine: { |
|||
show: false, |
|||
lineStyle: { |
|||
color: '#837b7b', |
|||
type: 'dotted' // 设置网格线类型 dotted:虚线 solid:实线 |
|||
} |
|||
}, |
|||
// min: 4, |
|||
scale: true, // 不强制包含0,不然k线图底部空余太多 |
|||
}, |
|||
{ |
|||
type: 'value', |
|||
gridIndex: 1, |
|||
splitNumber: 3, |
|||
axisLine: { |
|||
lineStyle: { |
|||
color: '#837b7b' |
|||
} |
|||
}, |
|||
axisTick: { |
|||
show: true |
|||
}, |
|||
splitNumber: 5, // 刻度数量 |
|||
axisLabel: { |
|||
width: 50, // 宽度限制 |
|||
color: '#000000', |
|||
formatter: function(value, index) { |
|||
// 如果没有刻度数量,其他方法获取不到y轴刻度总长 |
|||
if (index === 0) { |
|||
return '0' |
|||
} else if (index === 5) { |
|||
return '' |
|||
} |
|||
return value |
|||
} |
|||
}, |
|||
splitLine: { |
|||
show: false, |
|||
lineStyle: { |
|||
color: '#837b7b', |
|||
type: 'dotted' |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
type: 'value', |
|||
gridIndex: 2, |
|||
splitNumber: 2, |
|||
axisLine: { |
|||
lineStyle: { |
|||
color: '#837b7b' |
|||
} |
|||
}, |
|||
axisTick: { |
|||
show: true |
|||
}, |
|||
splitNumber: 5, // 刻度数量 |
|||
axisLabel: { |
|||
width: 50, // 宽度限制 |
|||
color: '#000000', |
|||
formatter: function(value, index) { |
|||
if (index === 5) { |
|||
return '' |
|||
} |
|||
return value |
|||
} |
|||
}, |
|||
splitLine: { |
|||
show: false, |
|||
lineStyle: { |
|||
color: '#837b7b', |
|||
type: 'dotted' // 设置网格线类型 dotted:虚线 solid:实线 |
|||
} |
|||
}, |
|||
splitNumber: 5, |
|||
min: function(value) { |
|||
return 0 // 最小值 |
|||
}, |
|||
max: function(value) { |
|||
return value.max + 10 // 比最大值高10, 避免最高点和上一个图表x轴重合 |
|||
} |
|||
} |
|||
], |
|||
dataZoom: [ |
|||
{ |
|||
type: 'slider', |
|||
xAxisIndex: [0, 2], |
|||
// start: 80, |
|||
// end: 100, |
|||
show: false |
|||
}, |
|||
{ |
|||
type: 'slider', |
|||
xAxisIndex: [0, 2], |
|||
// start: 80, |
|||
// end: 100, |
|||
show: false |
|||
}, |
|||
{ |
|||
type: 'slider', |
|||
xAxisIndex: [0, 2], |
|||
// 一次性全部 or 后20% |
|||
// start: 80, |
|||
// end: 100, |
|||
show: false, // 先不显示 |
|||
borderColor: '#CFD6E3', |
|||
dataBackground: { |
|||
// 线 |
|||
lineStyle: { |
|||
color: '#CFD6E3' |
|||
}, |
|||
// 阴影 |
|||
areaStyle: { |
|||
color: 'rgba(241,243,247,0.5)' |
|||
} |
|||
} |
|||
}, |
|||
], |
|||
series: [ |
|||
{ |
|||
name: 'K线', |
|||
type: 'candlestick', |
|||
data: kLineDataArray, |
|||
xAxisIndex: 0, |
|||
yAxisIndex: 0, |
|||
itemStyle: { |
|||
color: '#ef232a', // 上涨颜色 (红) |
|||
color0: '#14b143', // 下跌颜色 (绿) |
|||
borderColor: '#ef232a', |
|||
borderColor0: '#14b143', |
|||
normal: { |
|||
color: '#ef232a', // 上涨颜色 (红) |
|||
color0: '#14b143', // 下跌颜色 (绿) |
|||
borderColor: '#ef232a', |
|||
borderColor0: '#14b143', |
|||
opacity: function(params) { |
|||
// 收盘价 > 开盘价时为阳线,设置边框不透明、填充透明 |
|||
return params.data[2] > params.data[1] ? 0 : 1 |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
name: '红线', |
|||
type: 'line', |
|||
data: redLineDataArray, |
|||
xAxisIndex: 1, |
|||
yAxisIndex: 1, |
|||
symbol: 'none', |
|||
sampling: 'average', |
|||
itemStyle: { |
|||
normal: { |
|||
color: '#ef232a' |
|||
} |
|||
}, |
|||
areaStyle: { |
|||
color: { |
|||
type: 'linear', |
|||
x: 0, |
|||
y: 0, |
|||
x2: 0, |
|||
y2: 1, |
|||
colorStops: [ |
|||
{ offset: 0, color: 'rgba(33, 150, 243, 0.4)' }, |
|||
{ offset: 1, color: 'rgba(33, 150, 243, 0)' } |
|||
] |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
name: '基础base', |
|||
type: 'bar', |
|||
stack: 'total', |
|||
// barGap: '-100%', // 重叠 |
|||
xAxisIndex: 2, |
|||
yAxisIndex: 2, |
|||
barCategoryGap: '0%', |
|||
itemStyle: { |
|||
normal: { |
|||
color: '#ffffff', |
|||
borderWidth: 0, |
|||
} |
|||
}, |
|||
data: transparentFillingDataArray, |
|||
}, |
|||
{ |
|||
name: '黄色块', |
|||
type: 'bar', |
|||
stack: 'total', |
|||
// barGap: '-100%', // 重叠 |
|||
xAxisIndex: 2, |
|||
yAxisIndex: 2, |
|||
barCategoryGap: '0%', // 类目间柱条间距为0 |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'rgba(255, 255, 0, 1)', |
|||
borderWidth: 0, |
|||
// 加仓资金的文字显示 |
|||
label: { |
|||
show: (params) => { |
|||
return yellowLineDataArray[params.dataIndex] > 0 |
|||
}, |
|||
position: 'top', |
|||
textStyle: { |
|||
color: 'rgba(255, 255, 0, 1)' |
|||
}, |
|||
formatter: (params) => { |
|||
return yellowLineDataArray[params.dataIndex] > 0 ? '加仓资金' : '' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
data: yellowBlockDataArray, |
|||
}, |
|||
{ |
|||
name: '蓝色', |
|||
type: 'bar', |
|||
stack: 'total', |
|||
xAxisIndex: 2, |
|||
yAxisIndex: 2, |
|||
barCategoryGap: '0%', // 类目间柱条间距为0 |
|||
label: { |
|||
show: true, |
|||
position: 'inside' |
|||
}, |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'rgba(34, 196, 190, 1)', |
|||
borderWidth: 0 |
|||
} |
|||
}, |
|||
data: blueLineDataArray |
|||
}, |
|||
{ |
|||
name: '紫色', |
|||
type: 'bar', |
|||
stack: 'total', |
|||
xAxisIndex: 2, |
|||
yAxisIndex: 2, |
|||
barCategoryGap: '0%', // 类目间柱条间距为0 |
|||
label: { |
|||
show: true, |
|||
position: 'inside' |
|||
}, |
|||
itemStyle: { |
|||
normal: { |
|||
color: 'rgba(191, 87, 222, 1)', |
|||
borderWidth: 0 |
|||
} |
|||
}, |
|||
data: purpleLineDataArray |
|||
}, |
|||
] |
|||
} |
|||
|
|||
// 使用配置项显示图表 |
|||
bottomRadarChart.setOption(option) |
|||
|
|||
// 监听窗口大小变化,调整图表尺寸 |
|||
window.addEventListener('resize', () => { |
|||
bottomRadarChart.resize() |
|||
}) |
|||
} |
|||
|
|||
// 暴露给父级 |
|||
defineExpose({ |
|||
initEmotionalBottomRadar |
|||
}) |
|||
|
|||
onBeforeUnmount(() => { |
|||
// 组件卸载时销毁图表 |
|||
if (bottomRadarChart) { |
|||
bottomRadarChart.dispose() |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<style> |
|||
#bottomRadarChart{ |
|||
width: 100%; |
|||
height: 1040px; |
|||
} |
|||
</style> |
@ -0,0 +1,42 @@ |
|||
<template> |
|||
AI情绪大模型 |
|||
<emoEnergyConverter ref="emoEnergyConverterRef"></emoEnergyConverter> |
|||
<emotionDecod ref="emotionDecodRef"></emotionDecod> |
|||
<EmotionalBottomRadar ref="emotionalBottomRadarRef"></EmotionalBottomRadar> |
|||
</template> |
|||
<script setup> |
|||
import emoEnergyConverter from '@/views/components/emoEnergyConverter.vue' |
|||
import emotionDecod from '@/views/components/emotionDecod.vue' |
|||
import EmotionalBottomRadar from '@/views/components/emotionalBottomRadar.vue' |
|||
import { ref, onMounted, onBeforeUnmount, toRef } from 'vue'; |
|||
import axios from "axios"; |
|||
const emoEnergyConverterRef = ref(null) |
|||
const emotionDecodRef = ref(null) |
|||
const emotionalBottomRadarRef = ref(null) |
|||
|
|||
|
|||
onMounted(()=>{ |
|||
axios({ |
|||
method: "post",//请求方式 |
|||
url: 'http://39.101.133.168:8828/link/api/aiEmotion/client/getAiEmotionData',//请求接口 |
|||
data: { |
|||
"token": "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs", |
|||
"market": "my", |
|||
"code": "1295", |
|||
"language": "cn" |
|||
},//数据 |
|||
headers: { |
|||
'content-type': 'application/json' |
|||
},//请求头参数 |
|||
}).then((res) => { |
|||
nextTick(()=>{ |
|||
emoEnergyConverterRef.value.initQXNLZHEcharts(res.data.data.KLine20, res.data.data.QXNLZHQ) |
|||
emotionDecodRef.value.initQXNLZHEcharts(res.data.data.KLine20, res.data.data.QXJMQ) |
|||
|
|||
// 情绪探底雷达 |
|||
emotionalBottomRadarRef.value.initEmotionalBottomRadar(res.data.data.KLine20, res.data.data.QXTDLD) |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
</script> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue