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.
369 lines
12 KiB
369 lines
12 KiB
<template> |
|
<!-- <div > --> |
|
<div ref="ChartsTimeBarRef" style="height:100%;width: 100%;" class="chart-container"></div> |
|
<!-- </div> --> |
|
</template> |
|
<script setup> |
|
import * as echarts from "echarts"; |
|
import dayjs from 'dayjs' |
|
import { onMounted, nextTick, ref, onUnmounted } from "vue"; |
|
const props = defineProps({ |
|
legendData: { |
|
type: Array, |
|
default: () => [] |
|
}, |
|
xAxisData: { |
|
type: Array, |
|
default: () => [] |
|
}, |
|
seriesData: { |
|
type: Array, |
|
default: () => [] |
|
}, |
|
textTitle: { |
|
type: String, |
|
default: '' |
|
}, |
|
unit: { |
|
type: String, |
|
default: 'mm' |
|
}, |
|
grid: { |
|
type: Object, |
|
default: () => { |
|
return { |
|
left: "5%", |
|
right: "5%", |
|
bottom: "16%", |
|
top: "16%", |
|
containLabel: true, |
|
} |
|
} |
|
} |
|
}) |
|
const ChartsTimeBarRef = ref(null); |
|
let echartsBar = null; |
|
let resizeObserver = null; |
|
// 监听props变化,确保数据更新后重新初始化图表 |
|
watch([() => props.legendData, () => props.xAxisData, () => props.seriesData], () => { |
|
if (echartsBar) { |
|
updateChart(); |
|
} |
|
}, { deep: true }); |
|
onMounted(() => { |
|
console.log(props.xAxisData, '======xAxisData') |
|
initEcharts(); |
|
window.addEventListener('resize', handleResize); |
|
// 监听容器本身尺寸变化 |
|
if (ChartsTimeBarRef.value) { |
|
resizeObserver = new ResizeObserver(handleResize); |
|
resizeObserver.observe(ChartsTimeBarRef.value); |
|
} |
|
}); |
|
|
|
onUnmounted(() => { |
|
window.removeEventListener('resize', handleResize); |
|
if (resizeObserver && ChartsTimeBarRef.value) { |
|
resizeObserver.unobserve(ChartsTimeBarRef.value); |
|
} |
|
if (echartsBar) { |
|
echartsBar.dispose(); |
|
} |
|
}); |
|
const echartsLoading = ref(true); |
|
const initEcharts = () => { |
|
nextTick(() => { |
|
if (ChartsTimeBarRef.value) { |
|
echartsBar = echarts.init(ChartsTimeBarRef.value, "macarons"); |
|
let option = getChartOption(); |
|
echartsBar.setOption(option); |
|
|
|
// 确保图表尺寸适应容器 |
|
const resizeChart = () => { |
|
if (echartsBar && ChartsTimeBarRef.value) { |
|
echartsBar.resize({ |
|
width: ChartsTimeBarRef.value.clientWidth, |
|
height: ChartsTimeBarRef.value.clientHeight |
|
}); |
|
} |
|
}; |
|
|
|
// 初始化时调整一次大小 |
|
resizeChart(); |
|
} |
|
}); |
|
}; |
|
const updateChart = () => { |
|
nextTick(() => { |
|
if (echartsBar) { |
|
let option = getChartOption(); |
|
echartsBar.setOption(option, true); // 使用notMerge:true来完全替换旧选项 |
|
resizeChart(); |
|
} |
|
}); |
|
}; |
|
|
|
const resizeChart = () => { |
|
if (echartsBar && ChartsTimeBarRef.value) { |
|
echartsBar.resize({ |
|
width: ChartsTimeBarRef.value.clientWidth, |
|
height: ChartsTimeBarRef.value.clientHeight |
|
}); |
|
} |
|
}; |
|
const handleResize = () => { |
|
if (echartsBar && ChartsTimeBarRef.value) { |
|
// 添加防抖机制,避免频繁触发 |
|
clearTimeout(window.resizeTimer); |
|
window.resizeTimer = setTimeout(() => { |
|
echartsBar.resize({ |
|
width: ChartsTimeBarRef.value.clientWidth, |
|
height: ChartsTimeBarRef.value.clientHeight, |
|
animation: { |
|
duration: 300 |
|
} |
|
}); |
|
}, 100); |
|
} |
|
}; |
|
|
|
|
|
const getChartOption = () => { |
|
let allData = []; |
|
props.seriesData.forEach(series => { |
|
if (series.data) { |
|
series.data.forEach(item => { |
|
// 根据数据结构获取数值,可能是 [time, value] 或 {tm: time, value: value} 格式 |
|
const value = Array.isArray(item) ? item[1] : (item.value !== undefined ? item.value : item); |
|
if (value !== undefined && value !== null && !isNaN(value)) { |
|
allData.push(Number(value)); |
|
} |
|
}); |
|
} |
|
}); |
|
|
|
// 计算y轴范围 |
|
let yAxisMin = 0; |
|
let yAxisMax = 100; |
|
|
|
if (allData.length > 0) { |
|
const minVal = Math.min(...allData); |
|
const maxVal = Math.max(...allData); |
|
|
|
// 添加一些边距,使图表更美观 |
|
const range = maxVal - minVal; |
|
const margin = range > 0 ? range * 0.1 : 1; |
|
|
|
yAxisMin = Math.floor((minVal - margin) * 100) / 100; // 保留两位小数 |
|
yAxisMax = Math.ceil((maxVal + margin) * 100) / 100; // 保留两位小数 |
|
|
|
// 如果数据值都很小,确保最小值不为负数(可根据实际需求调整) |
|
if (minVal >= 0 && yAxisMin < 0) { |
|
yAxisMin = 0; |
|
} |
|
} |
|
const series = props.seriesData.map((item, index) => { |
|
const colors = [ |
|
["#fccb05", "#f5804d"], |
|
["#8bd46e", "#09bcb7"], |
|
["#248ff7", "#6851f1"] |
|
]; |
|
const color = colors[index % colors.length]; |
|
|
|
return { |
|
name: props.legendData[index], |
|
type: "line", |
|
barWidth: item.data.length > 100 ? '15%' : "20", |
|
showSymbol: false, |
|
itemStyle: { |
|
normal: { |
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|
{ offset: 0, color: color[0] }, |
|
{ offset: 1, color: color[1] } |
|
]), |
|
barBorderRadius: 12, |
|
}, |
|
}, |
|
data: item.data.map(val => val[1]), |
|
}; |
|
}); |
|
let option = { |
|
// backgroundColor: "#323a5e", |
|
title: { |
|
text: props.textTitle, |
|
textStyle: { |
|
align: "center", |
|
color: "#000", |
|
fontSize: 20, |
|
}, |
|
top: "3%", |
|
left: "50%", |
|
}, |
|
tooltip: { |
|
trigger: "axis", |
|
axisPointer: { |
|
// 坐标轴指示器,坐标轴触发有效 |
|
type: "shadow", // 默认为直线,可选为:'line' | 'shadow' |
|
}, |
|
}, |
|
grid: props.grid, |
|
legend: { |
|
data: props.legendData, |
|
right: 10, |
|
top: 12, |
|
textStyle: { |
|
color: "#000", |
|
}, |
|
itemWidth: 12, |
|
itemHeight: 10, |
|
// itemGap: 35 |
|
}, |
|
xAxis: { |
|
type: "category", |
|
data: props.xAxisData, |
|
axisLine: { |
|
lineStyle: { |
|
color: "#ccc", |
|
}, |
|
}, |
|
|
|
axisLabel: { |
|
textStyle: { |
|
fontFamily: "Microsoft YaHei", |
|
}, |
|
showMinLabel: true, // 强制显示最小值标签 |
|
showMaxLabel: true, // 强制显示最大值标签 |
|
formatter: function (value) { |
|
let date = dayjs(value) |
|
let yearMonthDay = date.format('YYYY-MM-DD') |
|
let hourMinute = date.format('HH:mm') |
|
let result = `{a|${hourMinute}}\n{b|${yearMonthDay}}`; |
|
if (props.timeType == 'day') { |
|
result = `{b|${yearMonthDay}}`; |
|
} |
|
return result |
|
}, |
|
rich: { |
|
a: { |
|
fontSize: 12, |
|
color: '#000', |
|
padding: [0, 0, 5, 0] // 上右下左的内边距,这里给上方小时分钟添加一点底部间距 |
|
}, |
|
b: { |
|
fontSize: 12, |
|
color: '#000' |
|
} |
|
} |
|
}, |
|
}, |
|
|
|
yAxis: { |
|
type: "value", |
|
min: yAxisMin, |
|
max: yAxisMax, |
|
|
|
name: 'mm', |
|
nameLocation: 'middle', |
|
|
|
nameTextStyle: {//y轴上方单位的颜色 |
|
color: '#464b50' |
|
}, |
|
nameGap: 50, |
|
axisLine: { |
|
show: true, |
|
lineStyle: { |
|
color: "#ccc", |
|
}, |
|
}, |
|
|
|
splitLine: { |
|
show: true, |
|
lineStyle: { |
|
color: "#ccc", |
|
}, |
|
}, |
|
axisLabel: { |
|
textStyle: { |
|
color: "#000000", |
|
}, |
|
// 添加formatter属性,保留两位小数 |
|
formatter: function (value) { |
|
return value.toFixed(2); |
|
} |
|
}, |
|
}, |
|
dataZoom: [ |
|
{ |
|
show: true, |
|
// height: 15, |
|
xAxisIndex: [0], |
|
bottom: "8%", |
|
start: 0, |
|
end: 100, |
|
handleIcon: |
|
"path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z", |
|
handleSize: "110%", |
|
handleStyle: { |
|
color: "#d3dee5", |
|
}, |
|
textStyle: { |
|
color: "#000", |
|
}, |
|
borderColor: "#90979c", |
|
}, |
|
{ |
|
type: "inside", |
|
show: true, |
|
// height: 15, |
|
start: 1, |
|
end: 35, |
|
}, |
|
// { |
|
// type: 'inside' |
|
// }, |
|
// { |
|
// type: 'slider' |
|
// } |
|
], |
|
series: series, |
|
}; |
|
|
|
return option |
|
// var app = { |
|
// currentIndex: -1, |
|
// }; |
|
// setInterval(function () { |
|
// var dataLen = option.series[0].data.length; |
|
|
|
// // 取消之前高亮的图形 |
|
// myChart.dispatchAction({ |
|
// type: "downplay", |
|
// seriesIndex: 0, |
|
// dataIndex: app.currentIndex, |
|
// }); |
|
// app.currentIndex = (app.currentIndex + 1) % dataLen; |
|
// //console.log(app.currentIndex); |
|
// // 高亮当前图形 |
|
// myChart.dispatchAction({ |
|
// type: "highlight", |
|
// seriesIndex: 0, |
|
// dataIndex: app.currentIndex, |
|
// }); |
|
// // 显示 tooltip |
|
// myChart.dispatchAction({ |
|
// type: "showTip", |
|
// seriesIndex: 0, |
|
// dataIndex: app.currentIndex, |
|
// }); |
|
// }, 1000); |
|
|
|
} |
|
</script> |
|
<style scoped> |
|
.chart-container { |
|
width: 100%; |
|
height: 100%; |
|
min-height: 300px; |
|
position: relative; |
|
} |
|
</style> |