Skip to content

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 | string0
height图表高度,数字按 pxnumber | string280
width图表宽度,数字按 pxnumber | string'100%'
labels外围标签:false 关闭,对象配置字号与颜色boolean | { fontSize?, color? }true
center环形中心内容:false 关闭boolean | { fontSize? }true
tooltip悬浮提示booleantrue
legend图例,点击可隐藏/显示分片booleanfalse
valueFormatter数值格式化,用于提示、图例与中心内容(value: number) => string千分位,最多 4 位小数
loading加载中boolean | objectfalse
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 描边在细长分片上糊成一片。