iter(g95): 统计数值支持倒计时模式

This commit is contained in:
HIsheR
2026-10-26 13:14:05 +08:00
parent 5e81cfe42b
commit 7e0612ef47
7 changed files with 57 additions and 12 deletions
+24 -3
View File
@@ -1,7 +1,17 @@
import { effect, h } from '@ui-arch/frame';
import { effect, h, signal } from '@ui-arch/frame';
import type { StatisticProps } from './types.js';
/*** 统计数值:标题 + 大号数字 + 前后缀 + 环比趋势,数值变化时 rAF 滚动过渡(400ms,easeOutCubic) ***/
/*** 剩余毫秒 → DD 天 HH:MM:SS(不足一天省略天数,保持等宽跳动不抖动) ***/
function fmtLeft(ms: number): string {
const total = Math.floor(ms / 1000);
const d = Math.floor(total / 86400);
const hh = String(Math.floor((total % 86400) / 3600)).padStart(2, '0');
const mm = String(Math.floor((total % 3600) / 60)).padStart(2, '0');
const ss = String(total % 60).padStart(2, '0');
return d > 0 ? `${d} 天 ${hh}:${mm}:${ss}` : `${hh}:${mm}:${ss}`;
}
/*** 统计数值:标题 + 大号数字 + 前后缀 + 环比趋势;数值滚动过渡,传 countdown 则改为每秒倒计时 ***/
export function statistic(props: StatisticProps): HTMLElement {
// —— 状态集中声明 ——
const precision = props.precision ?? 0; // 小数位
@@ -36,6 +46,17 @@ export function statistic(props: StatisticProps): HTMLElement {
props.trend ? h('span', { class: `ua-stat-trend is-${props.trend}` }, props.trend === 'up' ? '▲' : '▼') : null);
const view = h('div', { class: 'ua-stat' }, props.title ? h('div', { class: 'ua-stat-title' }, props.title) : null, row);
effect(() => roll(props.value())); // 信号变化即滚动到新值(首跑做入场动画)
const target = props.countdown;
if (target) {
const left = signal(Math.max(0, target - Date.now())); // 剩余毫秒
const timer = window.setInterval(() => {
const ms = Math.max(0, target - Date.now());
left(ms);
if (!ms || !view.isConnected) window.clearInterval(timer); // 到点或已卸载即停表,不留常驻定时器
}, 1000);
effect(() => { num.textContent = fmtLeft(left()); }); // 倒计时模式不走滚动动画,每秒直落
} else if (props.value) {
effect(() => roll(props.value!())); // 信号变化即滚动到新值(首跑做入场动画)
}
return view;
}
+2 -1
View File
@@ -419,7 +419,8 @@ export interface DescriptionsProps {
/*** 统计数值入参 ***/
export interface StatisticProps {
title?: string; // 指标名;缺省不渲染标题行
value: Signal<number>; // 数值信号:变化时滚动过渡到新值(首跑即入场动画)
value?: Signal<number>; // 数值信号:变化时滚动过渡到新值(首跑即入场动画);倒计时模式可不传
countdown?: number; // 目标时间戳(毫秒):传了即倒计时模式,每秒显示 DD 天 HH:MM:SS,忽略 value
precision?: number; // 小数位;缺省 0
prefix?: string; // 数字前缀(如 ¥);缺省不渲染
suffix?: string; // 数字后缀(如 元 / %);缺省不渲染