在使用 Vue 的 Echart 开发中,经常需要绘制不同的图表,因此,可以将 Echart 的公用部分进行多次封装,通过 vue 的 watch 机制,逐步具化最终的图表,以达到便捷简单通用的效果。也体现了职责单一的设计理念。
效果图:
分析:
结合效果图,可以拆分成上下两部分,上面由 Echart 组件构成,下面由 dataV 的数字翻牌器组成。本文主要讲述上面的封装。
代码:
-
对 Echart 的第一次封装,暴露出各种的属性,并监听 options 属性的变化,CommonEchart.vue
<template> <div :id="id" :class="className" :style="{ height: height, width: width }" /> </template> <script> export default { // 设置图表的属性 props: { id: { type: String, default: 'chart', }, className: { type: String, default: 'chart', }, width: { type: String, default: '100%', }, height: { type: String, default: '2.5rem', }, options: { type: Object, default: () => ({}), }, }, data() { return { chart: null, } }, watch: { // 监听图表的options属性 options: { handler(newVal, oldVal) { // 设置新的数据,并清空echart缓存 this.chart.setOption(newVal, true) }, // 深度监听(监听组件内部的变化,用于多层组件嵌套) deep: true, }, }, mounted() { this.initChart() }, beforeDestroy() { this.chart.dispose() this.chart = null }, methods: { initChart() { // 初始化图表 this.chart = this.$echarts.init(this.$el) // 设置图表数据 this.chart.setOption(this.options, true) }, }, } </script>
-
对 Echart 的第二次封装,设置属性,并定义新的数据属性 chartData,从而监听 chartData,完成 options 的具体化,Pie.vue
<template> <CommonEchart :options="options" height="220px" width="260px" /> </template> <script> // 导入公用的图表 import CommonEchart from '../echart/CommonEchart.vue' export default { components: { CommonEchart, }, // 定义当前组件属性,用于后面的监听 props: { chartData: { type: Object, default: () => ({}), }, }, data() { return { options: {}, } }, watch: { // 监听属性chartData,当数据变化时,更新图表的数据(options) chartData: { handler(newVal, oldVal) { this.options = { color: [ '#37a2da', '#32c5e9', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293', ], tooltip: { trigger: 'item', // 饼图、仪表盘、漏斗图: {a}(系列名称),{b}(数据项名称),{c}(数值), {d}(百分比) formatter: '{a} <br/>{b} : {c} ({d}%)', }, toolbox: { show: true, }, calculable: true, legend: { orient: 'horizontal', icon: 'circle', bottom: 0, x: 'center', data: newVal.xData, textStyle: { color: '#fff', }, }, series: [ { type: 'pie', name: '分布统计', radius: [5, 40], roseType: 'radius', center: ['50%', '40%'], data: newVal.seriesData, }, ], } }, // 立刻更新图表内容 immediate: true, // 深度监听(监听组件内部的变化,用于多层组件嵌套) deep: true, }, }, } </script>
-
最终应用到父组件中(设置 chartData 属性),PieData.vue
<template> <div class="container"> <div class="bg-color-black"> <div class="d-flex pt-2 pl-2"> <div class="d-flex"> <span class="fs-xl text mx-2">任务通过率</span> </div> </div> <div class="d-flex jc-center"> <!-- 使用组件并绑定属性,从而完成图表的展示 --> <Pie :chartData="chartData" /> </div> <!-- 4个主要的数据 --> <div class="bottom-data"> <div class="item-box mt-2" v-for="(item, index) in numberData" :key="index" > <div class="d-flex"> <span class="coin">¥</span> <dv-digital-flop class="dv-digital-flop" :config="item.number" /> </div> <p class="text" style="text-align: center;"> {{ item.text }} </p> </div> </div> </div> </div> </template> <script> // 导入第二次封装的组件 import Pie from './pie/Pie.vue' export default { components: { Pie, }, data() { return { chartData: { xData: ['渭滨区', '金台区', '陈仓区', '凤翔区'], seriesData: [ { value: 2, name: '渭滨区' }, { value: 15, name: '金台区' }, { value: 5, name: '陈仓区' }, { value: 1, name: '凤翔区' }, ], }, // 以下是dataV组件中数字翻牌器需要的数据 numberData: [ { number: { number: [1542], toFixed: 0, textAlign: 'left', content: '{nt}', style: { fontSize: 20, }, }, text: '总量', }, { number: { number: [1535], toFixed: 0, textAlign: 'left', content: '{nt}', style: { fontSize: 20, }, }, text: '在线数量', }, { number: { number: [7], toFixed: 0, textAlign: 'left', content: '{nt}', style: { fontSize: 20, }, }, text: '断线数量', }, { number: { number: [126], toFixed: 0, textAlign: 'left', content: '{nt}', style: { fontSize: 20, }, }, text: '待安装数量', }, ], } }, } </script> <style lang="scss" scoped> $box-width: 245px; $box-height: 410px; .container { padding: 10px; height: $box-height; width: $box-width; border-radius: 10px; .bg-color-black { height: $box-height; border-radius: 10px; } .text { color: #c3cbde; } .bottom-data { .item-box { & > div { padding-right: 5px; } font-size: 14px; float: right; position: relative; width: 50%; color: #d3d6dd; .dv-digital-flop { width: 120px; height: 30px; } // 金币 .coin { position: relative; top: 6px; font-size: 20px; color: #ffc107; } .colorYellow { color: yellowgreen; } p { text-align: center; } } } } </style>