Skip to content

日期选择器 DatePicker ​

赞助

输入或选择日期的控件

何时使用 ​

  • 需要输入或选择日期时
  • 需要输入或选择日期与时间时

基本使用 ​

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const basicValue = ref<number | null>(null)
const weekValue = ref<number | null>(null)
const monthValue = ref<number | null>(null)
const quarterValue = ref<number | null>(null)
const yearValue = ref<number | null>(null)
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="basicValue" placeholder="请选择日期" />
    <DatePicker v-model:value="weekValue" type="week" placeholder="请选择周" />
    <DatePicker v-model:value="monthValue" type="month" placeholder="请选择月份" />
    <DatePicker v-model:value="quarterValue" type="quarter" placeholder="请选择季度" />
    <DatePicker v-model:value="yearValue" type="year" placeholder="请选择年份" />
  </Space>
</template>

日期格式 ​

使用 format 定制展示格式


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { addDays, startOfMonth } from 'date-fns'
const fixedDate = new Date(2026, 9, 10).getTime()
const slashValue = ref<number | null>(fixedDate)
const chineseValue = ref<number | null>(fixedDate)
const monthFormatValue = ref<number | null>(startOfMonth(fixedDate).getTime())
const weekFormatValue = ref<number | null>(Date.now())
const rangeFormatValue = ref<[number, number] | null>([fixedDate, addDays(fixedDate, 5).getTime()])
const rangePlaceholder = ['开始日期', '结束日期']
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="slashValue" format="yyyy/MM/dd" placeholder="请选择日期" />
    <DatePicker v-model:value="chineseValue" format="yyyy年MM月dd日" placeholder="请选择日期" />
    <DatePicker v-model:value="monthFormatValue" type="month" format="yyyy年MM月" placeholder="请选择月份" />
    <!-- 周形态的周年占位符取 ISO 口径(RRRR),与周序号(II)配对才可解析 -->
    <DatePicker v-model:value="weekFormatValue" type="week" format="RRRR年第II周" placeholder="请选择周" />
    <DatePicker v-model:value="rangeFormatValue" type="daterange" format="yyyy/MM/dd" :placeholder="rangePlaceholder" />
  </Space>
</template>

范围选择器 ​

通过设置 type 属性,指定范围选择器类型


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const rangeValue = ref<[number, number] | null>(null)
const weekRangeValue = ref<[number, number] | null>(null)
const monthRangeValue = ref<[number, number] | null>(null)
const quarterRangeValue = ref<[number, number] | null>(null)
const yearRangeValue = ref<[number, number] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
// 其余范围形态的提示文字按形态区分(周 / 月 / 季 / 年各有独立文案)
const weekRangePlaceholder: [string, string] = ['开始周', '结束周']
const monthRangePlaceholder: [string, string] = ['开始月份', '结束月份']
const quarterRangePlaceholder: [string, string] = ['开始季度', '结束季度']
const yearRangePlaceholder: [string, string] = ['开始年份', '结束年份']
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="rangeValue" type="daterange" :placeholder="rangePlaceholder" />
    <DatePicker v-model:value="weekRangeValue" type="weekrange" :placeholder="weekRangePlaceholder" />
    <DatePicker v-model:value="monthRangeValue" type="monthrange" :placeholder="monthRangePlaceholder" />
    <DatePicker v-model:value="quarterRangeValue" type="quarterrange" :placeholder="quarterRangePlaceholder" />
    <DatePicker v-model:value="yearRangeValue" type="yearrange" :placeholder="yearRangePlaceholder" />
  </Space>
</template>

type="datetimerange" 在范围形态上叠加时间面板:两段共用同一个日期时间面板,点「确定」提交当前段并自动切到另一端,两段都确定后才收起;展开期间的选择只落在草稿上,关闭面板则丢弃草稿;配合 disabledTime 可让终点只选「晚于起点」的时分秒(日期整日越界由内核按激活段自动禁用)


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { getHours, getMinutes, getSeconds, isSameDay } from 'date-fns'
const dateTimeRangeValue = ref<[number, number] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
/** 半开区间 [start, end) 的整数序列 */
function range(start: number, end: number): number[] {
  return Array.from({ length: end - start }, (_, index) => index + start)
}
/**
 * 同一天内的时间约束:逐级判定,边界时刻本身视为越界
 *
 * @param boundary 约束边界的时刻(终点侧为起点、起点侧为终点)
 * @param timestamp 面板当前所编辑的时刻
 */
function disabledUnitsNotLaterThan(boundary: number, timestamp: number) {
  if (!isSameDay(timestamp, boundary)) {
    return {}
  }
  const hour = getHours(boundary)
  const minute = getMinutes(boundary)
  const second = getSeconds(boundary)
  return {
    disabledHours: () => range(0, hour),
    disabledMinutes: (currentHour: number) => (currentHour === hour ? range(0, minute) : []),
    disabledSeconds: (currentHour: number, currentMinute: number) =>
      currentHour === hour && currentMinute === minute ? range(0, second + 1) : []
  }
}
/** 同 disabledUnitsNotLaterThan,方向相反:禁用晚于边界时刻的时间单位 */
function disabledUnitsNotEarlierThan(boundary: number, timestamp: number) {
  if (!isSameDay(timestamp, boundary)) {
    return {}
  }
  const hour = getHours(boundary)
  const minute = getMinutes(boundary)
  const second = getSeconds(boundary)
  return {
    disabledHours: () => range(hour + 1, 24),
    disabledMinutes: (currentHour: number) => (currentHour === hour ? range(minute + 1, 60) : []),
    disabledSeconds: (currentHour: number, currentMinute: number) =>
      currentHour === hour && currentMinute === minute ? range(second + 1, 60) : []
  }
}
/** 终点侧:同一天只允许选择晚于起点时刻的时间 */
function disabledDateTimeRangeTime(timestamp: number, side: 'start' | 'end') {
  const rangeValue = dateTimeRangeValue.value
  const boundary = (side === 'end' ? rangeValue?.[0] : rangeValue?.[1]) ?? null
  if (boundary === null) {
    return {}
  }
  return side === 'end'
    ? disabledUnitsNotLaterThan(boundary, timestamp)
    : disabledUnitsNotEarlierThan(boundary, timestamp)
}
</script>
<template>
  <DatePicker
    v-model:value="dateTimeRangeValue"
    type="datetimerange"
    :disabled-time="disabledDateTimeRangeTime"
    :placeholder="rangePlaceholder"
  />
</template>

切换不同的选择器 ​

提供选择器,自由切换不同类型的日期选择器,常用于日期筛选场合;时间选择由 TimePicker 组件承担,故分段项不含「时间」项


日期
周
月份
季度
年份
Show Code
vue
<script setup lang="ts">
import { computed, ref } from 'vue'
type SwitchableType = 'date' | 'week' | 'month' | 'quarter' | 'year'
const switchableType = ref<SwitchableType>('date')
const switchableValue = ref<number | null>(null)
const switchableOptions: { label: string; value: SwitchableType }[] = [
  { label: '日期', value: 'date' },
  { label: '周', value: 'week' },
  { label: '月份', value: 'month' },
  { label: '季度', value: 'quarter' },
  { label: '年份', value: 'year' }
]
/** 切换选择器的提示文字:随形态切换,与各形态基本用例的提示保持一致 */
const switchablePlaceholder = computed(() => {
  const placeholderMap: Record<SwitchableType, string> = {
    date: '请选择日期',
    week: '请选择周',
    month: '请选择月份',
    quarter: '请选择季度',
    year: '请选择年份'
  }
  return placeholderMap[switchableType.value]
})
</script>
<template>
  <Space vertical>
    <Segmented v-model:value="switchableType" :options="switchableOptions" />
    <DatePicker v-model:value="switchableValue" :type="switchableType" :placeholder="switchablePlaceholder" />
  </Space>
</template>

日期时间选择 ​

type="datetime" 增加选择时间功能,时间面板选项经 timePickerProps 透传;展开期间的选择只落在草稿值上,点「确定」或「此刻」才提交,关闭面板则丢弃草稿




分钟按 5 分钟步长选择




12 小时制:use12Hours 与 12 小时制格式(hh)配合使用




隐藏「此刻」快捷


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const datetimeValue = ref<number | null>(null)
const minuteStepValue = ref<number | null>(null)
const twelveHourValue = ref<number | null>(null)
const showNowValue = ref<number | null>(null)
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="datetimeValue" type="datetime" placeholder="请选择日期时间" />
    <DatePicker
      v-model:value="minuteStepValue"
      type="datetime"
      :time-picker-props="{ minuteStep: 5 }"
      placeholder="请选择日期时间"
    />
    <DatePicker
      v-model:value="twelveHourValue"
      type="datetime"
      format="yyyy-MM-dd hh:mm:ss"
      :time-picker-props="{ use12Hours: true }"
      placeholder="请选择日期时间"
    />
    <DatePicker v-model:value="showNowValue" type="datetime" :show-now="false" placeholder="请选择日期时间" />
  </Space>
</template>

事件 ​

单选形态绑定 change、openChange、panelChange、focus、blur,范围形态绑定 change、calendarChange,带时间的形态还可绑定 ok;各事件的参数都会打印到控制台


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const eventValue = ref<number | null>(null)
const eventRangeValue = ref<[number, number] | null>(null)
const eventDateTimeValue = ref<number | null>(null)
const rangePlaceholder = ['开始日期', '结束日期']
/** 值变化时的回调:值与格式化值双轨 */
function onEventChange(
  value: number | [number | null, number | null] | null,
  formattedValue: string | [string, string] | null
): void {
  console.log('change', value, formattedValue)
}
/** 范围形态待选区间变化时的回调:`info.range` 标识本次改动落在哪一段 */
function onEventCalendarChange(
  value: [number | null, number | null] | null,
  formattedValue: [string, string] | null,
  info: { range: 'start' | 'end' }
): void {
  console.log('calendarChange', value, formattedValue, info)
}
/** 带时间的形态点击「确定」时的回调(「此刻」是快捷入口,不派发) */
function onEventOk(
  value: number | [number | null, number | null] | null,
  formattedValue: string | [string, string] | null
): void {
  console.log('ok', value, formattedValue)
}
/** 面板展开收起时的回调 */
function onEventOpenChange(open: boolean): void {
  console.log('openChange', open)
}
/** 面板视图切换时的回调:`mode` 为切换后的视图层级 */
function onEventPanelChange(value: number, mode: string): void {
  console.log('panelChange', value, mode)
}
/** 输入框获取焦点时的回调 */
function onEventFocus(event: FocusEvent): void {
  console.log('focus', event)
}
/** 输入框失去焦点时的回调 */
function onEventBlur(event: FocusEvent): void {
  console.log('blur', event)
}
</script>
<template>
  <Space vertical>
    <DatePicker
      v-model:value="eventValue"
      placeholder="请选择日期"
      @change="onEventChange"
      @open-change="onEventOpenChange"
      @panel-change="onEventPanelChange"
      @focus="onEventFocus"
      @blur="onEventBlur"
    />
    <DatePicker
      v-model:value="eventRangeValue"
      type="daterange"
      :placeholder="rangePlaceholder"
      @change="onEventChange"
      @calendar-change="onEventCalendarChange"
      @open-change="onEventOpenChange"
      @panel-change="onEventPanelChange"
    />
    <DatePicker
      v-model:value="eventDateTimeValue"
      type="datetime"
      placeholder="请选择日期时间"
      @change="onEventChange"
      @ok="onEventOk"
      @open-change="onEventOpenChange"
    />
  </Space>
</template>

禁用 ​



范围形态的 disabled 可传两段元组逐段禁用,被禁用的段不可输入、面板也不整体置灰


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { startOfDay, startOfMonth, subDays } from 'date-fns'
const fixedDate = new Date(2026, 9, 10).getTime()
const disabledValue = ref<number | null>(fixedDate)
const disabledMonthValue = ref<number | null>(startOfMonth(fixedDate).getTime())
const disabledFullRangeValue = ref<[number, number] | null>([fixedDate, fixedDate])
// 起点段禁用且持有值:起点不可输入但值仍展示
const disabledSideRangeValue = ref<[number, number] | null>([
  startOfDay(subDays(new Date(), 3)).getTime(),
  startOfDay(new Date()).getTime()
])
const rangePlaceholder = ['开始日期', '结束日期']
</script>
<template>
  <Space vertical>
    <DatePicker disabled v-model:value="disabledValue" placeholder="请选择日期" />
    <DatePicker disabled v-model:value="disabledMonthValue" type="month" placeholder="请选择月份" />
    <DatePicker disabled v-model:value="disabledFullRangeValue" type="daterange" :placeholder="rangePlaceholder" />
  </Space>
  <DatePicker
    v-model:value="disabledSideRangeValue"
    type="daterange"
    :disabled="[true, false]"
    :placeholder="rangePlaceholder"
  />
</template>

不可选择日期和时间 ​

disabledDate 与 disabledTime 分别禁止选择部分日期与时间,disabledTime 需与带时间的形态配合使用




不可选择今天之后的日期



只能选择未来七天内的日期



日期范围不可选择今天及之前(disabledDate 同时作用于两个面板)



不可选择周六与周日



月形态不可选择今天之前的月份(disabledDate 的粒度为整月,整月都不可选该格才禁用)



用 time-picker-props 的 hideDisabledOptions 把禁用的时分秒从面板中隐藏(而非置灰),适合禁用项过多的场景

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { addDays, endOfDay, getDay, startOfDay } from 'date-fns'
const disabledDateTimeValue = ref<number | null>(null)
const hideDisabledOptionsValue = ref<number | null>(null)
const disabledRangeValue = ref<[number, number] | null>(null)
const disabledMonthBeforeValue = ref<number | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
const beforeTodayValue = ref<number | null>(null)
const afterTodayValue = ref<number | null>(null)
const weekendValue = ref<number | null>(null)
// 选中日期时的默认时分秒
const defaultTime = startOfDay(new Date()).getTime()
/** 半开区间 [start, end) 的整数序列 */
function range(start: number, end: number): number[] {
  return Array.from({ length: end - start }, (_, index) => index + start)
}
function disabledDateTodayOrBefore(timestamp: number): boolean {
  return endOfDay(timestamp).getTime() <= endOfDay(new Date()).getTime()
}
function disabledDateBefore(timestamp: number): boolean {
  return startOfDay(timestamp).getTime() > startOfDay(new Date()).getTime()
}
function disabledDateAfter(timestamp: number): boolean {
  const current = endOfDay(timestamp).getTime()
  return current <= endOfDay(new Date()).getTime() || current > endOfDay(addDays(new Date(), 7)).getTime()
}
function disabledWeekendDate(timestamp: number): boolean {
  const day = getDay(timestamp)
  return day === 0 || day === 6
}
function disabledMonthBefore(timestamp: number): boolean {
  return endOfDay(timestamp).getTime() < startOfDay(new Date()).getTime()
}
function disabledDateTimeUnits() {
  return {
    disabledHours: () => range(4, 24),
    disabledMinutes: () => range(30, 60),
    disabledSeconds: () => [55, 56]
  }
}
</script>
<template>
  <Space vertical>
    <DatePicker
      v-model:value="disabledDateTimeValue"
      type="datetime"
      :disabled-date="disabledDateTodayOrBefore"
      :disabled-time="disabledDateTimeUnits"
      :default-time="defaultTime"
      placeholder="请选择日期时间"
    />
    <DatePicker v-model:value="beforeTodayValue" :disabled-date="disabledDateBefore" placeholder="请选择日期" />
    <DatePicker v-model:value="afterTodayValue" :disabled-date="disabledDateAfter" placeholder="请选择日期" />
    <DatePicker
      v-model:value="disabledRangeValue"
      type="daterange"
      :disabled-date="disabledDateTodayOrBefore"
      :placeholder="rangePlaceholder"
    />
    <DatePicker v-model:value="weekendValue" :disabled-date="disabledWeekendDate" placeholder="请选择日期" />
    <DatePicker
      v-model:value="disabledMonthBeforeValue"
      type="month"
      :disabled-date="disabledMonthBefore"
      placeholder="请选择月份"
    />
    <DatePicker
      v-model:value="hideDisabledOptionsValue"
      type="datetime"
      :disabled-date="disabledDateTodayOrBefore"
      :disabled-time="disabledDateTimeUnits"
      :time-picker-props="{ hideDisabledOptions: true }"
      placeholder="请选择日期时间"
    />
  </Space>
</template>

选择不超过七天的范围 ​

用 calendarChange 记录草稿区间、用 disabledDate 读草稿收窄可选范围


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { differenceInCalendarDays } from 'date-fns'
const sevenDaysValue = ref<[number, number] | null>(null)
/** 草稿区间:`calendarChange` 直接给出的时间戳区间 */
const sevenDaysDraft = ref<[number | null, number | null] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
/** 草稿期间把可选日期限制在草稿两端 ±7 天内 */
function disabledSevenDaysDate(timestamp: number): boolean {
  const draft = sevenDaysDraft.value
  if (!draft || (draft[0] === null && draft[1] === null)) {
    return false
  }
  const tooLate = draft[0] === null ? false : differenceInCalendarDays(timestamp, draft[0]) > 7
  const tooEarly = draft[1] === null ? false : differenceInCalendarDays(draft[1], timestamp) > 7
  return tooLate || tooEarly
}
function onSevenDaysCalendarChange(value: [number | null, number | null] | null): void {
  sevenDaysDraft.value = value
}
/** 展开时丢弃上一轮草稿:否则上次的区间会继续限制本次可选范围 */
function onSevenDaysOpenChange(open: boolean): void {
  if (open) {
    sevenDaysDraft.value = null
  }
}
</script>
<template>
  <DatePicker
    v-model:value="sevenDaysValue"
    type="daterange"
    :disabled-date="disabledSevenDaysDate"
    :placeholder="rangePlaceholder"
    @calendar-change="onSevenDaysCalendarChange"
    @open-change="onSevenDaysOpenChange"
  />
</template>

预设范围 ​

用 presets 预设常用日期与区间,点击即填入并收起;值可为时间戳,也可为返回时间戳的函数


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { addDays, addMonths, startOfDay, startOfMonth } from 'date-fns'
import type { DatePickerProps } from 'vue-amazing-ui'
const presetValue = ref<number | null>(null)
const presetRangeValue = ref<[number, number] | null>(null)
const presetMonthRangeValue = ref<[number, number] | null>(null)
const presetDateTimeRangeValue = ref<[number, number] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
// 月范围的提示文字按形态区分
const monthRangePlaceholder: [string, string] = ['开始月份', '结束月份']
/** 近 N 天区间:起点取 N 天前的零点、终点取当前时刻 */
function recentDaysRange(days: number): [number, number] {
  return [startOfDay(addDays(new Date(), -days)).getTime(), Date.now()]
}
/** 预设值可为时间戳,也可为返回时间戳的函数(函数值在点击时才求值) */
const datePresets: NonNullable<DatePickerProps['presets']> = [
  { label: '昨天', value: startOfDay(addDays(new Date(), -1)).getTime() },
  { label: '一周前', value: () => startOfDay(addDays(new Date(), -7)).getTime() },
  { label: '一个月前', value: () => startOfDay(addMonths(new Date(), -1)).getTime() }
]
const rangePresets: NonNullable<DatePickerProps['presets']> = [
  { label: '近 7 天', value: () => recentDaysRange(7) },
  { label: '近 14 天', value: () => recentDaysRange(14) },
  { label: '近 30 天', value: () => recentDaysRange(30) },
  { label: '近 90 天', value: () => recentDaysRange(90) }
]
const monthRangePresets: NonNullable<DatePickerProps['presets']> = [
  {
    label: '近 3 个月',
    value: () => [startOfMonth(addMonths(new Date(), -2)).getTime(), startOfMonth(new Date()).getTime()]
  },
  {
    label: '近半年',
    value: () => [startOfMonth(addMonths(new Date(), -5)).getTime(), startOfMonth(new Date()).getTime()]
  }
]
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="presetValue" :presets="datePresets" placeholder="请选择日期" />
    <DatePicker
      v-model:value="presetRangeValue"
      type="daterange"
      :presets="rangePresets"
      :placeholder="rangePlaceholder"
    />
    <DatePicker
      v-model:value="presetMonthRangeValue"
      type="monthrange"
      :presets="monthRangePresets"
      :placeholder="monthRangePlaceholder"
    />
    <DatePicker
      v-model:value="presetDateTimeRangeValue"
      type="datetimerange"
      format="yyyy/MM/dd HH:mm:ss"
      width="400px"
      :presets="rangePresets"
      :placeholder="rangePlaceholder"
    />
  </Space>
</template>

额外的页脚 ​

用 renderExtraFooter 在面板底部追加额外内容,渲染在「今天」「确定」等操作行之上


Show Code
vue
<template>
  <Space vertical>
    <DatePicker placeholder="请选择日期">
      <template #renderExtraFooter>
        <span>extra footer</span>
      </template>
    </DatePicker>
    <DatePicker type="datetime" placeholder="请选择日期时间">
      <template #renderExtraFooter>
        <span>extra footer</span>
      </template>
    </DatePicker>
    <DatePicker type="daterange" :placeholder="rangePlaceholder">
      <template #renderExtraFooter>
        <span>extra footer</span>
      </template>
    </DatePicker>
    <DatePicker type="datetimerange" :placeholder="rangePlaceholder">
      <template #renderExtraFooter>
        <span>extra footer</span>
      </template>
    </DatePicker>
    <DatePicker type="month" placeholder="请选择月份">
      <template #renderExtraFooter>
        <span>extra footer</span>
      </template>
    </DatePicker>
  </Space>
</template>
<script setup lang="ts">
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
</script>

三种大小 ​

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { DatePickerProps } from 'vue-amazing-ui'
const sizeCaseValue = ref<number | null>(null)
const sizeMonthValue = ref<number | null>(null)
const sizeRangeValue = ref<[number, number] | null>(null)
const sizeWeekValue = ref<number | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
const sizeValue = ref<DatePickerProps['size']>('middle')
const sizeOptions: Array<{ label: string; value: NonNullable<DatePickerProps['size']> }> = [
  { label: 'small', value: 'small' },
  { label: 'middle', value: 'middle' },
  { label: 'large', value: 'large' }
]
</script>
<template>
  <Space vertical>
    <RadioGroup v-model:value="sizeValue" :options="sizeOptions" button button-style="solid" />
    <DatePicker v-model:value="sizeCaseValue" :size="sizeValue" placeholder="请选择日期" />
    <DatePicker v-model:value="sizeMonthValue" type="month" :size="sizeValue" placeholder="请选择月份" />
    <DatePicker
      v-model:value="sizeRangeValue"
      type="daterange"
      :size="sizeValue"
      :placeholder="rangePlaceholder"
    />
    <DatePicker v-model:value="sizeWeekValue" type="week" :size="sizeValue" placeholder="请选择周" />
  </Space>
</template>

定制日期单元格 ​

用 dateRender 自定义日期单元格的内容与样式,入参 current 为该格日期的零点时间戳


Show Code
vue
<template>
  <Space vertical>
    <DatePicker v-model:value="dateRenderValue" placeholder="请选择日期">
      <template #dateRender="{ current }">
        <div :style="firstDayCellStyle(getDate(current))">{{ getDate(current) }}</div>
      </template>
    </DatePicker>
    <DatePicker v-model:value="dateRenderRangeValue" type="daterange" :placeholder="rangePlaceholder">
      <template #dateRender="{ current }">
        <div :style="firstDayCellStyle(getDate(current))">{{ getDate(current) }}</div>
      </template>
    </DatePicker>
  </Space>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { CSSProperties } from 'vue'
import { getDate } from 'date-fns'
/** 定制日期单元格:每月 1 号描一圈边框(单元格内层块高 24px,自定义节点须按 border-box 等高,否则外框会把圆圈撑高 2px) */
function firstDayCellStyle(dayOfMonth: number): CSSProperties {
  const cellStyle: CSSProperties = { boxSizing: 'border-box', height: '24px' }
  return dayOfMonth === 1 ? { ...cellStyle, border: '1px solid #1677ff', borderRadius: '50%' } : cellStyle
}
const dateRenderValue = ref<number | null>(null)
const dateRenderRangeValue = ref<[number, number] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
</script>

自定义日期范围选择 ​

用两个独立的选择器组合出范围选择:disabledDate 按整日互相约束起止、disabledTime 把同一天的时分秒精确到边界之后,open 与展开事件联动,起点选完自动展开终点


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { differenceInCalendarDays, getHours, getMinutes, getSeconds, isSameDay } from 'date-fns'
const startValue = ref<number | null>(null)
const endValue = ref<number | null>(null)
const endOpen = ref(false)
/** 半开区间 [start, end) 的整数序列 */
function range(start: number, end: number): number[] {
  return Array.from({ length: end - start }, (_, index) => index + start)
}
/** 起点不得晚于终点的「整日」:同一天仍可选,越界的时间点交给 disabledTime */
function disabledStartDate(timestamp: number): boolean {
  const end = endValue.value
  return end !== null && differenceInCalendarDays(timestamp, end) > 0
}
/** 终点不得早于起点的「整日」:同一天仍可选 */
function disabledEndDate(timestamp: number): boolean {
  const start = startValue.value
  return start !== null && differenceInCalendarDays(timestamp, start) < 0
}
/**
 * 同一天内的时间约束:逐级判定(只有高一级与边界相同,下一级才有额外禁用项),边界时刻本身视为越界
 *
 * @param boundary 约束边界的时刻(终点侧为起点、起点侧为终点)
 * @param timestamp 面板当前所编辑的时刻
 */
function disabledUnitsNotLaterThan(boundary: number, timestamp: number) {
  if (!isSameDay(timestamp, boundary)) {
    return {}
  }
  const hour = getHours(boundary)
  const minute = getMinutes(boundary)
  const second = getSeconds(boundary)
  return {
    disabledHours: () => range(0, hour),
    disabledMinutes: (currentHour: number) => (currentHour === hour ? range(0, minute) : []),
    disabledSeconds: (currentHour: number, currentMinute: number) =>
      currentHour === hour && currentMinute === minute ? range(0, second + 1) : []
  }
}
/** 同 disabledUnitsNotLaterThan,方向相反:禁用晚于边界时刻的时间单位 */
function disabledUnitsNotEarlierThan(boundary: number, timestamp: number) {
  if (!isSameDay(timestamp, boundary)) {
    return {}
  }
  const hour = getHours(boundary)
  const minute = getMinutes(boundary)
  const second = getSeconds(boundary)
  return {
    disabledHours: () => range(hour + 1, 24),
    disabledMinutes: (currentHour: number) => (currentHour === hour ? range(minute + 1, 60) : []),
    disabledSeconds: (currentHour: number, currentMinute: number) =>
      currentHour === hour && currentMinute === minute ? range(second + 1, 60) : []
  }
}
/** 终点侧:同一天只允许选择晚于起点时刻的时间 */
function disabledEndTime(timestamp: number) {
  const start = startValue.value
  return start === null ? {} : disabledUnitsNotLaterThan(start, timestamp)
}
/** 起点侧:同一天只允许选择早于终点时刻的时间 */
function disabledStartTime(timestamp: number) {
  const end = endValue.value
  return end === null ? {} : disabledUnitsNotEarlierThan(end, timestamp)
}
/** 起点选完即收起:把终点直接展开,省掉用户的一次点击 */
function onStartOpenChange(open: boolean): void {
  if (!open) {
    endOpen.value = true
  }
}
</script>
<template>
  <Space vertical>
    <DatePicker
      v-model:value="startValue"
      type="datetime"
      :disabled-date="disabledStartDate"
      :disabled-time="disabledStartTime"
      placeholder="开始日期"
      @open-change="onStartOpenChange"
    />
    <DatePicker
      v-model:value="endValue"
      v-model:open="endOpen"
      type="datetime"
      :disabled-date="disabledEndDate"
      :disabled-time="disabledEndTime"
      placeholder="结束日期"
    />
  </Space>
</template>

后缀图标 ​

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const suffixValue = ref<number | null>(null)
</script>
<template>
  <DatePicker v-model:value="suffixValue" placeholder="请选择日期">
    <template #suffixIcon>
      <svg width="1em" height="1em" viewBox="0 0 1024 1024" fill="currentColor" focusable="false" aria-hidden="true">
        <path
          d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
        />
        <path
          d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
        />
      </svg>
    </template>
  </DatePicker>
</template>

自定义状态 ​

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const errorValue = ref<number | null>(null)
const warningValue = ref<number | null>(null)
const errorRangeValue = ref<[number, number] | null>(null)
const warningRangeValue = ref<[number, number] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="errorValue" status="error" placeholder="请选择日期" />
    <DatePicker v-model:value="warningValue" status="warning" placeholder="请选择日期" />
    <DatePicker v-model:value="errorRangeValue" type="daterange" status="error" :placeholder="rangePlaceholder" />
    <DatePicker v-model:value="warningRangeValue" type="daterange" status="warning" :placeholder="rangePlaceholder" />
  </Space>
</template>

无边框 ​

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const borderlessValue = ref<number | null>(null)
const borderlessWeekValue = ref<number | null>(null)
const borderlessMonthValue = ref<number | null>(null)
const borderlessQuarterValue = ref<number | null>(null)
const borderlessYearValue = ref<number | null>(null)
const borderlessDateRangeValue = ref<[number, number] | null>(null)
const borderlessWeekRangeValue = ref<[number, number] | null>(null)
const borderlessMonthRangeValue = ref<[number, number] | null>(null)
const borderlessQuarterRangeValue = ref<[number, number] | null>(null)
const borderlessYearRangeValue = ref<[number, number] | null>(null)
const rangePlaceholder: [string, string] = ['开始日期', '结束日期']
// 其余范围形态的提示文字按形态区分(周 / 月 / 季 / 年各有独立文案)
const weekRangePlaceholder: [string, string] = ['开始周', '结束周']
const monthRangePlaceholder: [string, string] = ['开始月份', '结束月份']
const quarterRangePlaceholder: [string, string] = ['开始季度', '结束季度']
const yearRangePlaceholder: [string, string] = ['开始年份', '结束年份']
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="borderlessValue" :bordered="false" placeholder="请选择日期" />
    <DatePicker v-model:value="borderlessWeekValue" type="week" :bordered="false" placeholder="请选择周" />
    <DatePicker v-model:value="borderlessMonthValue" type="month" :bordered="false" placeholder="请选择月份" />
    <DatePicker v-model:value="borderlessQuarterValue" type="quarter" :bordered="false" placeholder="请选择季度" />
    <DatePicker v-model:value="borderlessYearValue" type="year" :bordered="false" placeholder="请选择年份" />
    <DatePicker
      v-model:value="borderlessDateRangeValue"
      type="daterange"
      :bordered="false"
      :placeholder="rangePlaceholder"
    />
    <DatePicker
      v-model:value="borderlessWeekRangeValue"
      type="weekrange"
      :bordered="false"
      :placeholder="weekRangePlaceholder"
    />
    <DatePicker
      v-model:value="borderlessMonthRangeValue"
      type="monthrange"
      :bordered="false"
      :placeholder="monthRangePlaceholder"
    />
    <DatePicker
      v-model:value="borderlessQuarterRangeValue"
      type="quarterrange"
      :bordered="false"
      :placeholder="quarterRangePlaceholder"
    />
    <DatePicker
      v-model:value="borderlessYearRangeValue"
      type="yearrange"
      :bordered="false"
      :placeholder="yearRangePlaceholder"
    />
  </Space>
</template>

弹出位置 ​

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const bottomLeftValue = ref<number | null>(null)
const bottomRightValue = ref<number | null>(null)
const topLeftValue = ref<number | null>(null)
const topRightValue = ref<number | null>(null)
const rangeBottomLeftValue = ref<[number, number] | null>(null)
const rangeTopLeftValue = ref<[number, number] | null>(null)
const rangePlaceholder = ['开始日期', '结束日期']
</script>
<template>
  <Space>
    <DatePicker v-model:value="bottomLeftValue" placement="bottomLeft" placeholder="左下" />
    <DatePicker v-model:value="bottomRightValue" placement="bottomRight" placeholder="右下" />
    <DatePicker v-model:value="topLeftValue" placement="topLeft" placeholder="左上" />
    <DatePicker v-model:value="topRightValue" placement="topRight" placeholder="右上" />
    <DatePicker
      v-model:value="rangeBottomLeftValue"
      type="daterange"
      placement="bottomLeft"
      :placeholder="rangePlaceholder"
    />
    <DatePicker
      v-model:value="rangeTopLeftValue"
      type="daterange"
      placement="topLeft"
      :placeholder="rangePlaceholder"
    />
  </Space>
</template>

键盘操作 ​

面板收起时按 Enter 展开,展开后按 Tab 把键盘交给面板,方向键按当前面板层级的粒度移动光标、Enter 提交并收起,Esc 收起;带时间的形态下 Tab 在日期与时间两列之间切换(Shift+Tab 反向收回键盘),非活动列淡化;焦点移出组件即收起面板


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const keyboardValue = ref<number | null>(null)
const keyboardRangeValue = ref<[number, number] | null>(null)
const keyboardDateTimeValue = ref<number | null>(null)
const rangePlaceholder = ['开始日期', '结束日期']
</script>
<template>
  <Space vertical>
    <DatePicker v-model:value="keyboardValue" placeholder="请选择日期" />
    <DatePicker v-model:value="keyboardRangeValue" type="daterange" :placeholder="rangePlaceholder" />
    <DatePicker v-model:value="keyboardDateTimeValue" type="datetime" placeholder="请选择日期时间" />
  </Space>
</template>

按段默认时刻 ​

defaultTime 指定空草稿时的默认时分秒,范围形态可传两段元组按段指定(起点 00:00:00、终点 23:59:59);某段为 undefined 时该段退回当前时刻


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { endOfDay, startOfDay } from 'date-fns'
const defaultTimeRangeValue = ref<[number, number] | null>(null)
const rangeDefaultTime = [startOfDay(new Date()).getTime(), endOfDay(new Date()).getTime()]
const rangePlaceholder = ['开始日期', '结束日期']
</script>
<template>
  <DatePicker
    v-model:value="defaultTimeRangeValue"
    type="datetimerange"
    :default-time="rangeDefaultTime"
    :placeholder="rangePlaceholder"
  />
</template>

APIs ​

受控 / 非受控:传入 v-model:value 即受控,组件只经 update:value 上报;不传时由组件自持,可用 defaultValue 指定初值。两者同时传时以 value 为准。

DatePicker Props ​

名称说明类型默认值
value
v-model
双向绑定值,毫秒时间戳;范围形态为两段元组,某段可为 null;传入即受控,不传时组件自持(可用 defaultValue 指定初值,缺省无选中值)number | [number | null, number | null] | nullundefined
defaultValue非受控初始值,未绑 v-model:value 时生效;与面板初始日期 defaultPickerValue、默认时分秒 defaultTime 不同,本字段决定选中值本身number | [number | null, number | null] | nullundefined
open
v-model
面板是否展开;传入即受控,内部触发只经 update:open / openChange 上报(不传时由组件内部维护,缺省收起)booleanundefined
formattedValue
v-model
字符串轨道的双向绑定值,传入时以它为准(受控);显示格式由 format / valueFormat 决定string | [string, string] | nullundefined
type选择形态DatePickerType'date'
format日期展示格式,未传时按 DefaultFormat,参考 formatstringundefined
valueFormat绑定值格式,默认与 format 相同stringundefined
placeholder输入框提示文字,范围形态可传入两段文案string | [string, string]''
defaultPickerValue面板初始日期(毫秒时间戳),默认取 value 或今天numberundefined
startDayOfWeek一周的开始是星期几,0-6,0 是周一;周形态恒按 ISO 周(周一起始),该属性对周形态不生效0 | 1 | 2 | 3 | 4 | 5 | 60
disabledDate不可选择的日期,入参为「当日零点」的毫秒时间戳;月 / 季 / 年形态下按整段判定(整段都不可选该格才禁用);手输文本同样校验,命中即不落值(timestamp: number) => booleanundefined
disabledTime不可选择的时间,仅带时间的形态生效;入参为当前面板草稿值的时间戳,范围形态额外接收段标识DatePickerDisabledTime | DatePickerRangeDisabledTimeundefined
defaultTime选中日期时的默认时分秒(只取其中的时分秒,不参与时间面板的预选展示);范围形态可按段传入两段元组,某段为 undefined 时该段退回当前时刻number | [number | undefined, number | undefined]undefined
timePickerProps时间面板选项(步长 / 12 小时制 / 隐藏禁用项)DatePickerTimePropsundefined
presets预设选项,点击即提交并收起DatePickerPreset[][]
width选择器宽度,单位 px,不传时随内容自适应string | numberundefined
size选择器大小'small' | 'middle' | 'large''middle'
status校验状态'warning' | 'error'undefined
bordered是否展示边框booleantrue
disabled是否禁用;范围形态可传两段元组逐段禁用(被禁用的段不可输入,且该段日期被对端的值锁死);切为禁用时已展开的面板立即收起,解除禁用不会自行展开boolean | [boolean, boolean]false
allowClear是否展示清除按钮booleantrue
allowEmpty范围形态各段是否允许为空:为 true 时该段为空也能对外派发 change;清空整段仍走 allowClear 的清除按钮(把某段输入框清空不会置空该段)[boolean, boolean][false, false]
inputReadOnly输入框是否只读(避免移动端唤起键盘)booleanfalse
to面板挂载的容器节点:显式传入时按此挂载(元素标签名 (例如 'body') 或元素本身,false 会待在原地);不传时优先挂到最近的承载层内容容器(Modal / Drawer / Dialog 卡片或上层浮层面板),无承载层时为 bodystring | HTMLElement | falseundefined
placement面板弹出位置'topLeft' | 'top' | 'topRight' | 'bottomLeft' | 'bottom' | 'bottomRight''bottomLeft'
showArrow范围形态的面板指示箭头是否展示:不传时跟随弹出方位(仅左侧对齐的 bottomLeft / topLeft 展示),true 始终展示、false 始终隐藏booleanundefined
showToday是否展示面板底部的「今天」快捷,面板切到周 / 月 / 季 / 年视图时隐藏booleantrue
showNow是否展示面板底部的「此刻」快捷,仅单选带时间形态(type="datetime")生效:范围形态的提交落在单段上,「此刻」无从决定属于哪一段booleantrue
dateRender自定义日期单元格的内容与样式(替换日号),单元格容器与选中 / 悬浮 / 区间底色仍由组件提供;入参 current 为该格日期的零点时间戳、today 为当前时刻时间戳(params: { current: number; today: number }) => VNode | VNode[]undefined
renderExtraFooter面板底部的额外页脚,渲染在「今天」「确定」等操作行之上() => VNode | VNode[]undefined
suffixIcon自定义选择框后缀图标VNode | (() => VNode)undefined
panelClass面板额外类名string''
panelStyle面板额外样式CSSPropertiesundefined
zIndex面板层级,优先级最高(覆盖默认层级与 ConfigProvider 的 baseZIndex 自动分配)numberundefined

DatePickerType Type ​

名称值
DatePickerType'date' | 'datetime' | 'week' | 'month' | 'quarter' | 'year' | 'daterange' | 'datetimerange' | 'weekrange' | 'monthrange' | 'quarterrange' | 'yearrange'

DatePickerDisabledTime Type ​


不可选择的时间判定,入参为当前面板草稿值的时间戳。

名称值
DatePickerDisabledTime(timestamp: number) => DatePickerDisabledTimeUnits

DatePickerRangeDisabledTime Type ​


范围形态的不可选择时间判定,side 标识当前作用在起点还是终点。

名称值
DatePickerRangeDisabledTime(timestamp: number, side: 'start' | 'end') => DatePickerDisabledTimeUnits

DatePickerDisabledTimeUnits Type ​

名称说明类型
disabledHours?禁用的「时」() => number[]
disabledMinutes?禁用的「分」,入参为选中的「时」(hour: number) => number[]
disabledSeconds?禁用的「秒」,入参为选中的「时」「分」(hour: number, minute: number) => number[]

DatePickerTimeProps Type ​

名称说明类型
hourStep?「时」的步长,默认 1;须能整除 24,非法值按 1 处理number
minuteStep?「分」的步长,默认 1;须能整除 60,非法值按 1 处理number
secondStep?「秒」的步长,默认 1;须能整除 60,非法值按 1 处理number
use12Hours?是否使用 12 小时制,与 12 小时制格式(hh)配合使用boolean
hideDisabledOptions?是否隐藏禁用的选项boolean

DatePickerPreset Type ​

名称说明类型
label预设名称string | VNode
value预设值:单选形态为一个时间戳,范围形态为两段元组;可为返回预设值的函数(点击时才求值)number | [number, number] | (() => number | [number, number])

DatePickerRangeInfo Type ​


范围形态草稿变更的来源信息。

名称说明类型
range草稿变更落在哪一段'start' | 'end'

DefaultFormat Value ​

名称值
date'yyyy-MM-dd'
datetime'yyyy-MM-dd HH:mm:ss'
week'RRRR-II周'
month'yyyy-MM'
quarter'yyyy-QQQ'
year'yyyy'
daterange'yyyy-MM-dd'
datetimerange'yyyy-MM-dd HH:mm:ss'
weekrange'RRRR-II周'
monthrange'yyyy-MM'
quarterrange'yyyy-QQQ'
yearrange'yyyy'

format 支持的格式化占位符列表 ​


format / valueFormat 采用 date-fns 占位符,常用占位符:

标识示例描述
yy26年,两位数
yyyy2026年,四位数
M1-12月
MM01-12月,两位数
Q1-4季度
QQQQ1-Q4季度,带 Q 前缀
d1-31日
dd01-31日,两位数
h1-12小时,12 小时制
hh01-12小时,12 小时制,两位数
H0-23小时
HH00-23小时,两位数
m0-59分钟
mm00-59分钟,两位数
s0-59秒
ss00-59秒,两位数
ww01-53第几周,两位数(本地周口径)
II01-53ISO 周序号,两位数
RRRR2026ISO 周年,四位数

周形态的默认格式为 RRRR-II周(如 2026-41周):「周」是字面量后缀,中文语境下用于指明该数字为周序号,不需要时传入 format="RRRR-II" 即可移除;date-fns 不允许「周年 + 本地周序号」同串(yyyy 与 ww 会解析失败),故周序号须与周年同为 ISO 口径。

Slots ​

名称说明用法
dateRender自定义日期单元格的内容与样式(替换日号),优先于同名属性v-slot:dateRender
renderExtraFooter面板底部的额外页脚,优先于同名属性v-slot:renderExtraFooter
suffixIcon自定义选择框后缀图标v-slot:suffixIcon
separator范围形态两段之间的分隔符v-slot:separator

Events ​

名称说明类型
change值变化时的回调(与已提交值相同的选择不派发)(value: number | [number | null, number | null] | null, formattedValue: string | [string, string] | null) => void
calendarChange范围形态待选区间变化时的回调,info.range 标识本次改动落在哪一段(value: [number | null, number | null] | null, formattedValue: [string, string] | null, info: DatePickerRangeInfo) => void
ok带时间的形态点击「确定」时的回调(value: number | [number | null, number | null] | null, formattedValue: string | [string, string] | null) => void
openChange面板展开收起时的回调(open: boolean) => void
panelChange面板视图切换时的回调(value: number, mode: string) => void
focus输入框获取焦点时的回调,relatedTarget 为失去焦点的元素(e: FocusEvent) => void
blur输入框失焦的回调,relatedTarget 为获得焦点的元素(e: FocusEvent) => void

Methods ​

名称说明类型
focus获取选择器焦点() => void
blur使选择器失去焦点() => void

Released under the MIT License.