封装一个圆柱体图表组件 发表于 2024-07-13 最近因为工作需要,封装了一个圆柱体图表组件。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911<template> <div id="barChart" ref="barChartContainer" style="width: 100%; height: 100%" ></div></template><script> import * as echarts from 'echarts' import { hexToRgb, deepMerge, addArrays, sumArraysUpToIndex, } from '@/utils/index.js' export default { name: 'CgBarChart', props: { // 图表配置项 chartOptions: { type: Object, default: () => ({ barWidth: 30, chartData: [], xAxisData: [], color: '#ed5b9c', echartOptions: {}, }), }, }, data() { return { xAxisData: [], barWidth: 30, color: '#ed5b9c', xAxisName: '区/县', yAxisName: '总数', maxValue: 100, chartInstance: null, transparency: 0.3, defaultOptions: {}, } }, mounted() { window.addEventListener('resize', this.handleResize) this.initChart() }, beforeDestroy() { window.removeEventListener('resize', this.handleResize) if (this.chartInstance) { this.chartInstance.dispose() } }, methods: { transformStackData(originalData) { // 创建一个Map来动态跟踪每种执法问题的总数 const issueCounts = new Map() // 第一步:收集所有不同的执法问题 const allIssues = new Set() originalData.forEach((cityData) => { cityData.data.forEach((issue) => { allIssues.add(issue.name) }) }) // 第二步:初始化每个执法问题的数据列表 allIssues.forEach((issueName) => { issueCounts.set(issueName, []) }) // 第三步:填充每个执法问题的数据 originalData.forEach((cityData) => { cityData.data.forEach((issue) => { const issueData = issueCounts.get(issue.name) issueData.push(issue.value) }) }) // 第四步:构造最终结果数组 const result = [] issueCounts.forEach((data, name) => { result.push({ name, data }) }) return { series: result, legendData: allIssues, } }, handleResize() { if (this.chartInstance) { this.chartInstance.resize() } }, generateSeriesConfig() { const series = [] const color = this.chartOptions.color ? this.chartOptions.color : this.color this.chartOptions.chartData.forEach((item, index) => { series.push( { // 上半截柱子(背部阴影bar) name: '', type: 'bar', tooltip: { show: false, }, barWidth: this.chartOptions.barWidth || this.barWidth, barGap: '-100%', z: 0, itemStyle: { //lenged文本 opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 0, 1, 0, [ { offset: 0, // color: rgba(237, 91, 156, 0.4)", // 0% 处的颜色 color: `${hexToRgb(color, 0.4)}`, }, { offset: 1, // color: "rgba(237, 91, 156, 0.4)", // 100% 处的颜色 color: `${hexToRgb(color, 0.4)}`, }, ], false ) }, }, data: Array.from( { length: this.chartOptions.chartData.length }, (_) => this.chartOptions.maxValue || this.maxValue ), }, { ///最顶部圆片(背部阴影最顶部圆片) name: '', tooltip: { show: false, }, type: 'pictorialBar', symbolSize: [30, 10], symbolOffset: [0, -5], z: 3, symbolPosition: 'end', itemStyle: { // color: "rgba(237, 91, 156, 0.4)", color: `${hexToRgb(color, 0.4)}`, opacity: 1, }, data: Array.from( { length: this.chartOptions.chartData.length }, (_) => this.chartOptions.maxValue || this.maxValue ), }, { //数据柱子 (蓝色柱子) name: item.name, type: 'bar', barWidth: this.chartOptions.barWidth || this.barWidth, barGap: '-100%', tooltip: { show: false, }, itemStyle: { //lenged文本 opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 0, 1, 0, [ { offset: 0, // color: "rgba(237, 91, 156)", // 0% 处的颜色 color: `${hexToRgb(color, 0.4)}`, }, { offset: 1, // color: "rgba(237, 91, 156)", // 100% 处的颜色 color: `${hexToRgb(color, 1)}`, }, ], false ) }, }, data: this.chartOptions.chartData.map((item) => { return item.value }), }, { //数据圆片(蓝色柱子的顶部圆片) name: item.name, type: 'pictorialBar', symbolSize: [30, 10], symbolOffset: [0, -5], z: 3, itemStyle: { opacity: 1, // color: "rgb(27, 140, 255)", color: color, }, symbolPosition: 'end', data: this.chartOptions.chartData.map((item) => { return item.value }), // 柱子顶部显示值 // label: { // show: true, // position: 'top', // distance: 0, // formatter: '{c}' // }, }, { //最底部圆片 name: item.name, type: 'pictorialBar', symbolSize: [30, 12], //圆片的形状大小 symbolOffset: [0, -4], //圆片的偏移量 z: 3, itemStyle: { opacity: 1, // color: "rgb(0, 111, 255)", color: color, // color: '#000' }, symbolPosition: 'end', data: Array.from( { length: this.chartOptions.chartData.length }, (_) => 1 ), } ) }) return series }, initChart() { const chartContainer = this.$refs.barChartContainer this.chartInstance = echarts.init(chartContainer) this.updateChart() }, // 生成单柱渐变配置项 generateSingleGradientOption() { const name = this.chartOptions.chartData[0].name const option = { title: { text: '', }, tooltip: { show: true, trigger: 'axis', backgroundColor: '#000342', textStyle: { color: '#fff', }, }, xAxis: { type: 'category', name: this.chartOptions.xAxisName || this.xAxisName, axisLabel: { fontSize: 14, margin: 20, }, axisLine: { show: true, onZero: false, lineStyle: { color: '#1d97cf', }, }, axisTick: { show: true, length: 9, alignWithLabel: true, lineStyle: { color: '#1d97cf', }, }, data: this.chartOptions.chartData.map((item) => item.label) || this.chartOptions.xAxisData, }, yAxis: { type: 'value', name: this.chartOptions.yAxisName ? this.chartOptions.yAxisName + ' ' : this.yAxisName + ' ', axisLine: { show: true, lineStyle: { color: '#1d97cf', }, }, splitLine: { show: true, lineStyle: { color: '#153d7d', type: 'dashed', }, }, axisTick: { show: true, inside: true, length: 10, }, axisLabel: { show: true, interval: 'auto', formatter: '{value}', }, }, series: [ // 上半截柱子(背部阴影bar) { name: '', type: 'bar', tooltip: { show: false, }, barWidth: this.chartOptions.barWidth || this.barWidth, barGap: '-100%', z: 0, itemStyle: { opacity: this.chartOptions.isShowBgBar ? 1 : 0, color: (params) => { return new echarts.graphic.LinearGradient( 0, 0, 1, 0, this.chartOptions.bgBarColors, false ) }, }, data: Array.from( { length: this.chartOptions.chartData.length }, (_) => this.chartOptions.maxValue || this.maxValue ), }, //最顶部圆片(背部阴影最顶部圆片) { name: '', tooltip: { show: false, }, type: 'pictorialBar', symbolSize: [this.chartOptions.barWidth || this.barWidth, 10], symbolOffset: [0, -5], z: 3, symbolPosition: 'end', itemStyle: { color: (params) => { return new echarts.graphic.LinearGradient( 0, 0, 1, 0, this.chartOptions.bgBarColors, false ) }, opacity: this.chartOptions.isShowBgBar ? 1 : 0, }, data: Array.from( { length: this.chartOptions.chartData.length }, (_) => this.chartOptions.maxValue || this.maxValue ), }, //数据柱子 (蓝色柱子) { name: name, type: 'bar', barWidth: this.chartOptions.barWidth || this.barWidth, barGap: '-100%', itemStyle: { //lenged文本 opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 1, 0, 0, this.chartOptions.barGradColors, false ) }, }, data: this.chartOptions.chartData.map((item) => { return item.value }), }, { //数据圆片(蓝色柱子的顶部圆片) name: '', tooltip: { show: false, }, type: 'pictorialBar', symbolSize: [this.chartOptions.barWidth || this.barWidth, 10], symbolOffset: [0, -5], z: 3, itemStyle: { opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 1, 0, 0, this.chartOptions.barGradColors, false ) }, }, symbolPosition: 'end', data: this.chartOptions.chartData.map((item) => { return item.value }), }, { //最底部圆片 name: '', type: 'pictorialBar', tooltip: { show: false, }, symbolSize: [this.chartOptions.barWidth || this.barWidth, 12], //圆片的形状大小 symbolOffset: [0, -4], //圆片的偏移量 z: 3, itemStyle: { opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 1, 0, 0, this.chartOptions.barGradColors, false ) }, }, symbolPosition: 'end', data: Array.from( { length: this.chartOptions.chartData.length }, (_) => 1 ), }, ], } return option }, // 生成堆积圆柱图 generateMutlpGradientOption() { const { series: newSeries, legendData: legendData } = this.transformStackData(this.chartOptions.chartData) let res = [] const arrays = newSeries.map((item) => item.data) newSeries.forEach((item, index) => { res.push( // 重残 { name: item.name, // Bar图的底部 type: 'pictorialBar', symbolSize: [this.chartOptions.barWidth || this.barWidth, 10], symbolOffset: [0, 0], z: 12, tooltip: { show: false, }, itemStyle: { normal: { color: this.chartOptions.barBottomColors[index], opacity: index === 0 ? 1 : 0, }, }, data: index === 0 ? item.data : sumArraysUpToIndex(arrays, index), }, { name: item.name, type: 'bar', stack: '分析', barWidth: this.chartOptions.barWidth || this.barWidth, data: item.data, itemStyle: { normal: { color: { type: 'linear', x: 0, y: 1, x2: 0, y2: 0, colorStops: this.chartOptions.barGradColors[index], global: false, // 缺省为 false }, opacity: 0.8, }, }, }, { name: item.name, // bar图的顶部 type: 'pictorialBar', symbolSize: [this.chartOptions.barWidth || this.barWidth, 10], symbolOffset: [0, -5], z: 12, tooltip: { show: false, }, itemStyle: { normal: { color: this.chartOptions.barTopColors[index], }, }, // label: { // show: true, // position: 'top', // fontSize: 16, // }, symbolPosition: 'end', data: index === 0 ? item.data : sumArraysUpToIndex(arrays, index), } ) }) const option = { title: { text: '', }, legend: { show: true, data: this.chartOptions.legendData.map((item, index) => { return { name: item, icon: 'none', // 可以是 'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none' 之一 textStyle: { color: '#fff', // 图例文字的样式 }, // 显式设置图例颜色 itemStyle: { color: this.chartOptions.barTopColors[index], // 这里设置为你柱子的实际颜色 }, } }), // data: legendData }, tooltip: { show: true, trigger: 'axis', backgroundColor: '#000342', textStyle: { color: '#fff', }, }, xAxis: { type: 'category', name: this.chartOptions.xAxisName || this.xAxisName, axisLabel: { interval: 0, fontSize: 14, margin: 20, formatter: function (params) { var newParamsName = '' var paramsNameNumber = params.length var provideNumber = 3 //一行显示几个字 var rowNumber = Math.ceil(paramsNameNumber / provideNumber) if (paramsNameNumber > provideNumber) { for (var p = 0; p < rowNumber; p++) { var tempStr = '' var start = p * provideNumber var end = start + provideNumber if (p == rowNumber - 1) { tempStr = params.substring(start, paramsNameNumber) } else { tempStr = params.substring(start, end) + '\n' } newParamsName += tempStr } } else { newParamsName = params } return newParamsName }, }, axisLine: { show: true, onZero: false, lineStyle: { color: '#1d97cf', }, }, axisTick: { show: true, length: 9, alignWithLabel: true, lineStyle: { color: '#1d97cf', }, }, data: this.chartOptions.xAxisData, }, yAxis: { type: 'value', name: this.chartOptions.yAxisName ? this.chartOptions.yAxisName + ' ' : this.yAxisName + ' ', axisLine: { show: true, lineStyle: { color: '#1d97cf', }, }, splitLine: { show: true, lineStyle: { color: '#153d7d', type: 'dashed', }, }, axisTick: { show: true, inside: true, length: 10, }, axisLabel: { show: true, interval: 'auto', formatter: '{value}', }, }, series: res, } return option }, // 生成默认圆柱图配置项 generateDefaultOption() { const color = this.chartOptions.color ? this.chartOptions.color : this.color const name = this.chartOptions.chartData[0].name const option = { title: { text: '', }, tooltip: { show: true, trigger: 'axis', backgroundColor: '#000342', textStyle: { color: '#fff', }, }, xAxis: { type: 'category', name: this.chartOptions.xAxisName || this.xAxisName, axisLabel: { fontSize: 14, margin: 20, }, axisLine: { show: true, onZero: false, lineStyle: { color: '#1d97cf', }, }, axisTick: { show: true, length: 9, alignWithLabel: true, lineStyle: { color: '#1d97cf', }, }, data: this.chartOptions.chartData.map((item) => item.label) || this.chartOptions.xAxisData, }, yAxis: { type: 'value', name: this.chartOptions.yAxisName ? this.chartOptions.yAxisName + ' ' : this.yAxisName + ' ', axisLine: { show: true, lineStyle: { color: '#1d97cf', }, }, splitLine: { show: true, lineStyle: { color: '#153d7d', type: 'dashed', }, }, axisTick: { show: true, inside: true, length: 10, }, axisLabel: { show: true, interval: 'auto', formatter: '{value}', }, }, series: [ { // 上半截柱子(背部阴影bar) name: '', type: 'bar', tooltip: { show: false, }, barWidth: this.chartOptions.barWidth || this.barWidth, barGap: '-100%', z: 0, itemStyle: { //lenged文本 opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 0, 1, 0, [ { offset: 0, // color: rgba(237, 91, 156, 0.4)", // 0% 处的颜色 color: `${hexToRgb(color, 0.4)}`, }, { offset: 1, // color: "rgba(237, 91, 156, 0.4)", // 100% 处的颜色 color: `${hexToRgb(color, 0.4)}`, }, ], false ) }, }, data: Array.from( { length: this.chartOptions.chartData.length }, (_) => this.chartOptions.maxValue || this.maxValue ), }, { ///最顶部圆片(背部阴影最顶部圆片) name: '', tooltip: { show: false, }, type: 'pictorialBar', symbolSize: [this.chartOptions.barWidth || this.barWidth, 10], symbolOffset: [0, -5], z: 3, symbolPosition: 'end', itemStyle: { // color: "rgba(237, 91, 156, 0.4)", color: `${hexToRgb(color, 0.4)}`, opacity: 1, }, data: Array.from( { length: this.chartOptions.chartData.length }, (_) => this.chartOptions.maxValue || this.maxValue ), }, { //数据柱子 (蓝色柱子) name: name, type: 'bar', barWidth: this.chartOptions.barWidth || this.barWidth, barGap: '-100%', itemStyle: { //lenged文本 opacity: 1, color: (params) => { return new echarts.graphic.LinearGradient( 0, 0, 1, 0, [ { offset: 0, // color: "rgba(237, 91, 156)", // 0% 处的颜色 color: `${hexToRgb(color, 0.4)}`, }, { offset: 1, // color: "rgba(237, 91, 156)", // 100% 处的颜色 color: `${hexToRgb(color, 1)}`, }, ], false ) }, }, data: this.chartOptions.chartData.map((item) => { return item.value }), }, { //数据圆片(蓝色柱子的顶部圆片) name: '', tooltip: { show: false, }, type: 'pictorialBar', symbolSize: [this.chartOptions.barWidth || this.barWidth, 10], symbolOffset: [0, -5], z: 3, itemStyle: { opacity: 1, // color: "rgb(27, 140, 255)", color: color, }, symbolPosition: 'end', data: this.chartOptions.chartData.map((item) => { return item.value }), // 柱子顶部显示值 // label: { // show: true, // position: 'top', // distance: 0, // formatter: '{c}' // }, }, { //最底部圆片 name: '', type: 'pictorialBar', tooltip: { show: false, }, symbolSize: [this.chartOptions.barWidth || this.barWidth, 12], //圆片的形状大小 symbolOffset: [0, -4], //圆片的偏移量 z: 3, itemStyle: { opacity: 1, // color: "rgb(0, 111, 255)", color: color, // color: '#000' }, symbolPosition: 'end', data: Array.from( { length: this.chartOptions.chartData.length }, (_) => 1 ), }, ], } return option }, generateChartOption() { let option = {} if (this.chartOptions.type === 'default') { option = this.generateDefaultOption() } else if (this.chartOptions.type == 'singleGrad') { option = this.generateSingleGradientOption() } else if (this.chartOptions.type === 'mutliGrad') { // option = this.generateTestOption(); option = this.generateMutlpGradientOption() } if (!this.chartOptions.type) { option = this.generateDefaultOption() } this.defaultOptions = option const mergedOptions = deepMerge({}, this.defaultOptions) const res = deepMerge( mergedOptions, this.chartOptions.echartOptions || {} ) return res }, updateChart() { const option = this.generateChartOption() this.chartInstance.setOption(option, true) }, }, watch: { chartOptions: { deep: true, handler() { this.updateChart() }, }, }, }</script><style lang="scss" scoped></style>