Date Picker 单日选择

用于单日选择场景的 MoriUI 组件。

导入

import { DatePicker } from 'moriui'

示例

概览

从弹层中选择日期。

<script setup lang="ts">
  import { ref } from 'vue'
  import { format } from 'date-fns'
  import { zhCN } from 'date-fns/locale'
  import { ChevronDown } from '@lucide/vue'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger
  } from 'moriui'

  const date = ref<Date>()
</script>

<template>
  <DatePicker v-model="date" class="mx-auto w-fit">
    <DatePickerTrigger class="flex w-[212px] items-center justify-between gap-2 rounded-lg border border-input bg-background px-3 py-2 text-left text-sm font-normal shadow-sm hover:bg-accent hover:text-accent-foreground data-[state=open]:ring-1 data-[state=open]:ring-ring">
      <span :class="date ? 'text-foreground' : 'text-muted-foreground'">
        {{ date ? format(date, 'PPP', { locale: zhCN }) : '选择日期' }}
      </span>
      <ChevronDown class="size-4 shrink-0 text-muted-foreground" />
    </DatePickerTrigger>
    <DatePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="start">
      <DatePickerCalendar v-slot="{ grid, weekDays }">
        <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
          <DatePickerPrev />
          <DatePickerHeading class="text-sm font-medium" />
          <DatePickerNext />
        </DatePickerHeader>
        <div class="p-3">
          <DatePickerGrid v-for="month in grid" :key="month.value.toString()">
            <DatePickerGridHead>
              <DatePickerGridRow>
                <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                  {{ day }}
                </DatePickerHeadCell>
              </DatePickerGridRow>
            </DatePickerGridHead>
            <DatePickerGridBody>
              <DatePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                <DatePickerCell
                  v-for="weekDate in weekDates"
                  :key="weekDate.toString()"
                  :date="weekDate"
                  class="h-8 w-8 p-0"
                >
                  <DatePickerCellTrigger :day="weekDate" :month="month.value" class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50" />
                </DatePickerCell>
              </DatePickerGridRow>
            </DatePickerGridBody>
          </DatePickerGrid>
        </div>
      </DatePickerCalendar>
    </DatePickerContent>
  </DatePicker>
</template>

基础

最小化的日期选择。

<script setup lang="ts">
  import { ref } from 'vue'
  import { format } from 'date-fns'
  import { zhCN } from 'date-fns/locale'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger,
    Label

  } from 'moriui'

  const date = ref<Date>()
</script>

<template>
  <div class="mx-auto w-44">
    <Label for="date-picker-simple" class="mb-2 block text-sm font-medium">日期</Label>
    <DatePicker v-model="date">
      <DatePickerTrigger
        id="date-picker-simple"
        class="flex w-full items-center justify-start gap-2 rounded-lg border border-input bg-background px-3 py-2 text-left text-sm font-normal shadow-sm hover:bg-accent hover:text-accent-foreground"
      >
        <span :class="date ? 'text-foreground' : 'text-muted-foreground'">
          {{ date ? format(date, 'PPP', { locale: zhCN }) : '选择日期' }}
        </span>
      </DatePickerTrigger>
      <DatePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="start">
        <DatePickerCalendar v-slot="{ grid, weekDays }">
          <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
            <DatePickerPrev />
            <DatePickerHeading class="text-sm font-medium" />
            <DatePickerNext />
          </DatePickerHeader>
          <div class="p-3">
            <DatePickerGrid v-for="month in grid" :key="month.value.toString()">
              <DatePickerGridHead>
                <DatePickerGridRow>
                  <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                    {{ day }}
                  </DatePickerHeadCell>
                </DatePickerGridRow>
              </DatePickerGridHead>
              <DatePickerGridBody>
                <DatePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                  <DatePickerCell
                    v-for="weekDate in weekDates"
                    :key="weekDate.toString()"
                    :date="weekDate"
                    class="h-8 w-8 p-0"
                  >
                    <DatePickerCellTrigger :day="weekDate" :month="month.value" class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50" />
                  </DatePickerCell>
                </DatePickerGridRow>
              </DatePickerGridBody>
            </DatePickerGrid>
          </div>
        </DatePickerCalendar>
      </DatePickerContent>
    </DatePicker>
  </div>
</template>

范围

选择日期区间。

<script setup lang="ts">
  import type { DateValue } from 'reka-ui'

  import { ref } from 'vue'
  import { format } from 'date-fns'
  import { Calendar } from '@lucide/vue'
  import { zhCN } from 'date-fns/locale'
  import { CalendarDate } from '@internationalized/date'
  import {
    DateRangePicker,
    DateRangePickerCalendar,
    DateRangePickerCell,
    DateRangePickerCellTrigger,
    DateRangePickerContent,
    DateRangePickerGrid,
    DateRangePickerGridBody,
    DateRangePickerGridHead,
    DateRangePickerGridRow,
    DateRangePickerHeadCell,
    DateRangePickerHeader,
    DateRangePickerHeading,
    DateRangePickerNext,
    DateRangePickerPrev,
    DateRangePickerTrigger,
    Label

  } from 'moriui'

  function toNativeDate(d: DateValue | undefined): Date | undefined {
    if (!d)
      return undefined
    if (d instanceof Date)
      return d
    if (d instanceof CalendarDate)
      return d.toDate('UTC')
    return undefined
  }

  const startDate = new Date(new Date().getFullYear(), 0, 20)
  const date = ref<{ start?: DateValue, end?: DateValue }>({
    start: new CalendarDate(startDate.getFullYear(), startDate.getMonth() + 1, startDate.getDate()),
    end: new CalendarDate(
      startDate.getFullYear(),
      startDate.getMonth() + 1,
      startDate.getDate() + 20
    )
  })
</script>

<template>
  <div class="mx-auto w-60">
    <Label for="date-picker-range" class="mb-2 block text-sm font-medium">日期范围</Label>
    <DateRangePicker v-model="date" class="mx-auto w-fit">
      <DateRangePickerTrigger
        id="date-picker-range"
        class="flex w-full items-center justify-start gap-2 rounded-lg border border-input bg-background px-2.5 py-2 text-left text-sm font-normal shadow-sm hover:bg-accent hover:text-accent-foreground"
      >
        <Calendar class="size-4 shrink-0 text-muted-foreground" />
        <span v-if="date?.start" class="text-foreground">
          <template v-if="date?.end">
            {{ format(toNativeDate(date.start)!, 'MMM dd, y', { locale: zhCN }) }} - {{ format(toNativeDate(date.end)!, 'MMM dd, y', { locale: zhCN }) }}
          </template>
          <template v-else>
            {{ format(toNativeDate(date.start)!, 'MMM dd, y', { locale: zhCN }) }}
          </template>
        </span>
        <span v-else class="text-muted-foreground">选择日期范围</span>
      </DateRangePickerTrigger>
      <DateRangePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="start">
        <DateRangePickerCalendar v-slot="{ grid, weekDays }">
          <DateRangePickerHeader class="flex items-center justify-between px-3 pt-3">
            <DateRangePickerPrev />
            <DateRangePickerHeading class="text-sm font-medium" />
            <DateRangePickerNext />
          </DateRangePickerHeader>
          <div class="p-3">
            <DateRangePickerGrid v-for="month in grid" :key="month.value.toString()" class="[&:not(:first-child)]:ml-4">
              <DateRangePickerGridHead>
                <DateRangePickerGridRow>
                  <DateRangePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                    {{ day }}
                  </DateRangePickerHeadCell>
                </DateRangePickerGridRow>
              </DateRangePickerGridHead>
              <DateRangePickerGridBody>
                <DateRangePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                  <DateRangePickerCell
                    v-for="weekDate in weekDates"
                    :key="weekDate.toString()"
                    :date="weekDate"
                    class="h-8 w-8 p-0"
                  >
                    <DateRangePickerCellTrigger :day="weekDate" :month="month.value" class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50" />
                  </DateRangePickerCell>
                </DateRangePickerGridRow>
              </DateRangePickerGridBody>
            </DateRangePickerGrid>
          </div>
        </DateRangePickerCalendar>
      </DateRangePickerContent>
    </DateRangePicker>
  </div>
</template>

输入

手动输入日期。

<script setup lang="ts">
  import { ref } from 'vue'
  import { Calendar } from '@lucide/vue'
  import { CalendarDate } from '@internationalized/date'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger,
    Label

  } from 'moriui'

  function toNativeDate(date: unknown): Date | undefined {
    if (!date)
      return undefined
    if (date instanceof Date)
      return date
    if (date instanceof CalendarDate)
      return date.toDate('UTC')
    return undefined
  }

  function formatDate(date: unknown): string {
    const native = toNativeDate(date)
    if (!native)
      return ''
    return native.toLocaleDateString('zh-CN', {
      day: '2-digit',
      month: 'long',
      year: 'numeric'
    })
  }

  function isValidDate(date: unknown): boolean {
    const native = toNativeDate(date)
    if (!native)
      return false
    return !isNaN(native.getTime())
  }

  const open = ref(false)
  const date = ref<CalendarDate>(new CalendarDate(2025, 6, 1))
  const placeholder = ref<Date | undefined>(new Date('2025-06-01'))
  const value = ref(formatDate(date.value))
</script>

<template>
  <div class="mx-auto w-48">
    <Label for="date-required" class="mb-2 block text-sm font-medium">订阅日期</Label>
    <div class="flex items-center rounded-lg border border-input bg-background shadow-sm focus-within:ring-1 focus-within:ring-ring">
      <input
        id="date-required"
        :value="value"
        placeholder="2025年6月1日"
        class="flex-1 rounded-md bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
        @input="(e: Event) => {
          const target = e.target as HTMLInputElement
          const parsed = new Date(target.value)
          value = target.value
          if (isValidDate(parsed)) {
            date = new CalendarDate(parsed.getFullYear(), parsed.getMonth() + 1, parsed.getDate())
            placeholder = parsed
          }
        }"
        @keydown.arrow-down.prevent="open = true"
      >
      <DatePicker
        v-model="date"
        v-model:open="open"
        :placeholder="placeholder"
        @update:placeholder="placeholder = $event"
      >
        <DatePickerTrigger
          class="mr-1 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground"
          aria-label="选择日期"
        >
          <Calendar class="size-4" />
        </DatePickerTrigger>
        <DatePickerContent
          class="w-auto rounded-lg border bg-popover p-0 shadow-md"
          align="end"
          :align-offset="-8"
          :side-offset="10"
        >
          <DatePickerCalendar v-slot="{ grid, weekDays }">
            <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
              <DatePickerPrev />
              <DatePickerHeading class="text-sm font-medium" />
              <DatePickerNext />
            </DatePickerHeader>
            <div class="p-3">
              <DatePickerGrid v-for="monthItem in grid" :key="monthItem.value.toString()">
                <DatePickerGridHead>
                  <DatePickerGridRow>
                    <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                      {{ day }}
                    </DatePickerHeadCell>
                  </DatePickerGridRow>
                </DatePickerGridHead>
                <DatePickerGridBody>
                  <DatePickerGridRow v-for="(weekDates, index) in monthItem.rows" :key="index">
                    <DatePickerCell
                      v-for="weekDate in weekDates"
                      :key="weekDate.toString()"
                      :date="weekDate"
                      class="h-8 w-8 p-0"
                    >
                      <DatePickerCellTrigger
                        :day="weekDate"
                        :month="monthItem.value"
                        class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50"
                      />
                    </DatePickerCell>
                  </DatePickerGridRow>
                </DatePickerGridBody>
              </DatePickerGrid>
            </div>
          </DatePickerCalendar>
        </DatePickerContent>
      </DatePicker>
    </div>
  </div>
</template>

生日

选择出生日期。

<script setup lang="ts">
  import { ref } from 'vue'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger,
    Label

  } from 'moriui'

  const open = ref(false)
  const date = ref<Date>()
</script>

<template>
  <div class="mx-auto w-44">
    <Label for="date" class="mb-2 block text-sm font-medium">出生日期</Label>
    <DatePicker v-model="date" v-model:open="open" close-on-select>
      <DatePickerTrigger
        id="date"
        class="flex w-full items-center justify-start gap-2 rounded-lg border border-input bg-background px-3 py-2 text-left text-sm font-normal shadow-sm hover:bg-accent hover:text-accent-foreground"
      >
        <span :class="date ? 'text-foreground' : 'text-muted-foreground'">
          {{ date ? date.toLocaleDateString('zh-CN') : '选择日期' }}
        </span>
      </DatePickerTrigger>
      <DatePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="start">
        <DatePickerCalendar v-slot="{ grid, weekDays }">
          <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
            <DatePickerPrev />
            <DatePickerHeading class="text-sm font-medium" />
            <DatePickerNext />
          </DatePickerHeader>
          <div class="p-3">
            <DatePickerGrid v-for="month in grid" :key="month.value.toString()">
              <DatePickerGridHead>
                <DatePickerGridRow>
                  <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                    {{ day }}
                  </DatePickerHeadCell>
                </DatePickerGridRow>
              </DatePickerGridHead>
              <DatePickerGridBody>
                <DatePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                  <DatePickerCell
                    v-for="weekDate in weekDates"
                    :key="weekDate.toString()"
                    :date="weekDate"
                    class="h-8 w-8 p-0"
                  >
                    <DatePickerCellTrigger
                      :day="weekDate"
                      :month="month.value"
                      class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50"
                    />
                  </DatePickerCell>
                </DatePickerGridRow>
              </DatePickerGridBody>
            </DatePickerGrid>
          </div>
        </DatePickerCalendar>
      </DatePickerContent>
    </DatePicker>
  </div>
</template>

时间

日期时间选择。

<script setup lang="ts">
  import { ref } from 'vue'
  import { format } from 'date-fns'
  import { zhCN } from 'date-fns/locale'
  import { ChevronDown } from '@lucide/vue'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger,
    Field,
    FieldGroup,
    FieldLabel,
    Input

  } from 'moriui'

  const open = ref(false)
  const date = ref<Date>()
</script>

<template>
  <FieldGroup class="mx-auto flex max-w-xs flex-row gap-4">
    <Field>
      <FieldLabel for="date-picker-optional" class="text-sm font-medium">
        日期
      </FieldLabel>
      <DatePicker v-model="date" v-model:open="open">
        <DatePickerTrigger
          id="date-picker-optional"
          class="flex w-32 items-center justify-between gap-2 rounded-lg border border-input bg-background px-3 py-2 text-left text-sm font-normal shadow-sm hover:bg-accent hover:text-accent-foreground"
        >
          <span :class="date ? 'text-foreground' : 'text-muted-foreground'">
            {{ date ? format(date, 'PPP', { locale: zhCN }) : '选择日期' }}
          </span>
          <ChevronDown class="size-4 shrink-0 text-muted-foreground" />
        </DatePickerTrigger>
        <DatePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="start">
          <DatePickerCalendar v-slot="{ grid, weekDays }">
            <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
              <DatePickerPrev />
              <DatePickerHeading class="text-sm font-medium" />
              <DatePickerNext />
            </DatePickerHeader>
            <div class="p-3">
              <DatePickerGrid v-for="month in grid" :key="month.value.toString()">
                <DatePickerGridHead>
                  <DatePickerGridRow>
                    <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                      {{ day }}
                    </DatePickerHeadCell>
                  </DatePickerGridRow>
                </DatePickerGridHead>
                <DatePickerGridBody>
                  <DatePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                    <DatePickerCell
                      v-for="weekDate in weekDates"
                      :key="weekDate.toString()"
                      :date="weekDate"
                      class="h-8 w-8 p-0"
                    >
                      <DatePickerCellTrigger
                        :day="weekDate"
                        :month="month.value"
                        class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50"
                      />
                    </DatePickerCell>
                  </DatePickerGridRow>
                </DatePickerGridBody>
              </DatePickerGrid>
            </div>
          </DatePickerCalendar>
        </DatePickerContent>
      </DatePicker>
    </Field>
    <Field class="w-32">
      <FieldLabel for="time-picker-optional" class="text-sm font-medium">
        时间
      </FieldLabel>
      <Input
        id="time-picker-optional"
        type="time"
        step="1"
        default-value="10:30:00"
        class="block w-full appearance-none rounded-lg border border-input bg-background px-3 py-2 text-sm shadow-sm [&::-webkit-calendar-picker-indicator]:hidden"
      />
    </Field>
  </FieldGroup>
</template>

自然语言

自然语言输入日期。

你的文章将于 发布。

<script setup lang="ts">
  import { ref } from 'vue'
  import { Calendar } from '@lucide/vue'
  import { parseDate } from 'chrono-node'
  import { CalendarDate } from '@internationalized/date'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger,
    Label

  } from 'moriui'

  function toNativeDate(date: unknown): Date | undefined {
    if (!date)
      return undefined
    if (date instanceof Date)
      return date
    if (date instanceof CalendarDate)
      return date.toDate('UTC')
    return undefined
  }

  function formatDate(date: unknown): string {
    const native = toNativeDate(date)
    if (!native)
      return ''
    return native.toLocaleDateString('zh-CN', {
      day: '2-digit',
      month: 'long',
      year: 'numeric'
    })
  }

  const open = ref(false)
  const value = ref('后天')
  const now = new Date()
  const initialDate = parseDate(value.value)
  const date = ref<CalendarDate | undefined>(
    initialDate ? new CalendarDate(initialDate.getFullYear(), initialDate.getMonth() + 1, initialDate.getDate()) : undefined
  )
</script>

<template>
  <div class="mx-auto max-w-xs">
    <Label for="date-optional" class="mb-2 block text-sm font-medium">日程日期</Label>
    <div class="flex items-center rounded-lg border border-input bg-background shadow-sm focus-within:ring-1 focus-within:ring-ring">
      <input
        id="date-optional"
        :value="value"
        placeholder="明天或下周"
        class="flex-1 rounded-md bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
        @input="(e: Event) => {
          const target = e.target as HTMLInputElement
          value = target.value
          const parsed = parseDate(target.value)
          if (parsed) date = new CalendarDate(parsed.getFullYear(), parsed.getMonth() + 1, parsed.getDate())
        }"
        @keydown.arrow-down.prevent="open = true"
      >
      <DatePicker v-model="date" v-model:open="open">
        <DatePickerTrigger
          class="mr-1 inline-flex size-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground"
          aria-label="选择日期"
        >
          <Calendar class="size-4" />
        </DatePickerTrigger>
        <DatePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="end" :side-offset="8">
          <DatePickerCalendar v-slot="{ grid, weekDays }">
            <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
              <DatePickerPrev />
              <DatePickerHeading class="text-sm font-medium" />
              <DatePickerNext />
            </DatePickerHeader>
            <div class="p-3">
              <DatePickerGrid v-for="month in grid" :key="month.value.toString()">
                <DatePickerGridHead>
                  <DatePickerGridRow>
                    <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                      {{ day }}
                    </DatePickerHeadCell>
                  </DatePickerGridRow>
                </DatePickerGridHead>
                <DatePickerGridBody>
                  <DatePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                    <DatePickerCell
                      v-for="weekDate in weekDates"
                      :key="weekDate.toString()"
                      :date="weekDate"
                      class="h-8 w-8 p-0"
                    >
                      <DatePickerCellTrigger
                        :day="weekDate"
                        :month="month.value"
                        class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50"
                      />
                    </DatePickerCell>
                  </DatePickerGridRow>
                </DatePickerGridBody>
              </DatePickerGrid>
            </div>
          </DatePickerCalendar>
        </DatePickerContent>
      </DatePicker>
    </div>
    <p class="mt-2 px-1 text-sm text-muted-foreground">
      你的文章将于 <span class="font-medium text-foreground">{{ formatDate(date) }}</span> 发布。
    </p>
  </div>
</template>

RTL

从右到左的日期选择。

<script setup lang="ts">
  import { ref } from 'vue'
  import { format } from 'date-fns'
  import { arSA } from 'date-fns/locale'
  import { ChevronDown } from '@lucide/vue'
  import {
    DatePicker,
    DatePickerCalendar,
    DatePickerCell,
    DatePickerCellTrigger,
    DatePickerContent,
    DatePickerGrid,
    DatePickerGridBody,
    DatePickerGridHead,
    DatePickerGridRow,
    DatePickerHeadCell,
    DatePickerHeader,
    DatePickerHeading,
    DatePickerNext,
    DatePickerPrev,
    DatePickerTrigger,
    DirectionProvider
  } from 'moriui'

  const date = ref<Date>()
</script>

<template>
  <DirectionProvider dir="rtl">
    <DatePicker v-model="date" class="mx-auto w-fit">
      <DatePickerTrigger class="flex w-[212px] items-center justify-between gap-2 rounded-lg border border-input bg-background px-3 py-2 text-left text-sm font-normal shadow-sm hover:bg-accent hover:text-accent-foreground data-[state=open]:ring-1 data-[state=open]:ring-ring">
        <span :class="date ? 'text-foreground' : 'text-muted-foreground'">
          {{ date ? format(date, 'PPP', { locale: arSA }) : 'اختر تاريخًا' }}
        </span>
        <ChevronDown class="size-4 shrink-0 text-muted-foreground" />
      </DatePickerTrigger>
      <DatePickerContent class="w-auto rounded-lg border bg-popover p-0 shadow-md" align="start">
        <DatePickerCalendar v-slot="{ grid, weekDays }">
          <DatePickerHeader class="flex items-center justify-between px-3 pt-3">
            <DatePickerPrev />
            <DatePickerHeading class="text-sm font-medium" />
            <DatePickerNext />
          </DatePickerHeader>
          <div class="p-3">
            <DatePickerGrid v-for="month in grid" :key="month.value.toString()">
              <DatePickerGridHead>
                <DatePickerGridRow>
                  <DatePickerHeadCell v-for="day in weekDays" :key="day" class="h-8 w-8 text-center text-xs text-muted-foreground">
                    {{ day }}
                  </DatePickerHeadCell>
                </DatePickerGridRow>
              </DatePickerGridHead>
              <DatePickerGridBody>
                <DatePickerGridRow v-for="(weekDates, index) in month.rows" :key="index">
                  <DatePickerCell
                    v-for="weekDate in weekDates"
                    :key="weekDate.toString()"
                    :date="weekDate"
                    class="h-8 w-8 p-0"
                  >
                    <DatePickerCellTrigger :day="weekDate" :month="month.value" class="flex h-8 w-8 items-center justify-center rounded-md text-sm hover:bg-accent hover:text-accent-foreground data-selected:bg-primary data-selected:text-primary-foreground data-today:bg-accent data-today:text-accent-foreground data-outside-view:text-muted-foreground/50" />
                  </DatePickerCell>
                </DatePickerGridRow>
              </DatePickerGridBody>
            </DatePickerGrid>
          </div>
        </DatePickerCalendar>
      </DatePickerContent>
    </DatePicker>
  </DirectionProvider>
</template>

使用说明

组合方式

DatePicker 组合 Calendar 和 Popover,通过 Trigger 打开日历弹层。

预设

与日历 preset 组合可提供快捷日期范围。

RTL

弹层定位与日历方向遵循 dir。

变体

DatePicker 没有视觉变体;弹层定位由 Popper 控制。

可访问性

DatePicker 将 Calendar 的 grid 语义放置在 Popover 弹层中,焦点在打开时移动到日历。输入框应有 Label 或 aria-label。

API 参考

属性类型默认值说明
DatePicker v-modelDateValue | nullundefined当前选中日期。
DatePicker placeholderDateValueundefined无选择时的占位日期。
DatePickerInput classHTMLAttributes["class"]undefined合并到输入框的调用方类。
DatePickerContent side / alignPopperContentProps'bottom' / 'center'弹层定位属性。