You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
4.3 KiB
183 lines
4.3 KiB
<template>
|
|
<div ref="chartContainer" class="pie-chart-container"></div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
|
import * as echarts from 'echarts'
|
|
|
|
// 定义组件props
|
|
interface Props {
|
|
chartData?: Array<{ name: string; value: number }>
|
|
title?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
chartData: () => [],
|
|
title: ''
|
|
})
|
|
|
|
const chartContainer = ref<HTMLDivElement | null>(null)
|
|
let chartInstance: echarts.ECharts | null = null
|
|
|
|
// 初始化图表
|
|
const initChart = () => {
|
|
if (chartContainer.value) {
|
|
chartInstance = echarts.init(chartContainer.value)
|
|
updateChart()
|
|
}
|
|
}
|
|
|
|
// 计算数据总和
|
|
const getTotalValue = () => {
|
|
if (!props.chartData) return 0
|
|
const total = props.chartData.reduce((sum, item) => sum + item.value, 0)
|
|
return Math.round(total * 100) / 100 // 保留两位小数
|
|
}
|
|
|
|
// 更新图表
|
|
const updateChart = () => {
|
|
if (!chartInstance || !props.chartData) return
|
|
|
|
const totalValue = getTotalValue()
|
|
|
|
const option = {
|
|
title: {
|
|
// text: props.title,
|
|
left: 'center',
|
|
textStyle: {
|
|
color: '#FFF'
|
|
}
|
|
},
|
|
tooltip: {
|
|
trigger: 'item'
|
|
},
|
|
legend: {
|
|
show: true,
|
|
// type: 'scroll', // 👈 开启可滚动图例
|
|
orient: 'horizontal', // 👈 水平排列
|
|
top: 10, // 👈 放在顶部
|
|
left: 'center', // 👈 居中对齐
|
|
textStyle: {
|
|
color: '#FFF'
|
|
}
|
|
},
|
|
series: [
|
|
{
|
|
name: props.title,
|
|
type: 'pie',
|
|
radius: ['35%', '55%'], // 调整内外半径,增加厚度
|
|
center: ['50%', '40%'], // 👈 调整饼图位置,因为图例已移到顶部
|
|
padAngle: 5, // 添加间隙角度
|
|
avoidLabelOverlap: true,
|
|
itemStyle: {
|
|
borderRadius: 0,
|
|
borderColor: 'transparent', // 取消边框
|
|
borderWidth: 0 // 边框宽度设为0
|
|
},
|
|
label: {
|
|
show: true,
|
|
formatter: (params) => {
|
|
return params.name + '\n' + params.value.toFixed(2)
|
|
},
|
|
fontSize: 16,
|
|
color: '#FFFFFF', // 设置文本颜色为白色
|
|
textBorderColor: 'transparent', // 取消文本边缘的发光效果
|
|
textBorderWidth: 0
|
|
},
|
|
emphasis: {
|
|
label: {
|
|
show: true,
|
|
formatter: (params) => {
|
|
return params.name + '\n' + params.value.toFixed(2)
|
|
},
|
|
fontSize: 14,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF', // 设置强调时文本颜色为白色
|
|
textBorderColor: 'transparent', // 取消文本边缘的发光效果
|
|
textBorderWidth: 0
|
|
}
|
|
},
|
|
labelLine: {
|
|
show: true
|
|
},
|
|
data: props.chartData
|
|
},
|
|
{
|
|
name: '总量',
|
|
type: 'pie',
|
|
radius: ['0%', '35%'], // 内部圆环用于显示总数,与外环内半径匹配
|
|
center: ['50%', '40%'],
|
|
hoverAnimation: false, // 禁用悬停动画
|
|
label: {
|
|
show: true,
|
|
position: 'center',
|
|
formatter: [`{title|总数}`, `{value|${totalValue.toFixed(2)}}`].join('\n'),
|
|
rich: {
|
|
title: {
|
|
color: '#FFF',
|
|
fontSize: 14,
|
|
lineHeight: 20
|
|
},
|
|
value: {
|
|
color: '#FFF',
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
lineHeight: 30
|
|
}
|
|
}
|
|
},
|
|
labelLine: {
|
|
show: false
|
|
},
|
|
data: [
|
|
{
|
|
value: totalValue,
|
|
itemStyle: {
|
|
color: 'rgba(0, 0, 0, 0)' // 透明色
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
chartInstance.setOption(option, true)
|
|
}
|
|
|
|
// 窗口大小改变时重置图表大小
|
|
const handleResize = () => {
|
|
if (chartInstance) {
|
|
chartInstance.resize()
|
|
}
|
|
}
|
|
|
|
// 监听数据变化
|
|
watch(
|
|
() => props.chartData,
|
|
() => {
|
|
updateChart()
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
// 初始化和销毁
|
|
onMounted(() => {
|
|
initChart()
|
|
window.addEventListener('resize', handleResize)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
if (chartInstance) {
|
|
chartInstance.dispose()
|
|
}
|
|
window.removeEventListener('resize', handleResize)
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.pie-chart-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
</style>
|
|
|