父组件
1<template> 2 <div class="app-container"> 3 <div class="content"> 4 <el-form 5 :model="echartsqueryParams" 6 ref="echartsqueryForm" 7 :inline="true" 8 > 9 <el-form-item label="号" prop="furnaceNumber"> 10 <el-select 11 v-model="echartsqueryParams.furnaceNumber" 12 placeholder="请选择" 13 style="width: 150px" 14 > 15 <el-option 16 v-for="item in deviceOptions" 17 :key="item" 18 :label="item" 19 :value="item" 20 > 21 </el-option> 22 </el-select> 23 </el-form-item> 24 <el-form-item label="日期"> 25 <el-date-picker 26 v-model="timeData" 27 popper-class="noClear" 28 size="small" 29 value-format="yyyy-MM-dd HH:mm:ss" 30 type="datetimerange" 31 range-separator="-" 32 start-placeholder="开始日期" 33 end-placeholder="结束日期" 34 :clearable="false" 35 @change="handleChange" 36 ></el-date-picker> 37 </el-form-item> 38 <el-form-item label="真空"> 39 <el-input 40 style="width: 150px" 41 v-model="csValue" 42 placeholder="请输入" 43 type="number" 44 @blur="inputChange" 45 /> 46 </el-form-item> 47 <el-form-item label="时间间隔" prop="interval"> 48 <el-select 49 v-model="echartsqueryParams.interval" 50 placeholder="请选择" 51 style="width: 150px" 52 > 53 <el-option 54 v-for="item in options" 55 :key="item.value" 56 :label="item.label" 57 :value="item.value" 58 > 59 </el-option> 60 </el-select> 61 </el-form-item> 62 <el-form-item> 63 <el-button 64 icon="el-icon-printer" 65 type="primary" 66 @click="ceshi = !ceshi" 67 size="mini" 68 >打印</el-button 69 > 70 <el-button 71 type="primary" 72 icon="el-icon-search" 73 size="mini" 74 @click="handleQuery_echarts" 75 >搜索</el-button 76 > 77 <el-button 78 icon="el-icon-refresh" 79 size="mini" 80 @click="resetQuery_echarts" 81 >重置</el-button 82 > 83 </el-form-item> 84 </el-form> 85 <div> 86 <el-checkbox-group 87 v-model="checkedCities" 88 @change="handleCheckedCitiesChange" 89 > 90 <el-checkbox v-for="city in cities" :label="city" :key="city">{{ 91 city 92 }}</el-checkbox> 93 </el-checkbox-group> 94 </div> 95 <div style="margin-bottom: 15px"></div> 96 <div 97 v-loading="echartsLoading" 98 style="width: 100%; height: calc(100% - 40px - 60px)" 99 > 100 <moreyaxis 101 v-if="axiosData.length > 0" 102 :legend="legend" 103 :axiosData="axiosData" 104 :yaxiosData="yaxios" 105 :serios="serios" 106 :colors="colors" 107 :gridRIght="gridRIght" 108 ></moreyaxis> 109 <el-empty description="暂无数据" v-else></el-empty> 110 </div> 111 </div> 112 <!-- 打印内容 --> 113 <el-dialog 114 class="dialogPrint" 115 title="请确认打印内容" 116 :visible.sync="ceshi" 117 style="height: 90vh" 118 > 119 <div id="printMe" style="width: 100%"> 120 <div style="width: 1px; height: 1px"></div> 121 <div v-if="echartsList.nodeTimes.length > 0"> 122 <div style="display: flex"> 123 <p style="margin: 0; margin-right: 20px"> 124 号:{{ echartsqueryParams.furnaceNumber }} 125 </p> 126 <p style="margin: 0; margin-right: 20px"> 127 开始时间:{{ echartsList.nodeTimes[0] }} 128 </p> 129 <p style="margin: 0"> 130 结束时间:{{ 131 echartsList.nodeTimes[Number(echartsList.nodeTimes.length) - 1] 132 }} 133 </p> 134 </div> 135 <ul 136 style=" 137 padding: 0; 138 display: grid; 139 grid-template-columns: repeat(4, 1fr); 140 grid-column-gap: 10px; 141 grid-row-gap: 10px; 142 " 143 > 144 <li v-for="(item, index) in echartsList.series" :key="index"> 145 <p 146 style=" 147 width: 100%; 148 border: 1px solid #f9f9f9; 149 padding: 10px; 150 margin: 0; 151 " 152 > 153 <span 154 style="border-left: 3px solid #4068e0; padding-left: 5px" 155 >{{ item.name }}</span 156 > 157 </p> 158 <div style="display: flex"> 159 <p 160 style=" 161 font-size: 12px; 162 margin: 0; 163 width: 50%; 164 border: 1px solid #f9f9f9; 165 border-top: none; 166 padding: 10px; 167 " 168 > 169 开始{{ item.name.substring(item.name.length - 2) }}:{{ 170 item.values[0] 171 }}{{ item.unit }} 172 </p> 173 <p 174 style=" 175 font-size: 12px; 176 margin: 0; 177 width: 50%; 178 border: 1px solid #f9f9f9; 179 border-top: none; 180 padding: 10px; 181 " 182 > 183 结束{{ item.name.substring(item.name.length - 2) }}:{{ 184 item.values[Number(item.values.length) - 1] 185 }}{{ item.unit }} 186 </p> 187 </div> 188 </li> 189 </ul> 190 </div> 191 <div style="width: 100%; height: 500px" v-if="axiosData.length > 0"> 192 <moreyaxis 193 style="width: 100%; height: 500px" 194 :legend="legend" 195 :axiosData="axiosData" 196 :yaxiosData="yaxios" 197 :serios="serios" 198 :colors="colors" 199 :gridRIght="gridRIght" 200 ></moreyaxis> 201 </div> 202 </div> 203 204 <div slot="footer"> 205 <el-button @click="ceshi = false">取 消</el-button> 206 <el-button type="primary" v-print="printObj">确 定</el-button> 207 </div> 208 </el-dialog> 209 </div> 210</template> 211<script> 212import moreyaxis from "@/views/echarts/moreyaxis.vue"; 213import { echartsData } from "@/api/collocation/records"; 214export default { 215 components: { moreyaxis }, 216 data() { 217 return { 218 deviceOptions: [28030002, 13020101, 28030003, 13020100], 219 ceshi: false, 220 // 图表参数 221 start: true, 222 echartsList: { 223 nodeTimes: [], 224 series: [], 225 }, 226 echartsLoading: true, 227 colors: ["#6488FE", "#FF6F6F", "#FEE177", "#2AF0CF", "#08BE87"], 228 legend: [], 229 axiosData: [], 230 serios: [], 231 yaxios: [], 232 // 查询参数 233 echartsqueryParams: { 234 startTime: null, 235 endTime: null, 236 interval: 30, 237 id: null, 238 // limit: 200, 239 furnaceNumber: null, 240 }, 241 timeData: [], 242 options: [ 243 { 244 label: "十秒", 245 value: 10, 246 }, 247 { 248 label: "三十秒", 249 value: 30, 250 }, 251 { 252 label: "一分钟", 253 value: 60, 254 }, 255 { 256 label: "五分钟", 257 value: 300, 258 }, 259 { 260 label: "十分钟", 261 value: 600, 262 }, 263 { 264 label: "十五分钟", 265 value: 900, 266 }, 267 { 268 label: "半小时", 269 value: 1800, 270 }, 271 { 272 label: "一小时", 273 value: 3600, 274 }, 275 ], 276 printObj: { 277 id: "printMe", 278 popTitle: "记录", 279 closeCallback: this.closeCallback, 280 }, 281 detailsTable: [], 282 gridRIght: 0, 283 csValue: null, 284 cities: [], 285 cityOptions: [], 286 checkAll: false, 287 isIndeterminate: true, 288 checkedCities: [], 289 data: {}, 290 }; 291 }, 292 methods: { 293 handleChange(value) { 294 if (value) { 295 const start = new Date(value[0]); 296 const end = new Date(value[1]); 297 298 if (end - start > 24 * 60 * 60 * 1000) { 299 this.$message.error("结束时间不能大于开始时间一天"); 300 this.timeData = null; // 清空选择 301 } 302 } 303 }, 304 // 设备列表 305 deviceData() { 306 listDevice().then((res) => [console.log(res)]); 307 }, 308 handleCheckedCitiesChange(value) { 309 this.echartsSerios(this.data.series); 310 }, 311 // 导出 312 handleExport_echarts() {}, 313 // 图表搜索 314 handleQuery_echarts() { 315 this.echartsLoading = true; 316 this.echartsDataList(); 317 }, 318 // 图表重置 319 resetQuery_echarts() { 320 this.updateCurrentTime(); 321 this.echartsLoading = true; 322 this.echartsqueryParams.interval = null; 323 this.echartsDataList(); 324 }, 325 // 图表数据解析 326 echartsDataList(row) { 327 this.echartsqueryParams.startTime = this.timeData[0]; 328 this.echartsqueryParams.endTime = this.timeData[1]; 329 echartsData(this.echartsqueryParams).then((res) => { 330 this.data = res.data; 331 this.checkedCities = this.data.series.map((i) => i.name); 332 this.cityOptions = this.data.series.map((i) => i.name); 333 this.cities = this.data.series.map((i) => i.name); 334 this.axiosData = this.data.nodeTimes; 335 this.legend = this.data.series.map((i) => i.name); 336 this.echartsLoading = false; 337 this.echartsList = JSON.parse(JSON.stringify(this.data)); 338 this.echartsList.series = this.echartsList.series.filter( 339 (item) => item.name !== "真空" 340 ); 341 this.echartsSerios(this.data.series); 342 }); 343 }, 344 echartsSerios(data) { 345 this.serios = data.map((i, index) => { 346 const name = { 347 name: i.name, 348 type: "line", 349 yAxisIndex: index, 350 data: i.values, 351 unit: i.unit, 352 z: 5, 353 zLevel: 5, 354 symbolSize: 10, 355 label: { 356 show: true, 357 color: "#999999", // 标签文字颜色 358 fontSize: 12, // 标签文字大小 359 fontWeight: 400, 360 avoidLabelOverlap: true, 361 overlap: false, 362 formatter: function (params) { 363 if (params.dataIndex % 4 === 0) { 364 return params.value; 365 } else { 366 return ""; // 不显示标签以避免重叠 367 } 368 }, 369 }, 370 }; 371 if (i.name === "真空" && this.csValue != 0) { 372 name.markLine = { 373 data: [{ type: "average", name: i.name, yAxis: this.csValue }], 374 }; 375 } 376 return name; 377 }); 378 379 let currentOffset = 0; // 总体的偏移量 380 let nameLengthOffset = 0; 381 this.yaxios = data.map((i, index) => { 382 nameLengthOffset = i.name.length * 20; // 名称长度偏移量 383 const name = { 384 type: "value", 385 name: i.name, 386 position: "right", 387 alignTicks: true, 388 offset: currentOffset, 389 axisLine: { 390 show: true, 391 lineStyle: { 392 color: this.colors[index % this.colors.length], 393 }, 394 }, 395 axisLabel: { 396 formatter: "{value}" + i.unit, 397 }, 398 axisTick: { 399 show: true, 400 }, 401 }; 402 403 // 判断哪个轴不显示 404 if (this.checkedCities.indexOf(i.name) > -1) { 405 name.show = true; 406 } else { 407 name.show = false; 408 nameLengthOffset = nameLengthOffset - i.name.length * 20; 409 } 410 if (i.yAxisMin != null) { 411 name.min = i.yAxisMin; 412 } 413 414 if (i.yAxisMax != null) { 415 name.max = i.yAxisMax; 416 } 417 418 currentOffset += nameLengthOffset; // 更新偏移量 419 return name; 420 }); 421 422 this.gridRIght = currentOffset - nameLengthOffset / 2; // 更新网格右侧偏移量 423 }, 424 updateCurrentTime() { 425 const now = new Date(); 426 const year = now.getFullYear(); 427 const month = String(now.getMonth() + 1).padStart(2, "0"); 428 const day = String(now.getDate()).padStart(2, "0"); 429 const hours = String(now.getHours()).padStart(2, "0"); 430 const minutes = String(now.getMinutes()).padStart(2, "0"); 431 const seconds = String(now.getSeconds()).padStart(2, "0"); 432 // 获取前一个小时的时间 433 const previousHour = new Date(now.getTime() - 3600000); // 3600000 毫秒 = 1 小时 434 const previousYear = previousHour.getFullYear(); 435 const previousMonth = String(previousHour.getMonth() + 1).padStart( 436 2, 437 "0" 438 ); 439 const previousDay = String(previousHour.getDate()).padStart(2, "0"); 440 const previousHours = String(previousHour.getHours()).padStart(2, "0"); 441 const previousMinutes = String(previousHour.getMinutes()).padStart( 442 2, 443 "0" 444 ); 445 const previousSeconds = String(previousHour.getSeconds()).padStart( 446 2, 447 "0" 448 ); 449 this.$set(this, "timeData", [ 450 `${previousYear}-${previousMonth}-${previousDay} ${previousHours}:${previousMinutes}:${previousSeconds}`, 451 `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`, 452 ]); 453 454 // this.echartsqueryParams.furnaceNumber = 13020100; 455 // this.timeData = ["2025-09-08 00:00:00", "2025-09-08 00:03:00"]; 456 }, 457 inputChange(value) { 458 this.echartsDataList(); 459 }, 460 }, 461 created() { 462 this.echartsqueryParams.furnaceNumber = this.deviceOptions[0]; 463 this.updateCurrentTime(); 464 }, 465 mounted() { 466 this.echartsDataList(); 467 }, 468}; 469</script> 470<style lang="scss" scoped> 471ul li { 472 list-style: none; 473} 474.app-container { 475 width: 100%; 476 height: calc(100vh - 100px); 477 .content { 478 width: 100%; 479 height: 100%; 480 overflow: auto; 481 } 482} 483::v-deep .el-dialog { 484 width: 80vw !important; 485 border-radius: 8px; 486 margin-bottom: 0; 487 margin-top: 5vh !important; 488 display: flex; 489 flex-direction: column; 490 max-height: calc(90vh - 100px); 491 overflow: hidden; 492 box-sizing: border-box; 493 .el-dialog__header { 494 padding-top: 14px; 495 } 496 .el-dialog__body { 497 margin: 0 20px 20px 20px; 498 padding: 0; 499 overflow: auto; 500 } 501} 502@media print { 503 @page { 504 size: auto; 505 // margin: 3mm; 506 } 507} 508</style> 509 510
子组件
1<template> 2 <div ref="chartRef" style="width: 100%; height: 100%"></div> 3</template> 4 5<script> 6import * as echarts from "echarts"; 7 8export default { 9 name: "moreyaxis", 10 props: { 11 legend: { 12 type: Array, 13 default: () => [], 14 }, 15 axiosData: { 16 type: Array, 17 default: () => [], 18 }, 19 yaxiosData: { 20 type: Array, 21 default: () => [], 22 }, 23 serios: { 24 type: Array, 25 default: () => [], 26 }, 27 colors: { 28 type: Array, 29 default: () => [], 30 }, 31 gridRIght: { 32 type: Number, 33 default: () => 0, 34 }, 35 }, 36 data() { 37 return { 38 chartInstance: null, 39 selectedPoints: [], // 用于存储选中的节点 40 }; 41 }, 42 watch: { 43 yaxiosData: { 44 deep: true, 45 handler() { 46 this.selectedPoints = []; 47 this.destroyChart(); 48 this.initChart(); 49 }, 50 }, 51 }, 52 mounted() { 53 if (this.serios.length > 0) { 54 this.initChart(); 55 } 56 this.setupResizeObserver(); 57 }, 58 beforeDestroy() { 59 if (this.chartInstance) { 60 this.chartInstance.dispose(); 61 } 62 }, 63 methods: { 64 initChart() { 65 if (!this.chartInstance) { 66 this.chartInstance = echarts.init(this.$refs.chartRef); 67 } 68 let currentData = null; // 69 const option = { 70 color: this.colors, 71 tooltip: { 72 trigger: "axis", 73 axisPointer: { 74 type: "cross", 75 }, 76 textStyle: { 77 fontSize: 10, 78 }, 79 // triggerOn: "click", 80 alwaysShowContent: false, // 关键:永久显示 81 enterable: false, 82 show: true, // 默认不显示 83 }, 84 dataZoom: [ 85 { 86 type: "inside", 87 }, 88 ], 89 grid: { 90 top: "60px", 91 left: "40px", 92 bottom: "40px", 93 right: this.gridRIght + "px", 94 }, 95 legend: { 96 data: this.legend, 97 top: 0, 98 }, 99 xAxis: [ 100 { 101 boundaryGap: false, 102 type: "category", 103 axisTick: { 104 alignWithLabel: true, 105 }, 106 data: this.axiosData, 107 axisLabel: { 108 formatter: function (params) { 109 var newParamsName = ""; 110 var paramsNameNumber = params.length; 111 var provideNumber = 10; 112 var rowNumber = Math.ceil(paramsNameNumber / provideNumber); 113 for (let row = 0; row < rowNumber; row++) { 114 newParamsName += 115 params.substring( 116 row * provideNumber, 117 (row + 1) * provideNumber 118 ) + "\n"; 119 } 120 return newParamsName; 121 }, 122 color: "#999", 123 }, 124 }, 125 ], 126 yAxis: this.yaxiosData, 127 series: this.serios, 128 graphic: { 129 elements: this.selectedPoints.map((point) => ({ 130 type: "group", 131 children: [ 132 { 133 z: 10, 134 zLevel: 10, 135 type: "rect", 136 shape: { width: 140, height: 90 }, 137 style: { 138 fill: "#FFF", 139 // stroke: "#666", // 边框颜色 140 // lineWidth: 1, // 边框宽度 141 shadowBlur: 5, // 阴影模糊度 142 shadowColor: "#666", // 阴影颜色 143 shadowOffsetX: 2, // 阴影水平偏移 144 shadowOffsetY: 2, // 阴影垂直偏移 145 padding: ["10px", "5px"], 146 }, 147 }, 148 { 149 type: "text", 150 z: 10, 151 zLevel: 10, 152 style: { 153 text: this.formatTooltipContent(point), 154 fill: "#999", 155 fontSize: 12, 156 textBaseline: "middle", 157 padding: [10, 0, 0, 0], 158 }, 159 position: [10, 0], 160 }, 161 ], 162 position: [point.x, point.y], 163 draggable: true, 164 })), 165 }, 166 }; 167 this.chartInstance.setOption(option); 168 169 // 监听点击事件 170 this.chartInstance.on("click", (params) => { 171 if (params.componentType === "series") { 172 const existingPoint = this.selectedPoints.find( 173 (point) => 174 point.seriesName === params.seriesName && 175 point.dataIndex === params.dataIndex 176 ); 177 if (existingPoint) { 178 this.selectedPoints = this.selectedPoints.filter( 179 (point) => 180 point.seriesName !== params.seriesName || 181 point.dataIndex !== params.dataIndex 182 ); 183 } else { 184 this.selectedPoints.push({ 185 seriesIndex: params.seriesIndex, 186 name: params.name, 187 seriesName: params.seriesName, 188 value: params.value, 189 x: params.event.offsetX, 190 y: params.event.offsetY, 191 dataIndex: params.dataIndex, 192 }); 193 } 194 // this.selectedPoints.forEach((item) => { 195 // this.chartInstance.dispatchAction({ 196 // type: "showTip", 197 // // 系列的 index,在 tooltip 的 trigger 为 axis 的时候可选。 198 // seriesIndex: item.seriesIndex, 199 // // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项 200 // dataIndex: item.dataIndex, 201 // }); 202 // }); 203 204 this.destroyChart(); 205 this.initChart(); 206 } 207 }); 208 }, 209 formatTooltipContent(point) { 210 // 格式化 tooltip 内容,显示多个轴的数据 211 let content = `${this.axiosData[point.dataIndex]}\n`; 212 this.serios.forEach((series) => { 213 if (series.data[point.dataIndex] !== undefined) { 214 content += `${series.name}: ${series.data[point.dataIndex]}${ 215 series.unit 216 }\n`; 217 } 218 }); 219 return content; 220 }, 221 destroyChart() { 222 if (this.chartInstance) { 223 this.chartInstance.dispose(); 224 this.chartInstance = null; 225 } 226 }, 227 setupResizeObserver() { 228 const resizeObserver = new ResizeObserver((entries) => { 229 for (let entry of entries) { 230 if (entry.target === this.$refs.chartRef) { 231 if (this.chartInstance) { 232 this.chartInstance.resize(); 233 } 234 } 235 } 236 }); 237 resizeObserver.observe(this.$refs.chartRef); 238 }, 239 }, 240}; 241</script> 242 243<style scoped> 244/* 添加一些样式 */ 245</style> 246
源代码记录
《vue2动态实现多Y轴echarts图表,及节点点击事件》 是转载文章,点击查看原文。

