statistic — Statistic
The statistic component displays “key numbers”: daily active users, revenue, completion rate. Its core risk is not layout but precision: Number(12345678901234567890).toFixed(2) gives a wrong result in JS (exceeds the safe integer limit), and the rounding of (0.1).toFixed-style operations has floating-point traps as well. formatStatisticValue in core/statistic fills these traps once in the shared layer, shared by all three platforms.
When to use
Section titled “When to use”- Each platform implements
LhStatistic: dashboard numbers, report summaries, completion displays. - Any scenario that needs to format an arbitrary number with “fixed decimal places + thousands separator”; you can call
formatStatisticValuedirectly even without using the component. - Do not use it for: currency symbols, unit conversion, or timezone formatting — those are business-layer concerns; for date formatting use
core/shared/date.
Import
Section titled “Import”import { formatStatisticValue, getStatisticClassNames } from '@lh-ui/share/core/statistic'import type { LhStatisticState } from '@lh-ui/share/core/statistic'Design intent
Section titled “Design intent”The three precision defenses of formatStatisticValue:
- Invalid input does not throw: NaN / Infinity are returned as-is via
String(value); it does not attempt to format “something that is not a number”. - precision sanitization:
toFixed(101)throws a RangeError, so here it is first sanitized to an integer in[0, 100]bysafePrecisionfromshared/sanitize. This defense is shared withinput-number(see input-number). - Super-large integers go through a string path: when the integer part exceeds
Number.MAX_SAFE_INTEGER, there is no Number conversion anywhere in the process — rounding uses decimal string carry (the internalincrementDigits), and grouping uses string slicing (formatBigString). This is the only way to guarantee that99999999999999999999formats correctly.
Function table
Section titled “Function table”| Function | Signature | Returns | Purpose |
|---|---|---|---|
formatStatisticValue |
(value: number | string, state: Pick<LhStatisticState, 'precision' | 'separator'>) => string |
formatted string | precision + thousands separation |
getStatisticClassNames |
(prefixCls?: string) => string[] |
class name array | container class, prefix can be swapped |
Behavior details:
| Input | Output | Path |
|---|---|---|
1234567.891, { precision: 2, separator: ',' } |
'1,234,567.89' |
toFixed + grouping |
1234567.8, { precision: 0, separator: '' } |
'1234568' |
no separator |
'99999999999999999999', { precision: 0, separator: ',' } |
'99,999,999,999,999,999,999' |
string path |
NaN, any |
'NaN' |
returned as-is |
'abc', any |
'abc' |
Number('abc') is NaN |
The separator is only used for integer grouping; the decimal point is always . (if a platform needs a localized decimal point, replace it in the UI layer).
Type definitions
Section titled “Type definitions”export interface LhStatisticState { precision: number // decimal places (sanitized to [0, 100] via safePrecision) separator: string // thousands separator; an empty string means no grouping}Accessibility
Section titled “Accessibility”core/statistic does not output an aria attribute bundle. The accessibility essentials of a statistic live in the UI layer:
- The number itself can be read by screen readers as plain text, but the thousands-separator commas are announced as “comma” (e.g. “1,234,567” is read as “one comma two three four…”). You should provide an
aria-labelfor the whole numeric block, using a spoken formulation (either a locale-appropriate number reading or digit by digit). - Animations when the value changes (scrolling to the target value) are purely visual and carry no information; if the value is not readable during the animation, keep the old value’s aria-label until it settles.
Usage examples
Section titled “Usage examples”import { formatStatisticValue } from '@lh-ui/share/core/statistic'
formatStatisticValue(9876543.21, { precision: 1, separator: ' ' })// '9 876 543.2'
formatStatisticValue(0.1 + 0.2, { precision: 2, separator: '' })// '0.30' (instead of part of 0.30000000000000004)
formatStatisticValue('12345678901234567890', { precision: 0, separator: ',' })// '12,345,678,901,234,567,890'<!-- Vue3 --><div :class="getStatisticClassNames()"> <span v-if="prefix" class="lh-statistic__prefix">{{ prefix }}</span> <span class="lh-statistic__content"> <span class="lh-statistic__title">{{ title }}</span> <span class="lh-statistic__value" :aria-label="ariaLabel"> {{ formatStatisticValue(value, { precision, separator }) }} </span> </span> <span v-if="suffix" class="lh-statistic__suffix">{{ suffix }}</span></div>// Reactconst text = formatStatisticValue(value, { precision, separator })return ( <div className="lh-statistic"> <span className="lh-statistic__content"> <span className="lh-statistic__title">{title}</span> <span className="lh-statistic__value" aria-label={spoken}>{text}</span> </span> </div>)// uniapp: pure string output, no platform differences; each platform uses its own// scrolling solution for animationsStyles and tokens
Section titled “Styles and tokens”| Class | Semantic meaning |
|---|---|
.lh-statistic |
base class: layout |
.lh-statistic__title |
title (secondary text color, smaller font size) |
.lh-statistic__content |
content area (title + value stacked vertically) |
.lh-statistic__value |
value (primary text color, large font size, tabular numerals font-variant-numeric: tabular-nums) |
.lh-statistic__prefix / .lh-statistic__suffix |
prefix and suffix (e.g. ¥, %) |
The numeric font goes through the --lh-font-family-numeric token (if present), guaranteeing aligned digit widths.
- precision upper bound is 100: anything higher is clamped by
safePrecisionrather than throwing; a negative precision is normalized to 0. - Semantics of string input:
formatStatisticValue('123', ...)first triesNumber('123')and takes the numeric path if it succeeds; only'abc'is returned as-is. Super-large integer strings must be passed as strings — writing the numeric literal99999999999999999999in JS source already loses precision, and core cannot recover it. - Do not write your own thousands separation with
toLocaleStringin the UI layer: it is affected by the runtime locale and may differ across the three platforms; useformatStatisticValueuniformly. - The
prefixClsparameter ofgetStatisticClassNamesallows swapping the prefix (defaultlh-statistic), for scenarios on some platforms that need to coexist with existing class names.
@lh-ui/share/core/statistic → formatStatisticValue( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue