element日期选择范围、选择年份范围

根据业务需求elemet-ui使用日期选择范围只能选择年份范围,element-ui只有选择日期,月份范围不支持选择年份范围,为实现业务需求研究了类似element-ui日期选择器选择年份,代码如下

完成效果图

代码如下:对年份选择范围进去封装

<template>
  <el-popover ref="popover" placement="bottom" v-model="showPanel" popper-class="custom_year_range" trigger="manual"
    v-clickoutside="() => { showPanel = false }">
    <div class="_inner floatPanel">
      <div class="_inner leftPanel">
        <div class="_inner panelHead">
          <i class="_inner el-icon-d-arrow-left" @click="onClickLeft"></i>
          <span>
            {{ leftYearList[0] + '年 ' + '- ' + leftYearList[9] + '年' }}
          </span>
        </div>
        <div class="_inner panelContent">
          <div :class="{
            oneSelected: item === startYear && oneSelected,
            startSelected: item === startYear,
            endSelected: item === endYear,
            betweenSelected: item > startYear && item < endYear,
          }" v-for="item in leftYearList" :key="item">
            <a :class="{
              cell: true,
              _inner: true,
              selected: item === startYear || item === endYear,
            }" @click="onClickItem(item)" @mouseover="onHoverItem(item)">
              {{ item }}
            </a>
          </div>
        </div>
      </div>
      <div class="_inner rightPanel">
        <div class="_inner panelHead">
          <i class="_inner el-icon-d-arrow-right" @click="onClickRight"></i>
          <span>{{ rightYearList[0] + '年 ' + '- ' + rightYearList[9] + '年' }}</span>
        </div>
        <div class="_inner panelContent">
          <div :class="{
            startSelected: item === startYear,
            endSelected: item === endYear,
            betweenSelected: item > startYear && item < endYear,
          }" v-for="item in rightYearList" :key="item">
            <a :class="{
              cell: true,
              _inner: true,
              selected: item === endYear || item === startYear,
            }" @click="onClickItem(item)" @mouseover="onHoverItem(item)">
              {{ item }}
            </a>
          </div>
        </div>
      </div>
    </div>
    <div slot="reference">
      <div ref="yearPicker" style="width: 100%"
        class="el-date-editor el-range-editor el-input__inner el-date-editor--daterange el-range-editor--small">
        <i class="el-input__icon el-range__icon el-icon-date"></i>
        <input class="_inner range_input" ref="inputLeft" type="text" name="yearInput" placeholder="选择开始年份"
          v-model="startShowYear" @focus="onFocus" @keyup="handleInput('start')" />
        <span class="el-range-separator">{{ sp }}</span>
        <input class="_inner range_input" ref="inputRight" type="text" name="yearInput" placeholder="选择结束年份"
          v-model="endShowYear" @focus="onFocus" @keyup="handleInput('end')" />
      </div>
    </div>
  </el-popover>
</template>
  
<script>
import moment from 'moment'
import { clickoutside, SELECT_STATE } from '@/views/components/time/utils.js'
export default {
  name: 'yearPicker',
  directives: { clickoutside },
  computed: {
    oneSelected() {
      return this.curState === SELECT_STATE.selecting && (this.startYear === this.endYear || this.endYear == null)
    },
    leftYearList() {
      return this.yearList.slice(0, 10)
    },
    rightYearList() {
      return this.yearList.slice(10, 20)
    }
  },
  props: {
    sp: {
      default: '至'
    },
    value: {
      type: Array,
      default: [],
    }
  },
  data() {
    return {
      itemBg: {},
      startShowYear: null,
      endShowYear: null,
      yearList: [],
      showPanel: false,
      startYear: null,
      endYear: null,
      curYear: 0,
      curSelectedYear: 0,
      curState: SELECT_STATE.unselect
    }
  },
  methods: {
    handleInput(type) {
      switch (type) {
        case 'start':
          if (isNaN(this.startShowYear)) {
            this.startShowYear = this.startYear
            return
          }
          this.startYear = this.startShowYear * 1
          break
        case 'end':
          if (isNaN(this.endShowYear)) {
            this.endShowYear = this.endYear
            return
          }
          this.endYear = this.endShowYear * 1
          break
      }
      [this.startYear, this.endYear] = [this.endYear, this.startYear]
      this.startShowYear = this.startYear
      this.endShowYear = this.endYear
    },

    onHoverItem(iYear) {
      if (this.curState === SELECT_STATE.selecting) {
        const tmpStart = this.curSelectedYear
        this.endYear = Math.max(tmpStart, iYear)
        this.startYear = Math.min(tmpStart, iYear)
      }
    },

    async onClickItem(selectYear) {
      if (
        this.curState === SELECT_STATE.unselect ||
        this.curState === SELECT_STATE.selected
      ) {
        this.startYear = selectYear
        this.curSelectedYear = selectYear
        this.endYear = null
        this.curState = SELECT_STATE.selecting
      } else if (this.curState === SELECT_STATE.selecting) {
        this.endShowYear = this.endYear || this.startYear
        this.startShowYear = this.startYear
        this.curState = SELECT_STATE.selected
        await this.$nextTick()
        this.showPanel = false
        this.$parent?.$parent?.$parent?.$parent?.$parent.clearValidate?.()
      }
    },

    async onFocus() {
      await this.$nextTick()
      this.showPanel = true
    },

    updateYearList() {
      const startYear = ~~(this.curYear / 10) * 10
      console.log(startYear, this.curYear, 'this.curYearthis.curYearthis.curYear')
      this.yearList = []
      for (let index = 0; index < 20; index++) {
        this.yearList.push(startYear + index)
      }
    },

    onClickLeft() {
      this.curYear = this.curYear * 1 - 10
      this.updateYearList()
    },

    onClickRight() {
      this.curYear = this.curYear * 1 + 10
      this.updateYearList()
    }
  },
  watch: {
    value: {
      handler(val) {
        if (val.length == 0) {
          this.startShowYear = ''
          this.endShowYear = ''
        } else {
          const [first, end] = val || []
          this.startShowYear = val[0]
          this.endShowYear = val[1]
        }

      },
      immediate: true,
      deep: true
    },

    startShowYear: {
      handler(val) {
        this.$emit('input', [val, this.endShowYear || ''])
      },
      immediate: true,
      deep: true
    },

    endShowYear: {
      handler(val) {
        this.$emit('input', [this.startShowYear || '', val])
      },
      immediate: true,
      deep: true
    }
  },
  created() {
    console.log('value', this.value)
    const [startYear, endYear] = this.value || []
    if (startYear) {
      this.startYear = Number(startYear)
      this.endYear = Number(endYear)
      this.curState = SELECT_STATE.selected
      this.curYear = startYear
    } else {
      this.curYear = moment().format('yyyy')
    }
    this.updateYearList()
  },

  mounted() {
    window.Vue = this
  }
}
</script>
<style lang="scss">
.custom_year_range {
  .floatPanel {
    >div {
      width: 50%;
    }

    padding: 0 16px;
    // position: absolute;
    display: flex;
    background-color: #fff;
    z-index: 2000;
    border-radius: 4px;
    width: 650px;
    height: 250px;
    top: 40px;
    left: -50px;

    .panelContent {
      display: flex;
      flex-wrap: wrap;
      width: 100%;
      height: calc(100% - 70px);

      .oneSelected {
        border-top-right-radius: 24px;
        border-bottom-right-radius: 24px;
      }

      .startSelected {
        background-color: #f2f6fc;
        border-top-left-radius: 24px;
        border-bottom-left-radius: 24px;
      }

      .endSelected {
        background-color: #f2f6fc;
        border-top-right-radius: 24px;
        border-bottom-right-radius: 24px;
      }

      .betweenSelected {
        background-color: #f2f6fc;
      }

      >div {
        width: 75px;
        height: 48px;
        line-height: 48px;
        margin: 3px 0;
        // border-radius: 24px;
        text-align: center;

        a {
          display: inline-block;
          width: 60px;
          height: 36px;
          cursor: pointer;
          line-height: 36px;
          border-radius: 18px;

          &:hover {
            color: #409eff;
          }
        }

        .selected {
          background-color: #409eff;
          color: #fff;

          &:hover {
            color: #fff !important;
          }
        }
      }
    }

    .panelHead {
      position: relative;
      height: 46px;
      line-height: 46px;
      text-align: center;
      display: flex;
      align-items: center;
      justify-content: center;

      span {
        font-size: 16px;
        font-weight: 500;
        padding: 0 5px;
        line-height: 22px;
        text-align: center;
        cursor: pointer;
        color: #606266;

        &:hover {
          color: #409eff;
        }
      }

      i {
        position: absolute;
        cursor: pointer;

        &:hover {
          color: #3e77fc;
        }
      }
    }

    .rightPanel {
      padding-left: 8px;
    }

    .leftPanel .panelHead i {
      left: 20px;
    }

    .rightPanel .panelHead i {
      right: 20px;
    }
  }

  .floatPanel::before {
    content: "";
    height: 100%;
    top: 0;
    position: absolute;
    left: 50%;
    width: 1px;
    border-left: 1px solid #e4e4e4;
  }
}
</style>
<style lang="scss" scoped>
.range_input {
  appearance: none;
  border: none;
  outline: 0;
  padding: 0;
  width: 39%;
  color: #606266;
  line-height: 1;
  height: 100%;
  margin: 0;
  text-align: center;
  display: inline-block;
}

.yearPicker {

  // font-size: 14px;
  // display: flex;
  // position: relative;
  // transition: all 0.3s;
  input:first-child {
    text-align: right;
  }

  .labelText {
    position: absolute;
    left: 8px;
  }

  background-color: #fff;

  span {
    padding: 0 8px;
    height: 32px;
    line-height: 32px;
  }

  border: 1px solid #eff1f3;
  height: 34px;
  line-height: 34px;
  border-radius: 4px;
  padding: 0 28px 0 8px;
  box-sizing: border-box;
}

input {
  width: 60px;
  border: none;
  height: 32px;
  line-height: 32px;
  box-sizing: border-box;
  background-color: transparent;
}

input:focus {
  outline: none;
  background-color: transparent;
}

.yearPicker:hover {
  border-color: #3e77fc;
}

.dateIcon {
  position: absolute;
  right: 16px;
  top: 9px;
  color: #adb2bc;
}
</style>
 

utils.js文件

export const clickoutside = {
    bind(el, binding, vnode) {
      function documentHandler(e) {
        // 这里判断点击的元素是否是本身,是本身,则返回
        if (el.contains(e.target)) {
          return false
        }
        // 判断指令中是否绑定了函数
        if (binding && binding.expression) {
          // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
          if (binding.value && binding.value(e)) {
            binding.value(e)
          }
        }
      }
      // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
      el.__vueClickOutside__ = documentHandler
      document.addEventListener('click', documentHandler)
    },
    unbind(el, binding) {
      // 解除事件监听
      document.removeEventListener('click', el.__vueClickOutside__)
      delete el.__vueClickOutside__
    }
  }
  
  export const SELECT_STATE = {
    unselect: 0,
    selecting: 1,
    selected: 2
  }

使用组件

 

使用timeDate方法可以获取到选中的年份值。

功能还有不足点,欢迎各位互相交流。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Element UI提供了一个DatePicker组件,可以通过设置type属性为"daterange"来实现日期范围选择。而要实现季度选择功能,可以通过自定义快捷选项来实现。 以下是示例代码: ```html <template> <el-date-picker v-model="dateRange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" type="daterange" :picker-options="pickerOptions" ></el-date-picker> </template> <script> export default { data() { return { dateRange: [], // 存储选中日期范围 pickerOptions: { shortcuts: [ { text: "本季度", onClick(picker) { let start = new Date(), end = new Date(); let now = new Date(); let quarter = Math.floor(now.getMonth() / 3); // 获取当前季度 start.setMonth(quarter * 3); // 设置季度开始月份 start.setDate(1); // 设置季度开始日期 end.setMonth(quarter * 3 + 2); // 设置季度结束月份 end.setDate(31); // 设置季度结束日期 picker.$emit("pick", [start, end]); // 触发选中日期范围事件 }, }, { text: "上季度", onClick(picker) { let start = new Date(), end = new Date(); let now = new Date(); let quarter = Math.floor(now.getMonth() / 3) - 1; // 获取上个季度 start.setMonth(quarter * 3); // 设置季度开始月份 start.setDate(1); // 设置季度开始日期 end.setMonth(quarter * 3 + 2); // 设置季度结束月份 end.setDate(31); // 设置季度结束日期 picker.$emit("pick", [start, end]); // 触发选中日期范围事件 }, }, { text: "去年本季度", onClick(picker) { let start = new Date(), end = new Date(); let now = new Date(); let quarter = Math.floor(now.getMonth() / 3); // 获取当前季度 start.setFullYear(now.getFullYear() - 1); // 设置年份为去年 start.setMonth(quarter * 3); // 设置季度开始月份 start.setDate(1); // 设置季度开始日期 end.setFullYear(now.getFullYear() - 1); // 设置年份为去年 end.setMonth(quarter * 3 + 2); // 设置季度结束月份 end.setDate(31); // 设置季度结束日期 picker.$emit("pick", [start, end]); // 触发选中日期范围事件 }, }, ], }, }; }, }; </script> ``` 以上代码中,我们定义了一个DatePicker组件,同时设置了type属性为"daterange",以实现日期范围选择功能。同时,我们定义了三个快捷选项,分别是"本季度"、"上季度"和"去年本季度",通过onClick事件来实现选中相应日期范围的操作。最后,我们将pickerOptions传递给DatePicker组件的picker-options属性中,从而使得快捷选项可以在DatePicker组件中显示出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值