当前位置: 首页 > news >正文

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.lryc.cn/news/603604.html

相关文章:

  • 8.项目起步(2)
  • 数据库02 网页html01 day44
  • 图像增强11种几何变换方法示例
  • 从单机架构到分布式:Redis为何成为架构升级的关键一环?
  • 基于web的在线购物系统的设计与实现/在线商城的设计与实现
  • 架构实战——互联网架构模板(“网络层”技术)
  • MySQL MVCC:并发神器背后的原理解析
  • ElementUI表格 el-table实现自动循环滚动
  • MySQL图解索引篇
  • JavaWeb(苍穹外卖)--学习笔记15(分页查询PageHelper)
  • JavaWeb 入门:JavaScript 基础与实战详解(Java 开发者视角)
  • 一个人开发一个App(数据库)
  • vue3组件通信的几种方法,详解
  • ​第七篇:Python数据库编程与ORM实践
  • Vue 2.0响应式原理深度解析
  • 【C++算法】82.BFS解决FloodFill算法_被围绕的区域
  • 基于SpringBoot和Leaflet集成在线天气服务的区县当前天气WebGIS实战
  • 【CSS】盒子类型
  • Linux网络:多路转接 select
  • 7月29号打卡
  • 个人健康管理小程序(消息订阅、Echarts图形化分析)
  • C# CAN通信上位机系统设计与实现
  • Hyperchain安全与隐私机制详解
  • Prometheus + Grafana + Micrometer 监控方案详解
  • CodeRush AI 助手进驻 Visual Studio:AiGen/AiFind 亮相(三)
  • 【数据可视化-74】电信用户流失数据可视化分析:Python + Pyecharts 炫酷大屏(含完整的数据,代码)
  • Visual Studio的妙用
  • 【22】C# 窗体应用WinForm ——定时器Timer属性、方法、实例应用,定时切换画面
  • 从github同步新项目的两次挫折-2025.7.29
  • 工业补贴携手华为云,重塑汽车零部件供应链管理新趋势