Skip to content

Statistic 统计数值

展示一个突出的数字:标题、数值、前后缀与精度,数值变化时可选滚动过渡。

基础用法

本月订单
1,234.5
总用户
1,234,567
账户余额
¥8,900.50
转化率
0.9%
vue
<SrStatistic title="本月订单" :value="1234.5" suffix="单" />
<SrStatistic title="总用户" :value="1234567" />
<SrStatistic title="账户余额" :value="8900.5" prefix="¥" :precision="2" />
<SrStatistic title="转化率" :value="0.8934" suffix="%" :precision="1" />

精度不传时从数值本身推断——传 3.14 就显示两位,不必再写一遍 :precision="2"。显式传入时才按它四舍五入。

前后缀是独立的元素,不拼进数值文本:拼进去的话它们会跟着数字一起变化、 一起参与等宽对齐,而 ¥% 通常需要更小的字号与更弱的颜色。

滚动过渡

累计访问量
8,848
大号展示
8,848
vue
<SrStatistic title="累计访问量" :value="8848" animated suffix="次" />

animated 开启后,数值变化时滚动过渡。

为什么默认关闭

若首次挂载就从 0 滚起,服务端渲染输出的终值与客户端首帧会对不上, 数字会闪一下。

需要进场滚动时,由宿主把初值设为 0、挂载后再改成目标值—— 这样动画统一由「值变化」触发,SSR 与首帧始终一致:

vue
<script setup>
import { onMounted, ref } from 'vue'

const count = ref(0)

onMounted(() => {
  count.value = 8848
})
</script>

<template>
  <SrStatistic title="累计访问量" :value="count" animated />
</template>

两个细节

最后一帧会显式写入目标值。 缓动到 1 时理论上恰好等于目标, 但浮点相乘未必——不显式对齐会留下 8847.999999 这样的尾数。

动画期间精度取自目标值并固定,不是每帧从插值结果推断—— 后者会让小数位数忽多忽少、文本宽度剧烈抖动。

尺寸与加载态

12,345
12,345
12,345
正常
1,234,567
加载中
1,234,567
vue
<SrStatistic size="sm" title="小" :value="12345" suffix="单" />
<SrStatistic size="lg" title="大" :value="12345" suffix="单" />
<SrStatistic title="加载中" :value="12345" loading />

加载态保留标题、只把数值换成灰块,而不是整块留白—— 数据到位前后位置稳定,不会发生跳动。灰块宽度用 em 相对当前字号, 随 size 自动缩放。

格式化

大数字分组
1,234,567,890
欧式分隔符
1.234.567,89
不分组
1234567
vue
<SrStatistic title="大数字分组" :value="1234567890" suffix="元" />
<SrStatistic :value="1234567.891" :precision="2" group-separator="." decimal-separator="," />
<SrStatistic :value="1234567" group-separator="" />

分隔符可自定义,刻意不用 toLocaleString:它按运行环境的区域设置 决定分隔符,同一份代码在不同用户的浏览器上会显示出不同结果。 这类展示格式通常由产品定义,不该随系统语言漂移。

与数字输入框共用同一套舍入

精度走的是 SrInputNumber 里同一个 roundTo。浮点舍入只有一套正确写法, 两处若各写各的,迟早出现「输入框里是 1.01、统计卡上显示 1.00」这种自相矛盾。

超大整数

value 允许传字符串,用于超出安全整数范围的值。

字符串(正确)
9,007,199,254,740,993
转成 number(错误)
9,007,199,254,740,990
vue
<!-- ✅ 末位仍是 3 -->
<SrStatistic :value="'9007199254740993'" />

<!-- ❌ Number() 一转,末位变成 2 -->
<SrStatistic :value="Number('9007199254740993')" />

看上面两个的差别:走字符串路径分组后末位仍是 3; 而经 Number() 一转就成了 9007199254740992,末位变成 2—— 丢一位且悄无声息

这类值本来是标识符(雪花号、订单号)而非可运算的量, 字符串走「原样分组」路径、完全不参与运算。只认 -?\d+(\.\d+)? 这种最简单的形态,科学计数法之类一律原样返回。

自定义格式化

缩写为万
284.74 万
vue
<script setup>
const toWan = (value) => (value / 10000).toFixed(2) + ' 万'
</script>

<template>
  <SrStatistic title="缩写为万" :value="2847392" :formatter="toWan" />
</template>

formatter 接管全部格式化,此时不再有滚动动画——任意函数无法插值, 硬要对中间结果反复调用只会让数字乱跳。

Props

名称类型默认值说明
titlestring标题,也可用 title 插槽
valuenumber | string0数值;字符串用于超大整数
precisionnumber自动推断小数位数
prefix / suffixstring前缀 / 后缀文本
groupSeparatorstring','千分位分隔符,空串表示不分组
decimalSeparatorstring'.'小数分隔符
formatter(value: number) => string自定义格式化,接管全部格式化且关闭动画
animatedbooleanfalse数值变化时滚动过渡
durationnumber600滚动时长(毫秒)
size'sm' | 'md' | 'lg''md'尺寸
loadingbooleanfalse是否显示加载骨架

插槽

名称说明
title自定义标题
prefix / suffix自定义前后缀(可放图标而非纯文本)

无障碍

  • 数字用了 font-variant-numeric: tabular-nums:这对统计组件不是锦上添花 而是必需品——动画每帧都在改数字,字形宽度若随内容变化 (1 明显比 8 窄),整个数字块会左右抽动
  • 动画在 prefers-reduced-motion直接落位、不做任何过渡; 加载骨架的微光同样停掉,但保留灰块(它是加载态唯一的可见信号, 藏了就什么都没有了)

自用组件库 · 源码分发 · 不发布 npm