自定义时间范围选择组件使用教程(基于 Vue 3 + Element Plus)

发布于:2025-07-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

🕓 自定义时间范围选择组件使用教程(基于 Vue 3 + Element Plus)

✅ 一个灵活实用的时间范围选择器,支持开始时间、结束时间、快捷时间选项、本地双向绑定、插槽扩展等功能。


在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

📘 一、功能介绍

该组件基于 Element Plus<el-date-picker><el-select> 封装,支持以下特性:

  • v-model:startTimev-model:endTime 双向绑定;
  • 常用时间快捷选项:今天、昨天、本月、上月等;
  • 自定义插槽 before,可拓展前置内容;
  • 自动识别当前选择是否为快捷项并高亮;
  • 支持国际化 $t()
  • 可自定义初始选中项;
  • 样式美观、可无缝集成至查询表单。

🧱 二、组件源码

📂 components/TimeSeparation.vue

<template>

<template>
  <slot name="before" />
  <el-date-picker
    class="timeSeparationClassStart"
    style="width:160px"
    v-model="startTime"
    type="datetime"
    format="YYYY-MM-DD HH:mm:ss"
    value-format="YYYY-MM-DD HH:mm:ss"
    :default-time="new Date(2000, 1, 1, 0, 0, 0)" />
  <span class="timeSeparationClassCenter">-</span>
  <el-date-picker
    class="timeSeparationClassEnd"
    style="width:160px"
    v-model="endTime"
    type="datetime"
    format="YYYY-MM-DD HH:mm:ss"
    value-format="YYYY-MM-DD HH:mm:ss"
    :default-time="new Date(2000, 1, 1, 23, 59, 59)" />
  <el-select v-model="dateRangeType" class="timeSeparationClass_after" style="width:100px" @change="changeDateRange">
    <el-option
      v-for="dict in dateList"
      :key="dict.value"
      :label="$t(dict.label)"
      :value="dict.value" />
  </el-select>
</template>

<script setup>

<script setup>
import { useVModel } from '@vueuse/core'
import i18n from '@/i18n'
import { parseTime } from '@/utils/ruoyi'

const emit = defineEmits(['update:startTime', 'update:endTime', 'change'])

const props = defineProps({
  startTime: String,
  endTime: String,
  defaultTime: Number, // 默认快捷选中类型
  showAfter: { type: Boolean, default: true }
})

// 双向绑定
const startTime = useVModel(props, 'startTime', emit)
const endTime = useVModel(props, 'endTime', emit)
const dateRangeType = ref(props.defaultTime)

function changeDateRange(e) {
  switch (e) {
    case 1: setDaysAgo(7); break;
    case 2: setThisMonth(); break;
    case 4: setToday(); break;
    case 5: setYesterday(); break;
    case 6: setLastMonth(); break;
  }
}

// 日期处理
function setDaysAgo(days) {
  const now = new Date();
  const start = new Date(now)
  start.setDate(start.getDate() - (days - 1))
  start.setHours(0, 0, 0, 0)
  now.setHours(23, 59, 59, 999)
  startTime.value = parseTime(start)
  endTime.value = parseTime(now)
}

function setToday() {
  const now = new Date();
  const start = new Date(now)
  start.setHours(0, 0, 0, 0)
  now.setHours(23, 59, 59, 999)
  startTime.value = parseTime(start)
  endTime.value = parseTime(now)
}

function setYesterday() {
  const start = new Date()
  const end = new Date()
  start.setDate(start.getDate() - 1)
  end.setDate(end.getDate() - 1)
  start.setHours(0, 0, 0, 0)
  end.setHours(23, 59, 59, 999)
  startTime.value = parseTime(start)
  endTime.value = parseTime(end)
}

function setThisMonth() {
  const now = new Date()
  const start = new Date(now.getFullYear(), now.getMonth(), 1)
  const end = new Date(now.getFullYear(), now.getMonth() + 1, 0)
  end.setHours(23, 59, 59, 999)
  startTime.value = parseTime(start)
  endTime.value = parseTime(end)
}

function setLastMonth() {
  const now = new Date()
  const start = new Date(now.getFullYear(), now.getMonth() - 1, 1)
  const end = new Date(now.getFullYear(), now.getMonth(), 0)
  end.setHours(23, 59, 59, 999)
  startTime.value = parseTime(start)
  endTime.value = parseTime(end)
}

watch([() => startTime.value, () => endTime.value], ([newStart, newEnd]) => {
  const ranges = [
    { num: 2, range: getTimeRange('本月') },
    { num: 4, range: getTimeRange('今天') },
    { num: 5, range: getTimeRange('昨天') },
    { num: 6, range: getTimeRange('上月') },
  ]
  const nowRange = [newStart, newEnd]
  const matched = ranges.find(i => JSON.stringify(i.range) === JSON.stringify(nowRange))
  dateRangeType.value = matched ? matched.num : undefined
  emit('change')
})

function getTimeRange(type) {
  const now = new Date()
  let start = new Date(), end = new Date()
  switch (type) {
    case '上月':
      start = new Date(now.getFullYear(), now.getMonth() - 1, 1)
      end = new Date(now.getFullYear(), now.getMonth(), 0)
      break
    case '本月':
      start = new Date(now.getFullYear(), now.getMonth(), 1)
      end = new Date(now.getFullYear(), now.getMonth() + 1, 0)
      break
    case '今天':
      start.setHours(0, 0, 0, 0)
      end.setHours(23, 59, 59, 999)
      break
    case '昨天':
      start.setDate(start.getDate() - 1)
      end.setDate(end.getDate() - 1)
      start.setHours(0, 0, 0, 0)
      end.setHours(23, 59, 59, 999)
      break
  }
  return [parseTime(start), parseTime(end)]
}

const { t } = i18n.global
const dateList = [
  { label: t('this_month'), value: 2 },
  { label: t('today'), value: 4 },
  { label: t('yesterday'), value: 5 },
  { label: t('last_month'), value: 6 }
]
</script>

<style scoped lang="scss">

.timeSeparationClassCenter {
  display: flex;
  align-items: center;
  background-color: #fff;
  box-shadow:
    inset 0 1px 0 0 var(--el-input-border-color),
    inset 0 -1px 0 0 var(--el-input-border-color);
}

.timeSeparationClassStart .el-input__wrapper {
  border-radius: var(--el-border-radius-base) 0 0 var(--el-border-radius-base);
  box-shadow: inset 1px 0 0 0 var(--el-input-border-color);
}

.timeSeparationClassEnd .el-input__wrapper {
  border-radius: 0;
  box-shadow: inset -1px 0 0 0 var(--el-input-border-color);
}

.timeSeparationClass_after .el-select__wrapper {
  border-radius: 0 var(--el-border-radius-base) var(--el-border-radius-base) 0;
  background-color: var(--el-fill-color-light);
}

🚀 三、使用示例

<template>
  <TimeSeparation
    v-model:startTime="queryParams.startTime"
    v-model:endTime="queryParams.endTime"
    :default-time="2"
    @change="onDateChange"
  />
</template>

<script setup>
import TimeSeparation from '@/components/TimeSeparation.vue'
const queryParams = reactive({
  startTime: '',
  endTime: ''
})
function onDateChange() {
  console.log('时间范围变化:', queryParams)
}
</script>

📚 四、Props & Emits API 文档

Props

参数名 类型 默认值 说明
startTime string '' 外部绑定开始时间
endTime string '' 外部绑定结束时间
defaultTime number 初始选中快捷选项(如 2:本月)
showAfter boolean true 是否显示快捷时间选择

Emits

事件名 参数 说明
update:startTime string 绑定用 v-model:startTime
update:endTime string 绑定用 v-model:endTime
change 时间范围变更回调

🧩 五、拓展建议(可选)

拓展方向 建议实现方法
动态传入快捷项 增加 shortcuts: Array prop
时间范围校验 内置日期合法性校验 + disabledDate
表单集成支持 接入 <el-form-item> + rules
可读性文本输出 增加 displayRange: computed 返回“xx 至 yy”

网站公告

今日签到

点亮在社区的每一天
去签到