从vue2升级到vue3,对原来的柱状图、折线图组件进行了修改,这两个组件的语法保留了vue2,实际使用也没有问题。
1、通用柱状图 BarChart.vue
<template> <div :class="className" :style="{height: height, width: width}"/> </template> <script> import * as echarts from 'echarts'; import 'echarts/theme/macarons.js' export default { props: { className: { type: String, default: 'chart' }, width: { type: String, default: '100%' }, height: { type: String, default: '450px' }, title: { type: String, required: true }, chartData: { type: Object, required: true } }, data() { // vue3需要这样写,否则`tooltip`无效 chart: null return { // chart: null } }, watch: { chartData: { deep: true, handler(val) { this.setOptions(val) } } }, mounted() { this.$nextTick(() => { this.initChart() }) }, beforeDestroy() { if (!this.chart) { return } this.chart.dispose() this.chart = null }, methods: { initChart() { this.chart = echarts.init(this.$el, 'macarons') this.chart.setOption({ title: { text: this.title, textStyle: { fontSize: 15 } }, toolbox: { show: true, orient: 'horizontal', left: 'right', top: 'top', feature: { mark: { show: true }, magicType: { show: true, type: ['line', 'bar'] }, } }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { top: '10%', left: '2%', right: '2%', bottom: '4%', containLabel: true }, xAxis: [{ type: 'category', axisTick: { show: false }, }], yAxis: [{ type: 'value', }], series: [] }) }, setOptions(data) { let map = data.map let series = [] map.forEach((val, key) => { let obj = { name: key, data: Array.from(val), type: 'bar', barWidth: '10%', barGap: 0, emphasis: { focus: 'series' }, animationDuration: 2800, animationEasing: 'quadraticOut' } series.push(obj) }); this.chart.setOption({ xAxis: [{ data: data.date, }], legend: { data: Array.from(map.keys()) }, series: series }) } } } </script>
2、通用的折线图组件 LineChart.vue
<template> <div :class="className" :style="{height: height, width: width}"/> </template> <script> import * as echarts from 'echarts'; import 'echarts/theme/macarons.js' export default { props: { className: { type: String, default: 'chart' }, width: { type: String, default: '100%' }, height: { type: String, default: '450px' }, title: { type: String, required: true }, chartData: { type: Object, required: true } }, data() { // vue3需要这样写,否则`tooltip`无效 chart: null return { // chart: null } }, watch: { chartData: { deep: true, handler(val) { this.setOptions(val) } } }, mounted() { this.$nextTick(() => { this.initChart() }) }, beforeDestroy() { if (!this.chart) { return } this.chart.dispose() this.chart = null }, methods: { initChart() { this.chart = echarts.init(this.$el, 'macarons') this.chart.setOption({ title: { text: this.title, textStyle: { fontSize: 15 } }, xAxis: { type: 'category', boundaryGap: false, }, grid: { left: 15, right: 10, bottom: 20, top: '10%', containLabel: true }, toolbox: { show: true, orient: 'horizontal', left: 'right', top: 'top', feature: { mark: { show: true }, magicType: { show: true, type: ['line', 'bar'] }, } }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, }, yAxis: { type: 'value', axisTick: { show: false } }, series: [] }) }, setOptions(data) { let map = data.map let series = [] map.forEach((val, key) => { let obj = { name: key, data: Array.from(val), type: 'line', barWidth: '40', smooth: true, itemStyle: { normal: { lineStyle: { width: 2 } } }, animationDuration: 2800, animationEasing: 'quadraticOut' } series.push(obj) }); this.chart.setOption({ xAxis: { data: data.date, }, series: series }) } } } </script>
3、通用饼图组件 PieChart.vue
<template> <div :class="className" :style="{height:height,width:width}"/> </template> <script> import * as echarts from 'echarts'; import 'echarts/theme/macarons.js' const color = [ "#37a2da", "#32c5e9", "#9fe6b8", "#ffdb5c", "#ff9f7f", "#fb7293", "#e7bcf3", "#8378ea" ]; export default { props: { className: { type: String, default: 'chart' }, width: { type: String, default: '100%' }, height: { type: String, default: '350px' }, title: { type: String, required: true }, chartData: { type: Object, required: true } }, data() { return { chart: null } }, watch: { chartData: { deep: true, handler(val) { this.setOptions(val) } } }, mounted() { this.$nextTick(() => { this.initChart() }) }, beforeDestroy() { if (!this.chart) { return } this.chart.dispose() this.chart = null }, methods: { initChart() { this.chart = echarts.init(this.$el, 'macarons') this.chart.setOption({ title: { textStyle: { fontSize: 15 } }, tooltip: { trigger: 'item', formatter: '{b} : {c} ({d}%)' }, itemStyle: { normal:{ borderRadius: 10, borderColor: '#fff', borderWidth: 2 }, }, label: { color: '#32c5e9' }, labelLine: { lineStyle: { width: 2 } }, grid: { top: '10%', left: '2%', right: '2%', bottom: '4%', containLabel: true }, series: [{ type: "pie", radius: [50, 110], center: ['50%', '50%'], top: 10, itemStyle: { normal:{ color: item => { return color[item.dataIndex%(color.length)] }, borderRadius: 10, borderColor: '#fff', borderWidth: 2 }, }, label: { color: '#32c5e9' }, labelLine: { lineStyle: { width: 2 } }, emphasis: { label: { show: true, fontSize: '20', fontWeight: 'bold', } }, animationEasing: 'cubicInOut', animationDuration: 2600 }] }) }, setOptions(data) { this.chart.setOption({ title: { text: this.title, }, series: [{ data: data, }] }) } } } </script>
4、调用示例
<template> <div class="dashboard-editor-container"> <el-row :gutter="32"> <el-col :xs="24" :sm="24" :lg="24"> <div class="chart-wrapper"> <el-radio-group v-model="period" @change="getData" style="margin: 0 0 20px 0"> <el-radio-button label="30">近30天</el-radio-button> <el-radio-button label="180">近180天</el-radio-button> <el-radio-button label="365">近365天</el-radio-button> </el-radio-group> <line-chart :chart-data="lineChartData" :title="`每日任务接收情况统计`"/> </div> </el-col> </el-row> <el-row :gutter="32"> <el-col :xs="24" :sm="24" :lg="24"> <div class="chart-wrapper"> <bar-chart :chart-data="monthlyData" :title="`近半年统计月报`"/> </div> </el-col> </el-row> <el-row :gutter="32"> <el-col :xs="24" :sm="24" :lg="8"> <div class="chart-wrapper"> <pie-chart :chart-data="diagnosePieChartData" :title="`诊断任务统计`"/> </div> </el-col> </el-row> </div> </template> <script setup name="Index"> import LineChart from './LineChart' import BarChart from './BarChart' import PieChart from './PieChart' import { countByMonthly, countByReceiptTime, countByTypeAndDept } from '@/api/xxx' const {proxy} = getCurrentInstance(); const period = ref(180) const monthlyData = ref({}); const lineChartData = ref({}); const diagnosePieChartData = ref({}); function getData() { lineChartData.value = {} countByReceiptTime(period.value).then(res => { let arr = res.data let date = arr.map(item => item.time); let data = arr.map(item => item.total); let map = new Map(); map.set('每日接收任务', data); lineChartData.value = { date, map }; }) } function monthly() { monthlyData.value = {}; countByMonthly().then(res => { let arr = res.data let date = arr.map(item => item.item); let data1 = arr.map(item => item["xx"]); let data2 = arr.map(item => item["xx"]); let data3 = arr.map(item => item["xx"]); let data4 = arr.map(item => item["xx"]); let data5 = arr.map(item => item["xx"]); let map = new Map(); map.set('任务总数', data1); map.set('未开始', data2); map.set('进行中', data3); map.set('已完成', data4); map.set('已发布', data5); monthlyData.value = { date, map }; }) } function typeAndDept() { diagnosePieChartData.value = {} countByTypeAndDept().then(res => { let arr = res.data; diagnosePieChartData.value = arr.filter(item => item.type === 'diagnose').map(item => { return {"name": item.dept, "value": item.total} }); }) } getData(); monthly(); typeAndDept(); </script> <style lang="scss" scoped> .dashboard-editor-container { padding: 32px; background-color: rgb(240, 242, 245); position: relative; .chart-wrapper { background: #fff; padding: 16px 16px 0; margin-bottom: 32px; } } @media (max-width: 1024px) { .chart-wrapper { padding: 8px; } } </style>
5、实现效果:
- 折线图
- 柱状图
- 饼图