92 lines
2.3 KiB
Vue
92 lines
2.3 KiB
Vue
<template>
|
|
<div class="donut-wrap">
|
|
<v-chart class="chart" :option="option" autoresize />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { use } from 'echarts/core'
|
|
import { CanvasRenderer } from 'echarts/renderers'
|
|
import { PieChart } from 'echarts/charts'
|
|
import { TooltipComponent, LegendComponent } from 'echarts/components'
|
|
import type { EChartsOption } from 'echarts'
|
|
import VChart from 'vue-echarts'
|
|
import type { PositionRow } from '../../types'
|
|
|
|
use([CanvasRenderer, PieChart, TooltipComponent, LegendComponent])
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
list: PositionRow[]
|
|
valueKey?: keyof PositionRow
|
|
}>(),
|
|
{ valueKey: 'qty' },
|
|
)
|
|
|
|
const colors = ['#7c3aed', '#f59e0b', '#eab308', '#14b8a6', '#3b82f6', '#94a3b8']
|
|
|
|
interface TooltipParam {
|
|
name?: string
|
|
value?: string | number
|
|
percent?: number
|
|
}
|
|
|
|
const option = computed((): EChartsOption => {
|
|
const key = props.valueKey
|
|
const top = props.list.slice(0, 5)
|
|
const rest = props.list.slice(5)
|
|
const otherQty = rest.reduce((s, x) => s + Number(x[key]), 0)
|
|
const data = top.map((x) => ({ name: x.name, value: Number(x[key]) }))
|
|
if (otherQty > 0) data.push({ name: '其他', value: otherQty })
|
|
|
|
return {
|
|
color: colors,
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: (p) => {
|
|
const param = (Array.isArray(p) ? p[0] : p) as TooltipParam
|
|
const row = props.list.find((x) => x.name === param.name)
|
|
const change = row ? row.change : 0
|
|
const sign = change > 0 ? '+' : ''
|
|
return `${param.name}<br/>数量: ${param.value}<br/>增减: ${sign}${change}<br/>占比: ${param.percent}%`
|
|
},
|
|
},
|
|
legend: {
|
|
orient: 'vertical',
|
|
right: 0,
|
|
top: 'middle',
|
|
textStyle: { fontSize: 11 },
|
|
formatter: (name: string) => {
|
|
const item = data.find((d) => d.name === name)
|
|
if (!item) return name
|
|
const total = data.reduce((s, d) => s + d.value, 0)
|
|
const pct = ((item.value / total) * 100).toFixed(2)
|
|
return `${name} ${pct}%`
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
type: 'pie',
|
|
radius: ['48%', '72%'],
|
|
center: ['32%', '50%'],
|
|
avoidLabelOverlap: true,
|
|
label: { show: false },
|
|
data,
|
|
},
|
|
],
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.donut-wrap {
|
|
height: 220px;
|
|
}
|
|
|
|
.chart {
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
</style>
|