Skip to content

Time Select 时间选择

概述

Time Select 时间选择组件用于选择或输入时间,提供了一个用户友好的时间选择界面。它支持时、分、秒的选择,可以设置时间范围、步长等,适用于各种需要时间输入的场景。

主要特性

  • 灵活的时间格式:支持多种时间格式显示
  • 时间范围限制:可设置可选时间的范围
  • 步长控制:支持自定义时间步长
  • 禁用时间:可禁用特定时间段
  • 表单集成:与 el-form 完美集成
  • 键盘操作:支持键盘快捷操作

适用场景

  • 预约系统的时间选择
  • 会议安排和日程管理
  • 工作时间设置
  • 定时任务配置
  • 营业时间设置

学习目标

基础知识

  • 掌握 Time Select 组件的基本概念和使用场景
  • 学会基础时间选择功能的实现
  • 了解时间格式的配置方法
  • 掌握时间范围的设置

进阶技能

  • 学会自定义时间步长
  • 掌握禁用时间的配置
  • 了解时间验证和校验
  • 学会自定义时间显示格式

实战应用

  • 能够构建完整的预约系统
  • 掌握会议室预订功能
  • 了解性能优化和用户体验提升
  • 学会与其他组件的集成使用

基础用法

基本时间选择

最简单的时间选择功能:

vue
<template>
  <div>
    <h4>基础时间选择</h4>
    <el-time-select
      v-model="value"
      start="08:30"
      step="00:15"
      end="18:30"
      placeholder="选择时间"
      @change="handleChange"
    />
    <p>选择的时间:{{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref('')

const handleChange = (val) => {
  console.log('时间变化:', val)
}
</script>

固定时间点

设置固定的时间选项:

vue
<template>
  <div>
    <h4>固定时间点</h4>
    <el-time-select
      v-model="value"
      start="09:00"
      step="01:00"
      end="17:00"
      placeholder="选择工作时间"
      @change="handleChange"
    />
    <p>工作时间:{{ value }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const value = ref('')

const handleChange = (val) => {
  console.log('工作时间:', val)
}
</script>

限制时间范围

通过 start 和 end 属性限制可选时间范围:

vue
<template>
  <div>
    <h4>限制时间范围</h4>
    
    <div class="time-range-example">
      <label>上午时间段(9:00-12:00):</label>
      <el-time-select
        v-model="morningTime"
        start="09:00"
        step="00:30"
        end="12:00"
        placeholder="选择上午时间"
        @change="handleMorningChange"
      />
    </div>
    
    <div class="time-range-example">
      <label>下午时间段(14:00-18:00):</label>
      <el-time-select
        v-model="afternoonTime"
        start="14:00"
        step="00:30"
        end="18:00"
        placeholder="选择下午时间"
        @change="handleAfternoonChange"
      />
    </div>
    
    <div class="time-range-example">
      <label>晚上时间段(19:00-22:00):</label>
      <el-time-select
        v-model="eveningTime"
        start="19:00"
        step="00:15"
        end="22:00"
        placeholder="选择晚上时间"
        @change="handleEveningChange"
      />
    </div>
    
    <div class="selected-times">
      <h5>已选择的时间:</h5>
      <p>上午:{{ morningTime || '未选择' }}</p>
      <p>下午:{{ afternoonTime || '未选择' }}</p>
      <p>晚上:{{ eveningTime || '未选择' }}</p>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const morningTime = ref('')
const afternoonTime = ref('')
const eveningTime = ref('')

const handleMorningChange = (val) => {
  console.log('上午时间:', val)
}

const handleAfternoonChange = (val) => {
  console.log('下午时间:', val)
}

const handleEveningChange = (val) => {
  console.log('晚上时间:', val)
}
</script>

<style scoped>
.time-range-example {
  margin-bottom: 16px;
}

.time-range-example label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #303133;
}

.selected-times {
  margin-top: 20px;
  padding: 16px;
  background-color: #f5f7fa;
  border-radius: 6px;
}

.selected-times h5 {
  margin: 0 0 8px 0;
  color: #303133;
}

.selected-times p {
  margin: 4px 0;
  color: #606266;
}
</style>

任意时间范围

使用 min-time 和 max-time 属性设置动态时间范围:

vue
<template>
  <div>
    <h4>任意时间范围</h4>
    
    <div class="dynamic-range">
      <div class="range-controls">
        <el-form :inline="true">
          <el-form-item label="最早时间:">
            <el-time-select
              v-model="minTime"
              start="00:00"
              step="00:30"
              end="23:30"
              placeholder="设置最早时间"
              @change="updateTimeRange"
            />
          </el-form-item>
          
          <el-form-item label="最晚时间:">
            <el-time-select
              v-model="maxTime"
              start="00:00"
              step="00:30"
              end="23:30"
              placeholder="设置最晚时间"
              @change="updateTimeRange"
            />
          </el-form-item>
        </el-form>
      </div>
      
      <div class="time-selector">
        <label>选择时间:</label>
        <el-time-select
          v-model="selectedTime"
          :start="minTime || '00:00'"
          :end="maxTime || '23:30'"
          step="00:15"
          placeholder="在设定范围内选择时间"
          :disabled="!minTime || !maxTime"
          @change="handleTimeChange"
        />
      </div>
      
      <div class="range-info">
        <p>可选时间范围:{{ minTime || '未设置' }} - {{ maxTime || '未设置' }}</p>
        <p>选择的时间:{{ selectedTime || '未选择' }}</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

const minTime = ref('')
const maxTime = ref('')
const selectedTime = ref('')

const updateTimeRange = () => {
  if (minTime.value && maxTime.value) {
    if (minTime.value >= maxTime.value) {
      ElMessage.warning('最早时间不能晚于或等于最晚时间')
      return
    }
    
    // 如果选择的时间超出新范围,清空选择
    if (selectedTime.value) {
      if (selectedTime.value < minTime.value || selectedTime.value > maxTime.value) {
        selectedTime.value = ''
        ElMessage.info('已清空超出范围的时间选择')
      }
    }
  }
}

const handleTimeChange = (val) => {
  console.log('选择时间:', val)
}
</script>

<style scoped>
.dynamic-range {
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  padding: 16px;
}

.range-controls {
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid #e4e7ed;
}

.time-selector {
  margin-bottom: 16px;
}

.time-selector label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
  color: #303133;
}

.range-info {
  padding: 12px;
  background-color: #f0f9ff;
  border-radius: 4px;
  border-left: 4px solid #409eff;
}

.range-info p {
  margin: 4px 0;
  color: #606266;
}
</style>

禁用状态

设置组件的禁用状态:

vue
<template>
  <div>
    <h4>禁用状态</h4>
    
    <div class="disabled-example">
      <el-form :inline="true">
        <el-form-item label="启用状态:">
          <el-time-select
            v-model="enabledTime"
            start="09:00"
            step="00:30"
            end="18:00"
            placeholder="正常选择时间"
          />
        </el-form-item>
        
        <el-form-item label="禁用状态:">
          <el-time-select
            v-model="disabledTime"
            start="09:00"
            step="00:30"
            end="18:00"
            placeholder="禁用状态"
            disabled
          />
        </el-form-item>
      </el-form>
      
      <div class="control-section">
        <el-button @click="toggleDisabled">切换禁用状态</el-button>
        <el-button @click="clearTimes">清空时间</el-button>
      </div>
      
      <div class="status-info">
        <p>启用状态时间:{{ enabledTime || '未选择' }}</p>
        <p>禁用状态时间:{{ disabledTime || '未选择' }}</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const enabledTime = ref('')
const disabledTime = ref('14:30') // 预设值
const isDisabled = ref(true)

const toggleDisabled = () => {
  isDisabled.value = !isDisabled.value
}

const clearTimes = () => {
  enabledTime.value = ''
  disabledTime.value = ''
}
</script>

<style scoped>
.disabled-example {
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  padding: 16px;
}

.control-section {
  margin: 16px 0;
  padding: 12px 0;
  border-top: 1px solid #e4e7ed;
  border-bottom: 1px solid #e4e7ed;
}

.status-info {
  padding: 12px;
  background-color: #f5f7fa;
  border-radius: 4px;
}

.status-info p {
  margin: 4px 0;
  color: #606266;
}
</style>

在表单中使用

与 el-form 一起使用:

vue
<template>
  <div>
    <h4>表单中的时间选择</h4>
    
    <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
      <el-form-item label="会议标题" prop="title">
        <el-input v-model="form.title" placeholder="请输入会议标题" />
      </el-form-item>
      
      <el-form-item label="开始时间" prop="startTime">
        <el-time-select
          v-model="form.startTime"
          start="08:00"
          step="00:30"
          end="22:00"
          placeholder="选择开始时间"
          @change="handleStartTimeChange"
        />
      </el-form-item>
      
      <el-form-item label="结束时间" prop="endTime">
        <el-time-select
          v-model="form.endTime"
          :start="getEndTimeStart()"
          step="00:30"
          end="22:00"
          placeholder="选择结束时间"
          :disabled="!form.startTime"
          @change="handleEndTimeChange"
        />
      </el-form-item>
      
      <el-form-item label="会议室" prop="room">
        <el-select v-model="form.room" placeholder="选择会议室">
          <el-option label="会议室A" value="roomA" />
          <el-option label="会议室B" value="roomB" />
          <el-option label="会议室C" value="roomC" />
        </el-select>
      </el-form-item>
      
      <el-form-item label="重复设置" prop="repeat">
        <el-radio-group v-model="form.repeat">
          <el-radio label="none">不重复</el-radio>
          <el-radio label="daily">每天</el-radio>
          <el-radio label="weekly">每周</el-radio>
          <el-radio label="monthly">每月</el-radio>
        </el-radio-group>
      </el-form-item>
      
      <el-form-item>
        <el-button type="primary" @click="submitForm">预订会议室</el-button>
        <el-button @click="resetForm">重置</el-button>
        <el-button @click="presetQuickMeeting">快速会议</el-button>
      </el-form-item>
    </el-form>
    
    <div v-if="submittedData" class="submitted-data">
      <h4>预订信息:</h4>
      <div class="meeting-info">
        <p><strong>会议标题:</strong>{{ submittedData.title }}</p>
        <p><strong>时间:</strong>{{ submittedData.startTime }} - {{ submittedData.endTime }}</p>
        <p><strong>时长:</strong>{{ calculateDuration(submittedData.startTime, submittedData.endTime) }}</p>
        <p><strong>会议室:</strong>{{ getRoomName(submittedData.room) }}</p>
        <p><strong>重复:</strong>{{ getRepeatText(submittedData.repeat) }}</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'

const formRef = ref()
const submittedData = ref(null)

const form = reactive({
  title: '',
  startTime: '',
  endTime: '',
  room: '',
  repeat: 'none'
})

const rules = {
  title: [
    { required: true, message: '请输入会议标题', trigger: 'blur' }
  ],
  startTime: [
    { required: true, message: '请选择开始时间', trigger: 'change' }
  ],
  endTime: [
    { required: true, message: '请选择结束时间', trigger: 'change' },
    { validator: validateEndTime, trigger: 'change' }
  ],
  room: [
    { required: true, message: '请选择会议室', trigger: 'change' }
  ]
}

function validateEndTime(rule, value, callback) {
  if (!value) {
    callback(new Error('请选择结束时间'))
  } else if (form.startTime && value <= form.startTime) {
    callback(new Error('结束时间必须晚于开始时间'))
  } else {
    callback()
  }
}

const handleStartTimeChange = (val) => {
  console.log('开始时间变化:', val)
  // 如果结束时间早于或等于开始时间,清空结束时间
  if (form.endTime && form.endTime <= val) {
    form.endTime = ''
    ElMessage.info('已清空结束时间,请重新选择')
  }
}

const handleEndTimeChange = (val) => {
  console.log('结束时间变化:', val)
}

const getEndTimeStart = () => {
  if (!form.startTime) return '08:00'
  
  // 结束时间至少比开始时间晚30分钟
  const [hours, minutes] = form.startTime.split(':').map(Number)
  const totalMinutes = hours * 60 + minutes + 30
  const newHours = Math.floor(totalMinutes / 60)
  const newMinutes = totalMinutes % 60
  
  return `${String(newHours).padStart(2, '0')}:${String(newMinutes).padStart(2, '0')}`
}

const submitForm = async () => {
  try {
    await formRef.value.validate()
    submittedData.value = { ...form }
    ElMessage.success('会议室预订成功')
  } catch (error) {
    ElMessage.error('请检查表单内容')
  }
}

const resetForm = () => {
  formRef.value.resetFields()
  submittedData.value = null
}

const presetQuickMeeting = () => {
  const now = new Date()
  const currentHour = now.getHours()
  const currentMinute = now.getMinutes()
  
  // 设置为下一个半小时时间点
  let startHour = currentHour
  let startMinute = currentMinute < 30 ? 30 : 0
  if (startMinute === 0) {
    startHour += 1
  }
  
  const startTime = `${String(startHour).padStart(2, '0')}:${String(startMinute).padStart(2, '0')}`
  const endTime = `${String(startHour + 1).padStart(2, '0')}:${String(startMinute).padStart(2, '0')}`
  
  form.title = '快速会议'
  form.startTime = startTime
  form.endTime = endTime
  form.room = 'roomA'
  
  ElMessage.success('已设置快速会议时间')
}

const calculateDuration = (start, end) => {
  if (!start || !end) return '未知'
  
  const [startHours, startMinutes] = start.split(':').map(Number)
  const [endHours, endMinutes] = end.split(':').map(Number)
  
  const startTotalMinutes = startHours * 60 + startMinutes
  const endTotalMinutes = endHours * 60 + endMinutes
  const durationMinutes = endTotalMinutes - startTotalMinutes
  
  const hours = Math.floor(durationMinutes / 60)
  const minutes = durationMinutes % 60
  
  if (hours > 0) {
    return `${hours}小时${minutes > 0 ? minutes + '分钟' : ''}`
  } else {
    return `${minutes}分钟`
  }
}

const getRoomName = (roomValue) => {
  const roomMap = {
    roomA: '会议室A',
    roomB: '会议室B',
    roomC: '会议室C'
  }
  return roomMap[roomValue] || roomValue
}

const getRepeatText = (repeatValue) => {
  const repeatMap = {
    none: '不重复',
    daily: '每天',
    weekly: '每周',
    monthly: '每月'
  }
  return repeatMap[repeatValue] || repeatValue
}
</script>

<style scoped>
.submitted-data {
  margin-top: 20px;
  padding: 16px;
  background-color: #f0f9ff;
  border-radius: 6px;
  border-left: 4px solid #409eff;
}

.meeting-info p {
  margin: 8px 0;
  color: #606266;
}

.meeting-info strong {
  color: #303133;
}
</style>

实际应用示例

预约系统

vue
<template>
  <div class="appointment-system">
    <h3>医生预约系统</h3>
    
    <div class="appointment-form">
      <el-form :model="appointment" :rules="appointmentRules" ref="appointmentRef" label-width="100px">
        <el-form-item label="选择医生" prop="doctor">
          <el-select v-model="appointment.doctor" placeholder="请选择医生" @change="handleDoctorChange">
            <el-option
              v-for="doctor in doctors"
              :key="doctor.id"
              :label="doctor.name"
              :value="doctor.id"
            >
              <div class="doctor-option">
                <span class="doctor-name">{{ doctor.name }}</span>
                <span class="doctor-title">{{ doctor.title }}</span>
              </div>
            </el-option>
          </el-select>
        </el-form-item>
        
        <el-form-item label="预约日期" prop="date">
          <el-date-picker
            v-model="appointment.date"
            type="date"
            placeholder="选择预约日期"
            :disabled-date="disabledDate"
            @change="handleDateChange"
          />
        </el-form-item>
        
        <el-form-item label="预约时间" prop="time">
          <el-time-select
            v-model="appointment.time"
            :start="getAvailableStartTime()"
            :end="getAvailableEndTime()"
            step="00:30"
            placeholder="选择预约时间"
            :disabled="!appointment.doctor || !appointment.date"
            @change="handleTimeChange"
          />
        </el-form-item>
        
        <el-form-item label="患者姓名" prop="patientName">
          <el-input v-model="appointment.patientName" placeholder="请输入患者姓名" />
        </el-form-item>
        
        <el-form-item label="联系电话" prop="phone">
          <el-input v-model="appointment.phone" placeholder="请输入联系电话" />
        </el-form-item>
        
        <el-form-item label="症状描述" prop="symptoms">
          <el-input
            v-model="appointment.symptoms"
            type="textarea"
            :rows="3"
            placeholder="请简要描述症状"
          />
        </el-form-item>
        
        <el-form-item>
          <el-button type="primary" @click="submitAppointment" :loading="submitting">确认预约</el-button>
          <el-button @click="resetAppointment">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    
    <div v-if="selectedDoctor" class="doctor-schedule">
      <h4>{{ selectedDoctor.name }} 的可预约时间</h4>
      <div class="schedule-info">
        <p><strong>科室:</strong>{{ selectedDoctor.department }}</p>
        <p><strong>职称:</strong>{{ selectedDoctor.title }}</p>
        <p><strong>出诊时间:</strong>{{ selectedDoctor.schedule }}</p>
        <p><strong>预约费用:</strong>{{ selectedDoctor.fee }}元</p>
      </div>
      
      <div class="available-times">
        <h5>今日可预约时间段:</h5>
        <div class="time-slots">
          <el-tag
            v-for="slot in availableTimeSlots"
            :key="slot"
            :type="appointment.time === slot ? 'success' : 'info'"
            class="time-slot"
            @click="selectTimeSlot(slot)"
          >
            {{ slot }}
          </el-tag>
        </div>
      </div>
    </div>
    
    <div v-if="appointmentResult" class="appointment-result">
      <h4>预约成功</h4>
      <div class="result-info">
        <p><strong>预约号:</strong>{{ appointmentResult.id }}</p>
        <p><strong>医生:</strong>{{ appointmentResult.doctorName }}</p>
        <p><strong>时间:</strong>{{ appointmentResult.date }} {{ appointmentResult.time }}</p>
        <p><strong>患者:</strong>{{ appointmentResult.patientName }}</p>
        <p><strong>联系电话:</strong>{{ appointmentResult.phone }}</p>
      </div>
      
      <div class="result-actions">
        <el-button type="primary" @click="printAppointment">打印预约单</el-button>
        <el-button @click="newAppointment">新建预约</el-button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed } from 'vue'
import { ElMessage } from 'element-plus'

const appointmentRef = ref()
const submitting = ref(false)
const appointmentResult = ref(null)

const appointment = reactive({
  doctor: '',
  date: '',
  time: '',
  patientName: '',
  phone: '',
  symptoms: ''
})

const appointmentRules = {
  doctor: [{ required: true, message: '请选择医生', trigger: 'change' }],
  date: [{ required: true, message: '请选择预约日期', trigger: 'change' }],
  time: [{ required: true, message: '请选择预约时间', trigger: 'change' }],
  patientName: [{ required: true, message: '请输入患者姓名', trigger: 'blur' }],
  phone: [
    { required: true, message: '请输入联系电话', trigger: 'blur' },
    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
  ]
}

const doctors = [
  {
    id: 'doc1',
    name: '张医生',
    title: '主任医师',
    department: '内科',
    schedule: '周一至周五 9:00-17:00',
    fee: 50,
    workDays: [1, 2, 3, 4, 5], // 周一到周五
    workHours: { start: '09:00', end: '17:00' }
  },
  {
    id: 'doc2',
    name: '李医生',
    title: '副主任医师',
    department: '外科',
    schedule: '周二、周四、周六 8:30-16:30',
    fee: 40,
    workDays: [2, 4, 6], // 周二、周四、周六
    workHours: { start: '08:30', end: '16:30' }
  },
  {
    id: 'doc3',
    name: '王医生',
    title: '主治医师',
    department: '儿科',
    schedule: '周一至周日 8:00-18:00',
    fee: 30,
    workDays: [1, 2, 3, 4, 5, 6, 0], // 每天
    workHours: { start: '08:00', end: '18:00' }
  }
]

const selectedDoctor = computed(() => {
  return doctors.find(doc => doc.id === appointment.doctor)
})

const availableTimeSlots = computed(() => {
  if (!selectedDoctor.value || !appointment.date) return []
  
  const slots = []
  const start = selectedDoctor.value.workHours.start
  const end = selectedDoctor.value.workHours.end
  
  const [startHour, startMinute] = start.split(':').map(Number)
  const [endHour, endMinute] = end.split(':').map(Number)
  
  let currentHour = startHour
  let currentMinute = startMinute
  
  while (currentHour < endHour || (currentHour === endHour && currentMinute < endMinute)) {
    const timeSlot = `${String(currentHour).padStart(2, '0')}:${String(currentMinute).padStart(2, '0')}`
    slots.push(timeSlot)
    
    currentMinute += 30
    if (currentMinute >= 60) {
      currentMinute = 0
      currentHour += 1
    }
  }
  
  return slots
})

const disabledDate = (date) => {
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  
  // 不能选择过去的日期
  if (date < today) return true
  
  // 如果选择了医生,检查医生的工作日
  if (selectedDoctor.value) {
    const dayOfWeek = date.getDay()
    return !selectedDoctor.value.workDays.includes(dayOfWeek)
  }
  
  return false
}

const handleDoctorChange = () => {
  appointment.date = ''
  appointment.time = ''
}

const handleDateChange = () => {
  appointment.time = ''
}

const handleTimeChange = (val) => {
  console.log('选择时间:', val)
}

const getAvailableStartTime = () => {
  return selectedDoctor.value?.workHours.start || '08:00'
}

const getAvailableEndTime = () => {
  return selectedDoctor.value?.workHours.end || '18:00'
}

const selectTimeSlot = (slot) => {
  appointment.time = slot
}

const submitAppointment = async () => {
  try {
    await appointmentRef.value.validate()
    submitting.value = true
    
    // 模拟提交预约
    setTimeout(() => {
      appointmentResult.value = {
        id: 'APT' + Date.now(),
        doctorName: selectedDoctor.value.name,
        date: appointment.date,
        time: appointment.time,
        patientName: appointment.patientName,
        phone: appointment.phone
      }
      
      submitting.value = false
      ElMessage.success('预约成功')
    }, 2000)
  } catch (error) {
    ElMessage.error('请检查表单内容')
  }
}

const resetAppointment = () => {
  appointmentRef.value.resetFields()
  appointmentResult.value = null
}

const printAppointment = () => {
  ElMessage.info('打印预约单功能')
}

const newAppointment = () => {
  resetAppointment()
}
</script>

<style scoped>
.appointment-system {
  max-width: 800px;
  padding: 20px;
  border: 1px solid #dcdfe6;
  border-radius: 8px;
}

.doctor-option {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.doctor-name {
  font-weight: 500;
}

.doctor-title {
  font-size: 12px;
  color: #909399;
}

.doctor-schedule {
  margin-top: 20px;
  padding: 16px;
  background-color: #f5f7fa;
  border-radius: 6px;
}

.schedule-info p {
  margin: 8px 0;
  color: #606266;
}

.available-times {
  margin-top: 16px;
}

.time-slots {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

.time-slot {
  cursor: pointer;
  transition: all 0.2s;
}

.time-slot:hover {
  transform: translateY(-1px);
}

.appointment-result {
  margin-top: 20px;
  padding: 16px;
  background-color: #f0f9ff;
  border-radius: 6px;
  border-left: 4px solid #67c23a;
}

.result-info p {
  margin: 8px 0;
  color: #606266;
}

.result-actions {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid #e4e7ed;
}
</style>

API

Attributes

属性名说明类型默认值
model-value / v-model绑定值string
disabled禁用状态booleanfalse
editable文本框可输入booleantrue
clearable是否显示清除按钮booleantrue
size输入框尺寸string
placeholder占位符string
name原生属性string
effectTooltip 主题stringlight
prefix-icon自定义前缀图标string | Component
clear-icon自定义清空图标string | Component
start开始时间string09:00
end结束时间string18:00
step间隔时间string00:30
min-time最小时间,小于该时间的时间段将被禁用string
max-time最大时间,大于该时间的时间段将被禁用string

Events

事件名说明类型
change用户确认选定的值时触发Function
blur当 input 失去焦点时触发Function
focus当 input 获得焦点时触发Function

Methods

方法名说明类型
focus使 input 获取焦点Function
blur使 input 失去焦点Function

最佳实践

1. 时间范围设置

vue
<script setup>
// 根据业务场景设置合理的时间范围
const getBusinessHours = () => {
  return {
    start: '09:00',
    end: '18:00',
    step: '00:30'
  }
}

// 动态调整时间范围
const adjustTimeRange = (type) => {
  const ranges = {
    morning: { start: '08:00', end: '12:00' },
    afternoon: { start: '14:00', end: '18:00' },
    evening: { start: '19:00', end: '22:00' }
  }
  return ranges[type]
}
</script>

2. 时间验证

vue
<script setup>
// 验证时间格式
const validateTimeFormat = (time) => {
  const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/
  return timeRegex.test(time)
}

// 验证时间范围
const validateTimeRange = (startTime, endTime) => {
  if (!startTime || !endTime) return false
  return startTime < endTime
}

// 计算时间差
const calculateTimeDifference = (start, end) => {
  const [startHour, startMinute] = start.split(':').map(Number)
  const [endHour, endMinute] = end.split(':').map(Number)
  
  const startMinutes = startHour * 60 + startMinute
  const endMinutes = endHour * 60 + endMinute
  
  return endMinutes - startMinutes
}
</script>

3. 用户体验优化

vue
<template>
  <!-- 提供快捷选择 -->
  <div class="quick-select">
    <el-button-group>
      <el-button @click="setQuickTime('09:00')">上午9点</el-button>
      <el-button @click="setQuickTime('14:00')">下午2点</el-button>
      <el-button @click="setQuickTime('19:00')">晚上7点</el-button>
    </el-button-group>
  </div>
  
  <!-- 显示时间提示 -->
  <el-time-select
    v-model="time"
    start="08:00"
    end="22:00"
    step="00:30"
    placeholder="选择时间"
  />
  
  <!-- 时间预览 -->
  <div v-if="time" class="time-preview">
    <p>选择的时间:{{ formatTime(time) }}</p>
  </div>
</template>

<script setup>
const setQuickTime = (quickTime) => {
  time.value = quickTime
}

const formatTime = (time) => {
  const [hour, minute] = time.split(':')
  const hourNum = parseInt(hour)
  const period = hourNum < 12 ? '上午' : '下午'
  const displayHour = hourNum > 12 ? hourNum - 12 : hourNum
  return `${period} ${displayHour}:${minute}`
}
</script>

4. 响应式设计

vue
<template>
  <div class="responsive-time-select">
    <el-time-select
      v-model="time"
      :class="{ 'mobile-time-select': isMobile }"
      start="08:00"
      end="22:00"
      step="00:30"
      placeholder="选择时间"
    />
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const isMobile = ref(false)

const checkMobile = () => {
  isMobile.value = window.innerWidth < 768
}

onMounted(() => {
  checkMobile()
  window.addEventListener('resize', checkMobile)
})

onUnmounted(() => {
  window.removeEventListener('resize', checkMobile)
})
</script>

<style scoped>
.mobile-time-select {
  width: 100%;
}

@media (max-width: 768px) {
  .responsive-time-select {
    width: 100%;
  }
}
</style>

常见问题

1. 时间选择器不显示选项

问题:时间选择器下拉列表为空

解决方案

vue
<script setup>
// 检查 start、end、step 属性设置
const timeConfig = {
  start: '08:00', // 确保格式正确
  end: '18:00',   // 结束时间必须大于开始时间
  step: '00:30'   // 步长格式必须正确
}

// 确保时间格式正确
const validateTimeConfig = (config) => {
  const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/
  return timeRegex.test(config.start) && 
         timeRegex.test(config.end) && 
         timeRegex.test(config.step)
}
</script>

2. 动态时间范围设置

问题:需要根据条件动态设置时间范围

解决方案

vue
<template>
  <el-time-select
    v-model="time"
    :start="dynamicStart"
    :end="dynamicEnd"
    :step="dynamicStep"
  />
</template>

<script setup>
import { computed } from 'vue'

const selectedType = ref('business')

const dynamicStart = computed(() => {
  const configs = {
    business: '09:00',
    extended: '08:00',
    night: '18:00'
  }
  return configs[selectedType.value] || '09:00'
})

const dynamicEnd = computed(() => {
  const configs = {
    business: '18:00',
    extended: '22:00',
    night: '23:59'
  }
  return configs[selectedType.value] || '18:00'
})
</script>

3. 时间格式转换

问题:需要在不同时间格式间转换

解决方案

vue
<script setup>
// 12小时制转24小时制
const convertTo24Hour = (time12h) => {
  const [time, period] = time12h.split(' ')
  let [hours, minutes] = time.split(':')
  
  if (period === 'PM' && hours !== '12') {
    hours = parseInt(hours) + 12
  } else if (period === 'AM' && hours === '12') {
    hours = '00'
  }
  
  return `${hours.padStart(2, '0')}:${minutes}`
}

// 24小时制转12小时制
const convertTo12Hour = (time24h) => {
  let [hours, minutes] = time24h.split(':')
  const hour = parseInt(hours)
  const period = hour >= 12 ? 'PM' : 'AM'
  const displayHour = hour > 12 ? hour - 12 : (hour === 0 ? 12 : hour)
  
  return `${displayHour}:${minutes} ${period}`
}
</script>

4. 时间冲突检测

问题:需要检测时间段是否冲突

解决方案

vue
<script setup>
// 检测时间段冲突
const checkTimeConflict = (newStart, newEnd, existingTimes) => {
  const newStartMinutes = timeToMinutes(newStart)
  const newEndMinutes = timeToMinutes(newEnd)
  
  return existingTimes.some(existing => {
    const existingStartMinutes = timeToMinutes(existing.start)
    const existingEndMinutes = timeToMinutes(existing.end)
    
    return (newStartMinutes < existingEndMinutes && newEndMinutes > existingStartMinutes)
  })
}

const timeToMinutes = (time) => {
  const [hours, minutes] = time.split(':').map(Number)
  return hours * 60 + minutes
}

// 获取可用时间段
const getAvailableTimeSlots = (allSlots, bookedSlots) => {
  return allSlots.filter(slot => {
    return !bookedSlots.some(booked => booked === slot)
  })
}
</script>

总结

Time Select 时间选择组件是一个实用的时间输入组件,主要特点包括:

  • 灵活的时间配置:支持自定义时间范围和步长
  • 简单易用:提供直观的时间选择界面
  • 验证支持:内置时间格式验证
  • 表单集成:与表单组件无缝集成
  • 响应式设计:适配不同屏幕尺寸

适用场景

  • 预约和预订系统
  • 会议和日程安排
  • 工作时间设置
  • 定时任务配置
  • 营业时间管理

设计原则

  • 提供清晰的时间选择界面
  • 支持灵活的时间范围配置
  • 确保时间数据的准确性
  • 考虑用户操作习惯
  • 提供良好的错误提示

参考资料

Element Plus Study Guide