Preview Updated 2026-05-10

MetricCard

Value + unit + delta + embedded sparkline trend.

Basic usage

Data is passed via props; pure SVG rendering with no third-party chart library. Colors come from the --viz-1..8 tokens and are color-blind friendly.

背景 视口
今日 PV+8.4%
12,420
活跃用户-2.1%
3,180
付费转化+0.4%
6.2%
src/App.vue
<script setup lang="ts">
import { CfMetricCard } from '@chufix-design/vue';
</script>
<template>
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; width: 100%;">
    <CfMetricCard label="今日 PV" value="12,420" :delta="8.4" :trend="[40, 45, 50, 52, 60, 65, 72]" />
    <CfMetricCard label="活跃用户" value="3,180" :delta="-2.1" :trend="[80, 78, 82, 85, 79, 76, 73]" />
    <CfMetricCard label="付费转化" value="6.2" unit="%" :delta="0.4" :trend="[50, 52, 50, 55, 56, 58, 58]" />
  </div>
</template>
<script setup>
import { CfMetricCard } from '@chufix-design/vue';
</script>
<template>
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; width: 100%;">
    <CfMetricCard label="今日 PV" value="12,420" :delta="8.4" :trend="[40, 45, 50, 52, 60, 65, 72]" />
    <CfMetricCard label="活跃用户" value="3,180" :delta="-2.1" :trend="[80, 78, 82, 85, 79, 76, 73]" />
    <CfMetricCard label="付费转化" value="6.2" unit="%" :delta="0.4" :trend="[50, 52, 50, 55, 56, 58, 58]" />
  </div>
</template>
import { CfMetricCard } from '@chufix-design/react';

export default function Demo() {
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 12, width: "100%" }}>
          <CfMetricCard label="今日 PV" value="12,420" delta={8.4} trend={[40, 45, 50, 52, 60, 65, 72]} />
          <CfMetricCard label="活跃用户" value="3,180" delta={-2.1} trend={[80, 78, 82, 85, 79, 76, 73]} />
          <CfMetricCard label="付费转化" value="6.2" unit="%" delta={0.4} trend={[50, 52, 50, 55, 56, 58, 58]} />
        </div>
    </>
  );
}
import { CfMetricCard } from '@chufix-design/react';

export default function Demo() {
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 12, width: "100%" }}>
          <CfMetricCard label="今日 PV" value="12,420" delta={8.4} trend={[40, 45, 50, 52, 60, 65, 72]} />
          <CfMetricCard label="活跃用户" value="3,180" delta={-2.1} trend={[80, 78, 82, 85, 79, 76, 73]} />
          <CfMetricCard label="付费转化" value="6.2" unit="%" delta={0.4} trend={[50, 52, 50, 55, 56, 58, 58]} />
        </div>
    </>
  );
}

Grid layout

Pair with grid-template-columns: repeat(auto-fit, minmax(...)) for adaptive density.

背景 视口
今日 PV+8.4%
12,420
活跃用户-2.1%
3,180
付费转化+0.4%
6.2%
平均停留0.0%
3:42
错误率-0.1%
0.18%
src/App.vue
<script setup lang="ts">
import { CfMetricCard } from '@chufix-design/vue';
function trend(n: number, base: number, jitter: number, phase: number) {
  return Array.from({ length: n }, (_, i) => Number((
    base
    + Math.sin((i + phase) / 2.4) * jitter * 0.36
    + Math.cos((i + phase) / 5) * jitter * 0.18
  ).toFixed(3)));
}
</script>
<template>
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; width: 100%;">
    <CfMetricCard label="今日 PV" value="12,420" unit="" :delta="8.4" :trend="trend(20, 65, 30, 0)" />
    <CfMetricCard label="活跃用户" value="3,180" :delta="-2.1" :trend="trend(20, 80, 20, 4)" />
    <CfMetricCard label="付费转化" value="6.2" unit="%" :delta="0.4" :trend="trend(20, 55, 15, 8)" />
    <CfMetricCard label="平均停留" value="3:42" unit="" :delta="0" :trend="trend(20, 50, 8, 12)" />
    <CfMetricCard label="错误率" value="0.18" unit="%" :delta="-0.06" :trend="trend(20, 40, 25, 16)" />
  </div>
</template>
<script setup>
import { CfMetricCard } from '@chufix-design/vue';
function trend(n, base, jitter, phase) {
  return Array.from({ length: n }, (_, i) => Number((
    base
    + Math.sin((i + phase) / 2.4) * jitter * 0.36
    + Math.cos((i + phase) / 5) * jitter * 0.18
  ).toFixed(3)));
}
</script>
<template>
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; width: 100%;">
    <CfMetricCard label="今日 PV" value="12,420" unit="" :delta="8.4" :trend="trend(20, 65, 30, 0)" />
    <CfMetricCard label="活跃用户" value="3,180" :delta="-2.1" :trend="trend(20, 80, 20, 4)" />
    <CfMetricCard label="付费转化" value="6.2" unit="%" :delta="0.4" :trend="trend(20, 55, 15, 8)" />
    <CfMetricCard label="平均停留" value="3:42" unit="" :delta="0" :trend="trend(20, 50, 8, 12)" />
    <CfMetricCard label="错误率" value="0.18" unit="%" :delta="-0.06" :trend="trend(20, 40, 25, 16)" />
  </div>
</template>
import { CfMetricCard } from '@chufix-design/react';

export default function Demo() {
  function trend(n: number, base: number, jitter: number, phase: number) {
    return Array.from({ length: n }, (_, i) => Number((
      base
      + Math.sin((i + phase) / 2.4) * jitter * 0.36
      + Math.cos((i + phase) / 5) * jitter * 0.18
    ).toFixed(3)));
  }
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: 12, width: "100%" }}>
          <CfMetricCard label="今日 PV" value="12,420" unit="" delta={8.4} trend={trend(20, 65, 30, 0)} />
          <CfMetricCard label="活跃用户" value="3,180" delta={-2.1} trend={trend(20, 80, 20, 4)} />
          <CfMetricCard label="付费转化" value="6.2" unit="%" delta={0.4} trend={trend(20, 55, 15, 8)} />
          <CfMetricCard label="平均停留" value="3:42" unit="" delta={0} trend={trend(20, 50, 8, 12)} />
          <CfMetricCard label="错误率" value="0.18" unit="%" delta={-0.06} trend={trend(20, 40, 25, 16)} />
        </div>
    </>
  );
}
import { CfMetricCard } from '@chufix-design/react';

export default function Demo() {
  function trend(n, base, jitter, phase) {
    return Array.from({ length: n }, (_, i) => Number((
      base
      + Math.sin((i + phase) / 2.4) * jitter * 0.36
      + Math.cos((i + phase) / 5) * jitter * 0.18
    ).toFixed(3)));
  }
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: 12, width: "100%" }}>
          <CfMetricCard label="今日 PV" value="12,420" unit="" delta={8.4} trend={trend(20, 65, 30, 0)} />
          <CfMetricCard label="活跃用户" value="3,180" delta={-2.1} trend={trend(20, 80, 20, 4)} />
          <CfMetricCard label="付费转化" value="6.2" unit="%" delta={0.4} trend={trend(20, 55, 15, 8)} />
          <CfMetricCard label="平均停留" value="3:42" unit="" delta={0} trend={trend(20, 50, 8, 12)} />
          <CfMetricCard label="错误率" value="0.18" unit="%" delta={-0.06} trend={trend(20, 40, 25, 16)} />
        </div>
    </>
  );
}

Expandable series breakdown

Pass a series array and a chevron appears in the header; clicking it expands a list of channels / components / sources with their own sparkline, delta, and color dot. Use defaultExpanded for the initial state or controlled expanded + onExpandedChange to drive it externally.

背景 视口
DAU+3.2%
318K

无 series 时不渲染 chevron

点击渠道 尚未点击

src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfMetricCard, CfTag, toast } from '@chufix-design/vue';

const channels = [
  { label: '官网', value: '4,820', suffix: ' 万', delta: 8.2, trend: [120, 140, 158, 162, 180, 200, 220], color: 'var(--viz-1, oklch(64% 0.16 263))' },
  { label: '门店', value: '3,140', suffix: ' 万', delta: 2.4, trend: [80, 86, 92, 96, 100, 108, 112], color: 'var(--viz-2, oklch(70% 0.13 175))' },
  { label: 'App', value: '2,680', suffix: ' 万', delta: 14.6, trend: [60, 70, 88, 102, 118, 130, 142], color: 'var(--viz-3, oklch(74% 0.16 80))' },
  { label: '分销', value: '1,420', suffix: ' 万', delta: -4.1, trend: [80, 76, 72, 68, 64, 60, 58], color: 'var(--viz-4, oklch(64% 0.18 30))' },
];

const lastSelected = ref<string>('');

function onSelect(p: { index: number; item: { label: string; value: string | number } }) {
  lastSelected.value = `${p.item.label} (#${p.index + 1}) → ${p.item.value}`;
  toast({ type: 'info', message: `点击 ${p.item.label}` });
}
</script>
<template>
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; max-width: 640px;">
    <CfMetricCard
      label="本月营收"
      value="12,060"
      suffix=" 万"
      :delta="6.4"
      :trend="[820, 880, 920, 1020, 1140, 1180, 1206]"
      hint="点 chevron 展开,点行下钻"
      :series="channels"
      default-expanded
      @series-select="onSelect"
    />
    <CfMetricCard
      label="DAU"
      value="318"
      suffix="K"
      :delta="3.2"
      :trend="[260, 268, 275, 282, 296, 308, 318]"
      hint="无 series 时不渲染 chevron"
    />
  </div>
  <p style="margin-top: 8px; font-size: 12px;">
    <CfTag tone="info" size="sm">点击渠道</CfTag>
    {{ lastSelected || '尚未点击' }}
  </p>
</template>
<script setup>
import { ref } from 'vue';
import { CfMetricCard, CfTag, toast } from '@chufix-design/vue';

const channels = [
  { label: '官网', value: '4,820', suffix: ' 万', delta: 8.2, trend: [120, 140, 158, 162, 180, 200, 220], color: 'var(--viz-1, oklch(64% 0.16 263))' },
  { label: '门店', value: '3,140', suffix: ' 万', delta: 2.4, trend: [80, 86, 92, 96, 100, 108, 112], color: 'var(--viz-2, oklch(70% 0.13 175))' },
  { label: 'App', value: '2,680', suffix: ' 万', delta: 14.6, trend: [60, 70, 88, 102, 118, 130, 142], color: 'var(--viz-3, oklch(74% 0.16 80))' },
  { label: '分销', value: '1,420', suffix: ' 万', delta: -4.1, trend: [80, 76, 72, 68, 64, 60, 58], color: 'var(--viz-4, oklch(64% 0.18 30))' },
];

const lastSelected = ref<string>('');

function onSelect(p: { index: number; item: { label: string; value: string | number } }) {
  lastSelected.value = `${p.item.label} (#${p.index + 1}) → ${p.item.value}`;
  toast({ type: 'info', message: `点击 ${p.item.label}` });
}
</script>
<template>
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; max-width: 640px;">
    <CfMetricCard
      label="本月营收"
      value="12,060"
      suffix=" 万"
      :delta="6.4"
      :trend="[820, 880, 920, 1020, 1140, 1180, 1206]"
      hint="点 chevron 展开,点行下钻"
      :series="channels"
      default-expanded
      @series-select="onSelect"
    />
    <CfMetricCard
      label="DAU"
      value="318"
      suffix="K"
      :delta="3.2"
      :trend="[260, 268, 275, 282, 296, 308, 318]"
      hint="无 series 时不渲染 chevron"
    />
  </div>
  <p style="margin-top: 8px; font-size: 12px;">
    <CfTag tone="info" size="sm">点击渠道</CfTag>
    {{ lastSelected || '尚未点击' }}
  </p>
</template>
import { CfMetricCard } from '@chufix-design/react';

export default function Demo() {
  const channels = [
    { label: '官网', value: '4,820', suffix: ' 万', delta: 8.2, trend: [120, 140, 158, 162, 180, 200, 220], color: 'var(--viz-1, oklch(64% 0.16 263))' },
    { label: '门店', value: '3,140', suffix: ' 万', delta: 2.4, trend: [80, 86, 92, 96, 100, 108, 112], color: 'var(--viz-2, oklch(70% 0.13 175))' },
    { label: 'App', value: '2,680', suffix: ' 万', delta: 14.6, trend: [60, 70, 88, 102, 118, 130, 142], color: 'var(--viz-3, oklch(74% 0.16 80))' },
    { label: '分销', value: '1,420', suffix: ' 万', delta: -4.1, trend: [80, 76, 72, 68, 64, 60, 58], color: 'var(--viz-4, oklch(64% 0.18 30))' },
  ];
  return (
    <>
      <CfMetricCard label="Revenue" value="12,060" delta={6.4} series={channels} />
    </>
  );
}
import { CfMetricCard } from '@chufix-design/react';

export default function Demo() {
  const channels = [
    { label: '官网', value: '4,820', suffix: ' 万', delta: 8.2, trend: [120, 140, 158, 162, 180, 200, 220], color: 'var(--viz-1, oklch(64% 0.16 263))' },
    { label: '门店', value: '3,140', suffix: ' 万', delta: 2.4, trend: [80, 86, 92, 96, 100, 108, 112], color: 'var(--viz-2, oklch(70% 0.13 175))' },
    { label: 'App', value: '2,680', suffix: ' 万', delta: 14.6, trend: [60, 70, 88, 102, 118, 130, 142], color: 'var(--viz-3, oklch(74% 0.16 80))' },
    { label: '分销', value: '1,420', suffix: ' 万', delta: -4.1, trend: [80, 76, 72, 68, 64, 60, 58], color: 'var(--viz-4, oklch(64% 0.18 30))' },
  ];
  return (
    <>
      <CfMetricCard label="Revenue" value="12,060" delta={6.4} series={channels} />
    </>
  );
}

API

PropTypeDefaultDescription
labelstringSmall top label
valuestring | numberMain value
unitstringUnit (after the value)
deltanumberPercentage change; tinted by sign
trendnumber[]Embedded sparkline data
seriesMetricSeriesItem[]Items rendered when the card is expanded
expandablebooleantrueShow the chevron when series is present
defaultExpandedbooleanfalseInitial expanded state (uncontrolled)
expandedbooleanControlled expanded flag; pair with onExpandedChange

反馈与讨论

MetricCard · Discussion

0
0 / 600
正在加载评论...