引言

Element 1.x 停止维护了,但是有些项目中仍在使用,但是有些需求在这个版本中并不能实现,所以可以重构一下组件,不行就写一个组件,这个需求相对来说比之前写的mintui日期选择器更加简单。

理解需求

只要是和下面这个官网样例差不多就行
image.png

coding

<template>
  <div>
    <el-popover
    v-model="showFlag"
      placement="bottom"
      width="600">
      <div class="month-select-con">
        <div class="left-con">
          <div class="header-con">
            <i class="el-icon-d-arrow-left" @click="toLastYear" style="cursor: pointer"></i>
            <span>{{ leftYear }} 年</span>
            <i></i>
          </div>
          <ul class="month-con">
            <li v-for="item in monthList"
              :key="item.code"
              @mouseover="hoverStyle(leftYear + '' + item.code)"
              @click="selectMonth('left', item.code)">
                <span :id="leftYear + '' + item.code" >{{ item.name }}</span>
            </li>
          </ul>
        </div>
        <div class="right-con">
          <div class="header-con">
            <i></i>
              <span>{{ rightYear }} 年</span>
            <i class="el-icon-d-arrow-right" @click="toRightYear"  style="cursor: pointer"></i>
          </div>
          <ul class="month-con">
            <li v-for="item in monthList"
              :key="item.code"
               @mouseover="hoverStyle(rightYear + '' + item.code)"
               @click="selectMonth('right', item.code)">
                <span :id="rightYear + '' + item.code" >{{ item.name }}</span>
            </li>
          </ul>
        </div>
      </div>
      <el-input slot="reference" placeholder="" v-model="period"></el-input>
    </el-popover>
  </div>
</template>

<script>
export default {
  data () {
    return {
      startMonth: null,
      endMonth: null,
      temp: [], // 选择过程中的临时变量
      showFlag: false,
      leftYear: new Date().getFullYear(),
      monthList: [
        { code: '01', name: '一月' },
        { code: '02', name: '二月' },
        { code: '03', name: '三月' },
        { code: '04', name: '四月' },
        { code: '05', name: '五月' },
        { code: '06', name: '六月' },
        { code: '07', name: '七月' },
        { code: '08', name: '八月' },
        { code: '09', name: '九月' },
        { code: '10', name: '十月' },
        { code: '11', name: '十一月' },
        { code: '12', name: '十二月' }
      ]
    }
  },
  computed: {
    period () {
      if (!this.startMonth) return ''
      return this.startMonth + ' 至 ' + this.endMonth
    },
    rightYear () { return this.leftYear + 1 }
  },
  mounted () {},
  methods: {
    toLastYear () { // 上一年的事件
      this.leftYear = this.leftYear - 1
      this.flushStyle()
    },
    toRightYear () { // 下一年的事件
      this.leftYear = this.leftYear + 1
      this.flushStyle()
    },
    selectMonth (type, month) { // 鼠标点击月份的事件
      switch (this.temp.length) { // 临时变量长度
        case 0: // 此时并没有选择过,所以直接加进来就好
          this.temp.push(this[`${type}Year`] + '' + month)
          this.flushStyle()
          break
        case 1: // 这时候选择的是第二个月份
          this.temp.push(this[`${type}Year`] + '' + month)
          this.temp.sort((a, b) => a - b) // 排序日期,第一个是小的,第二个是大的
          this.startMonth = this.temp[0] // 赋值日期变量
          this.endMonth = this.temp[1]
          this.flushStyle() // 刷新样式
          this.leftYear = Number(this.startMonth.substr(0, 4)) // 再次打开,左侧年份为最小月份的年
          this.showFlag = false // 关闭弹窗
          break
        case 2: // 此时开始结束月份都有了,再点击的话,就重置
          this.temp = []
          this.temp.push(this[`${type}Year`] + '' + month) // 此时又是重复第一次点击的情况
          this.startMonth = 0 // 只不过重新开始了,开始结束月份要归零
          this.endMonth = 0
          this.flushStyle() // 刷新样式
          break
      }
    },
    hoverStyle (date) { // 鼠标指向时的事件
      if (this.temp.length === 1) { // 只有选择过一个日期才会执行
        this.temp.push(date)// 假装指向的就是选择的
        this.temp.sort((a, b) => a - b)
        this.startMonth = this.temp[0]
        this.endMonth = this.temp[1]
        this.flushStyle(date) // 刷新样式,但是有参数,这个地方有个细节
      }
    },

    flushStyle (tmp = 0) {
      this.$nextTick(() => { // 需要延迟回调
        document.querySelectorAll('.month-select-con li span').forEach(item => { // 初始化所有元素央视
          item.style.color = '#000'
          item.style.background = '#fff'
          item.parentElement.style.background = '#fff'
          item.style.borderRadius = '0'
          item.parentElement.style.borderRadius = 0
        })
        document.querySelectorAll('.month-select-con li span').forEach(item => { // 范围内的日期需要修改的样式
          if (Number(item.id) > this.startMonth && Number(item.id) < this.endMonth) {
            item.parentElement.style.background = '#f2f6fc'
            item.style.background = '#f2f6fc'
          }
        })
        if (document.getElementById(this.temp[0])) { // 最小月份的样式
          document.getElementById(this.temp[0]).style.background = '#409eff'
          document.getElementById(this.temp[0]).style.color = '#fff'
          document.getElementById(this.temp[0]).style.borderRadius = '18px'
          document.getElementById(this.temp[0]).parentElement.style.background = '#f2f6fc'
          document.getElementById(this.temp[0]).parentElement.style.borderRadius = '12px 0 0 12px' // 最小月份外围的边框
        }
        if (document.getElementById(this.temp[1])) { // 最大月份的样式
          document.getElementById(this.temp[1]).style.background = '#409eff'
          document.getElementById(this.temp[1]).style.color = '#fff'
          document.getElementById(this.temp[1]).style.borderRadius = '18px'
          document.getElementById(this.temp[1]).parentElement.style.background = '#f2f6fc'
          document.getElementById(this.temp[1]).parentElement.style.borderRadius = '0 12px 12px  0' // 最大月份外围的边框
        }
        if (this.temp.length === 1 && document.getElementById(this.temp[0]))document.getElementById(this.temp[0]).parentElement.style.borderRadius = '12px 12px 12px 12px'
        // 如果不存在最大月份,那么边框就是全部都是圆角的

        if (this.temp[0] === this.temp[1] && document.getElementById(this.temp[0]) && document.getElementById(this.temp[1])) {
          // 如果最小和最大月份相等,那么边框时圆角的
          document.getElementById(this.temp[1]).parentElement.style.borderRadius = '12px 12px 12px 12px'
        }
        if (tmp) { // 这个地方就是鼠标指向时的渲染恢复代码,因为当时把指向的当作了选择的,这时候需要还原回来。
          this.startMonth = 0
          this.endMonth = 0// 初始化开始结束月份
          this.temp.splice(this.temp.indexOf(tmp), 1) // 找到鼠标指向的元素,并且删除掉
        }

        console.info('flushDone', this.temp)
      })
    }

  }
}
</script>

<style lang="scss" scoped>

.month-select-con {
  display: flex;
  justify-content: space-between;
  .header-con {
    display: flex;
    justify-content: space-between;
  }
  .month-con {
    display: flex;
    justify-content: start;
    flex-wrap: wrap;
    width:100%;
    padding-left: 5px;
    li {
      width:25%;
    list-style: none;
    display: flex;
    justify-content: center;
    align-items: center;
    width:72px;
    height: 48px;
    margin-bottom: 5px;
    cursor: pointer;
    span{
      width: 85%;
      height: 85%;
      text-align: center;
      line-height: 36px;
    }
    }
  }
}
</style>

代码已经加上了注释,方便理解,并且可以看一下控制台输出,也许会带来灵感。

截图

image.png

image.png
image.png
image.png
image.png
image.png
image.png

结语

本人并非专业前端开发工程师,此代码可能存在一些瑕疵,大佬轻喷 :)

如果觉得我的文章对你有用,请随意赞赏