效果如下:
目录结构
useEchart.ts
import * as echarts from 'echarts'
import chinaMapData from '../data/china.json'
echarts.registerMap('china', chinaMapData)
export default function (el: HTMLElement) {
const echartInstance = echarts.init(el)
const setOptions = (options: echarts.EChartsOption) => {
echartInstance.setOption(options)
}
const updateSize = () => {
echartInstance.resize()
}
window.addEventListener('resize', () => {
echartInstance.resize()
})
return {
echartInstance,
setOptions,
updateSize
}
}
base-echart.vue
<template>
<div class="base-echart">
<div ref="echartDivRef" :style="{ width: width, height: height }"></div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, defineProps, withDefaults, watchEffect } from 'vue'
import { EChartsOption } from 'echarts'
import useEchart from '../hooks/useEchart'
// 定义props
const props = withDefaults(
defineProps<{
options: EChartsOption
width?: string
height?: string
}>(),
{
width: '100%',
height: '360px'
}
)
const echartDivRef = ref<HTMLElement>()
onMounted(() => {
const { setOptions } = useEchart(echartDivRef.value!)
watchEffect(() => {
setOptions(props.options)
})
})
</script>
index.ts导出
import BaseEchart from './src/base-echart.vue'
export default BaseEchart
然后再对这5个进行封装起来
pie-echart.vue
<template>
<div class="pie-echart">
<base-echart :options="options"></base-echart>
</div>
</template>
<script setup lang="ts">
import { defineProps, computed } from 'vue'
import BaseEchart from '@/base-ui/echart'
import { IDataType } from '../types'
const props = defineProps<{
pieData: IDataType[]
}>()
const options = computed(() => {
return {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'horizontal',
left: 'left'
},
series: [
{
name: '分类数据',
type: 'pie',
radius: '50%',
data: props.pieData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
})
</script>
map-echart.vue
<template>
<div class="bar-echart">
<base-echart :options="options"></base-echart>
</div>
</template>
<script setup lang="ts">
import { computed, defineProps, withDefaults } from 'vue'
import BaseEchart from '@/base-ui/echart'
import { IDataType } from '../types'
import { convertData } from '../utils/convert-data'
const props = withDefaults(
defineProps<{
title?: string
mapData: IDataType[]
}>(),
{
title: ''
}
)
const options = computed(() => {
return {
backgroundColor: '#fff',
title: {
text: '全国销量统计',
left: 'center',
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'item',
formatter: function (params: any) {
return params.name + ' : ' + params.value[2]
}
},
visualMap: {
min: 0,
max: 60000,
left: 20,
bottom: 20,
calculable: true,
text: ['高', '低'],
inRange: {
color: ['rgb(70, 240, 252)', 'rgb(250, 220, 46)', 'rgb(245, 38, 186)']
},
textStyle: {
color: '#fff'
}
},
geo: {
map: 'china',
roam: 'scale',
emphasis: {
areaColor: '#f4cccc',
borderColor: 'rgb(9, 54, 95)',
itemStyle: {
areaColor: '#f4cccc'
}
}
},
series: [
{
name: '销量',
type: 'scatter',
coordinateSystem: 'geo',
data: convertData(props.mapData),
symbolSize: 12,
emphasis: {
itemStyle: {
borderColor: '#fff',
borderWidth: 1
}
}
},
{
type: 'map',
map: 'china',
geoIndex: 0,
aspectScale: 0.75,
tooltip: {
show: false
}
}
]
}
})
</script>
rose-echart.vue
<template>
<div class="rose-echart">
<base-echart :options="options"></base-echart>
</div>
</template>
<script setup lang="ts">
import { computed, defineProps } from 'vue'
import BaseEchart from '@/base-ui/echart'
import { IDataType } from '../types'
const props = defineProps<{
roseData: IDataType[]
}>()
const options = computed(() => {
return {
// legend: {
// top: 'bottom'
// },
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true }
}
},
series: [
{
name: '类别数据',
type: 'pie',
radius: [10, 100],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 8
},
data: props.roseData
}
]
}
})
</script>
line-echart.vue
<template>
<div class="line-echart">
<base-echart :options="options"></base-echart>
</div>
</template>
<script setup lang="ts">
import { computed, defineProps, withDefaults } from 'vue'
import BaseEchart from '@/base-ui/echart'
const props = withDefaults(
defineProps<{
title?: string
xLabels: string[]
values: any[]
}>(),
{
title: ''
}
)
const options = computed(() => {
return {
title: {
text: props.title
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
legend: {},
toolbox: {
feature: {
saveAsImage: {}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: props.xLabels
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '分别销量',
type: 'line',
stack: '总量',
areaStyle: {},
emphasis: {
focus: 'series'
},
data: props.values
}
]
}
})
</script>
bar-echart.vue
<template>
<div class="bar-echart">
<base-echart :options="options"></base-echart>
</div>
</template>
<script setup lang="ts">
import { computed, defineProps, withDefaults } from 'vue'
import * as echarts from 'echarts'
import BaseEchart from '@/base-ui/echart'
const props = withDefaults(
defineProps<{
title?: string
xLabels: string[]
values: any[]
}>(),
{
title: ''
}
)
const options = computed(() => {
return {
xAxis: {
data: props.xLabels,
axisLabel: {
inside: true,
color: '#fff'
},
axisTick: {
show: false
},
axisLine: {
show: false
},
z: 10
},
yAxis: {
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#999'
}
},
dataZoom: [
{
type: 'inside'
}
],
series: [
{
type: 'bar',
showBackground: true,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
])
}
},
data: props.values
}
]
}
})
</script>
ok,然后进行使用
<template>
<div class="dashboard">
<el-row :gutter="10">
<el-col :span="7">
<hy-card title="分类商品数量(饼图)">
<pie-echart :pieData="categoryGoodsCount"></pie-echart>
</hy-card>
</el-col>
<el-col :span="10">
<hy-card title="不同城市商品销量">
<map-echart :mapData="addressGoodsSale"></map-echart>
</hy-card>
</el-col>
<el-col :span="7">
<hy-card title="分类商品数量(玫瑰图)">
<rose-echart :roseData="categoryGoodsCount"></rose-echart>
</hy-card>
</el-col>
</el-row>
<el-row :gutter="10" class="content-row">
<el-col :span="12">
<hy-card title="分类商品的销量">
<line-echart v-bind="categoryGoodsSale"></line-echart>
</hy-card>
</el-col>
<el-col :span="12">
<hy-card title="分类商品的收藏">
<bar-echart v-bind="categoryGoodsFavor"></bar-echart>
</hy-card>
</el-col>
</el-row>
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue'
import { useStore } from '@/store'
import HyCard from '@/base-ui/card'
import {
PieEchart,
RoseEchart,
LineEchart,
BarEchart,
MapEchart
} from '@/components/page-echarts'
export default defineComponent({
name: 'dashboard',
components: {
HyCard,
PieEchart,
RoseEchart,
LineEchart,
BarEchart,
MapEchart
},
setup() {
const store = useStore()
// 请求数据
store.dispatch('dashboard/getDashboardDataAction')
// 获取数据
const categoryGoodsCount = computed(() => {
return store.state.dashboard.categoryGoodsCount.map((item: any) => {
return { name: item.name, value: item.goodsCount }
})
})
const categoryGoodsSale = computed(() => {
const xLabels: string[] = []
const values: any[] = []
const categoryGoodsSale = store.state.dashboard.categoryGoodsSale
for (const item of categoryGoodsSale) {
xLabels.push(item.name)
values.push(item.goodsCount)
}
return { xLabels, values }
})
const categoryGoodsFavor = computed(() => {
const xLabels: string[] = []
const values: any[] = []
const categoryGoodsFavor = store.state.dashboard.categoryGoodsFavor
for (const item of categoryGoodsFavor) {
xLabels.push(item.name)
values.push(item.goodsFavor)
}
return { xLabels, values }
})
const addressGoodsSale = computed(() => {
return store.state.dashboard.addressGoodsSale.map((item: any) => {
return { name: item.address, value: item.count }
})
})
return {
categoryGoodsCount,
categoryGoodsSale,
categoryGoodsFavor,
addressGoodsSale
}
}
})
</script>
<style scoped>
.content-row {
margin-top: 20px;
}
</style>
如果不明白的可以去github里面有所有代码,github项目地址https://github.com/lsh555/vue3-ems