DateField 日期字段
按年、月、日逐段输入日期,适合已经知道目标日期、无需浏览日历的场景。
用法
输入日期
组件结构
加粗的是必需部件。
data-scope="date-field":root · label · control · segment-group · segment · clear-trigger · hidden-input
示例
地区格式
根据 locale 调整日期顺序
日期范围
限制可输入日期
状态
禁用、只读与校验失败
变体
设置输入框外观
日期与时间
输入精确到分钟的日期
设计指引
何时使用
- 用户已知确切日期,例如生日或证件有效期。
- 需要使用键盘快速逐段输入。
何时不用
特性
locale决定日期段的顺序和分隔方式。min与max限制可输入范围。granularity支持日期或精确到分钟的日期时间。year + week段集使用 ISO 周历,固定周一到周日,不随显示语言改变。- 标准组合包含标签、输入框、日期段和隐藏表单输入;支持受控值与原生表单提交。
- 聚焦只强调正在编辑的日期段,错误段使用独立的危险色反馈。
- 清空按钮默认收起,输入任一段后出现;点按后回到第一段,聚焦边界平滑过渡。
组合
- 日期选择器与日期范围选择器的输入区就是这一套逐段输入,只是多了日历浮层。
- 日期与时间分开录入时与时间字段并排;只用一个字段时通过
granularity精确到分钟。 - 在表单中以 ISO 日期字符串参与校验与提交。
最佳实践
- 使用清晰的字段标签。
- 给参与表单提交的字段设置
name,并渲染隐藏输入部件。 - 有业务范围限制时设置
min与max。 - 对外统一使用 ISO 日期字符串。
反模式
- 使用普通文本输入接收日期并自行解析。
API 参考
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-date-field> |
| Vue 组件 | XhDateFieldClearTrigger XhDateFieldControl XhDateFieldHiddenInput XhDateFieldLabel XhDateFieldRoot XhDateFieldSegment XhDateFieldSegmentGroup |
| 组合式函数 | useDateField |
| 状态机 | dateFieldMachine |
| 皮肤 | @xihan-ui/styles/date-field.css |
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
value | string | null | 受控值,ISO 串('2026-07-28' / '2026-07-28T13:45');null 表示空。提供即受控。 | |
defaultValue | string | null | 非受控初值,同样是 ISO 串。 | |
min | string | 下界,ISO 串。参与各段区间的收窄,并决定 outOfRange。 | |
max | string | 上界,ISO 串。 | |
locale | string | BCP 47 语言标记,决定年月日三段的先后。未提供时按宿主语言,宿主也没有时按 en-US(月日年)排列。 | |
timeZone | string | IANA 时区名,只用于取今天:空段上按上下键时从今天的对应位起步。 | |
granularity | DateGranularity | 精度,默认 day(只有年月日三段)。提供 segments 时它不再生效。 | |
segments | DateSegmentSet | 段集:该控件由哪几段组成,提供后以它为准,granularity 让位。写 ['year', 'quarter'] 得到「2026 Q2」、['year', 'week'] 得到「2026 33」。归一后为空(如 [])视同未提供。 值仍是 ISO 日期(时间)串,因此段集中必须有 year,否则段位可编辑但无法拼出值。 | |
disabled | boolean | ||
readOnly | boolean | ||
invalid | boolean | ||
required | boolean | ||
name | string | 表单字段名;提供后隐藏输入才带 name,ISO 串随表单一并提交。 | |
placeholder | { readonly [K in DateSegmentType]?: string } | 各段未填时显示的占位串,逐段覆盖内置默认(yyyy / mm / dd / hh / mm / ss)。 | |
translations | DateFieldTranslations | 各段的读屏名字,逐段覆盖内置默认。段是 spinbutton,没有名字时读屏只能朗读一串数字。 | |
variant | ControlVariant | 形态:outline / subtle / ghost,决定底色与描边的绘制方式。默认 outline。 | |
tone | Tone | 语气:brand / neutral / success / warning / danger / info,决定聚焦与强调使用哪族颜色。 | |
size | Size | 尺寸:sm / md / lg。 | |
onValueChange | (details: DateFieldValueChangeDetails) => void |
事件
自定义元素将载荷放在 detail;Vue 使用同名 emit。
| 事件 | 载荷 | 说明 |
|---|---|---|
value-change | DateFieldValueChangeDetails | 值变化;detail 为 { value: string | null } |
插槽
仅列出带载荷的插槽。
| Vue 组件 | 插槽 | 载荷 | 说明 |
|---|---|---|---|
XhDateFieldRoot | default | DateFieldRootSlotProps | |
XhDateFieldSegment | default | DateFieldSegmentSlotProps |
React 适配器 props
只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。
| React 组件 | 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
XhDateFieldRoot | children | SlotChildren<DateFieldRootSlotProps> | ||
XhDateFieldSegment | index | number | string | 下标由作者声明,对应哪一段由 locale 与段集计算;兼收字符串。 | |
XhDateFieldSegment | segment | DateSegmentType | 按段名声明该格。段集中没有该段时它收起;与 index 二选一,两个都写时按段名计算。 | |
XhDateFieldSegment | children | SlotChildren<DateFieldSegmentSlotProps> |
状态
以下名称仅用于内部状态机。
状态:idle
事件:VALUE.SET · VALUE.CLEAR · SEGMENT.STEP · SEGMENT.TYPE · SEGMENT.CLEAR · SEGMENT.PERIOD · SEGMENT.FOCUS · SEGMENT.BLUR · FORM.RESET · PRESS.START · PRESS.END
判据:canEdit · canPress
connect API
getXxxProps() 返回对应部件的宿主属性。
| 成员 | 类型 | 说明 |
|---|---|---|
value | string | null | ISO 串;段位未填齐时为 null。 |
valueAsDate | Date | null | 同一个值的原生 Date;空值或无法计算时为 null。按 timeZone 换算。 |
segments | DateFieldSegmentState[] | 逐段投影,文档序即当前的段序(提供 segments 时是其归一后的顺序,否则由 locale 排列)。 |
complete | boolean | 段位已填齐(value 非 null)。 |
empty | boolean | 没有任何段已填。 |
outOfRange | boolean | 已填齐但落在 min / max 之外。 |
disabled | boolean | |
readOnly | boolean | |
invalid | boolean | |
focusedSegment | DateSegmentType | null | 焦点所在的段;焦点在组外时为 null。 |
locale | string | |
granularity | DateGranularity | |
setValue | (next: string | null) => void | 直接写整份值;传 null 等于清空。 |
clear | () => void | 清空全部段位;disabled / readOnly 下不生效。 |
canClear | boolean | 清空按钮当前是否可用:有段已填值、且可编辑。 |
getRootProps | () => T['element'] | |
getLabelProps | () => T['element'] | 标题不是原生 label(段位是 div,不可被 label 标注),点击它由连接层代为把焦点送进首段。 |
getControlProps | () => T['element'] | role=group 的分段容器。 |
getSegmentGroupProps | () => T['element'] | 段位与分隔符的外壳:占满盒内剩余宽度,把清空按钮推到框内末端。 |
segmentOf | (props: DateFieldSegmentProps) => DateFieldSegmentState | undefined | 作者的声明落在哪一段上;段集中没有该段(或下标越界)时缺席。文字由适配器按它渲染。 |
getSegmentProps | (props: DateFieldSegmentProps) => T['element'] | |
getClearTriggerProps | () => T['button'] | 清空按钮:不占 Tab 位,无值或不可编辑时收起;点击后焦点回到首段。 |
getHiddenInputProps | () => T['input'] | 表单出口:一份 type=hidden 的原生输入,值是 ISO 串。 |
无障碍
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
ArrowUp | focus in a segment, not disabled/readOnly | 本段加一,到区间上界回绕到下界;空段则落到今天的对应位 |
ArrowDown | focus in a segment, not disabled/readOnly | 本段减一,到区间下界回绕到上界;空段则落到今天的对应位 |
ArrowRight | focus in a segment, not disabled | 焦点移到下一段(跳过收起的段);已在末段则不动,不回绕 |
ArrowLeft | focus in a segment, not disabled | 焦点移到上一段;已在首段则不动,不回绕 |
Home | focus in a segment, not disabled | 焦点移到首段 |
End | focus in a segment, not disabled | 焦点移到末段 |
Backspace | focus in a segment, not disabled/readOnly | 清掉本段,焦点不动;整份值随之变成 null |
0 / 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 | focus in a segment, not disabled/readOnly | 往本段补一位数字;补满(再补一位必溢出或位数用尽)即自动跳下一段。上下午段没有数字位,不收数字 |
Enter / Space | held in clear-trigger, 填了哪怕一段, not disabled/readOnly | 按住期间清空按钮投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,段位清空后按钮藏起一并撤下。清空按钮不占 Tab 位,键盘这一路只在焦点落到它身上时有面 |
a / p | focus in 上下午段, not disabled/readOnly | 直接指定上午 / 下午;上下键在两者之间翻面 |
ARIA
以下属性由 connect 生成。
| 部件 | 属性 | 值 |
|---|---|---|
control | aria-disabled | 'true' | 'false' |
control | aria-labelledby | label 部件的 id |
control | role | 'group' |
segment | aria-disabled | undefined | 'true' | 'false' |
segment | aria-invalid | undefined | 'true' | 'false' |
segment | aria-label | item?.label |
segment | aria-readonly | undefined | 'true' | 'false' |
segment | aria-required | undefined | 'true' | 'false' |
segment | aria-valuemax | undefined | String(item.max) |
segment | aria-valuemin | undefined | String(item.min) |
segment | aria-valuenow | undefined | String(item.value) |
segment | aria-valuetext | item?.text |
segment | role | undefined | 'spinbutton' |
clear-trigger | aria-label | props.translations.clearTrigger |
样式参考
皮肤
@xihan-ui/styles/date-field.css 使用 [data-scope="date-field"][data-part="root"] 部件选择器,位于 xihan.components 层。覆盖样式使用 xihan.overrides。
forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。
数据属性
由 connect 生成;条件不成立时不输出无值属性。
| 部件 | 属性 | 值 |
|---|---|---|
root | data-complete | ''(条件成立时才出现) |
root | data-disabled | ''(条件成立时才出现) |
root | data-empty | ''(条件成立时才出现) |
root | data-invalid | ''(条件成立时才出现) |
root | data-out-of-range | ''(条件成立时才出现) |
root | data-readonly | ''(条件成立时才出现) |
root | data-size | props.size |
root | data-tone | props.tone |
root | data-variant | props.variant |
label | data-disabled | ''(条件成立时才出现) |
control | data-disabled | ''(条件成立时才出现) |
control | data-invalid | ''(条件成立时才出现) |
control | data-readonly | ''(条件成立时才出现) |
control | data-variant | props.variant |
control | data-xh-field-chrome | '' |
control | data-xh-field-size | props.size |
segment-group | data-disabled | ''(条件成立时才出现) |
segment-group | data-invalid | ''(条件成立时才出现) |
segment-group | data-readonly | ''(条件成立时才出现) |
segment | data-disabled | ''(条件成立时才出现) |
segment | data-focus | ''(条件成立时才出现) |
segment | data-index | String(index) | undefined |
segment | data-invalid | ''(条件成立时才出现) |
segment | data-placeholder | ''(条件成立时才出现) |
segment | data-readonly | ''(条件成立时才出现) |
segment | data-segment | item?.type |
clear-trigger | data-pressed | ''(条件成立时才出现) |
clear-trigger | data-xh-action-control | '' |
clear-trigger | data-xh-action-display | 'has-value' |
clear-trigger | data-xh-action-has-value | ''(条件成立时才出现) |
clear-trigger | data-xh-action-profile | 'field-inset' |
clear-trigger | data-xh-action-size | props.size |
clear-trigger | data-xh-action-variant | 'ghost' |
CSS 变量
本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。
| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
|---|---|---|---|---|---|
--xh-date-field-action-bg | clear-trigger | --xh-ink-surfacebackground-color | defaultxh-ink-surface | --xh-_action-variant-bg-rest | date-field 的 clear-trigger 部件 --xh-ink-surface、background-color 覆盖槽。 |
--xh-date-field-action-bg-active | clear-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | date-field 的 clear-trigger 部件 background-color 覆盖槽。 |
--xh-date-field-action-bg-hover | clear-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | date-field 的 clear-trigger 部件 background-color 覆盖槽。 |
--xh-date-field-action-fg | clear-trigger | color | default | --xh-fg-muted | date-field 的 clear-trigger 部件 color 覆盖槽。 |
--xh-date-field-action-fg-hover | clear-trigger | color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-fg-default | date-field 的 clear-trigger 部件 color 覆盖槽。 |
--xh-date-field-action-font-size | clear-trigger | font-size | default | --xh-text-secondary-size | date-field 的 clear-trigger 部件 font-size 覆盖槽。 |
--xh-date-field-action-radius | clear-trigger | border-radius | default | --xh-shape-inset | date-field 的 clear-trigger 部件 border-radius 覆盖槽。 |
--xh-date-field-action-size | clear-trigger | block-sizeinline-sizemin-inline-size | defaultxh-action-profile=field-inset | --xh-_action-profile-visual-size | date-field 的 clear-trigger 部件 block-size、inline-size、min-inline-size 覆盖槽。 |
--xh-date-field-control-bg | control | background-color | xh-field-chrome | --xh-_field-variant-bg-rest | date-field 的 control 部件 background-color 覆盖槽。 |
--xh-date-field-control-bg-disabled | control | background-color | disabledxh-field-chrome | --xh-_field-variant-bg-disabled | date-field 的 control 部件 background-color 覆盖槽。 |
--xh-date-field-control-bg-hover | control | background-color | disabledhoverinvalidloadingnot([data-disabled])not([data-invalid])not([data-loading])not([data-readonly])readonlyxh-field-chrome | --xh-_field-variant-bg-hover | date-field 的 control 部件 background-color 覆盖槽。 |
--xh-date-field-control-bg-readonly | control | background-color | readonlyxh-field-chrome | --xh-_field-variant-bg-read-only | date-field 的 control 部件 background-color 覆盖槽。 |
--xh-date-field-control-border | control | border | xh-field-chrome | --xh-_field-variant-border-rest | date-field 的 control 部件 border 覆盖槽。 |
--xh-date-field-control-border-focus | control | border-color | disabledfocus-withinnot([data-disabled])xh-field-chrome | --xh-_field-variant-border-focus | date-field 的 control 部件 border-color 覆盖槽。 |
--xh-date-field-control-border-hover | control | border-color | disabledhoverinvalidloadingnot([data-disabled])not([data-invalid])not([data-loading])not([data-readonly])readonlyxh-field-chrome | --xh-_field-variant-border-hover | date-field 的 control 部件 border-color 覆盖槽。 |
--xh-date-field-control-border-invalid | control | border-color | invalidxh-field-chrome | --xh-_field-variant-border-invalid | date-field 的 control 部件 border-color 覆盖槽。 |
--xh-date-field-control-fg | control | color | xh-field-chrome | --xh-fg-default | date-field 的 control 部件 color 覆盖槽。 |
--xh-date-field-control-gap | control | gap | xh-field-chrome | --xh-_date-field-gap | date-field 的 control 部件 gap 覆盖槽。 |
--xh-date-field-control-h | control | block-sizemin-block-size | has([data-xh-field-input][data-xh-field-layout='multi-tag'])has([data-xh-field-input][data-xh-field-layout='single-line'])has([data-xh-field-input][data-xh-field-layout='textarea'])xh-field-chromexh-field-inputxh-field-layout=multi-tagxh-field-layout=single-linexh-field-layout=textarea | --xh-_date-field-control-h | date-field 的 control 部件 block-size、min-block-size 覆盖槽。 |
--xh-date-field-control-min-w | controlroot | min-inline-size | defaultxh-field-chrome | --xh-control-min-w | date-field 的 control、root 部件 min-inline-size 覆盖槽。 |
--xh-date-field-control-px | control | padding-inline | xh-field-chrome | --xh-_date-field-control-px | date-field 的 control 部件 padding-inline 覆盖槽。 |
--xh-date-field-control-radius | control | border-radius | xh-field-chrome | --xh-shape-control | date-field 的 control 部件 border-radius 覆盖槽。 |
--xh-date-field-control-shadow | control | box-shadow | xh-field-chrome | none | date-field 的 control 部件 box-shadow 覆盖槽。 |
--xh-date-field-control-w | root | inline-sizemin-inline-size | default | --xh-control-w | date-field 的 root 部件 inline-size、min-inline-size 覆盖槽。 |
--xh-date-field-font-size | control | font-size | default | --xh-_date-field-font-size | date-field 的 control 部件 font-size 覆盖槽。 |
--xh-date-field-gap | root | gap | default | --xh-space-1 | date-field 的 root 部件 gap 覆盖槽。 |
--xh-date-field-icon-size | controlroot | --xh-icon-size | defaultsize=lgsize=smxh-field-chrome | --xh-_field-size-glyph-size--xh-glyph-size-lg--xh-glyph-size-md--xh-glyph-size-sm | date-field 的 control、root 部件 --xh-icon-size 覆盖槽。 |
--xh-date-field-label-fg | label | color | default | --xh-fg-default | date-field 的 label 部件 color 覆盖槽。 |
--xh-date-field-label-fg-disabled | label | color | disabled | --xh-fg-subtle | date-field 的 label 部件 color 覆盖槽。 |
--xh-date-field-label-font-size | label | font-size | default | --xh-text-label-size | date-field 的 label 部件 font-size 覆盖槽。 |
--xh-date-field-label-font-weight | label | font-weight | default | --xh-text-label-weight | date-field 的 label 部件 font-weight 覆盖槽。 |
--xh-date-field-literal-fg | segment-group | color | not([data-scope]) | --xh-fg-subtle | date-field 的 segment-group 部件 color 覆盖槽。 |
--xh-date-field-placeholder-fg | segment | color | placeholder | --xh-fg-subtle | date-field 的 segment 部件 color 覆盖槽。 |
--xh-date-field-segment-bg-focus | segment | background | focusfocus-visible | --xh-_date-field-segment-bg | date-field 的 segment 部件 background 覆盖槽。 |
--xh-date-field-segment-bg-invalid-focus | segment | background | focusinvalidis([data-focus], :focus-visible) | --xh-bg-subtle | date-field 的 segment 部件 background 覆盖槽。 |
--xh-date-field-segment-fg-focus | segment | color | focusfocus-visibleplaceholder | --xh-_date-field-segment-fg | date-field 的 segment 部件 color 覆盖槽。 |
--xh-date-field-segment-fg-invalid | segment | color | invalid | --xh-fg-danger | date-field 的 segment 部件 color 覆盖槽。 |
--xh-date-field-segment-fg-invalid-focus | segment | color | focusinvalidis([data-focus], :focus-visible) | --xh-fg-danger | date-field 的 segment 部件 color 覆盖槽。 |
--xh-date-field-segment-px | segment | padding-inline | default | --xh-space-0_5 | date-field 的 segment 部件 padding-inline 覆盖槽。 |
--xh-date-field-segment-py | segment | padding-block | default | --xh-space-0 | date-field 的 segment 部件 padding-block 覆盖槽。 |
--xh-date-field-segment-radius | segment | border-radius | default | --xh-shape-inset | date-field 的 segment 部件 border-radius 覆盖槽。 |
动效
动效角色:按压 · 状态(见动效规范)。
本组件皮肤不含过渡与关键帧,也没有脚本驱动的动效:状态一变,外观立即到位。
RTL
皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。
