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.
背景 视口
12,420
3,180
6.2%
<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.
背景 视口
12,420
3,180
6.2%
3:42
0.18%
<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.
背景 视口
12,060 万
点 chevron 展开,点行下钻
318K
无 series 时不渲染 chevron
点击渠道 尚未点击
<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
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Small top label |
value | string | number | — | Main value |
unit | string | — | Unit (after the value) |
delta | number | — | Percentage change; tinted by sign |
trend | number[] | — | Embedded sparkline data |
series | MetricSeriesItem[] | — | Items rendered when the card is expanded |
expandable | boolean | true | Show the chevron when series is present |
defaultExpanded | boolean | false | Initial expanded state (uncontrolled) |
expanded | boolean | — | Controlled expanded flag; pair with onExpandedChange |
反馈与讨论
MetricCard · Discussion