DateRangePicker 日期范围选择器

DateRangePicker 日期范围选择器,基于 el-date-picker 封装,默认按月选择,自动补齐首尾日期。

快速使用

bash 复制代码
<DateRangePicker v-model="dateRange" />
<DateRangePicker v-model="dateRange" type="daterange" />
<DateRangePicker v-model="dateRange" type="yearrange" />

Props

bash 复制代码
 modelValue        Array     v-model 绑定值,格式 ['YYYY-MM-DD', 'YYYY-MM-DD']
    type              String    选择类型:'monthrange'(默认) | 'daterange' | 'yearrange'
    valueFormat       String    输出日期格式,默认 'YYYY-MM-DD'
    clearable         Boolean   是否可清空,默认 false
    autoDefault       Boolean   无值时自动填入本年度,默认 true;设为 false 则不填
    startPlaceholder  String    开始日期占位,默认 '开始日期'
    endPlaceholder    String    结束日期占位,默认 '结束日期'

默认行为

bash 复制代码
 - 默认选中本年度(1月1日 ~ 12月31日)
    - 月模式:选中"1月→3月"自动返回 ['2026-01-01', '2026-03-31']
    - 年模式:选中"2024→2025"自动返回 ['2024-01-01', '2025-12-31']
    - 不可清空(可传入 clearable 开启)
    - 支持 $attrs 透传(disabledDate、size 等原生属性)
bash 复制代码
<template>
  <el-date-picker
      v-model="innerValue"
      :type="type"
      :value-format="innerValueFormat"
      :clearable="clearable"
      range-separator="-"
      :start-placeholder="startPlaceholder"
      :end-placeholder="endPlaceholder"
      v-bind="$attrs"
  />
</template>

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

defineOptions({ name: 'DateRangePicker' });

const props = defineProps({
  modelValue: { type: Array, default: () => [] },
  // 类型:monthrange(默认按月) | daterange(按日) | yearrange(按年)
  type: { type: String, default: 'monthrange' },
  valueFormat: { type: String, default: 'YYYY-MM-DD' },
  clearable: { type: Boolean, default: false },
  autoDefault: { type: Boolean, default: true },   // 是否自动填入本年度默认值
  startPlaceholder: { type: String, default: '开始日期' },
  endPlaceholder: { type: String, default: '结束日期' },
});

const emit = defineEmits(['update:modelValue']);

const now = new Date();
const currentYear = now.getFullYear();

// 默认值:本年度 1月1日 ~ 12月31日
const defaultRange = computed(() => {
  if (props.type === 'yearrange') return [`${currentYear}`, `${currentYear}`];
  return [`${currentYear}-01-01`, `${currentYear}-12-31`];
});

onMounted(() => {
  if (props.autoDefault && (!props.modelValue || props.modelValue.length < 2)) {
    emit('update:modelValue', [...defaultRange.value]);
  }
});

// 内部传给 el-date-picker 的 format
const innerValueFormat = computed(() => {
  if (props.type === 'monthrange') return 'YYYY-MM';
  if (props.type === 'yearrange') return 'YYYY';
  return props.valueFormat;
});

// 获取某月的最后一天
const lastDayOfMonth = (year, month) => new Date(year, month, 0).getDate();

const innerValue = computed({
  get() {
    if (!props.modelValue || props.modelValue.length < 2) {
      if (!props.autoDefault) return null;
      // 默认本年度
      if (props.type === 'monthrange') return [`${currentYear}-01`, `${currentYear}-12`];
      if (props.type === 'yearrange') return [`${currentYear}`, `${currentYear}`];
      return [`${currentYear}-01-01`, `${currentYear}-12-31`];
    }
    if (props.type === 'monthrange') {
      // YYYY-MM-DD → YYYY-MM
      return props.modelValue.map(v => (v || '').substring(0, 7));
    }
    if (props.type === 'yearrange') {
      // YYYY-MM-DD → YYYY
      return props.modelValue.map(v => (v || '').substring(0, 4));
    }
    return props.modelValue;
  },
  set(val) {
    if (!val || val.length < 2) {
      emit('update:modelValue', []);
      return;
    }
    if (props.type === 'monthrange') {
      // val = ['2024-01', '2024-03'] → ['2024-01-01', '2024-03-31']
      const [endYear, endMonth] = val[1].split('-').map(Number);
      const start = `${val[0]}-01`;
      const end = `${val[1]}-${String(lastDayOfMonth(endYear, endMonth)).padStart(2, '0')}`;
      emit('update:modelValue', [start, end]);
    } else if (props.type === 'yearrange') {
      // val = ['2024', '2025'] → ['2024-01-01', '2025-12-31']
      emit('update:modelValue', [`${val[0]}-01-01`, `${val[1]}-12-31`]);
    } else {
      emit('update:modelValue', val);
    }
  }
});
</script>