# DateRangePicker 日期范围

一次选出「开始 ~ 结束」两端。面板并排展示两个月，选中开始日期后悬停即可预览整段区间。与 `MDatePicker` 共用同一套日期工具与 format 语义：`format` 里出现 `H` / `mm` / `ss` 时启用时间选择，输出数组两端都是 `format` 格式字符串（未选的一端为 `null`）。

## 代码演示

### 基础用法

`v-model` 绑定 `[开始, 结束]`。未选满两端时，另一端是 `null`；整体清空时绑定值为 `null`。

**演示说明：** `clearable` 在两端都有值后显示清除按钮。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const range = ref<[string | null, string | null] | null>(null)
</script>

<template>
  <div class="m-row-demo--col">
    <MDateRangePicker
      v-model="range"
      :placeholder="['开始', '结束']"
      format="YYYY-MM-DD"
      clearable />
    <span>选中：{{ range ? `${range[0] ?? '未选'} ~ ${range[1] ?? '未选'}` : '未选择' }}</span>
  </div>
</template>
```

### 日期时间模式

`format` 含时间占位符时进入 datetime 模式，面板底部出现时钟按钮，可分别调整起止时间的时分秒。

**演示说明：** 点选日期不会立即关闭面板，需要点「确定」。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const range = ref<[string | null, string | null] | null>(null)

function fmt(value: [string | null, string | null] | null) {
  return value ? `${value[0] ?? '未选'} ~ ${value[1] ?? '未选'}` : '未选择'
}
</script>

<template>
  <div class="m-row-demo--col">
    <MDateRangePicker
      v-model="range"
      format="YYYY-MM-DD HH:mm"
      :placeholder="['开始时间', '结束时间']"
      clearable />
    <span>选中：{{ fmt(range) }}</span>
  </div>
</template>
```

### 自定义占位符与格式

`placeholder` 传单个字符串时两格共用，传二元组时分别用于开始 / 结束。`format` 决定回写的字符串格式。

**演示说明：** 最后两条分别展示两种占位符写法与两种输出格式。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const single = ref<[string | null, string | null] | null>(null)
const pair = ref<[string | null, string | null] | null>(null)

function fmt(value: [string | null, string | null] | null) {
  return value ? `${value[0] ?? '未选'} ~ ${value[1] ?? '未选'}` : '未选择'
}
</script>

<template>
  <div class="m-row-demo--col">
    <MDateRangePicker v-model="single" placeholder="请选择日期区间" format="YYYY/MM/DD" />
    <MDateRangePicker v-model="pair" :placeholder="['入住', '离店']" format="YYYY-MM-DD" />
    <span>单个字符串：{{ fmt(single) }}</span>
    <span>数组分别指定：{{ fmt(pair) }}</span>
  </div>
</template>
```

### 尺寸与圆角

`size` 三档；`radius` 改触发区圆角，`panelRadius` 改弹出面板圆角，`width` 改触发区宽度。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const small = ref<[string | null, string | null] | null>(null)
const normal = ref<[string | null, string | null] | null>(null)
const large = ref<[string | null, string | null] | null>(null)
const rounded = ref<[string | null, string | null] | null>(null)
</script>

<template>
  <div class="m-row-demo--col">
    <div class="m-row-demo">
      <MDateRangePicker v-model="small" size="small" :placeholder="['小', '小']" />
      <MDateRangePicker v-model="normal" size="default" :placeholder="['默认', '默认']" />
      <MDateRangePicker v-model="large" size="large" :placeholder="['大', '大']" />
    </div>
    <MDateRangePicker
      v-model="rounded"
      width="280px"
      radius="999px"
      panel-radius="16px"
      :placeholder="['自定义宽度', '自定义圆角']" />
  </div>
</template>
```

### 可选范围与禁用

`min` / `max` 限定两端可选区间；`disabled` 整体禁用。

**演示说明：** `min` / `max` 接受 `Date` 或可解析字符串。

```vue
<script setup lang="ts">
import { ref } from 'vue'

function pad(value: number) {
  return value < 10 ? `0${value}` : String(value)
}
function ymd(date: Date) {
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
}

const today = new Date()
const min = ymd(today)
const max = ymd(new Date(today.getFullYear(), today.getMonth() + 2, today.getDate()))

const inRange = ref<[string | null, string | null] | null>(null)
const locked = ref<[string | null, string | null] | null>(null)

function fmt(value: [string | null, string | null] | null) {
  return value ? `${value[0] ?? '未选'} ~ ${value[1] ?? '未选'}` : '未选择'
}
</script>

<template>
  <div class="m-row-demo--col">
    <MDateRangePicker
      v-model="inRange"
      :min="min"
      :max="max"
      :placeholder="['今天起', '两个月内']"
      clearable />
    <MDateRangePicker v-model="locked" disabled :placeholder="['禁用', '禁用']" />
    <span>范围内选中：{{ fmt(inRange) }}</span>
    <span>禁用态值：{{ fmt(locked) }}</span>
  </div>
</template>
```

## API

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelValue` | `DateRangeValue` | `undefined` | 当前值（v-model）：`[开始, 结束]` |
| `placeholder` | `string \| [string, string]` | `'开始日期 ~ 结束日期'` | 占位符：单个字符串同时用于两格，数组则分别用于开始 / 结束 |
| `format` | `string` | `'YYYY-MM-DD'` | 输出格式模板，同时决定是否启用时间选择 |
| `disabled` | `boolean` | `false` | 是否禁用 |
| `clearable` | `boolean` | `false` | 有值时显示清除按钮 |
| `size` | `'small' \| 'default' \| 'large'` | `'default'` | 尺寸 |
| `min` | `Date \| string` | `undefined` | 可选最小日期 |
| `max` | `Date \| string` | `undefined` | 可选最大日期 |
| `appendToBody` | `boolean` | `true` | 挂载到 body |
| `popupPlacement` | `'top' \| 'bottom'` | `'bottom'` | 弹出方向（空间不足时自动翻转） |
| `width` | `string` | `undefined` | 触发区宽度 |
| `radius` | `string` | `undefined` | 触发区圆角（任意 CSS 长度）；默认 4px |
| `panelRadius` | `string` | `undefined` | 弹出面板圆角（任意 CSS 长度）；默认 4px |
| `name` | `string` | `undefined` | 原生 input name（表单提交） |

### Events

| 事件 | 参数 | 说明 |
| --- | --- | --- |
| `update:modelValue` | `value: [string \| null, string \| null] \| null` | 范围变化（各端为 `format` 字符串，未选为 `null`），清空时为 `null` |
| `change` | `value: [string \| null, string \| null] \| null` | 范围变化 |
| `open` | 无 | 面板打开 |
| `close` | 无 | 面板关闭 |

### Slots

无。

### 类型定义

```ts
type DateRangeValue =
  | [Date | string | null, Date | string | null]
  | null
```

### 范围选择规则

- 当前无选择、或已选满两端时再次点击，会以该点为新的开始日期；
- 已有开始日期、再点击更早的日期，会把开始日期替换为新的这一天；
- 已有开始日期、再点击更晚的日期，补全结束日期，完成本次选择。

## 使用建议

::: tip 输出是数组，不是拼接字符串
回写值形如 `['2026-09-01', '2026-09-30']`，两端都可能为 `null`；「整体为 `null`」表示两侧都被清除。处理时先判 `null` 再取下标，不要把 `[null, null]` 当成有效范围。
:::

::: tip 与 DatePicker 保持同一套 format
两个组件的 format 语义完全一致，同一页面内建议统一，避免日期与区间出现不同分隔符或不同时间精度。
:::

::: warning 小写 mm 仍是「分」
和 `MDatePicker` 一样，`MM`（月）与 `mm`（分）区分大小写，`format` 里出现 `mm` 就会进入时间模式。只想要日期就用 `YYYY/MM/DD` 这类不含 `H`、`mm`、`ss` 的模板。
:::

::: warning 没有内置的「最近 7 天」快捷预设
面板只提供年 / 月快捷跳转，以及底部的「清除」「确定」。要「今天 / 近 7 天 / 本月」这类相对区间，需要业务侧算好两端日期后写进 `v-model`。
:::

## 相关组件

- [DatePicker 日期选择](/components/date-picker)：只需要选单个日期时使用。
- [Input 输入框](/components/input)：只需要自由文本输入、不做日期约束时使用。
- [Select 选择器](/components/select)：取值来自固定几项时使用。
