vue3 + element-plus 季度选择器组件
...大约 3 分钟
vue3 + element-plus 季度选择器组件
背景
项目中要搞一个季度选择器,但是 element-plus 没有,所以网上搜索了一个大神写的,记录一下,方便以后使用。
一、复制下面代码,到 ElQuarterPicker.vue 页面中
<template>
  <div class="el-quarter-wrap">
    <el-popover title="" content="" width="320" v-model="visible">
      <template #reference>
        <el-input
          v-model="quarterDate"
          placeholder="请选择季度"
          clearable
          :prefix-icon="Calendar"
          readonly
          @click.stop.native="visible = true"
          @change="quarterDateChange">
          <template #suffix>
            <el-icon
              v-if="quarterDate"
              class="el-quarter-clear"
              @click="clearData">
              <Close />
            </el-icon>
          </template>
        </el-input>
      </template>
      <div class="el-quarter__header">
        <span
          class="el-quarter-btn el-quarter-btn__pre"
          @click="changeShowYear(-1)">
          <el-icon>
            <DArrowLeft />
          </el-icon>
        </span>
        <div class="el-quarter__header-text" @click="showYearList">
          {{ quarterTitle }}
        </div>
        <span
          class="el-quarter-btn el-quarter-btn__next"
          @click="changeShowYear(1)">
          <el-icon>
            <DArrowRight />
          </el-icon>
        </span>
      </div>
      <div class="el-quarter__content" v-if="!isEditYear">
        <div class="el-quarter__row">
          <span
            class="quarter-index"
            :class="{
              'is-active': showYear === pickerYear && quarterIndex === 1,
            }"
            @click="pickerQuarte(1)"
            >第一季度</span
          >
          <span
            class="quarter-index"
            :class="{
              'is-active': showYear === pickerYear && quarterIndex === 2,
            }"
            @click="pickerQuarte(2)"
            >第二季度</span
          >
        </div>
        <div class="el-quarter__row">
          <span
            class="quarter-index"
            :class="{
              'is-active': showYear === pickerYear && quarterIndex === 3,
            }"
            @click="pickerQuarte(3)"
            >第三季度</span
          >
          <span
            class="quarter-index"
            :class="{
              'is-active': showYear === pickerYear && quarterIndex === 4,
            }"
            @click="pickerQuarte(4)"
            >第四季度</span
          >
        </div>
      </div>
      <div class="el-year__content" v-else>
        <div class="el-year-item" v-for="item in yearList">
          <div
            class="cell"
            :class="{ 'is-active': showYear == item }"
            @click="selectYear(item)">
            {{ item }}
          </div>
        </div>
      </div>
    </el-popover>
  </div>
</template>
<script lang="ts" setup>
import {
  DArrowLeft,
  DArrowRight,
  Close,
  Calendar,
} from '@element-plus/icons-vue';
import { computed, onMounted, reactive, ref } from 'vue';
let visible = ref(false);
const props = defineProps({
  modelValue: {
    type: String,
    default: '',
  },
});
const emits = defineEmits(['update:modelValue', 'change']);
// 绑定日期
let quarterDate = ref('');
// 选择的年
let pickerYear = ref('') as any;
// 展示的年
let showYear = ref('') as any;
// 选择的季度
let quarterIndex = ref(0);
// 是否展示年份列表
let isEditYear = ref(false);
// 年份列表开始年份
let startYear = ref('') as any;
// 年份列表
let yearList = reactive([] as any);
const quarterTitle = computed(() => {
  if (isEditYear.value) {
    return startYear.value + '年 - ' + (startYear.value + 9) + '年';
  } else {
    return showYear.value + '年';
  }
});
// 选择某季度
function pickerQuarte(index: number) {
  quarterIndex.value = index;
  pickerYear.value = showYear.value;
  let oldValue = quarterDate.value; // 记录上一次数据
  quarterDate.value = pickerYear.value + '-Q' + index;
  emits('update:modelValue', quarterDate.value);
  // 新老数据不一致,触发change时间
  if (oldValue !== quarterDate.value) {
    emits('change', quarterDate.value);
  }
}
// 更改展示的年
function changeShowYear(num: number) {
  if (isEditYear.value) {
    startYear.value = startYear.value + num * 10;
    // console.log('startYear.value', startYear.value)
    changeYearList();
  } else {
    showYear.value = showYear.value + num;
  }
}
// 清空选择的数据
function clearData() {
  quarterDate.value = '';
  pickerYear.value = '';
  showYear.value = new Date().getFullYear();
  quarterIndex.value = 0;
}
// 选择的数据
function quarterDateChange(value: any) {
  const splitArray = value.split('-Q');
  if (splitArray.length < 2) {
    pickerYear.value = '';
    showYear.value = new Date().getFullYear();
    quarterIndex.value = 0;
  } else {
    pickerYear.value = splitArray[0];
    showYear.value = splitArray[0];
    quarterIndex.value = splitArray[1];
  }
}
// 更改年份列表函数
function changeYearList() {
  yearList = [];
  let year = startYear.value;
  for (let i = 0; i < 10; i++) {
    yearList.push(year++);
  }
}
// 切换展示年份列表 和 季度
function showYearList() {
  if (!isEditYear.value) {
    startYear.value = Number(Math.floor(showYear.value / 10) + '0');
    changeYearList();
    isEditYear.value = true;
  } else {
    isEditYear.value = false;
  }
}
// 选中某个年份列表
function selectYear(item: any) {
  showYear.value = item;
  isEditYear.value = false;
}
onMounted(() => {
  // 初始化展示的年为当前年份
  showYear.value = new Date().getFullYear();
  startYear.value = Number(Math.floor(showYear.value / 10) + '0');
  changeYearList();
});
</script>
<style lang="scss">
.el-quarter__header {
  padding-bottom: 12px;
  border-bottom: 1px solid #ebeef5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  .el-quarter-btn {
    font-size: 12px;
  }
  .el-quarter__header-text {
    font-size: 16px;
    font-weight: 500;
    text-align: center;
    cursor: pointer;
  }
}
.el-quarter__content {
  min-height: 100px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  .el-quarter__row {
    display: flex;
    justify-content: space-around;
    .quarter-index {
      display: flex;
      padding: 4px 10px;
      width: fit-content;
      cursor: pointer;
      &:hover {
        color: #337ecc;
      }
    }
    .is-active {
      color: #409eff;
    }
  }
}
.el-quarter-clear {
  position: relative;
  color: #909399;
  display: none;
  height: 12px;
  width: 12px;
  cursor: pointer;
  &::after {
    content: '';
    position: absolute;
    height: 14px;
    width: 14px;
    margin: auto;
    border-radius: 50%;
    border: 1px solid #909399;
  }
}
.el-input {
  &:hover {
    .el-quarter-clear {
      display: flex;
    }
  }
}
.el-year__content {
  min-height: 100px;
  display: flex;
  padding: 10px 0;
  flex-wrap: wrap;
  .el-year-item {
    width: calc(100% / 4);
    display: flex;
    align-items: center;
    justify-content: center;
    .cell {
      padding: 4px 10px;
      width: fit-content;
      cursor: pointer;
      cursor: pointer;
      white-space: nowrap;
      &:hover {
        color: #337ecc;
      }
    }
    .is-active {
      color: #409eff;
    }
  }
}
</style>二、引用并使用组件
<template>
  <div class="app-container">
    <el-quarter-picker v-model="value" placeholder="选择季度" />
  </div>
</template>
<script setup>
import ElQuarterPicker from './ElQuarterPicker';
</script>