Calendar 日历
日期面板。支持单选、多选、区间三种模式,以及日期标记。
三种模式
单选:Tue Sep 15 2026
多选:(未选)
区间:(未选)
vue
<script setup>
import { ref } from 'vue'
const single = ref(new Date())
const multiple = ref([])
const range = ref(null)
</script>
<template>
<SrCalendar v-model="single" mode="single" />
<SrCalendar v-model="multiple" mode="multiple" />
<SrCalendar v-model="range" mode="range" />
</template>值类型随模式变化
这是使用 SrCalendar 最容易出错的地方:
| 模式 | v-model 类型 | 说明 |
|---|---|---|
single | Date | null | 单个日期 |
multiple | Date[] | 日期数组 |
range | [Date, Date] | null | 起止元组 |
与 SrDatePicker 的值类型不同
SrDatePicker 的 v-model 是 'YYYY-MM-DD' 字符串, 而 SrCalendar 是 Date 对象。
为什么不一样:日历面板通常嵌在页面里直接与其他 Date 计算交互 (比如算某天有没有安排),用对象更顺手;而日期选择器的值要提交到接口、 存进表单,字符串更通用。
两者都能用 parseDate() / formatDate() 互相转换。
日期标记
cellMeta 是一个函数,每次渲染每个日期时调用一次,返回该格子的附加信息。
带角标的日期表示「当天有多少安排」或「空闲」,由宿主数据决定
vue
<script setup>
// 返回 undefined 表示该日期无标记
const cellMeta = (date) => {
const day = date.getDate()
if (day % 7 === 0) return { badge: '3' } // 右上角角标
if (day % 5 === 0) return { badge: '空闲' } // 短文本
return undefined
}
</script>
<template>
<SrCalendar v-model="picked" :cell-meta="cellMeta" />
</template>返回对象支持三个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
disabled | boolean | 该日期是否不可选 |
badge | string | 右上角角标(数字或短文本) |
label | string | 替换日期数字的显示文本 |
用 cellMeta 而不是传一个「有数据的日期数组」
传数组需要组件每次渲染时都去数组里查找,复杂度是 O(n) 每格—— 一个月 42 格就是 42 次查找。
用函数则把「查询」交给宿主:宿主可以用 Map 做到 O(1), 或者直接从已有的数据结构里取。组件只负责调用与渲染。
周起始日
week-start 设为 1 时以周一开始——中国大陆的习惯。
vue
<SrCalendar :week-start="1" /> <!-- 周一 -->
<SrCalendar :week-start="0" /> <!-- 周日(默认) -->年月选择器
show-month-picker 显示顶部的年 / 月下拉,方便快速跳到较远的月份。
vue
<SrCalendar :show-month-picker="true" />指定初始月份
month 控制面板初始显示的月份,不影响选中值。
vue
<SrCalendar v-model="picked" :month="new Date(2026, 0, 1)" />用于「先跳到某个历史月份」这类场景——比如从「上次记录的日期」开始浏览。
禁用
vue
<SrCalendar disabled />Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | Date | Date[] | [Date, Date] | null | null | 当前值,类型随 mode 变化 |
mode | 'single' | 'multiple' | 'range' | 'single' | 选择模式 |
month | Date | 当前月 | 初始显示的月份 |
weekStart | 0 | 1 | 0 | 周起始日 |
showToday | boolean | true | 是否显示「今天」按钮 |
showMonthPicker | boolean | false | 是否显示年月下拉 |
cellMeta | (date) => { disabled?, badge?, label? } | — | 每个日期的附加信息 |
disabled | boolean | false | 是否整体禁用 |
事件
| 事件 | 参数 | 说明 |
|---|---|---|
update:modelValue | 同 modelValue | 选中值变化 |
change | 同 modelValue | 同上(语义化事件) |
panelChange | Date | 面板切换月份时触发 |
无障碍
- 用
<table>语义表达日历网格——日期天然是「行 × 列」的结构, 读屏软件能播报「第 3 行第 2 列」帮助定位 - 今天带
aria-current="date",选中日期带aria-selected - 每个日期格子的
aria-label是完整日期(如「2026 年 9 月 14 日,星期一」), 而不是只有数字14——脱离上下文,「14」对读屏用户毫无意义 - 方向键在格子间移动,
Enter/Space选中