Browse Source

K线图无法滑动

hxl
hongxilin 3 months ago
parent
commit
0221e8879d
  1. BIN
      src/assets/img/AIchat/AIgif1.gif
  2. BIN
      src/assets/img/AIchat/AIgif2.gif
  3. BIN
      src/assets/img/AIchat/AIgif3.gif
  4. BIN
      src/assets/img/AIchat/AIgif4.gif
  5. BIN
      src/assets/img/AIchat/AIgif5.gif
  6. BIN
      src/assets/img/AIchat/AIgif6.gif
  7. BIN
      src/assets/img/AIchat/AIgif7.gif
  8. 342
      src/views/Echarts/KLine.vue

BIN
src/assets/img/AIchat/AIgif1.gif

Before

Width: 750  |  Height: 550  |  Size: 4.2 MiB

After

Width: 398  |  Height: 224  |  Size: 1.2 MiB

BIN
src/assets/img/AIchat/AIgif2.gif

Before

Width: 750  |  Height: 550  |  Size: 5.8 MiB

After

Width: 398  |  Height: 224  |  Size: 1.5 MiB

BIN
src/assets/img/AIchat/AIgif3.gif

Before

Width: 750  |  Height: 550  |  Size: 7.3 MiB

After

Width: 398  |  Height: 224  |  Size: 1.9 MiB

BIN
src/assets/img/AIchat/AIgif4.gif

Before

Width: 750  |  Height: 550  |  Size: 3.5 MiB

After

Width: 398  |  Height: 224  |  Size: 1.2 MiB

BIN
src/assets/img/AIchat/AIgif5.gif

Before

Width: 750  |  Height: 550  |  Size: 5.8 MiB

After

Width: 398  |  Height: 224  |  Size: 1.6 MiB

BIN
src/assets/img/AIchat/AIgif6.gif

Before

Width: 750  |  Height: 550  |  Size: 4.4 MiB

After

Width: 398  |  Height: 224  |  Size: 1.6 MiB

BIN
src/assets/img/AIchat/AIgif7.gif

Before

Width: 750  |  Height: 550  |  Size: 4.5 MiB

After

Width: 398  |  Height: 224  |  Size: 1.6 MiB

342
src/views/Echarts/KLine.vue

@ -1,5 +1,9 @@
<template> <template>
<div ref="chartContainer" class="KlineClass"></div>
<div ref="chartContainer" class="KlineClass">
<div v-if="!hasValidData" class="no-data-message">
<p>暂无K线数据</p>
</div>
</div>
</template> </template>
<script setup> <script setup>
@ -17,87 +21,166 @@ const props = defineProps({
const chartContainer = ref() // const chartContainer = ref() //
const chartInstance = ref(null) // const chartInstance = ref(null) //
const hasValidData = ref(false) //
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]))
);
}
// //
watch( watch(
() => props.chartData, () => props.chartData,
(newValue) => { (newValue) => {
console.log('KLine组件收到数据:', newValue) console.log('KLine组件收到数据:', newValue)
if (newValue) {
// 使
const isValid = checkDataValid(newValue);
hasValidData.value = isValid;
if (isValid) {
nextTick(() => { nextTick(() => {
//
if (chartContainer.value && !chartInstance.value) {
chartInstance.value = echarts.init(chartContainer.value)
// 使
const instanceData = JSON.parse(JSON.stringify(newValue))
KlineCanvsEcharts(instanceData, chartInstance.value)
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)
}
} catch (error) {
console.error('初始化K线图表时出错:', error)
hasValidData.value = false;
} }
}) })
} else {
console.error('无效的K线数据格式:', newValue)
} }
}, },
{ immediate: true } { immediate: true }
) )
function KlineCanvsEcharts(datatok, instance) { function KlineCanvsEcharts(datatok, instance) {
console.log('传入的K线数据:', datatok);
//
if (!datatok) {
console.error('K线数据为空');
return;
}
if (!datatok.Kline) {
console.error('K线数据中没有Kline属性:', datatok);
return;
}
if (!Array.isArray(datatok.Kline)) {
console.error('Kline数据不是数组:', typeof datatok.Kline);
return;
}
if (datatok.Kline.length === 0) {
console.error('Kline数组为空');
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]);
return;
}
// //
const data = datatok.Kline
const data = datatok.Kline;
const spliteDate = (a) => { const spliteDate = (a) => {
const categoryData = []
let value = []
for (let i = 0; i < a.length; i++) {
categoryData.push(a[i][0])
value.push([a[i][1], a[i][2], a[i][3], a[i][4]])
const categoryData = [];
let value = [];
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])
]);
}
}
} catch (error) {
console.error('处理K线数据时出错:', error);
return { categoryData: [], value: [] };
} }
return { categoryData, value }
}
const dealData = spliteDate(data)
return { categoryData, value };
};
const dealData = spliteDate(data);
console.log('处理后的K线数据:', dealData)
console.log('处理后的K线数据:', dealData);
if (dealData.value.length === 0 || dealData.categoryData.length === 0) { if (dealData.value.length === 0 || dealData.categoryData.length === 0) {
console.error('空数据,无法渲染图表')
return
console.error('空数据,无法渲染图表');
return;
} }
// //
const KlineOption = { const KlineOption = {
title: { title: {
text: datatok.name,
text: datatok.name || '股票K线图',
top: 20, top: 20,
left: 20 left: 20
}, },
tooltip: { tooltip: {
trigger: 'axis', //
//
formatter: function (a, b, d) {
let def =
a[0].name +
'<br/>' +
'开盘价' +
a[0].data[1] +
'<br/>' +
'收盘价' +
a[0].data[2] +
'<br/>' +
'最低价' +
a[0].data[3] +
'<br/>' +
'最高价' +
a[0].data[4]
// a[1]
if (a[1] && a[1].seriesName) {
def += '<br/>' + a[1].seriesName + ':' + a[1].value
}
return def
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>' : ''}
`;
}, },
//
axisPointer: { axisPointer: {
animation: false, animation: false,
type: 'line',
linestyle: {
type: 'cross',
lineStyle: {
color: '#376df4', color: '#376df4',
width: 2, width: 2,
opacity: 1 opacity: 1
} }
},
confine: true,
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#ccc',
borderWidth: 1,
padding: 10,
textStyle: {
color: '#333'
} }
}, },
// //
@ -108,11 +191,10 @@ function KlineCanvsEcharts(datatok, instance) {
}, },
// //
grid: { grid: {
left: '3%',
right: '3%',
left: '12%',
right: '10%',
bottom: '10%', bottom: '10%',
top: '15%',
containLabel: true //
top: '18%'
}, },
yAxis: { yAxis: {
scale: !0, //true scale: !0, //true
@ -202,27 +284,139 @@ function KlineCanvsEcharts(datatok, instance) {
} }
// //
instance.setOption(KlineOption)
instance.setOption(KlineOption, true);
// ref使
chartOptions.value = KlineOption;
//
instance.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]}`);
// 使
}
}
});
//
instance.on('datazoom', function(params) {
console.log('图表缩放事件:', params);
//
});
instance.on('legendselectchanged', function(params) {
console.log('图例选择变化:', params);
});
// resize // resize
window.addEventListener('resize', () => {
instance.resize()
})
const resizeHandler = () => {
if (instance) {
instance.resize();
}
};
window.addEventListener('resize', resizeHandler);
// 使
return () => {
window.removeEventListener('resize', resizeHandler);
instance.off('click');
instance.off('datazoom');
instance.off('legendselectchanged');
};
} }
// //
onUnmounted(() => { onUnmounted(() => {
if (chartInstance.value) { if (chartInstance.value) {
chartInstance.value.dispose()
chartInstance.value = null
//
chartInstance.value.off('click');
chartInstance.value.off('datazoom');
chartInstance.value.off('legendselectchanged');
//
chartInstance.value.dispose();
chartInstance.value = null;
} }
// resize
window.removeEventListener('resize', () => {})
}) })
onMounted(() => { onMounted(() => {
console.log('KLine组件挂载完成') 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> </script>
<style scoped> <style scoped>
@ -231,5 +425,35 @@ onMounted(() => {
width: 100%; width: 100%;
min-height: 400px; min-height: 400px;
height: 100%; height: 100%;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 10px;
margin-bottom: 20px;
overflow: visible !important;
/* 确保dataZoom可见 */
}
.no-data-message {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 8px;
font-size: 16px;
color: #909399;
}
/* 确保图表容器在移动设备上也有足够的空间 */
@media (max-width: 768px) {
.KlineClass {
min-height: 450px;
/* 给移动设备增加额外高度确保dataZoom可见 */
}
} }
</style> </style>
Loading…
Cancel
Save