From 7e0612ef47d7a9f54464983f9893831b51f5e37d Mon Sep 17 00:00:00 2001 From: HIsheR Date: Mon, 26 Oct 2026 13:14:05 +0800 Subject: [PATCH] =?UTF-8?q?iter(g95):=20=E7=BB=9F=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E5=80=BC=E6=94=AF=E6=8C=81=E5=80=92=E8=AE=A1=E6=97=B6=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/backlog.json | 8 ++++++++ .ua/index.json | 12 +++++++++--- apps/demo/main.ts | 3 ++- llms-full.txt | 6 ++++-- packages/skin/ui/src/statistic.ts | 27 ++++++++++++++++++++++++--- packages/skin/ui/src/types.ts | 3 ++- packages/skin/ui/ua.manifest.json | 10 ++++++++-- 7 files changed, 57 insertions(+), 12 deletions(-) diff --git a/.ua/backlog.json b/.ua/backlog.json index 036cf9c..acba882 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -1116,6 +1116,14 @@ "acceptance": "recipes.md 新增「配方 5:数据表 + 独立分页器 + 导出」,覆盖同一 page 信号双绑、exportable 导全量行与当页的区别、拖拽只对数字列宽生效、rowKey 记账;npm run docs 后 llms-full.txt 出现该配方;npm run check 全绿", "notes": "自补:文档完善;只写用法不碰实现,配方随 docs 注入 llms-full", "status": "done" + }, + { + "id": "g95", + "title": "统计数值支持倒计时模式", + "motivation": "活动剩余时间这类需求只能调用方自己 setInterval 改信号再塞进 statistic,还得自己管停表;组件本身只认一个数值信号,秒级刷新没有落脚点", + "acceptance": "StatisticProps 新增 countdown(目标时间戳毫秒),传了即每秒显示 DD 天 HH:MM:SS(不足一天省天数),value 转为可选;到点或组件被移除后自动 clearInterval,不留常驻定时器;倒计时不走滚动动画;文档与演示同步;npm run check 全绿", + "notes": "自补:功能补齐;停表条件放在 tick 内自检 isConnected,无需额外卸载钩子", + "status": "open" } ] } diff --git a/.ua/index.json b/.ua/index.json index 8d99832..dbb6bbc 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T05:12:11.001Z", + "generatedAt": "2026-10-26T05:13:47.565Z", "repoRoot": ".", "packages": [ { @@ -1953,7 +1953,12 @@ "n": "value", "t": "Signal", "r": 1, - "d": "数值信号:变化时从当前显示值滚动到新值,首跑即入场动画" + "d": "数值信号:变化时滚动过渡到新值;倒计时模式可不传" + }, + { + "n": "countdown", + "t": "number", + "d": "目标时间戳(毫秒):传了即倒计时模式,每秒显示 DD 天 HH:MM:SS,忽略 value" }, { "n": "precision", @@ -1984,7 +1989,8 @@ "precision 控制小数位,千分位由 toLocaleString 自动加,不要自己拼格式化", "trend 只表达方向与颜色(涨红跌绿),数值本身与环比幅度仍由调用方组织", "多条指标并排时用 descriptions 或 flex 容器排列,本组件不自带网格", - "用户开启「减弱动效」时组件直接落终值,无需调用方判断" + "用户开启「减弱动效」时组件直接落终值,无需调用方判断", + "做倒计时(活动剩余时间等)传 countdown: 目标时间戳,组件每秒刷新并在到点/卸载时自动停表" ] }, { diff --git a/apps/demo/main.ts b/apps/demo/main.ts index f91142b..6e13e3d 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -552,12 +552,13 @@ const content = h( section( 'sec-statistic', '统计数值', - 'statistic:大号等宽数字 + 前后缀 + 环比趋势;value 变化时 400ms 缓出滚动(减弱动效偏好下直落终值)。', + 'statistic:大号等宽数字 + 前后缀 + 环比趋势;value 变化时 400ms 缓出滚动(减弱动效偏好下直落终值);传 countdown 目标时间戳即变倒计时,到点自动停表。', h( 'div', { class: 'row' }, statistic({ title: '本月 GMV', value: gmv, prefix: '¥', precision: 2, trend: 'up' }), statistic({ title: '订单数', value: orders, suffix: '单', trend: 'down' }), + statistic({ title: '活动倒计时', countdown: Date.now() + 95 * 60 * 1000 }), ), h( 'div', diff --git a/llms-full.txt b/llms-full.txt index b9f467a..c32c79c 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T05:12:11.001Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T05:13:47.565Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -1052,7 +1052,8 @@ descriptions({ title: '订单信息', column: 2, bordered: true, items: [{ label 参数: - title:string —— 指标名(小字灰标题);缺省不渲染 -- value(必填):Signal —— 数值信号:变化时从当前显示值滚动到新值,首跑即入场动画 +- value(必填):Signal —— 数值信号:变化时滚动过渡到新值;倒计时模式可不传 +- countdown:number —— 目标时间戳(毫秒):传了即倒计时模式,每秒显示 DD 天 HH:MM:SS,忽略 value - precision(默认 0):number —— 小数位,千分位分隔自动带;缺省 0 - prefix:string —— 数字前缀(如 ¥);缺省不渲染 - suffix:string —— 数字后缀(如 元 / %);缺省不渲染 @@ -1072,6 +1073,7 @@ const gmv = signal(128000); statistic({ title: '本月 GMV', value: gmv, prefix: 4. trend 只表达方向与颜色(涨红跌绿),数值本身与环比幅度仍由调用方组织 5. 多条指标并排时用 descriptions 或 flex 容器排列,本组件不自带网格 6. 用户开启「减弱动效」时组件直接落终值,无需调用方判断 +7. 做倒计时(活动剩余时间等)传 countdown: 目标时间戳,组件每秒刷新并在到点/卸载时自动停表 现场演示:apps/docs/demo.html#sec-statistic diff --git a/packages/skin/ui/src/statistic.ts b/packages/skin/ui/src/statistic.ts index 2343c53..a153e84 100644 --- a/packages/skin/ui/src/statistic.ts +++ b/packages/skin/ui/src/statistic.ts @@ -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; } diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 0db69a3..c82793c 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -419,7 +419,8 @@ export interface DescriptionsProps { /*** 统计数值入参 ***/ export interface StatisticProps { title?: string; // 指标名;缺省不渲染标题行 - value: Signal; // 数值信号:变化时滚动过渡到新值(首跑即入场动画) + value?: Signal; // 数值信号:变化时滚动过渡到新值(首跑即入场动画);倒计时模式可不传 + countdown?: number; // 目标时间戳(毫秒):传了即倒计时模式,每秒显示 DD 天 HH:MM:SS,忽略 value precision?: number; // 小数位;缺省 0 prefix?: string; // 数字前缀(如 ¥);缺省不渲染 suffix?: string; // 数字后缀(如 元 / %);缺省不渲染 diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 6a00f6f..f965e70 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -1824,7 +1824,12 @@ "n": "value", "t": "Signal", "r": 1, - "d": "数值信号:变化时从当前显示值滚动到新值,首跑即入场动画" + "d": "数值信号:变化时滚动过渡到新值;倒计时模式可不传" + }, + { + "n": "countdown", + "t": "number", + "d": "目标时间戳(毫秒):传了即倒计时模式,每秒显示 DD 天 HH:MM:SS,忽略 value" }, { "n": "precision", @@ -1855,7 +1860,8 @@ "precision 控制小数位,千分位由 toLocaleString 自动加,不要自己拼格式化", "trend 只表达方向与颜色(涨红跌绿),数值本身与环比幅度仍由调用方组织", "多条指标并排时用 descriptions 或 flex 容器排列,本组件不自带网格", - "用户开启「减弱动效」时组件直接落终值,无需调用方判断" + "用户开启「减弱动效」时组件直接落终值,无需调用方判断", + "做倒计时(活动剩余时间等)传 countdown: 目标时间戳,组件每秒刷新并在到点/卸载时自动停表" ] }, {