1、resize.js 主要处理Echarts组件自适应
export default {
data() {
return {
$_resizeHandler: null
}
},
mounted() {
this.initListener()
},
activated() {
if (!this.$_resizeHandler) {
// avoid duplication init
this.initListener()
}
// when keep-alive chart activated, auto resize
this.resize()
},
beforeDestroy() {
this.destroyListener()
},
deactivated() {
this.destroyListener()
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.$_resizeHandler()
}
},
initListener() {
this.$_resizeHandler = debounce(() => {
this.resize()
}, 100)
window.addEventListener('resize', this.$_resizeHandler)
},
destroyListener() {
window.removeEventListener('resize', this.$_resizeHandler)
this.$_resizeHandler = null
},
resize() {
// 单个绘图实例用chart,否则用charts
const { chart, charts } = this
if (Array.isArray(charts)) {
charts.forEach(chart => {
chart && chart.resize()
})
} else {
chart && chart.resize()
}
}
}
}
// import { debounce } from '@/utils'
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
2、baseChart.js 抽取公共绘图属性、处理逻辑
import resize from ‘./resize’
export default {
mixins: [resize],
props: {
chartData: {
type: Object,
require: true,
default: () => {}
},
chartOption: {
type: Object,
default: () => {}
},
chartId: {
type: String,
require: true,
default: 'chart'
},
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '400px'
}
},
data() {
return {
chart: null
}
},
watch: {
chartData: {
deep: true,
handler(chartData) {
this.updateOption(this.getOption(chartData))
}
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose()
this.chart = null
}
},
methods: {
initChart() {
//customed 已注册的主题名称
this.chart = this.$echarts.init(
document.getElementById(this.chartId)
// 'customed'
)
const option = this.getOption(this.chartData)
this.updateOption(option)
},
updateOption(option) {
const { seriesData = [] } = this.chartData
if (seriesData.length === 0) {
this.chart.clear()
this.chart.showLoading({
text: '暂无数据',
color: '#fff',
textColor: '#909399',
fontSize: '14px'
})
} else {
this.chart.hideLoading()
this.chart.setOption(option, true)
}
}
// getOption(data) {
// const option = {}
// // 合并传入option
// Object.assign(option, this.chartOption)
// return option
// }
}
}
3、示例封装一个柱图组件Bar.vue
<template>
<div :id="chartId" :class="className" :style="{width: width, height:height}"></div>
</template>
<script>
import baseChart from './mixins/baseChart.js'
export default {
mixins:[baseChart],//混入图表通用属性和处理逻辑
methods: {
getOption() {
const { seriesData, xAxisData} = this.chartData
let option = {
xAxis: {
type: 'category',
data: xAxisData //['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: seriesData
// [{
// data: [120, 200, 150, 80, 70, 110, 130],
// type: 'bar'
// }]
};
//合并option,处理组件需要覆盖option属性
option = Object.assign(option, this.chartOption)
return option
}
}
}
</script>
注意事项:
1、暂无数据处理,如果后端返回数据组装图表数据为空,seriesData = []
2、重置通用组件option某个属性,如果涉及到后端返回的接口数据,
在组件使用的地方:option-data="getOption(chartData)",通过调用函数处理
4、再封装一个pie图
<template>
<div :id="chartId" :class="className" :style="{width: width, height:height}"></div>
</template>
<script>
import baseChart from './mixins/baseChart.js'
export default {
mixins: [baseChart],
methods: {
getOption() {
const {seriesData} = this.chartData
const option = {
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '40',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: seriesData
}]
};
return option
}
}
}
</script>
5、看看具体如何使用
<template>
<div class="chart-page" id="chart">
<Bar :chart-data="barChartData" :chart-option="{}" chart-id="barChart" class="chart" />
<Pie :chart-data="pieChartData" :chart-option="{}" chart-id="pieChart" class="chart" />
<LineChart :chart-data="{}" :chart-option="{}" chart-id="lineChart" class="chart" />
</div>
</template>
<script>
import Bar from '../components/Bar.vue'
import LineChart from '../components/Line.vue'
import Pie from '../components/Pie.vue'
export default {
name: "ChartDemo",
components: {
Bar,
LineChart,
Pie
},
data() {
return {
barChartData: {},
pieChartData: {
seriesData: []
}
}
},
mounted() {
this.initPage()
},
methods: {
initPage() {
//axios
this.barChartData = {
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
seriesData: [{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar'
}]
}
//axios
this.pieChartData = {
seriesData: [{
value: 1048,
name: 'Search Engine'
},
{
value: 735,
name: 'Direct'
},
{
value: 580,
name: 'Email'
},
{
value: 484,
name: 'Union Ads'
},
{
value: 300,
name: 'Video Ads'
}
]
}
},
getOption(chartData) {
const option = {
yAxis: {
data: chartData.yAxisData,
type: 'value'
}
}
return option
}
}
}
</script>
<style scoped>
.chart-page {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 100%;
}
.chart {
height: 400px ;
width: 500px ;
}
</style>
最终效果如下图: