封装一个多类型柱状图组件 发表于 2024-07-13 最近因为工作需要,封装了一个多类型的柱状图组件。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542<template> <div id="multiTypeBarChart" ref="multiTypeBarChart"></div></template><script> import * as echarts from 'echarts' import { hexToRgb, deepMerge } from '@/utils/index.js' export default { name: 'CgMultiTypeBarChart', props: { // 图表配置项 chartOptions: { type: Object, default: () => ({ type: '', // 柱状图的类型 barWidth: 30, // 柱状图柱子的宽度 chartData: [], // 数据源 yAxisData: [], // y轴数据 xAxisData: [], // x轴数据 color: '#ed5b9c', // 柱子的颜色 echartOptions: {}, // 配置项 }), }, }, data() { return { // 渐变色柱子数组 gradientColors: [ { offset: 0, color: '#29dd9b', }, { offset: 0.5, color: '#26b9c5', }, { offset: 1, color: '#2392f3', }, ], xAxisData: [], barWidth: 30, color: '#ed5b9c', xAxisName: '区/县', yAxisName: '总数', maxValue: 100, chartInstance: null, transparency: 0.3, defaultOptions: {}, colors: ['#0d8888', '#af3b14', '#70980c'], } }, 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 = [] return series }, initChart() { const chartContainer = this.$refs.multiTypeBarChart this.chartInstance = echarts.init(chartContainer) this.updateChart() }, // 生成默认配置 generateDefaultOption() { const option = { xAxis: { type: 'category', data: this.chartOptions.xAxisData, }, yAxis: { type: 'value', }, series: [ { barWidth: this.chartOptions.barWidth || null, data: this.chartOptions.chartData, type: 'bar', }, ], } return option }, // 生成渐变色柱子的配置 generateGradChartOption() { const option = { tooltip: { show: true, backgroundColor: this.chartOptions.backgroundColor || '#061e3f', borderColor: '#ddd', borderWidth: 1, textStyle: { color: '#fff', fontSize: 16, }, }, backgroundColor: this.chartOptions.backgroundColor || '#061a34', grid: { top: 20, left: 80, bottom: 20, right: 80, }, xAxis: { show: true, type: this.chartOptions.direction === 'crosswise' ? 'value' : 'category', data: this.chartOptions.direction === 'crosswise' ? null : this.chartOptions.xAxisData, axisLabel: { show: true, }, axisTick: { show: false, }, axisLine: { show: false, }, }, yAxis: [ { type: this.chartOptions.direction === 'crosswise' ? 'category' : 'value', // y轴显示的数据,这里例子中是河北省的11个市的名字 data: this.chartOptions.direction === 'crosswise' ? this.chartOptions.xAxisData : null, // y轴的轴线 刻度标签旁边的线 axisLine: { show: false, }, // y轴刻度相关设置 刻度右侧的线 axisTick: { show: false, }, // y轴刻度标签 0 100 200 300 axisLabel: { color: '#fff', }, // 分割线 splitLine: { show: this.chartOptions.direction === 'crosswise' ? false : true, lineStyle: { color: '#eee', }, }, }, // 双y轴,右侧的数据显示,这里指的是每个市对应的数据量 { type: 'category', inverse: true, axisTick: 'none', axisLine: 'none', show: this.chartOptions.isShowRightAxis ? this.chartOptions.isShowRightAxis : false, axisLabel: { textStyle: { color: '#fff', fontSize: '14', }, }, data: this.chartOptions.chartData, }, ], series: [ { type: 'bar', barWidth: this.chartOptions.barWidth || null, label: { show: this.chartOptions.isShowLabel || false, position: this.chartOptions.labelPosition || 'inside', }, // showBackground: true, // 是否显示柱子的背景色 itemStyle: { color: new echarts.graphic.LinearGradient( 0, 0, this.chartOptions.direction === 'crosswise' ? 1 : 0, this.chartOptions.direction === 'crosswise' ? 0 : 1, this.chartOptions.gradientColors || this.gradientColors ), }, emphasis: { itemStyle: { color: new echarts.graphic.LinearGradient( 0, 0, this.chartOptions.direction === 'crosswise' ? 1 : 0, this.chartOptions.direction === 'crosswise' ? 0 : 1, this.chartOptions.gradientColors || this.gradientColors ), }, }, data: this.chartOptions.chartData, }, ], } return option }, // 生成每个柱子渐变色的配置 generateMultipleGradChartOption() { const lineY = [] this.chartOptions.chartData.forEach((item, index) => { let data = { name: item.name, value: item.data, itemStyle: { normal: { show: true, color: new echarts.graphic.LinearGradient( 0, 0, 1, 0, this.chartOptions.colors[index] || this.colors ), barBorderRadius: 10, }, }, emphasis: { shadowBlur: 15, shadowColor: 'rgba(0, 0, 0, 0.1)', }, } lineY.push(data) }) const option = { tooltip: { show: false, backgroundColor: this.chartOptions.backgroundColor || '#061e3f', borderColor: '#ddd', borderWidth: 1, textStyle: { color: '#fff', fontSize: 16, }, }, backgroundColor: this.chartOptions.backgroundColor || '#061a34', grid: { left: 80, right: 80, }, xAxis: { show: false, type: 'value', data: null, axisLabel: { show: true, }, axisTick: { show: false, }, axisLine: { show: false, }, }, yAxis: [ { show: false, type: 'category', // y轴显示的数据,这里例子中是河北省的11个市的名字 data: this.chartOptions.xAxisData, // y轴的轴线 刻度标签旁边的线 axisLine: { show: false, }, // y轴刻度相关设置 刻度右侧的线 axisTick: { show: false, }, // y轴刻度标签 0 100 200 300 axisLabel: { color: '#fff', }, // 分割线 splitLine: { show: false, lineStyle: { color: '#eee', }, }, }, // 双y轴,右侧的数据显示,这里指的是每个市对应的数据量 { type: 'category', axisTick: 'none', axisLine: 'none', show: this.chartOptions.isShowRightAxis ? this.chartOptions.isShowRightAxis : false, axisLabel: { verticalAlign: 'bottom', lineHeight: '28', textStyle: { color: '#fff', fontSize: '18', }, }, data: this.chartOptions.chartData.map((item) => item.data), }, ], series: [ { name: '视频监督报警分析', type: 'bar', barWidth: this.chartOptions.barWidth || null, data: lineY, label: { normal: { color: '#09abe6', show: true, position: [0, '-16px'], textStyle: { fontSize: 12, }, formatter: (params) => { return params.name }, }, }, }, ], } return option }, // 生成堆积柱子的配置 generateStackBarOption() { const { series: newSeries, legendData: legendData } = this.transformStackData(this.chartOptions.chartData) const option = { legend: { data: [...legendData], textStyle: { color: '#fff', }, }, tooltip: { show: true, trigger: this.chartOptions.triggerType || null, backgroundColor: this.chartOptions.backgroundColor || '#061e3f', borderColor: '#ddd', borderWidth: 1, textStyle: { color: '#fff', fontSize: 16, }, }, xAxis: { show: true, type: this.chartOptions.direction === 'portrait' ? 'category' : 'value', data: this.chartOptions.direction === 'portrait' ? this.chartOptions.chartData.map((item) => item.city) : null, axisLabel: { show: true, color: '#97c3e1', }, axisTick: { show: false, }, axisLine: { show: true, }, splitLine: { show: false, }, }, yAxis: [ { type: this.chartOptions.direction === 'portrait' ? 'value' : 'category', data: this.chartOptions.direction === 'portrait' ? null : this.chartOptions.chartData.map((item) => item.city), axisLabel: { fontSize: 16, color: '#97c3e1', }, axisLine: { show: true, }, axisTick: { show: false, }, splitLine: { show: false, }, }, { show: false, data: this.chartOptions.chartData.map((item) => item.city), axisLine: { show: false, }, }, ], series: newSeries.map((item, index) => { return { type: 'bar', name: item.name, stack: this.chartOptions.isMultipleSeries ? index : '1', label: { show: this.chartOptions.isShowLabel || false, position: this.chartOptions.labelPosition || 'inside', }, legendHoverLink: false, barWidth: this.chartOptions.barWidth || null, itemStyle: { normal: { color: this.chartOptions.colors ? this.chartOptions.colors[index] : this.colors[index], }, emphasis: { color: this.chartOptions.colors ? this.chartOptions.colors[index] : this.colors[index], }, }, data: item.data, } }), } return option }, generateChartOption() { let options = {} if (!this.chartOptions.type) { const defaultOption = this.generateDefaultOption() options = defaultOption } if (this.chartOptions.type === 'default') { const defaultOption = this.generateDefaultOption() options = defaultOption } else if (this.chartOptions.type === 'gradient') { const gradientOption = this.generateGradChartOption() options = gradientOption } else if (this.chartOptions.type === 'stack') { const stackOption = this.generateStackBarOption() options = stackOption } else if (this.chartOptions.type === 'multipleGradient') { const multipleGradientOption = this.generateMultipleGradChartOption() options = multipleGradientOption } this.defaultOptions = options 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> #multiTypeBarChart { width: 100%; height: 100%; }</style>