Appearance
PieChart 饼图
用于展示各部分在整体中的占比:支持饼图与环形图、中心内容、外围标签与图例联动。
何时使用
- 需要表达「各部分占整体的比例」,且只关心少数几个主要类别(建议不超过 7 个)。
- 数据总和有意义(如各类别调用量、各渠道订单数)。
- 环形模式适合在中心放一个合计值或关键指标,信息密度更高。
什么时候不要用
饼图表达力弱:角度比长度更难精确比较,分片一多就会退化成一圈小色块。
| 情况 | 改用 |
|---|---|
| 类别超过 7 个 | 堆叠柱状(BarChart 的 stack),或先把小项合并为「其他」 |
| 需要精确比较各类别大小 | BarChart 柱状趋势图 并排柱状 |
| 要表达随时间的变化 | LineChart 折线图 |
| 只有一个占比(如使用率 62%) | 用 Progress 进度条 或环形进度,不要用两个分片的饼图 |
基础用法
data 为 { name, value }[];值为负、为 0 或非有限数会被跳过,不计入总量。
vue
<template>
<x-pie-chart :data="data" :value-formatter="formatCompact" aria-label="各模型 Token 占比" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const data = ref([
{ name: 'deepseek-flash', value: 13260000 },
{ name: 'gpt-4o-mini', value: 4820000 },
{ name: 'claude-haiku', value: 2140000 },
]);
const formatCompact = (value: number) => `${(value / 10000).toFixed(1)} 万`;
</script>环形与中心内容
innerRadius 大于 0 时渲染为环形:数字按 px,'62%' 按外半径比例。环形默认在中心展示合计,也可用 center 插槽自定义。
vue
<template>
<x-pie-chart
:data="data"
:inner-radius="innerRadius"
:value-formatter="formatCalls"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const innerRadius = ref('62%');
const data = ref([
{ name: 'Web', value: 4280 },
{ name: 'iOS', value: 3160 },
{ name: 'Android', value: 2870 },
{ name: 'API', value: 1520 },
]);
const formatCalls = (value: number) => `${value} 次`;
</script>自定义中心
center 插槽接管环形中心,可放合计之外的内容(如环比的箭头与增幅)。
vue
<template>
<x-pie-chart :data="data" inner-radius="62%">
<template #center="{ total, formatValue }">
<text x="50%" y="46%" text-anchor="middle">总调用量</text>
<text x="50%" y="56%" text-anchor="middle">{{ formatValue(total) }}</text>
</template>
</x-pie-chart>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const data = ref([
{ name: 'Web', value: 4280 },
{ name: 'iOS', value: 3160 },
{ name: 'Android', value: 2870 },
{ name: 'API', value: 1520 },
]);
</script>类别过多时合并「其他」
饼图不宜超过 7 个分片。先按值排序,把尾部合并成一项再传入,是最省事也最可控的做法。
vue
<template>
<x-pie-chart :data="data" inner-radius="62%" :value-formatter="formatCalls" legend />
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const raw = ref([
{ name: 'search', value: 820 },
{ name: 'read_file', value: 610 },
{ name: 'write_file', value: 430 },
{ name: 'run_shell', value: 320 },
{ name: 'browse', value: 210 },
{ name: 'exec_python', value: 180 },
{ name: 'grep', value: 140 },
{ name: 'edit', value: 110 },
{ name: 'todo_write', value: 90 },
]);
const LIMIT = 5;
// 取前 N 项,其余合并为「其他」
const data = computed(() => {
const sorted = [...raw.value].sort((a, b) => b.value - a.value);
const top = sorted.slice(0, LIMIT);
const rest = sorted.slice(LIMIT).reduce((sum, item) => sum + item.value, 0);
return rest > 0 ? [...top, { name: '其他', value: rest }] : top;
});
const formatCalls = (value: number) => `${value} 次`;
</script>图例联动与点击事件
legend 开启后,图例项展示数值与占比,点击可临时隐藏分片——剩余分片会重新分配角度、中心合计同步更新。点击分片触发 item-click。
尚未点击分片
vue
<template>
<x-pie-chart
:data="data"
inner-radius="62%"
legend
@item-click="onItemClick"
@legend-change="onLegendChange"
/>
<p>{{ clickLog }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const data = ref([
{ name: 'Web', value: 4280 },
{ name: 'iOS', value: 3160 },
{ name: 'Android', value: 2870 },
{ name: 'API', value: 1520 },
]);
const clickLog = ref('尚未点击分片');
const visibleCount = ref(4);
const onItemClick = (data: { name: string; value: number; percent: number; formatPercent: (p: number) => string }) => {
clickLog.value = `${data.name}:${data.value}(${data.formatPercent(data.percent)})`;
};
const onLegendChange = (indexes: number[]) => {
visibleCount.value = indexes.length;
};
</script>自定义提示
tooltip 插槽可显示调用次数、占比与环比等组合信息。
vue
<template>
<x-pie-chart :data="data" inner-radius="62%" :value-formatter="formatCalls">
<template #tooltip="{ name, value, percent, formatValue, formatPercent }">
<div style="font-weight: 500">{{ name }}</div>
<div>{{ formatValue(value) }} · {{ formatPercent(percent) }}</div>
</template>
</x-pie-chart>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const data = ref([
{ name: 'Web', value: 4280 },
{ name: 'iOS', value: 3160 },
{ name: 'Android', value: 2870 },
{ name: 'API', value: 1520 },
]);
const formatCalls = (value: number) => `${value} 次`;
</script>加载与空数据
loading 覆盖加载遮罩;没有正数分片时显示空态,可用 empty 插槽替换。
vue
<template>
<x-pie-chart :data="data" inner-radius="62%" :loading="loading">
<template #empty>该时段没有调用记录</template>
</x-pie-chart>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const loading = ref(true);
const data = ref([
{ name: 'Web', value: 4280 },
{ name: 'iOS', value: 3160 },
]);
</script>按需导入
ts
import { PieChart } from 'x-next';ts
import { createApp } from 'vue';
import { PieChart } from 'x-next';
import 'x-next/style/base.css';
import 'x-next/style/pie-chart.css';
createApp(App).use(PieChart);样式按需引入(base.css 为共享基础层,多个组件只需引入一次):
ts
import 'x-next/style/base.css';
import 'x-next/style/pie-chart.css';Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
data | 分片数据,必填 | PieChartItem[] | [] |
innerRadius | 内半径:0 为饼图,数字按 px,'62%' 按比例 | number | string | 0 |
height | 图表高度,数字按 px | number | string | 280 |
width | 图表宽度,数字按 px | number | string | '100%' |
labels | 外围标签:false 关闭,对象配置字号与颜色 | boolean | { fontSize?, color? } | true |
center | 环形中心内容:false 关闭 | boolean | { fontSize? } | true |
tooltip | 悬浮提示 | boolean | true |
legend | 图例,点击可隐藏/显示分片 | boolean | false |
valueFormatter | 数值格式化,用于提示、图例与中心内容 | (value: number) => string | 千分位,最多 4 位小数 |
loading | 加载中 | boolean | object | false |
ariaLabel | 无障碍名称;设置后画布带 role="img" | string | - |
PieChartItem
| 字段 | 说明 | 类型 |
|---|---|---|
name | 分片名称,用于标签、提示与图例 | string |
value | 分片数值;负值 / 0 / 非有限数会被跳过 | number |
Events
| 事件名 | 说明 | 参数 |
|---|---|---|
itemClick | 点击分片时触发 | (data: PieChartClickData) |
legendChange | 图例切换后触发 | (visibleIndexes: number[]) |
PieChartClickData 包含 index / name / value / percent / color / total,以及 formatValue 与 formatPercent 两个便捷格式化函数。
Slots
| 插槽名 | 说明 | 参数 |
|---|---|---|
center | 环形中心内容 | { total: number; formatValue: (value: number) => string } |
tooltip | 自定义悬浮提示内容 | PieChartActiveData |
label | 自定义外围标签 | PieChartActiveData |
legend | 自定义图例 | { items: PieChartItem[]; hiddenIndexes: number[] } |
empty | 自定义空态内容 | - |
交互说明
- 占比计算:按可见分片求和;隐藏某个分片后,其余分片重新分配角度,中心合计同步更新。
- 单分片:只有一个可见分片时不做缝隙,渲染为完整圆(否则会被切掉一小角,看起来像渲染错误)。
- 小分片标签:占比低于 4% 的分片不绘制外围标签,避免标签挤在一起;
tooltip与legend仍可查看其数值。 - 零值处理:值为 0、负数或非有限数的分片不渲染,也不计入总量。
- 键盘导航:画布可聚焦,左右方向键在分片间循环移动、Home / End 跳到首尾、Esc 关闭提示;键盘导航时通过
aria-live播报。 - 性能:分片数量建议控制在 7 个以内,既符合可视化最佳实践,也避免
stroke描边在细长分片上糊成一片。