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
|
1 month ago
|
<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: "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>
|