<script lang="ts" setup name="PieChart"> import type { ECharts } from 'echarts' import { init, number } from 'echarts' import type { Ref } from 'vue' import type { ECBasicOption } from 'echarts/types/dist/shared' import tdTheme from './theme.json' // 引入默认主题 const props = defineProps({ /** * id */ id: { type: String, default: 'chart', }, /** * 加载中 */ loading: { type: Boolean, default: false, }, /** * 标题 */ title: { type: String, default: '', }, /** * 图例是否显示 */ legend: { type: Object, default: () => { return { show: true, type: 'scroll', orient: 'horizontal', right: '10%', top: 'top', icon: 'circle', itemWidth: 16, itemHeight: 16, itemStyle: { fontSize: 16, }, } }, }, /** * 网格配置 */ grid: { type: Object, default: () => { return { top: 0, left: '15%', right: '50%', bottom: 50, containLabel: true, // 是否包含坐标轴的刻度标签 } }, }, /** * 图表宽 */ width: { type: String, default: '100%', }, /** * 图表高 */ height: { type: String, default: '100%', }, /** * 距离容器右侧距离,默认0,为legend在右侧时做准备 */ right: { type: [Number, String], default: 0, }, /** * 数据 */ data: { type: Array, default: () => { return [] }, }, unit: { type: String, default: '', }, /** * 悬停格式化 */ hoverFormatter: { type: String, default: '{b}<br/>数量:{c}' + '<br/>占比:{d}%', }, radar: { type: Object, }, /** * 颜色 */ colors: { type: Array, default: () => { return [ '#46edf2', '#45c7f2', '#ff9900', '#E46CBB', '#9A66E4', '#ed3f14', '#01C2F9', ] }, }, }) // 图表对象 let chart: ECharts const chartRef: Ref<HTMLElement | null> = ref(null) // 监听数据变化 watch( [() => props.data], ([newNums], [oldNums]) => { refreshChart() }, { immediate: true, deep: true, }, ) // 构建option function buildOption() { const option: any = { title: [{ text: props.title }], legend: props.legend, // 图例 grid: props.grid, // 网格 tooltip: { show: true, confine: true, // 是否限制提示框在图表范围内 trigger: 'item', // formatter: `{b}<br/>数量:{c}${props.unit}<br/>占比:{d}%`, textStyle: { fontSize: 14, color: '#fff', }, backgroundColor: 'rgba(3, 53, 139, .9)', }, // 提示框 series: [], } // 标题 if (props.title) { option.title = { show: true, text: props.title, textStyle: { fontWeight: 'normal', fontSize: 14, color: '#0b2c5b', }, bottom: 0, left: 'center', } } // 标题 if (props.colors) { option.color = props.colors as string[] } // 数据 if (props.data) { const series = { type: 'radar', center: ['50%', '50%'], // 圆心坐标[横坐标,纵坐标] right: props.right, itemStyle: { borderColor: '#fff', borderWidth: 1, borderRadius: 8, }, label: { fontSize: 16, }, data: props.data, } option.series = [series] } if (props.radar) { const radarData = { indicator: props.radar, nameGap: 5, // 文字距离 radius: '55%', // 图比例大小 axisName: { color: '#a1cfd1', }, splitLine: { // (这里是指所有圆环)坐标轴在 grid 区域中的分隔线。 lineStyle: { color: '#89aeb2', // 分隔线颜色 width: 2, // 分隔线线宽 }, }, splitArea: { show: true, areaStyle: { // color: '#89aeb2', // 图表背景网格的颜色 }, }, name: { // 文本名称样式 textStyle: { color: '#a1cfd1', }, }, } option.radar = radarData } return option } // 初始化图表 function initChart() { chart = init(chartRef.value as HTMLElement, tdTheme) chart.setOption({}) } // 刷新图表 function refreshChart() { if (chart) { const option = buildOption() chart.setOption(option as unknown as ECBasicOption, true) chart.resize() } } window.addEventListener('resize', () => { chart.resize() }) onMounted(() => { initChart() }) </script> <template> <div :id="id" ref="chartRef" v-loading="loading" class="chart" :style="{ height, width }" /> </template> <style lang="scss" scoped> .chart { width: 100%; height: 100%; z-index: 100; } </style>