开发预览 更新于 2026-05-10

Pivot 透视表

行 × 列 二维聚合表格 —— sum / avg / count / min / max 五种聚合、自带行列合计、可选热力着色、单元格 drill-down。

基础用法

把扁平的对象数组按 rowField(行)/ colField(列)分组,对 valueField 字段做聚合。组件会自动计算行合计、列合计、总计。

背景
region/channel官网门店App合计
华北¥12,400¥8,200¥4,900¥25,500
华东¥22,800¥14,200¥9,800¥46,800
华南¥18,200¥6,800¥5,400¥30,400
西部¥7,400¥3,200¥2,100¥12,700
合计¥60,800¥32,400¥22,200¥115,400
<script setup lang="ts">
import { CfPivot } from '@chufix-design/vue';

interface Order {
  region: string;
  channel: string;
  amount: number;
}

const data: Order[] = [
  { region: '华北', channel: '官网', amount: 12400 },
  { region: '华北', channel: '门店', amount: 8200 },
  { region: '华北', channel: 'App', amount: 4900 },
  { region: '华东', channel: '官网', amount: 22800 },
  { region: '华东', channel: '门店', amount: 14200 },
  { region: '华东', channel: 'App', amount: 9800 },
  { region: '华南', channel: '官网', amount: 18200 },
  { region: '华南', channel: '门店', amount: 6800 },
  { region: '华南', channel: 'App', amount: 5400 },
  { region: '西部', channel: '官网', amount: 7400 },
  { region: '西部', channel: '门店', amount: 3200 },
  { region: '西部', channel: 'App', amount: 2100 },
];
</script>

<template>
  <CfPivot
    :data="data"
    row-field="region"
    col-field="channel"
    value-field="amount"
    aggregator="sum"
    :format="(v: number) => '¥' + v.toLocaleString()"
  />
</template>

热力图模式

heatmap 打开后,每个单元格按数值大小渲染为一层透明 accent 颜色(数值越大颜色越深)。配合不同 aggregator 切换可以快速观察分布形态。

背景
aggregator

周 × 小时 出行热力(模拟数据)

weekday/hour000408121620
周一5310930430927352
周二737130327327499
周三639430430131489
周四998429232431962
周五675628931227685
周六124177143282155286
周日175157161280151298
<script setup lang="ts">
import { ref } from 'vue';
import { CfPivot, CfSelect } from '@chufix-design/vue';
import type { PivotAggregator } from '@chufix-design/vue';

interface Trip {
  weekday: string;
  hour: string;
  count: number;
}

const WEEKDAYS = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
const HOURS = ['00', '04', '08', '12', '16', '20'];

const data: Trip[] = [];
for (const w of WEEKDAYS) {
  for (const h of HOURS) {
    let base = 50;
    if (h === '08' || h === '12' || h === '16') base += 220;
    if (w === '周六' || w === '周日') base = h === '12' || h === '20' ? 280 : 120;
    data.push({ weekday: w, hour: h, count: Math.round(base + Math.random() * 60) });
  }
}

const agg = ref<PivotAggregator>('sum');
const aggOptions = [
  { value: 'sum', label: 'sum' },
  { value: 'avg', label: 'avg' },
  { value: 'count', label: 'count' },
  { value: 'max', label: 'max' },
];
</script>

<template>
  <div style="display: flex; gap: 8px; align-items: center; margin-bottom: 8px;">
    <span style="font-size: 12px; color: var(--fg-3);">aggregator</span>
    <CfSelect v-model="agg" :options="aggOptions" size="sm" style="max-width: 120px;" />
  </div>
  <CfPivot
    :data="data"
    row-field="weekday"
    col-field="hour"
    value-field="count"
    :aggregator="agg"
    heatmap
    :show-totals="false"
    caption="周 × 小时 出行热力(模拟数据)"
  />
</template>

Drill-down

onCellClick 回调会拿到 { row, col, value, rows } —— rows 是聚合时归并到这个单元格的所有原始数据,可用于触发抽屉、弹窗等下钻交互。

背景
product/store上海北京深圳合计
A6,0001,9007,900
B5,4006,1004,20015,700
C1,2002,4003,3006,900
合计12,60010,4007,50030,500
<script setup lang="ts">
import { ref } from 'vue';
import { CfPivot } from '@chufix-design/vue';

interface Sale {
  product: string;
  store: string;
  amount: number;
  qty: number;
}

const data: Sale[] = [
  { product: 'A', store: '上海', amount: 3200, qty: 12 },
  { product: 'A', store: '上海', amount: 2800, qty: 9 },
  { product: 'A', store: '北京', amount: 1900, qty: 7 },
  { product: 'B', store: '上海', amount: 5400, qty: 22 },
  { product: 'B', store: '北京', amount: 6100, qty: 26 },
  { product: 'B', store: '深圳', amount: 4200, qty: 18 },
  { product: 'C', store: '上海', amount: 1200, qty: 4 },
  { product: 'C', store: '北京', amount: 2400, qty: 8 },
  { product: 'C', store: '深圳', amount: 3300, qty: 11 },
];

const drilled = ref<{ row: string; col: string; rows: Sale[] } | null>(null);
</script>

<template>
  <CfPivot
    :data="data"
    row-field="product"
    col-field="store"
    value-field="amount"
    aggregator="sum"
    :on-cell-click="(p: { row: string; col: string; rows: unknown[] }) => drilled = { row: p.row, col: p.col, rows: p.rows as Sale[] }"
  />
  <div v-if="drilled" style="margin-top: 12px; padding: 10px 12px; border: 1px solid var(--line-1); border-radius: 6px; background: var(--bg-2); font-size: 12px;">
    <div style="margin-bottom: 6px;">
      <strong>{{ drilled.row }} × {{ drilled.col }}</strong> 命中 {{ drilled.rows.length }} 条原始记录
    </div>
    <ul style="margin: 0; padding-left: 18px; color: var(--fg-2);">
      <li v-for="(r, i) in drilled.rows" :key="i">
        ¥{{ r.amount.toLocaleString() }} · {{ r.qty }} 件
      </li>
    </ul>
  </div>
</template>

API

Prop类型默认说明
dataT[]扁平数据数组
rowFieldkeyof T用于分行的字段
colFieldkeyof T用于分列的字段
valueFieldkeyof T数值字段(aggregator='count' 时可省略)
aggregator'sum' | 'avg' | 'count' | 'min' | 'max''sum'聚合方式
format(value, { row, col }) => string单元格自定义格式化
showTotalsbooleantrue是否显示行 / 列合计
heatmapbooleanfalse按数值大小渲染热力背景
heatmapColorstringvar(--accent-1)热力主色
captionstring顶部说明文字
size'sm' | 'md' | 'lg''md'字号档位
onCellClick(cell) => void单元格点击回调,参数包含原始 rows

工具函数

import { pivotCompute, pivotAggregate } from '@chufix-design/vue';

const result = pivotCompute(data, 'region', 'channel', 'amount', 'sum');
// → { rowKeys, colKeys, cells, raw, rowTotals, colTotals, grandTotal, min, max }

pivotCompute 是组件内部用的纯函数,外部也可以直接复用:导出 CSV、画图、做下钻等场景都用得上。

反馈与讨论

Pivot 透视表 的讨论

0
0 / 600
一键发送
正在加载评论...