You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

522 lines
13 KiB

<template>
<!-- <div ref="qxnlzhqEchartsRef" id="qxnlzhqEcharts"></div> -->
<div class="qxjmqbox">
<div ref="KlineCanvs" class="qxjmqEcharts"></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][0]);
value.push(a[i].slice(1)); // 使用 slice 而不是 splice
}
return { categoryData, value };
};
const dealData = spliteDate(data);
// 给配置项
const KlineOption = {
tooltip: {
trigger: 'item', // 触发类型 坐标轴触发
axisPointer: {
type: 'cross', // 十字准星效果
crossStyle: {
color: '#999'
}
},
formatter: function (params) {
const date = params.name
// 开收低高分别取参数的第2到第5个数
const open = params.data[1]
const close = params.data[2]
const low = params.data[3]
const high = params.data[4]
return `日期: ${date}<br/>开盘价: ${open}<br/>收盘价: ${close}<br/>最低价: ${low}<br/>最高价: ${high}`
}
},
//控制坐标轴
grid: [
{
top: "5%",
height: window.innerWidth <= 768 ? "30%" : "40%"
},
{ top: window.innerWidth <= 768 ? "35%" :"45%",
height: "35%" },
{ top: window.innerWidth <= 768 ? "70%" : "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: "line",
label: {
show: true,
backgroundColor: 'rgba(0,191,255)',
color: 'black'
},
},
axisTick: { show: false }, // 隐藏刻度线
axisLabel: { show: false, rotate: 45 }, // 隐藏刻度标签
axisLine: {
show: true,
lineStyle: {
color: "white",
},
},
},
{
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: "white" } },
axisPointer: {
show: false,
label: {
show: false,
},
type: "line",
},
axisTick: {
show: true,
alignWithLabel: true, // 刻度线与标签对齐
lineStyle: {
color: "white", // 与十字线颜色保持一致
width: 1,
type: "dashed" // 与十字线样式保持一致
}
},
},
],
yAxis: [
{
scale: true,
axisLabel: {
formatter: function (value) {
return value;
},
},
axisLine: {
show: true,
lineStyle: { color: "white" },
},
splitLine: {
show: false,
},
axisPointer: {
show: true,
label: {
show: true,
backgroundColor: 'rgba(0,255,127)',
color: 'black'
},
type: "line",
},
},
{
gridIndex: 1,
type: "category",
data: [0, 1, 2, 3], // 倒序显示
axisLine: { lineStyle: { color: "white" } },
axisLabel: {
show: false, // 显示刻度标签
color: "#fff", // 白色文字
backgroundColor: "transparent",
fontSize: 12,
margin: 8,
},
axisPointer: {
show: true,
label: {
show: false,
},
type: "line",
},
axisTick: { show: false }, // 隐藏刻度线
splitLine: {
show: true,
lineStyle: {
color: "#8392A5",
width: 1,
type: "solid",
},
interval: 0, // 强制显示间隔
},
},
{
gridIndex: 2,
type: "value",
axisLine: {
show: true,
lineStyle: { color: "white" },
},
splitLine: {
show: !1,
},
axisTick: { show: false }, // 隐藏刻度线
axisLabel: { show: false }, // 隐藏刻度标签
axisPointer: {
show: false,
label: {
show: false,
},
type: "line",
},
},
],
// 下拉条
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,
},
start: 50, // 默认展示后半部分数据
end: 100,
bottom: "8%", // 下移数据缩放滑块
},
// {
// show: true,
// type: "slider",
// xAxisIndex: [0, 1, 2],
// bottom: "0%",
// textStyle: {
// color: "white",
// },
// },
{
type: "inside",
xAxisIndex: [0, 1, 2],
filterMode: "filter",
},
],
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: {
trigger: "item",
axisPointer: {
type: 'cross', // 十字准星效果
crossStyle: {
color: '#999'
}
},
// 覆盖全局 tooltip 配置
formatter: function (params) {
return `${params.value[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图
if (KlineCanvsChart) {
KlineCanvsChart.dispose();
}
KlineCanvsChart = echarts.init(KlineCanvs.value);
KlineCanvsChart.setOption(KlineOption);
// 防抖函数,避免频繁触发resize
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
// 监听窗口大小变化,参考股市温度计的实现
const resizeHandler = debounce(() => {
if (KlineCanvsChart && !KlineCanvsChart.isDisposed()) {
try {
KlineCanvsChart.resize();
console.log('情绪解码器图表已重新调整大小');
} catch (error) {
console.error('情绪解码器图表resize失败:', error);
}
}
}, 100); // 100ms防抖延迟
// 移除之前的监听器(如果存在)
if (window.emotionDecodResizeHandler) {
window.removeEventListener('resize', window.emotionDecodResizeHandler);
}
// 添加新的监听器
window.addEventListener('resize', resizeHandler);
// 存储resize处理器以便后续清理
window.emotionDecodResizeHandler = resizeHandler;
// 添加容器大小监听器
if (KlineCanvs.value && window.ResizeObserver) {
const containerObserver = new ResizeObserver(debounce(() => {
if (KlineCanvsChart && !KlineCanvsChart.isDisposed()) {
try {
KlineCanvsChart.resize();
console.log('情绪解码器容器大小变化,图表已调整');
} catch (error) {
console.error('情绪解码器容器resize失败:', error);
}
}
}, 100));
containerObserver.observe(KlineCanvs.value);
window.emotionDecodContainerObserver = containerObserver;
}
}
const windowHeight = ref(window.innerHeight);
const updateHeight = () => {
windowHeight.value = window.innerHeight; // 更新响应式变量
};
// 组件挂载时添加窗口高度监听
onMounted(() => {
// 避免重复添加监听器
if (!window.emotionDecodHeightHandler) {
window.addEventListener("resize", updateHeight);
window.emotionDecodHeightHandler = updateHeight;
}
});
onBeforeUnmount(() => {
// 组件卸载时销毁图表
if (KlineCanvsChart) {
KlineCanvsChart.dispose();
KlineCanvsChart = null;
}
// 移除窗口resize监听器
if (window.emotionDecodResizeHandler) {
window.removeEventListener('resize', window.emotionDecodResizeHandler);
window.emotionDecodResizeHandler = null;
}
// 移除窗口高度监听器
if (window.emotionDecodHeightHandler) {
window.removeEventListener('resize', window.emotionDecodHeightHandler);
window.emotionDecodHeightHandler = null;
}
// 清理容器观察器
if (window.emotionDecodContainerObserver) {
window.emotionDecodContainerObserver.disconnect();
window.emotionDecodContainerObserver = null;
}
});
</script>
<style scoped>
.qxjmqbox {
height: auto;
width: 100%;
margin: 0 auto;
}
.qxjmqEcharts {
width: 100%;
height: 542px;
margin: 0;
/* top: 5rem; */
box-sizing: border-box;
overflow: hidden;
}
/* 手机端适配样式 */
@media only screen and (max-width: 768px) {
.qxjmqEcharts {
width: 100%;
height: 400px;
margin: 0;
/* top: 5rem; */
}
.qxjmqbox {
height: auto;
width: 90%;
}
}
</style>