vue-echarts公共组件,切换页面图表显示不全【解决】

问题场景:
多个页面使用同一个echarts公共组件,在切换页面的时候,echarts图表渲染不完整

原因:
echarts图表宽度使用的是百分比,不是具体px数值,初次渲染时,可正确渲染,但页面被缓存后,100%会被渲染成宽度100

解决方案:

方案一:固定px宽度

为echarts图表设置固定px宽度

缺点:不灵活,有时样式是自适应的,无法给一个确定值

方案二:去除缓存

不缓存echarts图表所在的当前页面,每次打开页面时都初始化echarts
利用keep-alive的include/exclude属性,或给router-view增加key属性,key的值保证唯一性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<keep-alive>
<router-view :key="key"></router-view>
</keep-alive>
</template>

<script>
export default {
computed: {
key() {
return this.$route.name !== undefined ? this.$route.name + new Date() : this.$route + new Date()
}
}
}
</script>

缺点:我的项目中,路由内容都是动态获取的,无法用include/exclude确定name值,router-view覆盖网站全部页面,如果设定唯一key值,则所有页面都不会缓存,不符合业务需求

方案三:activated钩子函数

利用vue生命周期activated
在使用了keep-alive 组件的页面中,keep-alive 组件激活时调用activated 生命周期函数,即每次进入页面时都会调用activated函数,可以利用这一特性,获取echarts图表组件父元素的具体宽度值,再利用echarts的resize事件,重新resize

以下为具体实现代码

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="chart-box">
// echarts公共组件
<line-chart :chart-data="lineChartData" v-loading="chartLoading"/>
</div>
</template>

<script>
// 引入echarts公共组件
import LineChart from '@/components/Charts/LineChart'

export default {
components: {
LineChart
},
data() {
return {
lineChartData: {},
chartLoading: false
}
}
}
</script>

lineChart组件:异步获取数据更新echarts,重点activated函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<template>
<div ref="lineChart" :class="className" :style="{height:height,width:width}" />
</template>

<script>
import echarts from 'echarts'
// window resize事件
import resize from './mixins/resize'

// 传入数据格式请参考下面:
// const lineChartData = {
// chartData: [
// { name: '是的方式方法', value: [100, 120, 161, 134, 105, 160, 165] },
// { name: '手动阀手动阀', value: [120, 82, 91, 154, 162, 140, 145] }
// { name: '6666', value: [100, 82, 92, 164, 132, 140, 140] },
// { name: 'sdfsdf', value: [80, 82, 62, 154, 122, 150, 110] },
// { name: '444', value: [90, 78, 87, 164, 152, 140, 110] }
// ],
// xaxis: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
// }

// 数据覆盖颜色 可增加
const itemStyleColor = [
// 圆圈节点 线 0%处的颜色 100%处的颜色
['#3888fa', '#638de6', '#8cbcdf', '#e0eff4'], // 蓝
['#5fbfa4', '#5ccfae', '#a8e5d3', '#e3f6f1'], // 绿
['#8c68d4', '#a78cde', '#aa86f1', '#dcd1f2'], // 紫
['#f1899b', '#ee6e73', '#ee6f74', '#fde8e9'], // 橘粉
['#0caec1', '#26c6d9', '#5bcbd8', '#87d1da'] // 湖蓝
]

export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '350px'
},
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: [Object, Array],
required: true
},
title: {
type: String,
default: ''
}
},
data() {
return {
chart: null
}
},
watch: {
chartData: {
handler(val) {
val && this.setOptions(val)
},
deep: true
},
watch(val) {
this.chart.resize(val, 'auto')
}
},
activated() {
const widthVal = window.getComputedStyle(this.$refs.lineChart).width
this.$nextTick(() => {
this.chart.resize(widthVal)
})
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
// 切换页面时 若宽度变化 重新resize图表
resizeChart(widthVal) {
if (this.chart) {
this.chart.resize(widthVal)
}
},
initChart() {
this.chart = echarts.init(this.$el)
this.setOptions(this.chartData)
},
setOptions({ chartData, xaxis } = {}) {
const options = {
title: {
text: this.title || '',
textStyle: {
color: '#606266',
fontSize: 14
}
},
xAxis: {
data: xaxis || [],
boundaryGap: false,
axisTick: {
show: true,
lineStyle: {
color: '#ccc'
}
},
axisLabel: {
rotate: 60
}
},
grid: {
left: 50,
right: 50,
bottom: 20,
top: 30,
containLabel: true
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: {
axisTick: {
show: false
}
},
legend: {
icon: 'circle',
data: chartData && chartData.map(item => item.name)
},
series: []
}
options.series = this.setSeries(chartData)
this.chart.setOption(options, {
notMerge: true // 每次刷新清空上次数据
})
},
setSeries(chartData) {
const seriesData = []
chartData && chartData.map((item, index) => {
seriesData.push(
{
name: item.name || '',
itemStyle: {
normal: {
color: itemStyleColor[index][0], // 圆圈节点
lineStyle: {
color: itemStyleColor[index][1], // 线
width: 2
},
areaStyle: { // 填充色
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: itemStyleColor[index][2] }, // 0%处的颜色
{ offset: 1, color: itemStyleColor[index][3] } // 100%处的颜色
],
global: false
}
}
}
},
smooth: true, // 曲线
type: 'line', // 线
data: item.value || [],
animationDuration: 2800,
animationEasing: 'cubicInOut'
}
)
})
return seriesData
}
}
}
</script>

resize.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { debounce } from '@/utils'

export default {
data() {
return {
$_sidebarElm: null
}
},
mounted() {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
// 侧边栏元素 展开收缩
this.$_sidebarElm = document.getElementsByClassName('side-bar-box')[0]
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.__resizeHandler)

this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
},
methods: {
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}
}
}
}
------ 本文结束------
坚持原创技术分享,您的支持将鼓励我继续创作!