vue2动态实现多Y轴echarts图表,及节点点击事件
父组件
<template><div class="app-container"><div class="content"><el-form:model="echartsqueryParams"ref="echartsqueryForm":inline="true"><el-form-item label="号" prop="furnaceNumber"><el-selectv-model="echartsqueryParams.furnaceNumber"placeholder="请选择"style="width: 150px"><el-optionv-for="item in deviceOptions":key="item":label="item":value="item"></el-option></el-select></el-form-item><el-form-item label="日期"><el-date-pickerv-model="timeData"popper-class="noClear"size="small"value-format="yyyy-MM-dd HH:mm:ss"type="datetimerange"range-separator="-"start-placeholder="开始日期"end-placeholder="结束日期":clearable="false"@change="handleChange"></el-date-picker></el-form-item><el-form-item label="真空"><el-inputstyle="width: 150px"v-model="csValue"placeholder="请输入"type="number"@blur="inputChange"/></el-form-item><el-form-item label="时间间隔" prop="interval"><el-selectv-model="echartsqueryParams.interval"placeholder="请选择"style="width: 150px"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"></el-option></el-select></el-form-item><el-form-item><el-buttonicon="el-icon-printer"type="primary"@click="ceshi = !ceshi"size="mini">打印</el-button><el-buttontype="primary"icon="el-icon-search"size="mini"@click="handleQuery_echarts">搜索</el-button><el-buttonicon="el-icon-refresh"size="mini"@click="resetQuery_echarts">重置</el-button></el-form-item></el-form><div><el-checkbox-groupv-model="checkedCities"@change="handleCheckedCitiesChange"><el-checkbox v-for="city in cities" :label="city" :key="city">{{city}}</el-checkbox></el-checkbox-group></div><div style="margin-bottom: 15px"></div><divv-loading="echartsLoading"style="width: 100%; height: calc(100% - 40px - 60px)"><moreyaxisv-if="axiosData.length > 0":legend="legend":axiosData="axiosData":yaxiosData="yaxios":serios="serios":colors="colors":gridRIght="gridRIght"></moreyaxis><el-empty description="暂无数据" v-else></el-empty></div></div><!-- 打印内容 --><el-dialogclass="dialogPrint"title="请确认打印内容":visible.sync="ceshi"style="height: 90vh"><div id="printMe" style="width: 100%"><div style="width: 1px; height: 1px"></div><div v-if="echartsList.nodeTimes.length > 0"><div style="display: flex"><p style="margin: 0; margin-right: 20px">号:{{ echartsqueryParams.furnaceNumber }}</p><p style="margin: 0; margin-right: 20px">开始时间:{{ echartsList.nodeTimes[0] }}</p><p style="margin: 0">结束时间:{{echartsList.nodeTimes[Number(echartsList.nodeTimes.length) - 1]}}</p></div><ulstyle="padding: 0;display: grid;grid-template-columns: repeat(4, 1fr);grid-column-gap: 10px;grid-row-gap: 10px;"><li v-for="(item, index) in echartsList.series" :key="index"><pstyle="width: 100%;border: 1px solid #f9f9f9;padding: 10px;margin: 0;"><spanstyle="border-left: 3px solid #4068e0; padding-left: 5px">{{ item.name }}</span></p><div style="display: flex"><pstyle="font-size: 12px;margin: 0;width: 50%;border: 1px solid #f9f9f9;border-top: none;padding: 10px;">开始{{ item.name.substring(item.name.length - 2) }}:{{item.values[0]}}{{ item.unit }}</p><pstyle="font-size: 12px;margin: 0;width: 50%;border: 1px solid #f9f9f9;border-top: none;padding: 10px;">结束{{ item.name.substring(item.name.length - 2) }}:{{item.values[Number(item.values.length) - 1]}}{{ item.unit }}</p></div></li></ul></div><div style="width: 100%; height: 500px" v-if="axiosData.length > 0"><moreyaxisstyle="width: 100%; height: 500px":legend="legend":axiosData="axiosData":yaxiosData="yaxios":serios="serios":colors="colors":gridRIght="gridRIght"></moreyaxis></div></div><div slot="footer"><el-button @click="ceshi = false">取 消</el-button><el-button type="primary" v-print="printObj">确 定</el-button></div></el-dialog></div>
</template>
<script>
import moreyaxis from "@/views/echarts/moreyaxis.vue";
import { echartsData } from "@/api/collocation/records";
export default {components: { moreyaxis },data() {return {deviceOptions: [28030002, 13020101, 28030003, 13020100],ceshi: false,// 图表参数start: true,echartsList: {nodeTimes: [],series: [],},echartsLoading: true,colors: ["#6488FE", "#FF6F6F", "#FEE177", "#2AF0CF", "#08BE87"],legend: [],axiosData: [],serios: [],yaxios: [],// 查询参数echartsqueryParams: {startTime: null,endTime: null,interval: 30,id: null,// limit: 200,furnaceNumber: null,},timeData: [],options: [{label: "十秒",value: 10,},{label: "三十秒",value: 30,},{label: "一分钟",value: 60,},{label: "五分钟",value: 300,},{label: "十分钟",value: 600,},{label: "十五分钟",value: 900,},{label: "半小时",value: 1800,},{label: "一小时",value: 3600,},],printObj: {id: "printMe",popTitle: "记录",closeCallback: this.closeCallback,},detailsTable: [],gridRIght: 0,csValue: null,cities: [],cityOptions: [],checkAll: false,isIndeterminate: true,checkedCities: [],data: {},};},methods: {handleChange(value) {if (value) {const start = new Date(value[0]);const end = new Date(value[1]);if (end - start > 24 * 60 * 60 * 1000) {this.$message.error("结束时间不能大于开始时间一天");this.timeData = null; // 清空选择}}},// 设备列表deviceData() {listDevice().then((res) => [console.log(res)]);},handleCheckedCitiesChange(value) {this.echartsSerios(this.data.series);},// 导出handleExport_echarts() {},// 图表搜索handleQuery_echarts() {this.echartsLoading = true;this.echartsDataList();},// 图表重置resetQuery_echarts() {this.updateCurrentTime();this.echartsLoading = true;this.echartsqueryParams.interval = null;this.echartsDataList();},// 图表数据解析echartsDataList(row) {this.echartsqueryParams.startTime = this.timeData[0];this.echartsqueryParams.endTime = this.timeData[1];echartsData(this.echartsqueryParams).then((res) => {this.data = res.data;this.checkedCities = this.data.series.map((i) => i.name);this.cityOptions = this.data.series.map((i) => i.name);this.cities = this.data.series.map((i) => i.name);this.axiosData = this.data.nodeTimes;this.legend = this.data.series.map((i) => i.name);this.echartsLoading = false;this.echartsList = JSON.parse(JSON.stringify(this.data));this.echartsList.series = this.echartsList.series.filter((item) => item.name !== "真空");this.echartsSerios(this.data.series);});},echartsSerios(data) {this.serios = data.map((i, index) => {const name = {name: i.name,type: "line",yAxisIndex: index,data: i.values,unit: i.unit,z: 5,zLevel: 5,symbolSize: 10,label: {show: true,color: "#999999", // 标签文字颜色fontSize: 12, // 标签文字大小fontWeight: 400,avoidLabelOverlap: true,overlap: false,formatter: function (params) {if (params.dataIndex % 4 === 0) {return params.value;} else {return ""; // 不显示标签以避免重叠}},},};if (i.name === "真空" && this.csValue != 0) {name.markLine = {data: [{ type: "average", name: i.name, yAxis: this.csValue }],};}return name;});let currentOffset = 0; // 总体的偏移量let nameLengthOffset = 0;this.yaxios = data.map((i, index) => {nameLengthOffset = i.name.length * 20; // 名称长度偏移量const name = {type: "value",name: i.name,position: "right",alignTicks: true,offset: currentOffset,axisLine: {show: true,lineStyle: {color: this.colors[index % this.colors.length],},},axisLabel: {formatter: "{value}" + i.unit,},axisTick: {show: true,},};// 判断哪个轴不显示if (this.checkedCities.indexOf(i.name) > -1) {name.show = true;} else {name.show = false;nameLengthOffset = nameLengthOffset - i.name.length * 20;}if (i.yAxisMin != null) {name.min = i.yAxisMin;}if (i.yAxisMax != null) {name.max = i.yAxisMax;}currentOffset += nameLengthOffset; // 更新偏移量return name;});this.gridRIght = currentOffset - nameLengthOffset / 2; // 更新网格右侧偏移量},updateCurrentTime() {const now = new Date();const year = now.getFullYear();const month = String(now.getMonth() + 1).padStart(2, "0");const day = String(now.getDate()).padStart(2, "0");const hours = String(now.getHours()).padStart(2, "0");const minutes = String(now.getMinutes()).padStart(2, "0");const seconds = String(now.getSeconds()).padStart(2, "0");// 获取前一个小时的时间const previousHour = new Date(now.getTime() - 3600000); // 3600000 毫秒 = 1 小时const previousYear = previousHour.getFullYear();const previousMonth = String(previousHour.getMonth() + 1).padStart(2,"0");const previousDay = String(previousHour.getDate()).padStart(2, "0");const previousHours = String(previousHour.getHours()).padStart(2, "0");const previousMinutes = String(previousHour.getMinutes()).padStart(2,"0");const previousSeconds = String(previousHour.getSeconds()).padStart(2,"0");this.$set(this, "timeData", [`${previousYear}-${previousMonth}-${previousDay} ${previousHours}:${previousMinutes}:${previousSeconds}`,`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`,]);// this.echartsqueryParams.furnaceNumber = 13020100;// this.timeData = ["2025-09-08 00:00:00", "2025-09-08 00:03:00"];},inputChange(value) {this.echartsDataList();},},created() {this.echartsqueryParams.furnaceNumber = this.deviceOptions[0];this.updateCurrentTime();},mounted() {this.echartsDataList();},
};
</script>
<style lang="scss" scoped>
ul li {list-style: none;
}
.app-container {width: 100%;height: calc(100vh - 100px);.content {width: 100%;height: 100%;overflow: auto;}
}
::v-deep .el-dialog {width: 80vw !important;border-radius: 8px;margin-bottom: 0;margin-top: 5vh !important;display: flex;flex-direction: column;max-height: calc(90vh - 100px);overflow: hidden;box-sizing: border-box;.el-dialog__header {padding-top: 14px;}.el-dialog__body {margin: 0 20px 20px 20px;padding: 0;overflow: auto;}
}
@media print {@page {size: auto;// margin: 3mm;}
}
</style>
子组件
<template><div ref="chartRef" style="width: 100%; height: 100%"></div>
</template><script>
import * as echarts from "echarts";export default {name: "moreyaxis",props: {legend: {type: Array,default: () => [],},axiosData: {type: Array,default: () => [],},yaxiosData: {type: Array,default: () => [],},serios: {type: Array,default: () => [],},colors: {type: Array,default: () => [],},gridRIght: {type: Number,default: () => 0,},},data() {return {chartInstance: null,selectedPoints: [], // 用于存储选中的节点};},watch: {yaxiosData: {deep: true,handler() {this.selectedPoints = [];this.destroyChart();this.initChart();},},},mounted() {if (this.serios.length > 0) {this.initChart();}this.setupResizeObserver();},beforeDestroy() {if (this.chartInstance) {this.chartInstance.dispose();}},methods: {initChart() {if (!this.chartInstance) {this.chartInstance = echarts.init(this.$refs.chartRef);}let currentData = null; //const option = {color: this.colors,tooltip: {trigger: "axis",axisPointer: {type: "cross",},textStyle: {fontSize: 10,},// triggerOn: "click",alwaysShowContent: false, // 关键:永久显示enterable: false,show: true, // 默认不显示},dataZoom: [{type: "inside",},],grid: {top: "60px",left: "40px",bottom: "40px",right: this.gridRIght + "px",},legend: {data: this.legend,top: 0,},xAxis: [{boundaryGap: false,type: "category",axisTick: {alignWithLabel: true,},data: this.axiosData,axisLabel: {formatter: function (params) {var newParamsName = "";var paramsNameNumber = params.length;var provideNumber = 10;var rowNumber = Math.ceil(paramsNameNumber / provideNumber);for (let row = 0; row < rowNumber; row++) {newParamsName +=params.substring(row * provideNumber,(row + 1) * provideNumber) + "\n";}return newParamsName;},color: "#999",},},],yAxis: this.yaxiosData,series: this.serios,graphic: {elements: this.selectedPoints.map((point) => ({type: "group",children: [{z: 10,zLevel: 10,type: "rect",shape: { width: 140, height: 90 },style: {fill: "#FFF",// stroke: "#666", // 边框颜色// lineWidth: 1, // 边框宽度shadowBlur: 5, // 阴影模糊度shadowColor: "#666", // 阴影颜色shadowOffsetX: 2, // 阴影水平偏移shadowOffsetY: 2, // 阴影垂直偏移padding: ["10px", "5px"],},},{type: "text",z: 10,zLevel: 10,style: {text: this.formatTooltipContent(point),fill: "#999",fontSize: 12,textBaseline: "middle",padding: [10, 0, 0, 0],},position: [10, 0],},],position: [point.x, point.y],draggable: true,})),},};this.chartInstance.setOption(option);// 监听点击事件this.chartInstance.on("click", (params) => {if (params.componentType === "series") {const existingPoint = this.selectedPoints.find((point) =>point.seriesName === params.seriesName &&point.dataIndex === params.dataIndex);if (existingPoint) {this.selectedPoints = this.selectedPoints.filter((point) =>point.seriesName !== params.seriesName ||point.dataIndex !== params.dataIndex);} else {this.selectedPoints.push({seriesIndex: params.seriesIndex,name: params.name,seriesName: params.seriesName,value: params.value,x: params.event.offsetX,y: params.event.offsetY,dataIndex: params.dataIndex,});}// this.selectedPoints.forEach((item) => {// this.chartInstance.dispatchAction({// type: "showTip",// // 系列的 index,在 tooltip 的 trigger 为 axis 的时候可选。// seriesIndex: item.seriesIndex,// // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项// dataIndex: item.dataIndex,// });// });this.destroyChart();this.initChart();}});},formatTooltipContent(point) {// 格式化 tooltip 内容,显示多个轴的数据let content = `${this.axiosData[point.dataIndex]}\n`;this.serios.forEach((series) => {if (series.data[point.dataIndex] !== undefined) {content += `${series.name}: ${series.data[point.dataIndex]}${series.unit}\n`;}});return content;},destroyChart() {if (this.chartInstance) {this.chartInstance.dispose();this.chartInstance = null;}},setupResizeObserver() {const resizeObserver = new ResizeObserver((entries) => {for (let entry of entries) {if (entry.target === this.$refs.chartRef) {if (this.chartInstance) {this.chartInstance.resize();}}}});resizeObserver.observe(this.$refs.chartRef);},},
};
</script><style scoped>
/* 添加一些样式 */
</style>
源代码记录