|
|
@ -1,7 +1,7 @@ |
|
|
|
<template> |
|
|
|
<div ref="chartContainer" class="KlineClass"> |
|
|
|
<div ref="KlineCanvs" class="KlineClass"> |
|
|
|
<div v-if="!hasValidData" class="no-data-message"> |
|
|
|
<p>暂无K线数据</p> |
|
|
|
暂无K线数据 |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
@ -10,160 +10,181 @@ |
|
|
|
import * as echarts from 'echarts' |
|
|
|
import { useDataStore } from '@/store/dataList' |
|
|
|
import { onMounted, watch, ref, computed, nextTick, onUnmounted } from 'vue' |
|
|
|
const dataStore = useDataStore() |
|
|
|
const hasValidData = ref(false) |
|
|
|
|
|
|
|
// 接收从父组件传入的图表数据 |
|
|
|
const props = defineProps({ |
|
|
|
chartData: { |
|
|
|
type: Object, |
|
|
|
default: null |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
const chartContainer = ref() // 容器引用 |
|
|
|
const chartInstance = ref(null) // 当前图表实例 |
|
|
|
const hasValidData = ref(false) // 是否有有效数据 |
|
|
|
const chartOptions = ref(null) // 保存当前配置 |
|
|
|
// 添加必要的变量 |
|
|
|
const KlineCanvs = ref(null) |
|
|
|
const chartInstance = ref(null) |
|
|
|
const chartOptions = ref(null) |
|
|
|
|
|
|
|
// 检查数据是否有效 |
|
|
|
const checkDataValid = (data) => { |
|
|
|
return data && |
|
|
|
data.Kline && |
|
|
|
Array.isArray(data.Kline) && |
|
|
|
data.Kline.length > 0 && |
|
|
|
data.Kline.every(item => |
|
|
|
Array.isArray(item) && |
|
|
|
item.length >= 5 && |
|
|
|
!isNaN(Number(item[1])) && |
|
|
|
!isNaN(Number(item[2])) && |
|
|
|
!isNaN(Number(item[3])) && |
|
|
|
!isNaN(Number(item[4])) |
|
|
|
); |
|
|
|
// 添加计算MA数据的函数 |
|
|
|
function calculateMA(dayCount, data) { |
|
|
|
const result = []; |
|
|
|
for (let i = 0; i < data.length; i++) { |
|
|
|
if (i < dayCount - 1) { |
|
|
|
result.push('-'); |
|
|
|
continue; |
|
|
|
} |
|
|
|
let sum = 0; |
|
|
|
for (let j = 0; j < dayCount; j++) { |
|
|
|
sum += parseFloat(data[i - j][1]); |
|
|
|
} |
|
|
|
result.push((sum / dayCount).toFixed(2)); |
|
|
|
} |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
// 监听传入的数据变化 |
|
|
|
// 监听数据变化 |
|
|
|
watch( |
|
|
|
() => props.chartData, |
|
|
|
dataStore.KLineData, |
|
|
|
(newValue) => { |
|
|
|
console.log('KLine组件收到数据:', newValue) |
|
|
|
// 使用数据验证函数检查 |
|
|
|
const isValid = checkDataValid(newValue); |
|
|
|
hasValidData.value = isValid; |
|
|
|
console.log('KLine组件: 监听到数据变化', newValue); |
|
|
|
if (!newValue) { |
|
|
|
console.warn('KLine组件: 数据为null或undefined'); |
|
|
|
hasValidData.value = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (isValid) { |
|
|
|
nextTick(() => { |
|
|
|
try { |
|
|
|
// 仅当容器存在且没有初始化过图表时创建 |
|
|
|
if (chartContainer.value && !chartInstance.value) { |
|
|
|
// 使用try-catch捕获图表初始化和数据处理过程中的错误 |
|
|
|
chartInstance.value = echarts.init(chartContainer.value) |
|
|
|
// 使用深拷贝防止数据被修改 |
|
|
|
const instanceData = JSON.parse(JSON.stringify(newValue)) |
|
|
|
KlineCanvsEcharts(instanceData, chartInstance.value) |
|
|
|
const currentData = JSON.parse(JSON.stringify(toRaw(newValue))); // 深拷贝防止切换回来数据更改 |
|
|
|
if (currentData) { |
|
|
|
nextTick(() => { |
|
|
|
KlineCanvsEcharts(currentData); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
console.warn('KLine组件: 深拷贝后数据为空'); |
|
|
|
hasValidData.value = false; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('初始化K线图表时出错:', error) |
|
|
|
console.error('KLine组件: 数据处理错误', error); |
|
|
|
hasValidData.value = false; |
|
|
|
} |
|
|
|
}) |
|
|
|
} else { |
|
|
|
console.error('无效的K线数据格式:', newValue) |
|
|
|
} |
|
|
|
}, |
|
|
|
{ immediate: true } |
|
|
|
) |
|
|
|
{ immediate: true, deep: true } |
|
|
|
); |
|
|
|
|
|
|
|
function KlineCanvsEcharts(datatok, instance) { |
|
|
|
console.log('传入的K线数据:', datatok); |
|
|
|
// 组件挂载时初始化 |
|
|
|
onMounted(() => { |
|
|
|
console.log('KLine组件挂载完成'); |
|
|
|
|
|
|
|
// 更严格的数据验证 |
|
|
|
if (!datatok) { |
|
|
|
console.error('K线数据为空'); |
|
|
|
return; |
|
|
|
// 添加窗口resize事件监听 |
|
|
|
const resizeHandler = () => { |
|
|
|
if (chartInstance.value) { |
|
|
|
chartInstance.value.resize(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
if (!datatok.Kline) { |
|
|
|
console.error('K线数据中没有Kline属性:', datatok); |
|
|
|
return; |
|
|
|
window.addEventListener('resize', resizeHandler); |
|
|
|
|
|
|
|
// 组件卸载时清理 |
|
|
|
onUnmounted(() => { |
|
|
|
window.removeEventListener('resize', resizeHandler); |
|
|
|
if (chartInstance.value) { |
|
|
|
chartInstance.value.dispose(); |
|
|
|
chartInstance.value = null; |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
// K线图表生成函数 |
|
|
|
function KlineCanvsEcharts(datatok) { |
|
|
|
console.log('KLine组件: 开始处理数据', datatok); |
|
|
|
|
|
|
|
if (!Array.isArray(datatok.Kline)) { |
|
|
|
console.error('Kline数据不是数组:', typeof datatok.Kline); |
|
|
|
// 数据验证 |
|
|
|
if (!datatok) { |
|
|
|
console.warn('KLine组件: 数据无效,为null或undefined'); |
|
|
|
hasValidData.value = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (datatok.Kline.length === 0) { |
|
|
|
console.error('Kline数组为空'); |
|
|
|
// 兼容两种可能的数据格式 |
|
|
|
let klineData = null; |
|
|
|
let chartName = ''; |
|
|
|
|
|
|
|
if (datatok.Kline && Array.isArray(datatok.Kline)) { |
|
|
|
klineData = datatok.Kline; |
|
|
|
chartName = datatok.name || '股票K线图'; |
|
|
|
} else if (datatok.KLine20 && Array.isArray(datatok.KLine20)) { |
|
|
|
klineData = datatok.KLine20; |
|
|
|
chartName = datatok.name || '股票K线图'; |
|
|
|
} else { |
|
|
|
console.warn('KLine组件: 找不到有效的K线数据'); |
|
|
|
hasValidData.value = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 验证数组中的每个元素是否符合要求 |
|
|
|
const isValidKlineItem = datatok.Kline.every(item => |
|
|
|
Array.isArray(item) && item.length >= 5 && |
|
|
|
!isNaN(Number(item[1])) && !isNaN(Number(item[2])) && |
|
|
|
!isNaN(Number(item[3])) && !isNaN(Number(item[4])) |
|
|
|
); |
|
|
|
|
|
|
|
if (!isValidKlineItem) { |
|
|
|
console.error('Kline数据格式不正确, 期望格式: [[date, open, close, low, high], ...]'); |
|
|
|
console.error('实际数据样例:', datatok.Kline[0]); |
|
|
|
if (klineData.length === 0) { |
|
|
|
console.warn('KLine组件: K线数据为空数组'); |
|
|
|
hasValidData.value = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 切割数据方法 |
|
|
|
const data = datatok.Kline; |
|
|
|
const spliteDate = (a) => { |
|
|
|
// 数据处理 |
|
|
|
const categoryData = []; |
|
|
|
let value = []; |
|
|
|
const values = []; |
|
|
|
|
|
|
|
try { |
|
|
|
for (let i = 0; i < a.length; i++) { |
|
|
|
// 确保每个数据项都是有效的 |
|
|
|
if (Array.isArray(a[i]) && a[i].length >= 5) { |
|
|
|
categoryData.push(a[i][0]); |
|
|
|
// 确保转换为数字类型 |
|
|
|
value.push([ |
|
|
|
Number(a[i][1]), |
|
|
|
Number(a[i][2]), |
|
|
|
Number(a[i][3]), |
|
|
|
Number(a[i][4]) |
|
|
|
for (let i = 0; i < klineData.length; i++) { |
|
|
|
const item = klineData[i]; |
|
|
|
if (!Array.isArray(item) || item.length < 5) { |
|
|
|
console.warn(`KLine组件: 第${i + 1}个数据点格式无效`, item); |
|
|
|
continue; // 跳过无效数据点而不是中断整个处理 |
|
|
|
} |
|
|
|
categoryData.push(item[0]); // 日期 |
|
|
|
values.push([ |
|
|
|
parseFloat(item[1]), // 开盘 |
|
|
|
parseFloat(item[2]), // 收盘 |
|
|
|
parseFloat(item[3]), // 最低 |
|
|
|
parseFloat(item[4]) // 最高 |
|
|
|
]); |
|
|
|
} |
|
|
|
|
|
|
|
if (categoryData.length === 0 || values.length === 0) { |
|
|
|
console.warn('KLine组件: 处理后无有效数据点'); |
|
|
|
hasValidData.value = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('处理K线数据时出错:', error); |
|
|
|
return { categoryData: [], value: [] }; |
|
|
|
} |
|
|
|
return { categoryData, value }; |
|
|
|
}; |
|
|
|
|
|
|
|
const dealData = spliteDate(data); |
|
|
|
// 计算MA5 |
|
|
|
const ma5Data = calculateMA(5, klineData); |
|
|
|
|
|
|
|
console.log('处理后的K线数据:', dealData); |
|
|
|
if (dealData.value.length === 0 || dealData.categoryData.length === 0) { |
|
|
|
console.error('空数据,无法渲染图表'); |
|
|
|
// 确保容器存在 |
|
|
|
if (!KlineCanvs.value) { |
|
|
|
console.warn('KLine组件: 找不到容器元素'); |
|
|
|
hasValidData.value = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 给配置项 |
|
|
|
// 创建或更新图表实例 |
|
|
|
if (chartInstance.value) { |
|
|
|
chartInstance.value.dispose(); |
|
|
|
} |
|
|
|
chartInstance.value = echarts.init(KlineCanvs.value); |
|
|
|
|
|
|
|
// 图表配置 |
|
|
|
const KlineOption = { |
|
|
|
title: { |
|
|
|
text: datatok.name || '股票K线图', |
|
|
|
top: 20, |
|
|
|
left: 20 |
|
|
|
text: chartName, |
|
|
|
left: 0 |
|
|
|
}, |
|
|
|
tooltip: { |
|
|
|
trigger: 'axis', |
|
|
|
show: true, |
|
|
|
formatter: function (params) { |
|
|
|
if (!params || params.length === 0) return ''; |
|
|
|
const param = params[0]; |
|
|
|
return ` |
|
|
|
<div style="font-weight:bold;margin-bottom:5px">${param.name}</div> |
|
|
|
<div style="margin:3px 0">开盘价: ${param.data[1]}</div> |
|
|
|
<div style="margin:3px 0">收盘价: ${param.data[2]}</div> |
|
|
|
<div style="margin:3px 0">最低价: ${param.data[3]}</div> |
|
|
|
<div style="margin:3px 0">最高价: ${param.data[4]}</div> |
|
|
|
${params[1] && params[1].value !== '-' ? '<div style="margin:3px 0">MA5: ' + params[1].value + '</div>' : ''} |
|
|
|
`; |
|
|
|
let result = param.name + '<br/>' + |
|
|
|
'开盘价' + param.data[0] + '<br/>' + |
|
|
|
'收盘价' + param.data[1] + '<br/>' + |
|
|
|
'最低价' + param.data[2] + '<br/>' + |
|
|
|
'最高价' + param.data[3]; |
|
|
|
|
|
|
|
// 判断MA5是否存在 |
|
|
|
if (params[1] && params[1].value !== '-') { |
|
|
|
result += '<br/>' + params[1].seriesName + ':' + params[1].value; |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
}, |
|
|
|
axisPointer: { |
|
|
|
animation: false, |
|
|
@ -173,254 +194,95 @@ function KlineCanvsEcharts(datatok, instance) { |
|
|
|
width: 2, |
|
|
|
opacity: 1 |
|
|
|
} |
|
|
|
}, |
|
|
|
confine: true, |
|
|
|
backgroundColor: 'rgba(255, 255, 255, 0.95)', |
|
|
|
borderColor: '#ccc', |
|
|
|
borderWidth: 1, |
|
|
|
padding: 10, |
|
|
|
textStyle: { |
|
|
|
color: '#333' |
|
|
|
} |
|
|
|
}, |
|
|
|
// 横坐标内容 |
|
|
|
xAxis: { |
|
|
|
type: 'category', |
|
|
|
data: dealData.categoryData, |
|
|
|
axisLine: { lineStyle: { color: '#8392A5' } } |
|
|
|
}, |
|
|
|
//控制坐标轴 |
|
|
|
grid: { |
|
|
|
left: '12%', |
|
|
|
right: '10%', |
|
|
|
bottom: '10%', |
|
|
|
bottom: '15%', |
|
|
|
top: '18%' |
|
|
|
}, |
|
|
|
xAxis: { |
|
|
|
type: 'category', |
|
|
|
data: categoryData, |
|
|
|
axisLine: { lineStyle: { color: '#8392A5' } } |
|
|
|
}, |
|
|
|
yAxis: { |
|
|
|
scale: !0, //true |
|
|
|
// 自定义纵坐标现实的数据 |
|
|
|
scale: true, |
|
|
|
axisLabel: { |
|
|
|
formatter: function (value) { |
|
|
|
return value // 返回原始值 |
|
|
|
return value; // 返回原始值 |
|
|
|
} |
|
|
|
}, |
|
|
|
axisLine: { lineStyle: { color: '#8392A5' } }, |
|
|
|
splitLine: { |
|
|
|
show: !1 |
|
|
|
} |
|
|
|
splitLine: { show: false } |
|
|
|
}, |
|
|
|
// 下拉条 |
|
|
|
dataZoom: [ |
|
|
|
{ |
|
|
|
textStyle: { |
|
|
|
color: '#8392A5' |
|
|
|
}, |
|
|
|
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' |
|
|
|
} |
|
|
|
}, |
|
|
|
handleStyle: { |
|
|
|
color: '#fff', |
|
|
|
shadowBlur: 3, |
|
|
|
shadowColor: 'rgba(0, 0, 0, 0.6)', |
|
|
|
shadowOffsetX: 2, |
|
|
|
shadowOffsetY: 2 |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
show: !1, |
|
|
|
type: 'slider' |
|
|
|
type: 'inside', |
|
|
|
start: 50, |
|
|
|
end: 100 |
|
|
|
}, |
|
|
|
{ |
|
|
|
type: 'inside' |
|
|
|
show: true, |
|
|
|
type: 'slider', |
|
|
|
top: '90%', |
|
|
|
start: 50, |
|
|
|
end: 100 |
|
|
|
} |
|
|
|
], |
|
|
|
animation: !1, //false |
|
|
|
// 线条的数据 |
|
|
|
series: [ |
|
|
|
{ |
|
|
|
name: chartName, |
|
|
|
type: 'candlestick', |
|
|
|
name: '\u65e5K', |
|
|
|
// 数据 |
|
|
|
data: dealData.value, |
|
|
|
data: values, |
|
|
|
itemStyle: { |
|
|
|
normal: { |
|
|
|
color0: '#FD1050', |
|
|
|
color: '#0CF49B', |
|
|
|
borderColor0: '#FD1050', |
|
|
|
borderColor: '#0CF49B' |
|
|
|
} |
|
|
|
color: '#ef232a', |
|
|
|
color0: '#14b143', |
|
|
|
borderColor: '#ef232a', |
|
|
|
borderColor0: '#14b143' |
|
|
|
} |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: 'MA5', |
|
|
|
type: 'line', |
|
|
|
// 此处需要接口调用同类型数据 |
|
|
|
// 计算出MA5的数据 |
|
|
|
data: (function (a) { |
|
|
|
for (var MA5 = [], d = 0, g = dealData.value.length; d < g; d++) { |
|
|
|
if (d < a) { |
|
|
|
MA5.push('-') |
|
|
|
} else { |
|
|
|
for (var f = 0, e = 0; e < a; e++) { |
|
|
|
f += dealData.value[d - e][1] |
|
|
|
} |
|
|
|
MA5.push((f / a).toFixed(2)) |
|
|
|
} |
|
|
|
data: ma5Data, |
|
|
|
smooth: true, |
|
|
|
lineStyle: { |
|
|
|
opacity: 0.5 |
|
|
|
} |
|
|
|
return MA5 |
|
|
|
})(5), |
|
|
|
smooth: !0 |
|
|
|
} |
|
|
|
] |
|
|
|
} |
|
|
|
|
|
|
|
// 应用配置到图表实例 |
|
|
|
instance.setOption(KlineOption, true); |
|
|
|
}; |
|
|
|
|
|
|
|
// 保存配置到ref,供暴露的方法使用 |
|
|
|
// 应用配置 |
|
|
|
chartInstance.value.setOption(KlineOption); |
|
|
|
chartOptions.value = KlineOption; |
|
|
|
|
|
|
|
// 绑定点击事件 |
|
|
|
instance.on('click', function(params) { |
|
|
|
// 绑定事件 |
|
|
|
chartInstance.value.off('click'); |
|
|
|
chartInstance.value.on('click', function(params) { |
|
|
|
console.log('图表点击事件:', params); |
|
|
|
if (params.componentType === 'series') { |
|
|
|
const date = params.name; |
|
|
|
const data = params.data; |
|
|
|
if (Array.isArray(data) && data.length >= 5) { |
|
|
|
console.log(`点击了 ${date} 的K线数据: 开盘${data[1]}, 收盘${data[2]}, 最低${data[3]}, 最高${data[4]}`); |
|
|
|
// 在实际使用中可以替换为更友好的交互方式,如显示详细信息面板 |
|
|
|
} |
|
|
|
console.log(`点击了 ${date} 的K线数据:`, data); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 添加更多事件监听 |
|
|
|
instance.on('datazoom', function(params) { |
|
|
|
console.log('图表缩放事件:', params); |
|
|
|
// 可以在这里处理缩放事件,如记录当前缩放状态等 |
|
|
|
}); |
|
|
|
|
|
|
|
instance.on('legendselectchanged', function(params) { |
|
|
|
console.log('图例选择变化:', params); |
|
|
|
}); |
|
|
|
// 设置数据有效标志 |
|
|
|
hasValidData.value = true; |
|
|
|
console.log('KLine组件: 图表渲染完成'); |
|
|
|
|
|
|
|
// 绑定窗口resize事件 |
|
|
|
const resizeHandler = () => { |
|
|
|
if (instance) { |
|
|
|
instance.resize(); |
|
|
|
} catch (error) { |
|
|
|
console.error('KLine组件: 图表渲染错误', error); |
|
|
|
hasValidData.value = false; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
window.addEventListener('resize', resizeHandler); |
|
|
|
|
|
|
|
// 返回清理函数供组件卸载时使用 |
|
|
|
return () => { |
|
|
|
window.removeEventListener('resize', resizeHandler); |
|
|
|
instance.off('click'); |
|
|
|
instance.off('datazoom'); |
|
|
|
instance.off('legendselectchanged'); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
// 组件卸载时清理资源 |
|
|
|
onUnmounted(() => { |
|
|
|
if (chartInstance.value) { |
|
|
|
// 移除所有事件监听 |
|
|
|
chartInstance.value.off('click'); |
|
|
|
chartInstance.value.off('datazoom'); |
|
|
|
chartInstance.value.off('legendselectchanged'); |
|
|
|
|
|
|
|
// 销毁图表实例 |
|
|
|
chartInstance.value.dispose(); |
|
|
|
chartInstance.value = null; |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
console.log('KLine组件挂载完成') |
|
|
|
}) |
|
|
|
|
|
|
|
// 对外暴露方法,允许父组件访问图表实例和方法 |
|
|
|
defineExpose({ |
|
|
|
getChartInstance: () => chartInstance.value, |
|
|
|
getOptions: () => chartOptions.value, |
|
|
|
resize: () => { |
|
|
|
if (chartInstance.value) { |
|
|
|
chartInstance.value.resize() |
|
|
|
} |
|
|
|
}, |
|
|
|
// 缩放到指定日期范围 |
|
|
|
zoomToDateRange: (startDate, endDate) => { |
|
|
|
if (!chartInstance.value || !chartOptions.value) return; |
|
|
|
|
|
|
|
const categoryData = chartOptions.value.xAxis[0].data; |
|
|
|
if (!Array.isArray(categoryData)) return; |
|
|
|
|
|
|
|
const startIndex = categoryData.findIndex(date => date >= startDate); |
|
|
|
const endIndex = categoryData.findIndex(date => date >= endDate); |
|
|
|
|
|
|
|
if (startIndex === -1 || endIndex === -1) return; |
|
|
|
|
|
|
|
// 计算百分比 |
|
|
|
const total = categoryData.length; |
|
|
|
const start = (startIndex / total) * 100; |
|
|
|
const end = (endIndex / total) * 100; |
|
|
|
|
|
|
|
// 应用缩放 |
|
|
|
chartInstance.value.dispatchAction({ |
|
|
|
type: 'dataZoom', |
|
|
|
start: start, |
|
|
|
end: end |
|
|
|
}); |
|
|
|
}, |
|
|
|
// 高亮特定日期的K线 |
|
|
|
highlightDate: (date) => { |
|
|
|
if (!chartInstance.value || !chartOptions.value) return; |
|
|
|
|
|
|
|
const categoryData = chartOptions.value.xAxis[0].data; |
|
|
|
if (!Array.isArray(categoryData)) return; |
|
|
|
|
|
|
|
const index = categoryData.findIndex(d => d === date); |
|
|
|
if (index !== -1) { |
|
|
|
chartInstance.value.dispatchAction({ |
|
|
|
type: 'highlight', |
|
|
|
seriesIndex: 0, |
|
|
|
dataIndex: index |
|
|
|
}); |
|
|
|
|
|
|
|
// 同时显示tooltip |
|
|
|
chartInstance.value.dispatchAction({ |
|
|
|
type: 'showTip', |
|
|
|
seriesIndex: 0, |
|
|
|
dataIndex: index |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
// 清除高亮 |
|
|
|
clearHighlight: () => { |
|
|
|
if (chartInstance.value) { |
|
|
|
chartInstance.value.dispatchAction({ |
|
|
|
type: 'downplay' |
|
|
|
}); |
|
|
|
|
|
|
|
chartInstance.value.dispatchAction({ |
|
|
|
type: 'hideTip' |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.KlineClass { |
|
|
|
.chart-mount-point { |
|
|
|
position: relative; |
|
|
|
width: 100%; |
|
|
|
min-height: 400px; |
|
|
@ -451,7 +313,7 @@ defineExpose({ |
|
|
|
|
|
|
|
/* 确保图表容器在移动设备上也有足够的空间 */ |
|
|
|
@media (max-width: 768px) { |
|
|
|
.KlineClass { |
|
|
|
.chart-mount-point { |
|
|
|
min-height: 450px; |
|
|
|
/* 给移动设备增加额外高度确保dataZoom可见 */ |
|
|
|
} |
|
|
|