echarts图表点击legend报错问题(折线图)

原因是:echats 实例,不能够用响应式变量去接收。
在这里插入图片描述

<template><div class="attendance-chart"><div v-if="loading" class="loading">加载中...</div><div v-else-if="error" class="error">数据加载失败: {{ error }}<button @click="fetchData">重新加载</button></div><div v-else><div ref="chart" style="width: 100%; height: 350px;"></div><div v-if="!chartData.attendanceData.length" class="no-data">暂无数据</div></div></div>
</template><script>
import * as echarts from 'echarts';
import homeService from '@api/home/home';export default {name: 'AttendanceChart',data() {return {chartInstance: null,  //这里是响应式chartData: {projectList: [],dateList: [],attendanceData: [],},loading: false,error: null,};},mounted() {this.fetchData();window.addEventListener('resize', this.resizeChart);},beforeDestroy() {window.removeEventListener('resize', this.resizeChart);this.destroyChart();},methods: {async fetchData() {this.loading = true;this.error = null;try {const response = await homeService.getData();if (this.validateData(response)) {this.chartData = response;this.$nextTick(() => this.initChart());} else {throw new Error('数据格式不正确');}} catch (err) {this.error = err.message || '请求失败';console.error('获取数据失败:', err);} finally {this.loading = false;}},validateData(data) {return (Array.isArray(data.projectList) &&Array.isArray(data.dateList) &&Array.isArray(data.attendanceData) &&data.attendanceData.every(item => item.projectName && Array.isArray(item.data) &&item.data.every(val => typeof val === 'number')));},initChart(attempt = 0) {if (attempt > 3) {this.error = '图表初始化超时';return;}if (!this.$refs.chart) {setTimeout(() => this.initChart(attempt + 1), 200);return;}this.destroyChart();try {this.chartInstance = echarts.init(this.$refs.chart);this.updateChart();} catch (err) {console.error('图表初始化失败:', err);this.error = '图表初始化失败';}},updateChart() {if (!this.chartInstance) return;try {const option = {tooltip: {trigger: 'axis',backgroundColor: 'rgba(0,0,0,0.7)',borderWidth: 0,padding: 10,textStyle: {color: '#fff',fontSize: 12},},legend: {data: this.chartData.projectList,bottom: 10,textStyle: {fontSize: 12},},grid: {left: '3%',right: '4%',bottom: '15%',top: '5%',containLabel: true},xAxis: {type: 'category',boundaryGap: false,data: this.chartData.dateList,axisLabel: {rotate: 45,fontSize: 12}},yAxis: {type: 'value',name: '人数',nameTextStyle: {fontSize: 12}},series: this.chartData.attendanceData.map(item => ({name: item.projectName,type: 'line',data: item.data,symbol: 'circle',symbolSize: 6,showSymbol: false,lineStyle: {width: 2},emphasis: {lineStyle: {width: 3}}}))};this.chartInstance.setOption(option, true);} catch (err) {console.error('图表配置失败:', err);this.error = '图表渲染失败';}},resizeChart() {if (this.chartInstance) {this.chartInstance.resize();}},destroyChart() {if (this.chartInstance) {this.chartInstance.dispose();this.chartInstance = null;}}}
};
</script><style scoped>
.attendance-chart {width: 100%;height: 350px;padding: 12px;background: #fff;border-radius: 8px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);position: relative;
}.loading,
.error,
.no-data {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;color: #666;
}.error {color: #f56c6c;
}.error button {margin-top: 8px;padding: 4px 12px;background: #f56c6c;color: white;border: none;border-radius: 4px;cursor: pointer;
}.no-data {color: #999;
}
</style>

修正后的代码:

<template><div class="attendance-chart"><div v-if="loading" class="loading">加载中...</div><div v-else-if="error" class="error">数据加载失败: {{ error }}<button @click="fetchData">重新加载</button></div><div v-else><div ref="chart" style="width: 100%; height: 350px;"></div><div v-if="!chartData.attendanceData.length" class="no-data">暂无数据</div></div></div>
</template><script>
import * as echarts from 'echarts';
import homeService from '../../../api/home/home';export default {name: 'AttendanceChart',data() {return {// 不要将 chartInstance 放在 data 中chartData: {projectList: [],dateList: [],attendanceData: [],},loading: false,error: null,};},// 将 chartInstance 作为组件实例属性chartInstance: null,mounted() {this.fetchData();window.addEventListener('resize', this.resizeChart);},beforeDestroy() {window.removeEventListener('resize', this.resizeChart);this.destroyChart();},methods: {async fetchData() {this.loading = true;this.error = null;try {const response = await homeService.getData();if (this.validateData(response)) {this.chartData = response;this.$nextTick(() => this.initChart());} else {throw new Error('数据格式不正确');}} catch (err) {this.error = err.message || '请求失败';console.error('获取数据失败:', err);} finally {this.loading = false;}},validateData(data) {return (Array.isArray(data.projectList) &&Array.isArray(data.dateList) &&Array.isArray(data.attendanceData) &&data.attendanceData.every(item => item.projectName && Array.isArray(item.data) &&item.data.every(val => typeof val === 'number')));},initChart(attempt = 0) {if (attempt > 3) {this.error = '图表初始化超时';return;}if (!this.$refs.chart) {setTimeout(() => this.initChart(attempt + 1), 200);return;}this.destroyChart();try {// 直接赋值给组件实例属性,而不是响应式数据this.$options.chartInstance = echarts.init(this.$refs.chart);this.updateChart();} catch (err) {console.error('图表初始化失败:', err);this.error = '图表初始化失败';}},updateChart() {// 从组件实例属性获取const chartInstance = this.$options.chartInstance;if (!chartInstance) return;try {const option = {tooltip: {trigger: 'axis',backgroundColor: 'rgba(0,0,0,0.7)',borderWidth: 0,padding: 10,textStyle: {color: '#fff',fontSize: 12},},legend: {data: this.chartData.projectList,bottom: 10,textStyle: {fontSize: 12},},grid: {left: '3%',right: '4%',bottom: '15%',top: '5%',containLabel: true},xAxis: {type: 'category',boundaryGap: false,data: this.chartData.dateList,axisLabel: {rotate: 45,fontSize: 12}},yAxis: {type: 'value',name: '人数',nameTextStyle: {fontSize: 12}},series: this.chartData.attendanceData.map(item => ({name: item.projectName,type: 'line',data: item.data,symbol: 'circle',symbolSize: 6,showSymbol: false,lineStyle: {width: 2},emphasis: {lineStyle: {width: 3}}}))};chartInstance.setOption(option, true);} catch (err) {console.error('图表配置失败:', err);this.error = '图表渲染失败';}},resizeChart() {const chartInstance = this.$options.chartInstance;if (chartInstance) {chartInstance.resize();}},destroyChart() {const chartInstance = this.$options.chartInstance;if (chartInstance) {chartInstance.dispose();this.$options.chartInstance = null;}}}
};
</script><style scoped>
.attendance-chart {width: 100%;height: 350px;padding: 12px;background: #fff;border-radius: 8px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);position: relative;
}.loading,
.error,
.no-data {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;color: #666;
}.error {color: #f56c6c;
}.error button {margin-top: 8px;padding: 4px 12px;background: #f56c6c;color: white;border: none;border-radius: 4px;cursor: pointer;
}.no-data {color: #999;
}
</style>

ECharts 在 Vue 中的正确使用方式

关键修改点

1. 移除响应式的 chartInstance

  • ❌ 不再在 data() 中声明 chartInstance
  • ✅ 改为使用 this.$options.chartInstance 存储 ECharts 实例

2. 统一实例访问方式

  • 所有方法中通过 this.$options.chartInstance 访问图表实例
  • 确保不会触发 Vue 的响应式系统

3. 完善销毁机制

destroyChart() {const chartInstance = this.$options.chartInstanceif (chartInstance) {chartInstance.dispose()this.$options.chartInstance = null}
}
为什么不能使用响应式变量?
ECharts 实例被 Vue 响应式代理后会导致:
⚠️ ECharts 内部方法调用异常
⚠️ 图例点击等交互事件失效
⚠️ 可能引发内存泄漏
⚠️ 性能下降替代方案
// 方案1:使用组件选项
this.$options.chartInstance = echarts.init()// 方案2:在 created 中定义非响应式属性
created() {this.chartInstance = null
}

注意事项

确保容器存在

mounted() {this.$nextTick(() => {this.fetchData()})
}
处理动态数据更新
watch: {'chartData.attendanceData': {handler() {this.updateChart()},deep: true}
}
添加加载状态
updateChart() {if (this.$options.chartInstance) {this.$options.chartInstance.showLoading()// ...设置option...this.$options.chartInstance.hideLoading()}
}
最终效果:经过这些修改后,图表的所有交互功能(包括图例点击)都将正常工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91047.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django模型开发:模型字段、元数据与继承全方位讲解

文章目录一、模型字段类型详解Django 与 MySQL 字段类型映射整数类型深度对比二、常用字段选项null 与 blank 的区别注释与帮助文本默认值设置日期时间特殊选项选项列表&#xff08;choices&#xff09;三、模型元数据与方法模型 Meta 类模型管理器&#xff08;Manager&#xf…

墨者:SQL注入实战-MySQL

1. 墨者学院&#xff1a;SQL注入实战-MySQL&#x1f680; 2. 实训重点目标✨ 目标一&#xff1a; 了解sqlmap的使用及其tamper插件的使用&#xff1b; 目标二&#xff1a; 了解base64编码及解码。 3. 解题方向&#x1f50d; 目标网站的id参数通过Base64编码传输&#xff0c;…

Milvus 实战全流程

&#x1f4da; 学习路径总览1. Milvus 基础知识什么是向量数据库&#xff1f;Milvus 的核心概念&#xff08;collection、field、index、partition、segment&#xff09;Milvus 和 Faiss、Annoy、HNSW 的区别2. 安装与部署Docker 快速部署 Milvus&#xff08;推荐&#xff09;本…

Mysql数据库基础(入门)

目录 一.认识Sql 1.什么是Sql 2.Sql的作用 3.Sql通用语法 4.Sql分类 二.数据库的操作&#xff08;DDL&#xff09; 1.创建数据库 2.显示/使用数据库 3.修改数据库 4.删除数据库 三.常用数据类型 1.数值类型 2.字符串类型 3.日期类型 4.详细的数据类型 四.表的操…

MySQL 锁机制 15 连问 · 面试速答版

一、脑图&#xff1a;锁全景&#xff08;先记结构&#xff0c;再填细节&#xff09; 锁层级 ├─ 表锁 │ ├─ 意向锁 IS / IX │ └─ 表锁 READ / WRITE └─ 行锁├─ 记录锁 Record├─ 间隙锁 Gap└─ 临键锁 Next-Key二、15 问 15 答&#xff08;面试官一问一…

【Linux】发展历程

很高兴为您详细介绍Linux操作系统的详细发展历程。Linux是一个自由和开放源代码的操作系统内核&#xff0c;由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于1991年首次发布。以下是Linux操作系统的主要发展里程碑&#xff1a;1. Linux 0.01 (1991)发布日期&#xff1a…

LNMP架构+wordpress实现动静分离

WordPress简称WP&#xff0c;最初是一款博客系统&#xff0c;后逐步演化成一款免费的CMS&#xff08;内容管理系统/建站系统&#xff09;。 WordPress网站的适用场景&#xff1a; 博客 企业官网 作品集网站 电商平台 线上教育系统 论坛和社群网站 甚至会员系统、订阅内容…

智慧灯杆:不止于照明,塔能科技的城市感知网络野心

当夜幕悄然降临&#xff0c;城市里的路灯便依次亮了起来&#xff0c;它们可不单单照亮了行人前行的路以及车辆行驶的道路&#xff0c;实际上还在悄无声息地经历着一场变革。现如今的路灯&#xff0c;早已不再仅仅充当单纯的照明工具这么一个角色了&#xff0c;而是逐渐转变成了…

【Linux内核模块】调试技巧

内核模块开发最让人头疼的不是写代码&#xff0c;而是调试 —— 代码编译通过了&#xff0c;加载后却要么没反应&#xff0c;要么直接让系统崩溃。这就像在黑屋子里修机器&#xff0c;看不见摸不着。其实内核调试有一套成熟的工具箱&#xff0c;掌握这些工具和技巧&#xff0c;…

RK3568笔记九十一:QT环境搭建

若该文为原创文章,转载请注明原文出处。 记录按照正点原子给的手册搭建QT环境 参考《09【正点原子】ATK-DLRK3568_Qt开发环境搭建V1.2.pdf》 一、安装 1、下载 https://mirrors.sau.edu.cn/qt/archive/online_installers/4.6/qt-unified-linux-x64-4.6.0-online.run 2、赋…

面试实战,问题十六,Java面试,消息队列,如何避免消息重复消费,怎么回答

在Java面试中&#xff0c;关于消息队列如何防止消息被重复消费的问题&#xff0c;可以从以下几个方面进行回答&#xff0c;结合系统架构设计、消息队列机制和业务逻辑处理&#xff0c;确保在不同场景下实现消息的幂等性。 1. 消息队列重复消费的根本原因 消息重复消费的根本原因…

PDF转图片实用指南:如何批量高效转换?

将PDF转换为图片后&#xff0c;可以更方便地在演示文稿、网页或电子相册中使用这些资料&#xff0c;以便更好地展示信息。它 是一款支持多文件批量转换的工具&#xff0c;可将多个 PDF 文档一键转换为图片格式。虽然界面为英文&#xff0c;但操作简单&#xff0c;不影响使用。你…

走入Linux的世界:编辑器Vim

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…

PyTorch中神经网络的模型构建

要构建自定义模型&#xff0c;需完成两个核心步骤&#xff1a;继承 nn.Module 类&#xff1b;重载 __init__ 方法&#xff08;初始化&#xff09;和 forward 方法&#xff08;前向计算&#xff09; 神经网络的构造 初始化方法&#xff08;__init__&#xff09; def __init__…

QML QtCharts坐标轴系统

QtCharts是Qt框架中强大的数据可视化模块&#xff0c;它提供了丰富的图表类型和灵活的坐标轴系统&#xff0c;能够满足各种数据展示需求。本文将全面介绍QML中QtCharts的坐标轴系统&#xff0c;包括数值坐标轴(ValueAxis)、对数坐标轴(LogValueAxis)、分类坐标轴(CategoryAxis)…

TI 2025全国电赛猜题

本科组可能的题目方向本科组器材更侧重高频信号处理、复杂控制系统、精密测量及多设备协同&#xff0c;可能涉及以下题目&#xff1a;四旋翼飞行器相关任务题目示例&#xff1a;设计 “基于四旋翼的 UV 光控自主导航系统”任务要求&#xff1a;利用四旋翼飞行器&#xff08;最大…

Python自动化运维实战指南

什么是自动化运维定义与背景自动化运维是指利用工具和脚本自动执行传统上需要人工操作的IT运维任务&#xff0c;包括但不限于服务器配置管理、软件部署、监控告警、日志分析等日常工作。随着互联网业务规模的扩大&#xff0c;传统手工运维方式已无法满足快速部署、规模化管理等…

k8s的csi对接GPFS

在 Kubernetes&#xff08;k8s&#xff09;集群中&#xff0c;通过 CSI&#xff08;Container Storage Interface&#xff09;对接 GPFS&#xff08;General Parallel File System&#xff0c;现为 IBM Spectrum Scale&#xff09;是实现高性能共享存储的重要方案。GPFS 作为并…

HTB赛季8靶场 - era

nmap扫描 └─$ nmap -p- --min-rate 1000 -T4 10.129.137.201 -oA nmapfullscan Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-07-27 21:19 EDT Warning: 10.129.137.201 giving up on port because retransmission cap hit (6). …

Bug猫学习史#1:面向对象

在Java编程中&#xff0c;掌握几个核心概念对深入学习至关重要&#xff1a;类属性建议采用包装类以提升灵活性&#xff1b;建造者模式中this关键字能有效简化对象构建过程&#xff1b;static关键字涉及类的加载机制&#xff1b;接口默认使用public修饰符并支持默认方法实现&…