在Vue项目中使用Highcharts绘制饼图,常见做法是把图表容器封装成公共组件,再通过配置对象控制标题、提示框、图例、扇区点击和数据。原文示例适用于需要多个饼图、数据异步加载后再创建图表的页面。Highcharts与ECharts的差异原文也提到:ECharts免费,Highcharts商业用途需授权,个人使用会在图表上显示logo,选型时需结合授权场景。
一、安装与目录组织
先安装依赖:npm install highcharts。在components/commons下新建chart.vue作为图表基础组件;创建chart-options/options.js集中存放配置模型;业务页面引入chart组件和options。这样容器与配置分离,多个饼图可复用。
二、封装chart.vue
组件接收id和option两个props,id对应Highcharts渲染容器的DOM id,option是Highcharts配置对象。模板中保留一个带id的div。mounted中原文给出静态数据创建方式HighCharts.chart(this.id,this.option)的注释;动态数据场景下通过methods中的me()在数据准备好后执行创建。整理后的关键代码:- <template>
- <div class='x-bar'>
- <div :id='id' :option='option'></div>
- </div>
- </template>
- <script>
- import HighCharts from 'highcharts'
- export default {
- props: {
- id: { type: String },
- option: { type: Object }
- },
- mounted() {
- // 静态数据:HighCharts.chart(this.id, this.option)
- },
- methods: {
- me() {
- HighCharts.chart(this.id, this.option)
- }
- }
- }
- </script>
复制代码 这里需要注意:Highcharts.chart第一个参数是DOM id,模板中div的id必须与传入的id一致;动态数据不要在option尚未合并前调用me(),否则会拿到空series或空data。
三、配置options.js
options.js用构造函数维护pie配置,包含chart.type='pie'、title.text、tooltip.headerFormat/pointFormat、plotOptions.pie的allowPointSelect、cursor、dataLabels、showInLegend和events.click,以及series数组。series中colorByPoint为true,data初始为空,后续动态接收数据。funcc(op)通过Object.assign把传入的op.pie合并到this.pie。原文中let $back = $('#myBack')未被实际使用;如果项目没有引入jQuery,这行会带来额外依赖或报错,整理时可删除或确认已引入。关键配置如下:- let Options = function() {
- this.pie = {
- chart: {
- type: 'pie',
- plotBackgroundColor: null,
- plotBorderWidth: null,
- plotShadow: false
- },
- title: { text: '' },
- tooltip: {
- headerFormat: '{series.name}<br>',
- pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>'
- },
- plotOptions: {
- pie: {
- allowPointSelect: true,
- cursor: 'pointer',
- dataLabels: { enabled: true },
- showInLegend: true,
- events: {
- click: function(e) {
- // alert(e.point.url);
- // window.open(e.point.url);
- // location.href = e.point.url;
- }
- }
- }
- },
- series: [{
- name: '',
- colorByPoint: true,
- data: []
- }]
- }
- }
- Options.prototype.funcc = function(op) {
- if (op.pie != null) {
- this.pie = Object.assign(this.pie, op.pie)
- }
- }
- export default new Options()
复制代码 tooltip的pointFormat使用point.percentage并保留一位小数;数据点若需要点击跳转,可按事件注释中的e.point.url、window.open或location.href实现,但原文只是预留注释,没有启用跳转。
四、业务页面引用与动态渲染
页面中放两个容器pie1、pie2,分别用x-chart渲染,传入id1/id2和option1/option2。data中option1、option2初始为null,等chartPie1等异步方法合并配置后再赋值。chartPie1返回Promise,内部调用options.funcc合并title和series;series.data支持数组简写和对象写法:['A', p1]、['B', p2]、['C', p3],D点用{name:'D', y:p4, url:'', sliced:true, selected:true}强调扇区,E点为['E', p5],p1到p5表示百分占比。合并后把options.pie赋给this.option1,resolve后在then中调用this.$refs.XChart1.me()创建图表。关键代码:- <template>
- <div class='pie'>
- <div id='pie1'>
- <x-chart ref='XChart1' :id='id1' :option='option1'></x-chart>
- </div>
- <div id='pie2'>
- <x-chart ref='XChart2' :id='id2' :option='option2'></x-chart>
- </div>
- </div>
- </template>
- <script>
- import XChart from 'components/chart.vue'
- import options from './chart-options/options'
- export default {
- data() {
- return {
- id1: 'pie1',
- option1: null,
- id2: 'pie2',
- option2: null
- }
- },
- components: { XChart },
- methods: {
- chartPie1() {
- return new Promise((resolve, reject) => {
- options.funcc({
- pie: {
- title: { text: '饼图1name' },
- series: [{
- name: '情况占比',
- data: [
- ['A', p1],
- ['B', p2],
- ['C', p3],
- { name: 'D', y: p4, url: '', sliced: true, selected: true },
- ['E', p5]
- ]
- }]
- }
- })
- let option1 = options.pie
- this.option1 = option1
- resolve()
- }).then(resp => {
- this.$refs.XChart1.me()
- })
- }
- }
- }
- </script>
复制代码 原文data里还出现let option = options.bar,但后续实际使用的是options.pie,该变量未参与渲染,可视为遗留代码;整理时不必保留。另原文components: {XChart}后使用了中文逗号,复制到项目时必须改为英文逗号,否则编译会报语法错误。id重复也会导致Highcharts找不到容器或渲染到错误节点,页面中pie1/pie2与id1/id2需保持唯一对应。
五、排查与适用场景
如果图表不显示,优先检查:依赖是否安装、chart.vue的div id是否与传入id一致、me()是否在option赋值后调用、series.data是否已传入、funcc合并时是否覆盖了series、页面是否存在重复id、components对象是否使用英文逗号。该方案适合Vue2页面中多个Highcharts饼图复用同一配置模型、异步请求数据后渲染的场景;如果只是静态图表,可在mounted中直接创建;如果需要点击扇区跳转,可启用events.click中的e.point.url逻辑。选型上需注意Highcharts商业授权和个人使用logo显示的限制。 |