From 638f6237502278b180d3ad9f3b14684eccde3398 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Mon, 26 Oct 2026 16:00:40 +0800 Subject: [PATCH] =?UTF-8?q?iter(g158):=20=E6=97=A5=E6=9C=9F=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E6=94=AF=E6=8C=81=E5=BF=AB=E6=8D=B7=E5=8C=BA=E9=97=B4?= =?UTF-8?q?=E9=A2=84=E8=AE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/index.json | 10 ++++++++-- apps/demo/main.ts | 4 ++-- llms-full.txt | 4 +++- packages/skin/ui/src/datePicker.ts | 21 ++++++++++++++++++++- packages/skin/ui/src/types.ts | 9 +++++++++ packages/skin/ui/ua.manifest.json | 8 +++++++- packages/skin/ui/ui.css | 24 ++++++++++++++++++++++++ 7 files changed, 73 insertions(+), 7 deletions(-) diff --git a/.ua/index.json b/.ua/index.json index 9eb0771..5a60844 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T07:54:36.201Z", + "generatedAt": "2026-10-26T08:00:04.262Z", "repoRoot": ".", "packages": [ { @@ -2689,6 +2689,11 @@ "t": "boolean | Signal", "d": "禁用触发器" }, + { + "n": "presets", + "t": "DatePreset[]", + "d": "快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起" + }, { "n": "onChange", "t": "(date: string) => void", @@ -2703,7 +2708,8 @@ "min / max 为闭区间,越界日期置灰不可点也不可键盘选中", "value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈)", "选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条", - "区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号" + "区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号", + "传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange" ] }, { diff --git a/apps/demo/main.ts b/apps/demo/main.ts index 5c48d66..a78f2a1 100644 --- a/apps/demo/main.ts +++ b/apps/demo/main.ts @@ -995,13 +995,13 @@ const content = h( section( 'sec-datepicker', '日期选择', - 'datePicker:单月网格,翻月 / 今天 / ←→↑↓ 逐日逐周 / PageUp↓ 逐月,Esc 收起;min/max 闭区间置灰越界日。', + 'datePicker:单月网格,翻月 / 今天 / ←→↑↓ 逐日逐周 / PageUp↓ 逐月,Esc 收起;min/max 闭区间置灰越界日;区间模式可配 presets 快捷按钮。', h( 'div', { class: 'row' }, datePicker({ value: dateVal, onChange: (d) => toast.success(`已选 ${d}`) }), datePicker({ placeholder: '只能选 10 月下旬', min: '2026-10-20', max: '2026-10-31' }), - datePicker({ range: true, value: rangeFrom, end: rangeTo, onRangeChange: (a, b) => toast.success(`区间 ${a} ~ ${b}`) }), + datePicker({ range: true, value: rangeFrom, end: rangeTo, presets: [{ label: '最近 7 天', days: 7 }, { label: '最近 30 天', days: 30 }, { label: '10 月上旬', from: '2026-10-01', to: '2026-10-15' }], onRangeChange: (a, b) => toast.success(`区间 ${a} ~ ${b}`) }), h('span', { class: 'out' }, () => (dateVal() ? `当前日期:${dateVal()}` : '未选择')), h('span', { class: 'out' }, () => (rangeFrom() ? `区间:${rangeFrom()} ~ ${rangeTo() || '待定'}` : '区间未选择')), ), diff --git a/llms-full.txt b/llms-full.txt index 4715bf8..1dd9cd0 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T07:54:36.201Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T08:00:04.262Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -1406,6 +1406,7 @@ autoComplete({ value: acValue, placeholder: '输入组件名', options: [{ value - min:string —— 最早可选日期(含,YYYY-MM-DD);更早的日置灰不可点 - max:string —— 最晚可选日期(含,YYYY-MM-DD);更晚的日置灰不可点 - disabled:boolean | Signal —— 禁用触发器 +- presets:DatePreset[] —— 快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起 - onChange:(date: string) => void —— 选中回调(点击日期或 Enter) 示例: @@ -1423,6 +1424,7 @@ datePicker({ value: dateVal, min: '2026-10-20', max: '2026-10-31', onChange: (d) 5. value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈) 6. 选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条 7. 区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号 +8. 传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange 现场演示:apps/docs/demo.html#sec-datepicker diff --git a/packages/skin/ui/src/datePicker.ts b/packages/skin/ui/src/datePicker.ts index b7c6390..63f529a 100644 --- a/packages/skin/ui/src/datePicker.ts +++ b/packages/skin/ui/src/datePicker.ts @@ -2,7 +2,7 @@ import { effect, h, signal } from '@ui-arch/frame'; import type { Signal } from '@ui-arch/frame'; import { bindOutside, pushOverlay } from './overlay.js'; import { flip } from './position.js'; -import type { DatePickerProps } from './types.js'; +import type { DatePickerProps, DatePreset } from './types.js'; import { fmt, pad, parse, shiftDay, today } from './dateKit.js'; /*** 日期选择:单月网格 + 上下月翻页 + 今天快捷键,value 信号双向绑定,方向键/PageUp/PageDown 可导航 ***/ @@ -26,10 +26,16 @@ export function datePicker(props: DatePickerProps): HTMLElement { label, h('button', { type: 'button', class: 'ua-datepicker-nav', 'aria-label': '下一月', onClick: () => turn(1) }, '›'), ); + const presetRow = props.range && props.presets?.length // 快捷区间只在区间模式有意义 + ? h('div', { class: 'ua-datepicker-presets' }, ...props.presets.map((p) => h('button', { + type: 'button', class: 'ua-datepicker-preset', onClick: () => applyPreset(p), + }, p.label))) + : null; const pop = h( 'div', { class: () => `ua-datepicker-pop${open() ? ' ua-open' : ''}${place() === 'top' ? ' is-flip' : ''}` }, head, + ...(presetRow ? [presetRow] : []), h('div', { class: 'ua-datepicker-week' }, ...['日', '一', '二', '三', '四', '五', '六'].map((w) => h('span', {}, w))), grid, h('div', { class: 'ua-datepicker-foot' }, h('button', { type: 'button', class: 'ua-datepicker-today', onClick: () => commit(fmt(...today())) }, '今天')), @@ -65,6 +71,19 @@ export function datePicker(props: DatePickerProps): HTMLElement { trigger.focus(); } + /*** 应用快捷区间:days 优先(今天往前共 N 天),否则用绝对 from/to;按 min/max 钳制后写值并收起 ***/ + function applyPreset(p: DatePreset): void { + const stop = p.days ? fmt(...today()) : (p.to || fmt(...today())); + const start = p.days ? shiftDay(stop, -(p.days - 1)) : (p.from || stop); + const [lo, hi] = start <= stop ? [start, stop] : [stop, start]; // 防御:from 晚于 to 时自动交换 + const fit = (v: string): string => (props.min && v < props.min ? props.min : props.max && v > props.max ? props.max : v); // 钳进合法区间 + value(fit(lo)); + end(fit(hi)); + props.onRangeChange?.(value(), end()); + open(false); + trigger.focus(); + } + /*** 是否越界:min / max 为闭区间,缺省视为不限制 ***/ const outOfRange = (date: string): boolean => (props.min ? date < props.min : false) || (props.max ? date > props.max : false); diff --git a/packages/skin/ui/src/types.ts b/packages/skin/ui/src/types.ts index 1e713c2..f3c4029 100644 --- a/packages/skin/ui/src/types.ts +++ b/packages/skin/ui/src/types.ts @@ -597,6 +597,14 @@ export interface AutoCompleteProps { onSelect?: (option: AutoCompleteOption) => void; // 选中回调(点击或 Enter) } +/*** 日期快捷区间:days 与 from/to 二选一,days 优先 ***/ +export interface DatePreset { + label: string; // 按钮文案,如「最近 7 天」 + days?: number; // 相对今天的天数(含今天):7 表示今天往前共 7 天 + from?: string; // 绝对起始日(YYYY-MM-DD),未给 days 时使用 + to?: string; // 绝对结束日(YYYY-MM-DD),未给 days 时使用;缺省取今天 +} + /*** 日期选择入参 ***/ export interface DatePickerProps { value?: Signal; // 选中日期(YYYY-MM-DD);区间模式下是起始日 @@ -607,6 +615,7 @@ export interface DatePickerProps { min?: string; // 最早可选日期(含);早于此的日置灰 max?: string; // 最晚可选日期(含);晚于此的日置灰 disabled?: boolean | Signal; // 禁用触发器 + presets?: DatePreset[]; // 快捷区间按钮组(仅区间模式渲染);缺省不显示 onChange?: (date: string) => void; // 选中回调(点击或 Enter) } diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 1c1aed1..03393f5 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -2560,6 +2560,11 @@ "t": "boolean | Signal", "d": "禁用触发器" }, + { + "n": "presets", + "t": "DatePreset[]", + "d": "快捷区间按钮组(仅区间模式渲染):每项给 label + days(相对今天含今天),或 label + from/to 绝对日期;越界结果按 min/max 钳制,点击即写入 value/end 并收起" + }, { "n": "onChange", "t": "(date: string) => void", @@ -2574,7 +2579,8 @@ "min / max 为闭区间,越界日期置灰不可点也不可键盘选中", "value 传信号即受控;Esc 收起(与弹窗/抽屉共用逐层关闭栈)", "选日期区间传 range: true:第一次点击只定起点(触发器显示「起 ~ …」),第二次点击补终点并收起,两端之间用低比重底色连成一条", - "区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号" + "区间回调走 onRangeChange(start, end),参数已排序——用户先点晚的日期也不会出现 start > end;只需检视当前值就同时读 value 与 end 两个信号", + "传 presets 在弹层顶部加一排快捷区间按钮:{ label: '最近 7 天', days: 7 } 走相对今天,{ label: '本月', from: '2026-10-01', to: '2026-10-31' } 走绝对日期(两者都给时 days 优先),点一下即写入并收起,回调照旧走 onRangeChange" ] }, { diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index 69b2853..ba41b0f 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -3242,6 +3242,29 @@ button.ua-step-dot:hover { border: none; cursor: pointer; } +.ua-datepicker-presets { + display: flex; + flex-wrap: wrap; + gap: var(--ua-space-1); + padding-bottom: var(--ua-space-1); + margin-bottom: var(--ua-space-1); + border-bottom: 1px solid var(--ua-color-border); +} +.ua-datepicker-preset { + padding: 2px 8px; + font-size: var(--ua-font-size-sm); + color: var(--ua-color-text-muted); + background: transparent; + border: 1px solid var(--ua-color-border); + border-radius: var(--ua-radius-sm); + cursor: pointer; + transition: color 0.15s, border-color 0.15s, background-color 0.15s; +} +.ua-datepicker-preset:hover { + color: var(--ua-color-accent); + border-color: var(--ua-color-accent); + background: color-mix(in srgb, var(--ua-color-accent) 8%, transparent); +} @keyframes ua-dp-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } @@ -3250,6 +3273,7 @@ button.ua-step-dot:hover { .ua-datepicker-pop.ua-open { animation: none; } .ua-datepicker-trigger, .ua-datepicker-day, + .ua-datepicker-preset, .ua-datepicker-nav { transition: none; } } .ua-backtop {