Skip to content

TimePicker 时间选择 - Vant 4

TimePicker 时间选择器 ⏰

介绍

精准掌控每一分每一秒!TimePicker 是一个功能强大的时间选择器组件,支持时、分、秒的灵活选择。无论是设置闹钟、预约时间还是记录时长,都能为用户提供流畅直观的时间选择体验。✨

⏰ 时光魔法师 - 精准捕捉每一个时刻!TimePicker 时间选择器如同一位贴心的时间管家,让用户轻松穿梭在时光隧道中,精准定位到心仪的时刻。通常与弹出层组件完美搭档,为用户呈现优雅的时间选择体验。

引入

通过以下方式来全局注册组件,更多注册方式请参考组件注册

js
import { createApp } from 'vue';
import { TimePicker } from 'vant';

const app = createApp();
app.use(TimePicker);

代码演示

🎯 基础用法 - 时间之舞的第一步

轻松选择时间,就像转动时钟指针一样简单!通过滑动操作即可快速定位到目标时间。

html
<template>
  <van-time-picker 
    v-model="currentTime" 
    title="选择时间" 
    @confirm="onConfirm"
    @cancel="onCancel"
  />
</template>
js
import { ref } from 'vue';
import { showToast } from 'vant';

export default {
  setup() {
    const currentTime = ref(['12', '00']);
    
    const onConfirm = ({ selectedValues }) => {
      showToast(`已选择时间:${selectedValues.join(':')}`);
    };
    
    const onCancel = () => {
      showToast('取消选择');
    };
    
    return { 
      currentTime,
      onConfirm,
      onCancel
    };
  },
};

🎨 选项类型 - 时间积木的自由拼搭

时间也能像积木一样自由组合!通过 columns-type 这个神奇的调色板,你可以随心所欲地控制时间选项的类型,支持对 hour(小时)、minute(分钟)和 second(秒)进行任意顺序的排列组合,打造专属的时间选择器。

比如:

  • 传入 ['hour'] 来单独选择小时。
  • 传入 ['minute'] 来单独选择分钟。
  • 传入 ['minute', 'second'] 来选择分钟和秒。
  • 传入 ['hour', 'minute', 'second'] 来选择小时、分钟和秒。
html
<!-- 选择时分 - 适合日常时间设置 -->
<van-time-picker
  v-model="currentTime"
  title="选择时间"
  :columns-type="['hour', 'minute']"
  @confirm="onTimeConfirm"
/>

<!-- 选择时分秒 - 精确到秒的时间控制 -->
<van-time-picker
  v-model="preciseTime"
  title="精确时间"
  :columns-type="['hour', 'minute', 'second']"
  @confirm="onPreciseTimeConfirm"
/>

<!-- 选择分秒 - 适合倒计时或计时器场景 -->
<van-time-picker
  v-model="durationTime"
  title="设置时长"
  :columns-type="['minute', 'second']"
  @confirm="onDurationConfirm"
/>
js
import { ref } from 'vue';

export default {
  setup() {
    const currentTime = ref(['12', '00']);
    const preciseTime = ref(['12', '00', '00']);
    const durationTime = ref(['05', '30']);
    
    const onTimeConfirm = ({ selectedValues }) => {
      console.log('选择的时间:', selectedValues.join(':'));
    };
    
    const onPreciseTimeConfirm = ({ selectedValues }) => {
      console.log('精确时间:', selectedValues.join(':'));
    };
    
    const onDurationConfirm = ({ selectedValues }) => {
      console.log('设置时长:', `${selectedValues[0]}分${selectedValues[1]}秒`);
    };
    
    return {
      currentTime,
      preciseTime,
      durationTime,
      onTimeConfirm,
      onPreciseTimeConfirm,
      onDurationConfirm
    };
  }
};

🚧 时间范围 - 为时光设置边界

给时间画个圈圈!你可以使用 min-hourmax-hour 等属性这些贴心的守护者,为小时(hour)、分钟(minute)和秒(second)设置专属的活动范围,让时间选择更加精准有序。

比如以下示例,用户可以选择的小时是 10 ~ 20 ,分钟是 30 ~ 40

html
<template>
  <!-- 限制小时和分钟范围 -->
  <van-time-picker
    v-model="currentTime"
    title="选择时间"
    :min-hour="10"
    :max-hour="20"
    :min-minute="30"
    :max-minute="40"
    @confirm="onConfirm"
  />
  
  <!-- 午餐时间选择器 (11:30-14:00) -->
  <van-time-picker
    v-model="lunchTime"
    title="选择午餐时间"
    :min-hour="11"
    :max-hour="14"
    :min-minute="30"
    @confirm="onLunchTimeConfirm"
  />
</template>
js
import { ref } from 'vue';
import { showToast } from 'vant';

export default {
  setup() {
    const currentTime = ref(['12', '35']);
    const lunchTime = ref(['12', '30']);
    
    const onConfirm = ({ selectedValues }) => {
      showToast(`已选择时间:${selectedValues.join(':')}`);
    };
    
    const onLunchTimeConfirm = ({ selectedValues }) => {
      showToast(`午餐时间:${selectedValues.join(':')}`);
    };
    
    return { 
      currentTime,
      lunchTime,
      onConfirm,
      onLunchTimeConfirm
    };
  },
};

### 🌍 整体时间范围 - 时光的完整疆域

一网打尽,整体把控!你可以使用 `min-time``max-time` 属性这对黄金搭档来限制整体时间范围,遵循 `10:00:00` 这个优雅的时间格式约定,让时间选择更加统一协调。

-   设置 `min-time` 后,`min-hour``min-minute``min-second` 属性将不会生效。
-   设置 `max-time` 后,`max-hour``max-minute``max-second` 属性将不会生效。

比如以下示例,用户可以选择从 `09:40:10``20:20:50` 的任意时间。

```html
js
import { ref } from'vue'; exportdefault { setup() { const currentTime = ref(['12', '00', '00']); return { currentTime }; }, };

✨ 格式化选项 - 时间文字的华丽变身

让时间穿上华丽的外衣!通过传入 formatter 这个神奇的化妆师函数,可以对选项的文字进行精美的格式化处理,让每个时间单位都闪闪发光,展现独特的魅力。

html
<template>
  <!-- 中文时间格式 -->
  <van-time-picker
    v-model="chineseTime"
    title="选择时间"
    :formatter="chineseFormatter"
    @confirm="onChineseTimeConfirm"
  />
  
  <!-- 12小时制格式 -->
  <van-time-picker
    v-model="ampmTime"
    title="选择时间"
    :formatter="ampmFormatter"
    @confirm="onAmpmTimeConfirm"
  />
</template>
js
import { ref } from 'vue';
import { showToast } from 'vant';

export default {
  setup() {
    const chineseTime = ref(['12', '00']);
    const ampmTime = ref(['12', '00']);

    // 中文格式化器
    const chineseFormatter = (type, option) => {
      if (type === 'hour') {
        option.text += '时';
      }
      if (type === 'minute') {
        option.text += '分';
      }
      if (type === 'second') {
        option.text += '秒';
      }
      return option;
    };
    
    // 12小时制格式化器
    const ampmFormatter = (type, option) => {
      if (type === 'hour') {
        const hour = parseInt(option.value);
        if (hour === 0) {
          option.text = '12 AM';
        } else if (hour < 12) {
          option.text = `${hour} AM`;
        } else if (hour === 12) {
          option.text = '12 PM';
        } else {
          option.text = `${hour - 12} PM`;
        }
      }
      if (type === 'minute') {
        option.text += ' min';
      }
      return option;
    };

    const onChineseTimeConfirm = ({ selectedValues }) => {
      showToast(`中文时间:${selectedValues[0]}时${selectedValues[1]}分`);
    };
    
    const onAmpmTimeConfirm = ({ selectedValues }) => {
      showToast(`12小时制:${selectedValues.join(':')}`);
    };

    return {
      chineseTime,
      ampmTime,
      chineseFormatter,
      ampmFormatter,
      onChineseTimeConfirm,
      onAmpmTimeConfirm
    };
  },
};

🔍 过滤选项 - 时间的精准筛选

时间也需要精挑细选!通过传入 filter 这个智能筛选器函数,可以对选项数组进行精准过滤,优雅地剔除不需要的时间选项,轻松实现自定义时间间隔,让每个时刻都恰到好处。

html
<template>
  <!-- 5分钟间隔选择器 -->
  <van-time-picker
    v-model="intervalTime"
    title="选择时间(5分钟间隔)"
    :filter="intervalFilter"
    @confirm="onIntervalTimeConfirm"
  />
  
  <!-- 工作时间过滤器 -->
  <van-time-picker
    v-model="workTime"
    title="选择工作时间"
    :filter="workTimeFilter"
    @confirm="onWorkTimeConfirm"
  />
</template>
js
import { ref } from 'vue';
import { showToast } from 'vant';

export default {
  setup() {
    const intervalTime = ref(['12', '00']);
    const workTime = ref(['09', '00']);

    // 5分钟间隔过滤器
    const intervalFilter = (type, options) => {
      if (type === 'minute') {
        return options.filter((option) => Number(option.value) % 5 === 0);
      }
      return options;
    };
    
    // 工作时间过滤器
    const workTimeFilter = (type, options) => {
      if (type === 'hour') {
        // 只显示工作时间 9:00-18:00
        return options.filter((option) => {
          const hour = Number(option.value);
          return hour >= 9 && hour <= 18;
        });
      }
      if (type === 'minute') {
        // 只显示整点和半点
        return options.filter((option) => {
          const minute = Number(option.value);
          return minute === 0 || minute === 30;
        });
      }
      return options;
    };

    const onIntervalTimeConfirm = ({ selectedValues }) => {
      showToast(`间隔时间:${selectedValues.join(':')}`);
    };
    
    const onWorkTimeConfirm = ({ selectedValues }) => {
      showToast(`工作时间:${selectedValues.join(':')}`);
    };

    return {
      intervalTime,
      workTime,
      intervalFilter,
      workTimeFilter,
      onIntervalTimeConfirm,
      onWorkTimeConfirm
    };
  },
};

🚀 高级用法 - 时间筛选的终极奥义

解锁时间筛选的终极技能!filter 函数的第三个参数就像一个时间侦探,能够敏锐地获取到当前选择的时间状态。这在使用非受控模式时,为你提供了更加灵活智能的过滤能力,让不需要的时间选项无所遁形。

html
js
exportdefault { setup() { constfilter = (type, options, values) => { const hour = +values[0]; if (type === 'hour') { return options.filter( (option) =>Number(option.value) >= 8 && Number(option.value) <= 18, ); } if (type === 'minute') { options = options.filter((option) =>Number(option.value) % 10 === 0); if (hour === 8) { return options.filter((option) =>Number(option.value) >= 40); } if (hour === 18) { return options.filter((option) =>Number(option.value) <= 20); } } return options; }; return { filter, }; }, };

API

Props

参数说明类型默认值
v-model🎯 当前选中的时间,时间数组格式string[]-
columns-type⚙️ 选项类型,由 hourminutesecond 组成的数组string[]['hour', 'minute']
min-hour🌅 可选的最小小时,限制时间下限number | string0
max-hour🌇 可选的最大小时,限制时间上限number | string23
min-minute⏰ 可选的最小分钟,精确控制分钟范围number | string0
max-minute⏰ 可选的最大分钟,精确控制分钟范围number | string59
min-second⏱️ 可选的最小秒数,精确到秒级控制number | string0
max-second⏱️ 可选的最大秒数,精确到秒级控制number | string59
min-time v4.5.0🚫 可选的最小时间,格式参考 07:40:00,使用时 min-hour``min-minute``min-second 不会生效string-
max-time v4.5.0🚫 可选的最大时间,格式参考 10:20:00,使用时 max-hour``max-minute``max-second 不会生效string-
title📝 顶部栏标题,为选择器添加说明string''
confirm-button-text✅ 确认按钮文字,自定义确认操作文案string确认
cancel-button-text❌ 取消按钮文字,自定义取消操作文案string取消
show-toolbar🔧 是否显示顶部栏,控制工具栏可见性booleantrue
loading⏳ 是否显示加载状态,异步数据加载时使用booleanfalse
readonly🔒 是否为只读状态,只读状态下无法切换选项booleanfalse
filter🔍 选项过滤函数,自定义选项显示逻辑(type: string, options: PickerOption[], values: string[]) => PickerOption[]-
formatter🎨 选项格式化函数,自定义选项显示格式(type: string, option: PickerOption) => PickerOption-
option-height📏 选项高度,支持 px``vw``vh``rem 单位,默认 pxnumber | string44
visible-option-num👁️ 可见的选项个数,控制显示区域大小number | string6
swipe-duration🏃 快速滑动时惯性滚动的时长,单位 msnumber | string1000

Events

事件名说明回调参数
confirm✅ 点击完成按钮时触发,确认时间选择{ selectedValues, selectedOptions, selectedIndexes }
cancel❌ 点击取消按钮时触发,取消时间选择{ selectedValues, selectedOptions, selectedIndexes }
change🔄 选项改变时触发,实时响应用户操作{ selectedValues, selectedOptions, selectedIndexes, columnIndex }

Slots

名称说明参数
toolbar🔧 自定义整个顶部栏的内容,完全控制工具栏样式-
title📝 自定义标题内容,个性化标题显示-
confirm✅ 自定义确认按钮内容,打造独特确认体验-
cancel❌ 自定义取消按钮内容,个性化取消操作-
option🎨 自定义选项内容,完全控制选项显示样式option: PickerOption, index: number
columns-top⬆️ 自定义选项上方内容,添加额外信息展示-
columns-bottom⬇️ 自定义选项下方内容,补充说明或操作区域-

方法

通过 ref 可以获取到 Picker 实例并调用实例方法,详见组件实例方法

方法名说明参数返回值
confirm✅ 停止惯性滚动并触发 confirm 事件,程序化确认选择--
getSelectedTime🎯 获取当前选中的时间,便于外部获取选择结果-string[]

类型定义 📝

完善的 TypeScript 支持,让开发更加安全可靠!组件导出以下类型定义:

ts
import type { TimePickerProps, TimePickerColumnType } from 'vant';

TimePickerInstance 是组件实例的类型,用法如下:

ts
import { ref } from 'vue';
import type { TimePickerInstance } from 'vant';

const timePickerRef = ref<TimePickerInstance>();

// 程序化确认选择
timePickerRef.value?.confirm();

// 获取当前选中时间
const selectedTime = timePickerRef.value?.getSelectedTime();

常见问题

在桌面端无法操作组件?

参见桌面端适配

🎯 最佳实践

时间选择器与弹窗的完美结合

html
<template>
  <van-cell title="选择时间" :value="timeText" @click="showPicker = true" />
  <van-popup v-model:show="showPicker" position="bottom">
    <van-time-picker
      v-model="currentTime"
      title="选择时间"
      @confirm="onConfirm"
      @cancel="showPicker = false"
    />
  </van-popup>
</template>

<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    const showPicker = ref(false);
    const currentTime = ref(['12', '00']);
    
    const timeText = computed(() => {
      return currentTime.value.join(':');
    });
    
    const onConfirm = ({ selectedValues }) => {
      currentTime.value = selectedValues;
      showPicker.value = false;
    };
    
    return {
      showPicker,
      currentTime,
      timeText,
      onConfirm
    };
  }
};
</script>

响应式时间范围设置

javascript
// 根据当前时间动态设置可选范围
const getCurrentTimeRange = () => {
  const now = new Date();
  const currentHour = now.getHours();
  const currentMinute = now.getMinutes();
  
  return {
    minHour: currentHour,
    minMinute: currentHour === now.getHours() ? currentMinute : 0,
    maxHour: 23,
    maxMinute: 59
  };
};

智能默认值设置

javascript
// 设置合理的默认时间
const getDefaultTime = () => {
  const now = new Date();
  const hour = now.getHours().toString().padStart(2, '0');
  const minute = Math.ceil(now.getMinutes() / 15) * 15; // 向上取整到15分钟
  return [hour, minute.toString().padStart(2, '0')];
};

💡 使用技巧

多语言时间格式化

javascript
const createTimeFormatter = (locale = 'zh-CN') => {
  const formatMap = {
    'zh-CN': { hour: '时', minute: '分', second: '秒' },
    'en-US': { hour: 'h', minute: 'm', second: 's' },
    'ja-JP': { hour: '時', minute: '分', second: '秒' }
  };
  
  return (type, option) => {
    const suffix = formatMap[locale][type] || '';
    option.text += suffix;
    return option;
  };
};

高级过滤功能

javascript
// 工作时间过滤器
const workTimeFilter = (type, options, values) => {
  if (type === 'hour') {
    return options.filter(option => {
      const hour = Number(option.value);
      return hour >= 9 && hour <= 18; // 只显示工作时间
    });
  }
  
  if (type === 'minute') {
    return options.filter(option => {
      const minute = Number(option.value);
      return minute % 15 === 0; // 只显示15分钟间隔
    });
  }
  
  return options;
};

动态列类型切换

html
<template>
  <van-radio-group v-model="timeMode" direction="horizontal">
    <van-radio name="hour">仅小时</van-radio>
    <van-radio name="minute">小时分钟</van-radio>
    <van-radio name="second">精确到秒</van-radio>
  </van-radio-group>
  
  <van-time-picker
    v-model="currentTime"
    :columns-type="columnsType"
  />
</template>

<script>
const timeMode = ref('minute');
const columnsType = computed(() => {
  const modeMap = {
    hour: ['hour'],
    minute: ['hour', 'minute'],
    second: ['hour', 'minute', 'second']
  };
  return modeMap[timeMode.value];
});
</script>

🔧 常见问题解决

时间格式兼容性

javascript
// 处理不同时间格式的转换
const formatTimeValue = (timeArray) => {
  return timeArray.map(time => {
    // 确保时间值为两位数
    return time.toString().padStart(2, '0');
  });
};

// 解析时间字符串
const parseTimeString = (timeString) => {
  const parts = timeString.split(':');
  return parts.map(part => part.padStart(2, '0'));
};

性能优化

javascript
// 使用防抖优化频繁的change事件
import { debounce } from 'lodash-es';

const debouncedChange = debounce((selectedValues) => {
  console.log('时间选择变化:', selectedValues);
  // 执行相关业务逻辑
}, 300);

数据验证与错误处理

javascript
// 时间有效性验证
const validateTime = (timeArray) => {
  const [hour, minute, second] = timeArray.map(Number);
  
  if (hour < 0 || hour > 23) return false;
  if (minute < 0 || minute > 59) return false;
  if (second !== undefined && (second < 0 || second > 59)) return false;
  
  return true;
};

// 错误处理
const handleTimeError = (error) => {
  console.error('时间选择器错误:', error);
  // 重置为默认时间
  currentTime.value = ['12', '00'];
};

🎨 设计灵感

主题化时间选择器

css
/* 深色主题 */
.van-time-picker--dark {
  --van-picker-background: #1a1a1a;
  --van-picker-option-text-color: #ffffff;
  --van-picker-option-selected-text-color: #1989fa;
}

/* 渐变背景 */
.van-time-picker--gradient {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

/* 圆角风格 */
.van-time-picker--rounded {
  border-radius: 20px;
  overflow: hidden;
}

动画效果增强

css
/* 选项切换动画 */
.van-picker-column__item {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.van-picker-column__item--selected {
  transform: scale(1.1);
  font-weight: bold;
  color: #1989fa;
}

/* 弹出动画 */
.time-picker-enter-active,
.time-picker-leave-active {
  transition: all 0.3s ease;
}

.time-picker-enter-from,
.time-picker-leave-to {
  opacity: 0;
  transform: translateY(100%);
}

创意交互效果

javascript
// 震动反馈
const handleTimeChange = () => {
  if ('vibrate' in navigator) {
    navigator.vibrate(50); // 轻微震动反馈
  }
};

// 声音反馈
const playTickSound = () => {
  const audio = new Audio('/sounds/tick.mp3');
  audio.volume = 0.3;
  audio.play().catch(() => {
    // 静默处理音频播放失败
  });
};

🚀 高级功能扩展

智能时间推荐系统

javascript
// 基于用户历史选择的智能推荐
class TimeRecommendationSystem {
  constructor() {
    this.history = JSON.parse(localStorage.getItem('timeHistory') || '[]');
  }
  
  // 记录用户选择
  recordSelection(timeArray) {
    this.history.push({
      time: timeArray,
      timestamp: Date.now()
    });
    
    // 只保留最近50次记录
    if (this.history.length > 50) {
      this.history = this.history.slice(-50);
    }
    
    localStorage.setItem('timeHistory', JSON.stringify(this.history));
  }
  
  // 获取推荐时间
  getRecommendations() {
    const timeFrequency = {};
    
    this.history.forEach(record => {
      const timeKey = record.time.join(':');
      timeFrequency[timeKey] = (timeFrequency[timeKey] || 0) + 1;
    });
    
    return Object.entries(timeFrequency)
      .sort(([,a], [,b]) => b - a)
      .slice(0, 3)
      .map(([time]) => time.split(':'));
  }
}

多时区时间选择器

javascript
// 多时区支持
const createTimezoneTimePicker = (timezone = 'Asia/Shanghai') => {
  const getTimezoneTime = () => {
    const now = new Date();
    const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
    const targetTime = new Date(utc + (getTimezoneOffset(timezone) * 60000));
    
    return [
      targetTime.getHours().toString().padStart(2, '0'),
      targetTime.getMinutes().toString().padStart(2, '0')
    ];
  };
  
  const getTimezoneOffset = (tz) => {
    // 时区偏移量映射
    const timezones = {
      'Asia/Shanghai': 8,
      'America/New_York': -5,
      'Europe/London': 0,
      'Asia/Tokyo': 9
    };
    return (timezones[tz] || 0) * 3600000;
  };
  
  return {
    getTimezoneTime,
    formatTimezone: (time) => {
      return `${time.join(':')} (${timezone})`;
    }
  };
};

📚 延伸阅读

技术文档

设计指南

用户体验

相关文档 📚

🎯 核心组件

🔧 辅助组件

⚡ 高级功能

基於Vant構建的企業級移動端解決方案