拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Vuetify 日期范围选择器(v-date-range-picker)完全指南:双日历联动、预设侧栏与完整布局实战

Vuetify 日期范围选择器v-date-range-picker完全指南双日历联动、预设侧栏与完整布局实战【免费下载链接】vuetify Vue Component Framework项目地址: https://gitcode.com/gh_mirrors/vu/vuetifyv-date-range-picker是 Vuetify 提供的日期范围选择组件它在两个并排且相互联动的v-date-picker日历面板之上封装了一层让用户能一次性跨两个月选择一段连续的日期范围。本指南围绕该组件从安装、基础用法、API 到高级实战预设侧栏、完整布局展开并结合当前仓库源码packages/vuetify/src/labs/VDateRangePicker/剖析其双面板同步、键盘导航与独立月份模式等底层实现帮助你快速掌握并在实际项目中落地。提示该功能属于 Vuetify Labs 组件需要v4.1.0及以上版本并需手动引入注册。安装Labs 组件的手动引入Labs 组件默认不会随vuetify主包自动注册必须手动导入并注册到 Vuetify 实例中import { VDateRangePicker } from vuetify/labs/VDateRangePicker对应的导出入口定义在仓库的 index.ts组件主体位于 VDateRangePicker.tsx。若希望通过全量 Labs 出口引入也可从 labs/components.ts 获取该文件同时导出了VAvatarGroup、VCommandPalette、VMonthPicker、VHeatmap、VHighlight、VMaskInput、VPie、VProgress、VVideo等 Labs 组件。基础用法一次点击定起点两次点击成范围组件通过v-model绑定一个包含两个日期的数组。交互规则很直观第一次点击某个日期将其设为范围的起点start第二次点击另一个日期将其设为范围的终点end再次点击则开始一段新的范围。template v-date-range-picker v-modelrange/v-date-range-picker /template script setup const range ref([2026-04-17, 2026-05-12]) /script文档示例usage.vue还展示了组件支持的两个核心配置color通过color属性一键切换主题色可传primary、success、purple等语义色也支持#fa0这样的自定义十六进制色independent-months布尔属性开启后左右两个日历面板可各自独立翻月中间允许出现“月份间隙”详见下文原理部分。API 一览| 组件 | 说明 | | - | - | |v-date-range-picker| 主要组件Labs 组件 | |v-date-picker| 底层的日历面板组件 |v-date-range-picker本质上是一层“薄封装”其组件 props 定义见源码 VDateRangePicker.tsxexport const makeVDateRangePickerProps propsFactory({ independentMonths: Boolean, modelValue: null, ...makeVPickerProps({ title: $vuetify.datePicker.title }), ...pick(makeVDatePickerProps(), datePickerForwardedKeys), }, VDateRangePicker)即它合并了VPicker的基础 props并精选转发pick了一组VDatePicker的关键 props完整的转发键列表定义在 datePickerForwardedKeys范围与限制min、max、allowedDates、allowedMonths、allowedYears——用于约束可选日期范围周历与星期weekdays、firstDayOfWeek、firstDayOfYear、weekdayFormat、weeksInMonth、hideWeekdays、showWeek外观与行为color、readonly、disabled、prevIcon、nextIcon、transition、reverseTransition、controlHeight。这些属性会被同时应用到左右两个面板上保证双面板行为一致。实现原理双面板薄封装文档明确说明该组件是两个v-date-picker实例的薄包装。从 VDateRangePicker.tsx 的渲染函数可以确认这一设计外层是VPicker隐藏头部hideHeader默认插槽内渲染两个并排的VDatePicker分别带有v-date-range-picker__panel类名两者之间用一个roleseparator的分隔元素隔开两个面板都强制设置multiplerange启用范围多选模式noAutoNavigation关闭点击自动翻月showAdjacentMonths{ false }隐藏相邻月份的日期避免左侧面板里出现右侧月份的重叠日期hideHeader隐藏各面板自身的头部previewValue双向绑定悬停预览跨面板联动。月份同步机制默认非independent-months情况下右侧面板始终比左侧面板晚一个月初始化时调用syncRightFromLeft()将右面板设为leftMonth 1监听leftMonth/leftYear变化并调用syncRightFromLeft()监听rightMonth/rightYear变化则调用syncLeftFromRight()两个同步函数用syncing标志位防止互相递归触发源码 VDateRangePicker.tsx。翻月用的工具函数是shiftMonth(month, year, delta)通过total month delta year * 12的取模/整除运算安全地跨年翻月如 12 月翻到下一年 1 月。独立月份模式independent-months开启independent-months后左右面板可以各自翻月中间允许出现“间隙月份”每个面板会额外显示朝内侧的导航按钮分别通过leftNextDisabled/rightPrevDisabled计算当左面板下一个月与右面板重叠时禁用左面板的“下月”按钮反之亦然见源码 VDateRangePicker.tsx从源码的 watcher 可见VDateRangePicker.tsx当独立模式关闭时组件会重新“配对”两个面板——若右面板正好持有范围起点则回填左面板否则默认右面板 左面板 1模型更新时navigateToSelection()会根据范围的起点与终点自动导航默认模式直接把左面板移到起点月份、右面板跟随独立模式则把右面板定位到终点所在月份允许中间留出间隙。键盘导航与跨面板焦点组件实现了跨面板的键盘方向键导航onBoundaryNavigate源码 VDateRangePicker.tsx当方向键的目标日期落在某个面板中时直接调用该面板的focusDate(iso)转移焦点当目标日期在当前视野之外时根据方向移动对应面板的月份把目标日期带入视野随后通过nextTick聚焦独立模式下如果目标落在“间隙”里则由方向键left/up视为后退right/down视为前进决定移动哪个面板。这些行为在浏览器测试 VDateRangePicker.spec.browser.tsx 中有完整验证例如默认渲染两个相邻面板且showAdjacentMonths被强制关闭测试断言2026-05-01只渲染一次源码第 15-27 行左面板最后一天按ArrowRight焦点跳到右面板对应日期而非翻月第 47-64 行左面板第一天按ArrowLeft双面板整体回退一个月第 66-80 行一个面板内的悬停预览会跨面板延伸第 82-98 行模型被外部切换到视野外日期时双面板自动跟随导航第 100-140 行独立模式下左右面板可各自独立移动、导航按钮随“间隙”出现而启用/禁用第 142-247 行。悬停预览跨面板联动源码通过共享同一个previewValue让两个面板的悬停预览联动任一面板悬停日期另一面板也会渲染出从起点到该日期的预览色带。测试用例验证了在只选中起点2026-04-10的情况下悬停右面板的2026-05-15后左面板的2026-04-20与右面板的2026-05-05都会带上v-date-picker-month__day--preview-middle预览类VDateRangePicker.spec.browser.tsx。样式定制组件样式位于 VDateRangePicker.sass并暴露了以下 Sass 变量_variables.scss$date-range-picker-divider-color: rgba(var(--v-border-color), var(--v-border-opacity)) !default; $date-range-picker-footer-min-height: 52px !default; $date-range-picker-footer-padding: .5rem !default; $date-range-picker-footer-gap: .5rem !default;可分别定制两面板之间的分隔线颜色、底部 footer 区的最小高度/内边距/间距。实战一预设侧栏Presets sidebar最常见的搭配模式是左侧一排预设快捷项最近 7 天、本月、上月等 右侧日期范围选择器。两者放在一个 flex 容器里选择器保持自包含预设列表通过外部修改v-model驱动范围。完整示例见 misc-presets.vue核心结构如下template v-container v-row justifycenter v-card classd-flex elevation2 rounded v-list v-model:selectedpreset :itemspresets densitycompact width180 mandatory nav /v-list v-divider vertical/v-divider v-date-range-picker v-modelmodel/v-date-range-picker /v-card /v-row v-row classmt-4 justifycenter v-chip v-ifmodel?.length 2 :text${formatDate(model[0])} ~ ${formatDate(model[1])} /v-chip /v-row /v-container /template script setup import { useDate } from vuetify const adapter useDate() const formatDate v adapter.format(adapter.date(v), keyboardDate) const today adapter.date() function daysAgo (count) { return adapter.addDays(today, -count) } const presets [ { title: Last 7 days, value: last-7d }, { title: Last 30 days, value: last-30d }, { title: This month, value: this-month }, { title: Last month, value: last-month }, ] const preset ref([last-7d]) const model ref([daysAgo(6), today]) watch(preset, ([value]) { if (value last-7d) model.value [daysAgo(6), today] else if (value last-30d) model.value [daysAgo(29), today] else if (value this-month) model.value [adapter.startOfMonth(today), today] else if (value last-month) { const lastMonth adapter.addMonths(today, -1) model.value [adapter.startOfMonth(lastMonth), adapter.endOfMonth(lastMonth)] } }) /script关键点通过useDate()获取 Vuetify 的日期适配器实例使用addDays、addMonths、startOfMonth、endOfMonth等 API 计算预设日期v-list开启mandatory保证始终有一项被选中nav使其呈现导航式紧凑样式用watch(preset, ...)监听预设变化并整体替换model——这正是前面原理部分提到的“模型外部切换时组件自动导航面板”的场景无需手动同步月份。实战二完整布局Full layout在预设侧栏的基础上可以再叠加一个底部 footer放置两个只读输入框实时回显起止日期以及“Cancel / Apply”按钮。这里的关键技巧是引入一个draft草稿模型 applied已应用模型的双层结构用户先自由调整草稿点 Apply 才提交点 Cancel 则回滚。完整示例见 misc-full-layout.vuetemplate v-container v-row justifycenter v-card classd-flex elevation2 rounded v-list v-model:selectedpreset :itemspresets densitycompact width180 mandatory nav /v-list v-divider vertical/v-divider div classd-flex flex-column v-date-range-picker v-modeldraft independent-months template v-slot:footer v-text-field :model-valueformatDate(draft?.[0]) densitycompact stylemax-width: 140px variantoutlined hide-details readonly /v-text-field span classtext-medium-emphasis»/span v-text-field :model-valueformatDate(draft?.[1]) densitycompact stylemax-width: 140px variantoutlined hide-details readonly /v-text-field v-spacer/v-spacer v-btn textCancel varianttext clickcancel/v-btn v-btn :disabled!hasRange colorprimary textApply clickapply /v-btn /template /v-date-range-picker /div /v-card /v-row div v-ifapplied classd-flex align-center justify-center mt-4 v-chip :text${formatDate(applied[0])} ~ ${formatDate(applied[1])}/v-chip ← click v-code classmx-2Apply/v-code to sync /div /v-container /template script setup import { useDate } from vuetify const adapter useDate() const formatDate v (v ? adapter.format(adapter.date(v), keyboardDate) : ) const today adapter.date() function daysAgo (count) { return adapter.addDays(today, -count) } function monthsAgo (count) { return adapter.addMonths(today, -count) } const presets [ { title: Today, value: today }, { title: Last 7 days, value: last-7d }, { title: Last 30 days, value: last-30d }, { title: Last 3 months, value: last-3m }, { title: Last 12 months, value: last-12m }, { title: Month to date, value: month-to-date }, { title: Year to date, value: year-to-date }, ] const preset ref([last-30d]) const draft ref([daysAgo(6), today]) const applied ref([daysAgo(6), today]) const hasRange computed(() draft.value?.length 2) watch(preset, ([value]) { if (value today) draft.value [today, today] else if (value last-7d) draft.value [daysAgo(6), today] else if (value last-30d) draft.value [daysAgo(29), today] else if (value last-3m) draft.value [monthsAgo(3), today] else if (value last-12m) draft.value [monthsAgo(12), today] else if (value month-to-date) draft.value [adapter.startOfMonth(today), today] else if (value year-to-date) draft.value [adapter.startOfYear(today), today] }) function apply () { applied.value [...draft.value] } function cancel () { draft.value applied.value ? [...applied.value] : [] } /script值得注意的实现细节这里使用了independent-months模式当预设跨越较长时间如“Last 12 months”时左右面板不需要强制相邻用户可以单独翻动某一侧的面板footer 插槽通过template v-slot:footer注入内容。源码中该插槽被映射到VPicker的actions插槽VDateRangePicker.tsx渲染位置在日历主体下方对应样式变量$date-range-picker-footer-min-height: 52px等。浏览器测试也验证了 footer 插槽内容会渲染在.v-date-range-picker .v-picker__actions内、位于日历主体之后VDateRangePicker.spec.browser.tsx草稿/提交模式draft直接绑定组件applied保存已确认范围。Apply把草稿复制到已应用Cancel用已应用值回填草稿。未选满两个日期时hasRange为 falseApply 按钮自动禁用底部用两个只读v-text-fieldreadonly实时展示草稿的起止日期格式通过适配器的keyboardDate模板格式化。小结v-date-range-picker用两个联动的v-date-picker面板实现了跨月范围选择开箱即用、语义清晰v-model绑定[start, end]数组independent-months控制面板是否可独立翻月footer插槽支持自定义底部操作区。结合预设侧栏与草稿/提交模式可以快速搭建出符合生产要求的日期范围筛选器。若需深入了解其双面板同步、跨面板键盘导航与独立月份边界处理可直接阅读源码 VDateRangePicker.tsx 及其配套浏览器测试 VDateRangePicker.spec.browser.tsx。【免费下载链接】vuetify Vue Component Framework项目地址: https://gitcode.com/gh_mirrors/vu/vuetify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门