【vue自造轮子】日期范围选择器

2021-04-26  本文已影响0人  tysnd

组件介绍

1.样式类似element ui的日期范围选择器,但父组件中不强制使用input框来打开日期选择器。可以任意定义一个元素来控制日期选择器组件的显示隐藏,使用更为自由
2.返回的数据格式为开始和结束日期的Date对象,可以根据自己的需要转成任意格式

引用组件

在父组件中使用组件时,要绑定三个参数和两个事件

<choose-date-range
                :monthNum="6"
                :startDateStr="form.startDate"
                :endDateStr="form.endDate"
                @chooseDate="chooseDate" 
                @close="showDateRange = false">
</choose-date-range>

各参数和事件的含义为:
monthNum: 日期的范围从当前月份往后数monthNum个月,目前仅支持偶数,若传奇数,则默认加1
startDateStr:默认的开始日期,格式为"yyyy-mm-dd"
endDateStr: 默认的结束日期,格式为"yyyy-mm-dd"

chooseDate事件: 接收选择结果,参数为chooseDate (startDate, endDate),两个参数均为new Date()对象
close事件: 关闭日期选择器,根据自己的项目情况写

组件代码

<template>
  <div class="choose-date-range-components">
    <div class="opt-btns">
      <el-link @click="determine">确定</el-link>
      <el-link @click="cancel">取消</el-link>
    </div>
    <div class="tow-month">
      <div class="month-bar">
        <div class="header">
          <div class="pre-month">
            <i class="arrow el-icon-caret-left" @click="goPreMonth"></i>
          </div>
          <div class="month-date">{{curLeftYear}}年{{curLeftMonth}}月</div>
          <div class="next-month"></div>
        </div>
        <div class="weekdays">
          <div class="weekday">日</div>
          <div class="weekday">一</div>
          <div class="weekday">二</div>
          <div class="weekday">三</div>
          <div class="weekday">四</div>
          <div class="weekday">五</div>
          <div class="weekday">六</div>
        </div>
        <div class="days">
          <div class="day"
               @click="chooseDay(curLeftYear, curLeftMonth, day)"
               @mouseover="preChoose(curLeftYear, curLeftMonth, day)"
               @mouseout="preNotChoose(curLeftYear, curLeftMonth, day)"
               :class="{
                 'cannot-choose': judgeDisable(curLeftYear, curLeftMonth, day),
                 'in-range': judgeInRange(curLeftYear, curLeftMonth, day),
                 'in-pre-range': judgeInPreRange(curLeftYear, curLeftMonth, day),
                 'is-start': judgeStart(curLeftYear, curLeftMonth, day),
                 'is-end': judgeEnd(curLeftYear, curLeftMonth, day),
                 'is-today': judgeToday(curLeftYear, curLeftMonth, day)
               }"
               v-for="(day, index) in curLeftDays" :key="index">
            <div :class="{'day-inner': true,'chosen-day': judgeChosen(curLeftYear, curLeftMonth, day)}">
              {{day}}
            </div>
          </div>
        </div>
      </div>

      <div class="month-bar">
        <div class="header">
          <div class="pre-month"></div>
          <div class="month-date">{{curRightYear}}年{{curRightMonth}}月</div>
          <div class="next-month">
            <i class="arrow el-icon-caret-right" @click="goNextMonth"></i>
          </div>
        </div>
        <div class="weekdays">
          <div class="weekday">日</div>
          <div class="weekday">一</div>
          <div class="weekday">二</div>
          <div class="weekday">三</div>
          <div class="weekday">四</div>
          <div class="weekday">五</div>
          <div class="weekday">六</div>
        </div>
        <div class="days">
          <div class="day"
               @click="chooseDay(curRightYear, curRightMonth, day)"
               @mouseover="preChoose(curRightYear, curRightMonth, day)"
               @mouseout="preNotChoose(curRightYear, curRightMonth, day)"
               :class="{
                 'cannot-choose': judgeDisable(curRightYear, curRightMonth, day),
                 'in-range': judgeInRange(curRightYear, curRightMonth, day),
                 'in-pre-range': judgeInPreRange(curRightYear, curRightMonth, day),
                 'is-start': judgeStart(curRightYear, curRightMonth, day),
                 'is-end': judgeEnd(curRightYear, curRightMonth, day),
                 'is-today': judgeToday(curRightYear, curRightMonth, day)
               }"
               v-for="(day, index) in curRightDays" :key="index">
            <div :class="{'chosen-day': judgeChosen(curRightYear, curRightMonth, day)}">
              {{day}}
            </div>
          </div>
          <!--        <div class="day" v-for="(day, index) in curRightDays" :key="index">-->
          <!--          {{day}}-->
          <!--        </div>-->
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "ChooseDateRange",
  props: ['monthNum', 'startDateStr', 'endDateStr'],
  data () {
    return {
      leftDate: {},
      rightDate: {},
      chooseStartDateStr: '',
      chooseEndDateStr: '',
      preChooseDateStr: '',
      curLeftYear: '',
      curLeftMonth: '',
      curRightYear: '',
      curRightMonth: '',
      curLeftDays: [],
      curRightDays: []
    }
  },
  methods: {
    addMonth (date, monthNum = this.monthNum) {
      let year = date.getFullYear()
      let month = date.getMonth() + 1
      let year2 = year
      let month2 = month + monthNum
      if (month2 > 12) {
        year2 += 1
        month2 %= 12
      }
      return new Date (year2 + '-' + ('0' + month2).slice(-2) + '-01')
    },
    minusMonth (date, monthNum) {
      let year = date.getFullYear()
      let month = date.getMonth() + 1
      let year2 = year
      let month2 = month - monthNum
      if (month2 <= 0) {
        year2 -= 1
        month2 += 12
      }
      let dayNum = new Date(year2, month2, 0).getDate()
      return new Date (year2 + '-' + ('0' + month2).slice(-2) + '-' + dayNum + ' 00:00:00')
    },
    getDays (year, month) {
      let dayNum = new Date(year, month, 0).getDate()
      let days = Array.apply(null, new Array(new Date(year, month - 1, 1).getDay())).map(() => '')
      days = [...days, ...Array.apply(null, new Array(dayNum)).map((cur, index) => index + 1)]
      // console.log(days)
      return days
    },
    judgeChosen (year, month, day) {
      let tmpDateStr = year + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2)
      // console.log(tmpDateStr)
      if (tmpDateStr === this.chooseStartDateStr || tmpDateStr === this.chooseEndDateStr) {
        return true
      }
      return false
    },
    judgeDisable (year, month, day) {
      if (day === '') {
        return true
      }
      let tmpDate = new Date(year + '-' + month + '-' + day)
      if (tmpDate < this.leftDate || tmpDate > this.endDate) {
        return true
      }
      return false
    },
    judgeInRange(year, month, day) {
      if (day === '') {
        return false
      }
      let tmpDate = new Date(year + '-' + month + '-' + day)
      // console.log('.....................')
      // console.log(tmpDate)
      // console.log(new Date(this.chooseStartDateStr))
      // console.log(new Date(this.chooseEndDateStr))
      if (tmpDate >= new Date(this.chooseStartDateStr + ' 00:00:00') && tmpDate <= new Date(this.chooseEndDateStr + ' 00:00:00')) {
        return true
      }
      return false
    },
    judgeStart (year, month, day) {
      let tmpCurDateStr = year + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2)
      if (this.chooseStartDateStr && this.chooseEndDateStr) {
        // 开始日期和结束日期都选中
        if (tmpCurDateStr === this.chooseStartDateStr) {
          return true
        }
      } else if (this.chooseStartDateStr || this.chooseEndDateStr) {
        // 只选中一个, 要考虑hover的预选日期情况
        let tmpChooseDateStr = this.chooseStartDateStr || this.chooseEndDateStr
        if (!this.preChooseDateStr) {
          return true
        } else {
          if (this.preChooseDateStr >= tmpChooseDateStr) {
            if (tmpCurDateStr === tmpChooseDateStr) {
              return true
            }
          } else {
            if (tmpCurDateStr === this.preChooseDateStr) {
              return true
            }
          }
        }
      }
      return false
    },
    judgeEnd (year, month, day) {
      let tmpCurDateStr = year + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2)
      if (this.chooseStartDateStr && this.chooseEndDateStr) {
        // 开始日期和结束日期都选中
        if (tmpCurDateStr === this.chooseEndDateStr) {
          return true
        }
      } else if (this.chooseStartDateStr || this.chooseEndDateStr) {
        // 只选中一个, 要考虑hover的预选日期情况
        let tmpChooseDateStr = this.chooseStartDateStr || this.chooseEndDateStr
        if (!this.preChooseDateStr) {
          return true
        } else {
          if (this.preChooseDateStr <= tmpChooseDateStr) {
            if (tmpCurDateStr === tmpChooseDateStr) {
              return true
            }
          } else {
            if (tmpCurDateStr === this.preChooseDateStr) {
              return true
            }
          }
        }
      }
      return false
    },
    judgeToday (year, month, day) {
      let today = new Date()
      if (year === today.getFullYear() && month === today.getMonth() + 1 && day === today.getDate()) {
        return true
      }
      return false
    },
    chooseDay (year, month, day) {
      if (this.judgeDisable(year, month, day)) {
        return
      }
      let tmpDateStr = year + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2)
      console.log(tmpDateStr)
      if (this.chooseStartDateStr && this.chooseEndDateStr) {
        // 开始和结束日期都已选中
        if (tmpDateStr === this.chooseStartDateStr) {
          this.chooseStartDateStr = this.chooseEndDateStr
          this.chooseEndDateStr = ''
        } else if (tmpDateStr === this.chooseEndDateStr) {
          this.chooseEndDateStr = ''
        } else if (tmpDateStr > this.chooseEndDateStr) {
          this.chooseEndDateStr = tmpDateStr
        } else if (tmpDateStr < this.startDateStr) {
          this.chooseStartDateStr = tmpDateStr
        } else {
          this.chooseEndDateStr = tmpDateStr
        }
      } else if (this.chooseStartDateStr || this.chooseEndDateStr) {
        // 开始和结束日期仅选中一个
        if (tmpDateStr > this.chooseStartDateStr) {
          this.chooseEndDateStr = tmpDateStr
        } else if (tmpDateStr < this.chooseStartDateStr) {
          this.chooseEndDateStr = this.chooseStartDateStr
          this.chooseStartDateStr = tmpDateStr
        } else {
          this.chooseStartDateStr = ''
        }
      } else {
        // 开始和结束日期均未选中
        this.chooseStartDateStr = tmpDateStr
      }
    },
    preChoose (year, month, day) {
      if (!this.judgeDisable(year, month, day)) {
        this.preChooseDateStr = year + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2)
        // console.log(this.preChooseDateStr)
        this.$forceUpdate()
      }
    },
    preNotChoose (year, month, day) {
      this.preChooseDateStr = ''
    },
    judgeInPreRange (year, month, day) {
      // console.log(year, month, day)
      if (!this.preChooseDateStr) {
        return false
      }
      if (this.judgeDisable(year, month, day)) {
        return false
      }
      if ((this.chooseStartDateStr && this.chooseEndDateStr) || (!this.chooseStartDateStr && !this.chooseEndDateStr)) {
        return false
      } else {
        let tmpCurDateStr = year + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2)
        let tmpDateStr = this.chooseStartDateStr || this.chooseEndDateStr
        let left, right
        if (tmpDateStr < this.preChooseDateStr) {
          left = tmpDateStr
          right = this.preChooseDateStr
        } else {
          left = this.preChooseDateStr
          right = tmpDateStr
        }
        console.log(left, right, tmpCurDateStr)
        if (tmpCurDateStr <= right && tmpCurDateStr >= left) {
          // console.log(tmpCurDateStr)
          return true
        } else {
          return false
        }
      }
    },

    goPreMonth () {
      // console.log(new Date(this.curLeftYear, this.curLeftMonth - 1, 1))
      // console.log(this.minusMonth(new Date(this.curLeftYear, this.curLeftMonth - 1, 1),2))
      // console.log(this.leftDate)
      if (this.minusMonth(new Date(this.curLeftYear, this.curLeftMonth - 1, 1), 2) >= this.leftDate ) {
        let curLeftDate = this.minusMonth(new Date(this.curLeftYear, this.curLeftMonth - 1, 1), 2)
        let curRightDate = this.minusMonth(new Date(this.curLeftYear, this.curLeftMonth - 1, 1), 1)
        this.curLeftYear = curLeftDate.getFullYear()
        this.curLeftMonth = curLeftDate.getMonth() + 1
        this.curRightYear = curRightDate.getFullYear()
        this.curRightMonth = curRightDate.getMonth() + 1
        this.curLeftDays = this.getDays(this.curLeftYear, this.curLeftMonth)
        this.curRightDays = this.getDays(this.curRightYear, this.curRightMonth)
      }
    },
    goNextMonth () {
      if (this.addMonth(new Date(this.curRightYear, this.curRightMonth - 1, 1), 2) <= this.rightDate ) {
        let curLeftDate = this.addMonth(new Date(this.curRightYear, this.curRightMonth - 1, 1), 1)
        let curRightDate = this.addMonth(new Date(this.curRightYear, this.curRightMonth - 1, 1), 2)
        this.curLeftYear = curLeftDate.getFullYear()
        this.curLeftMonth = curLeftDate.getMonth() + 1
        this.curRightYear = curRightDate.getFullYear()
        this.curRightMonth = curRightDate.getMonth() + 1
        this.curLeftDays = this.getDays(this.curLeftYear, this.curLeftMonth)
        this.curRightDays = this.getDays(this.curRightYear, this.curRightMonth)
      }
    },
    determine () {
      if (this.chooseStartDateStr && this.chooseEndDateStr) {
        this.$emit('chooseDate', new Date(this.chooseStartDateStr), new Date(this.chooseEndDateStr))
      } else {
        this.$notify.warning({
          title: '提醒',
          message: '开始和结束日期都要选择!'
        })
      }
    },
    cancel () {
      this.$emit('close')
    }
  },
  created() {
    if (this.monthNum % 2) {
      this.monthNum += 1
    }
    let leftDate = new Date(new Date(new Date().getTime()).setHours(0,0,0,0))
    let rightDate = this.addMonth(leftDate, this.monthNum)
    this.leftDate = leftDate
    this.rightDate = rightDate
    console.log(leftDate, rightDate)
    let startDate = new Date(this.startDateStr)
    let endDate = new Date(this.endDateStr)
    console.log(startDate, endDate)
    if (startDate >= endDate || startDate < leftDate || endDate > rightDate) {
      this.chooseStartDateStr = ''
      this.chooseEndDateStr = ''
      // console.log('超范围', this.leftDate)
      this.curLeftYear = this.leftDate.getFullYear()
      this.curLeftMonth = this.leftDate.getMonth() + 1
      this.curRightYear = this.addMonth(this.leftDate, 1).getFullYear()
      this.curRightMonth = this.addMonth(this.leftDate, 1).getMonth() + 1
      this.$notify.warning({
        title: '提醒',
        message: '日期超出范围,请重新选择'
      })
    } else {
      this.chooseStartDateStr = this.startDateStr
      this.chooseEndDateStr = this.endDateStr
      // console.log('没超', this.chooseStartDateStr)
      let chooseStartDate = new Date(this.chooseStartDateStr)
      // console.log(chooseStartDate)
      // let chooseEndDate = new Date(this.chooseEndDateStr)
      this.curLeftYear = chooseStartDate.getFullYear()
      this.curLeftMonth = chooseStartDate.getMonth() + 1
      this.curRightYear = this.addMonth(chooseStartDate, 1).getFullYear()
      this.curRightMonth = this.addMonth(chooseStartDate, 1).getMonth() + 1
      // console.log(this.addMonth(chooseStartDate, 1))
    }
    this.curLeftDays = this.getDays(this.curLeftYear, this.curLeftMonth)
    this.curRightDays = this.getDays(this.curRightYear, this.curRightMonth)
  }
}
</script>

<style lang="less" scoped>
.choose-date-range-components {
  color: #000000;
  display: flex;
  flex-direction: column;
  box-shadow: 0 0 4px 0 rgba(117,117,117,0.5);
  //opacity: 1!important;
  border-radius: 2px;
  .opt-btns {
    padding: 10px;
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid #dddddd;
  }
  .tow-month {
    display: flex;
    .month-bar {
      width: 50%;
      border-right: 1px solid #dddddd;
      padding: 0 10px;
      .header {
        display: flex;
        justify-content: space-between;
        padding: 5px 0;
        .arrow {
          cursor: pointer;
        }
      }
      .weekdays {
        display: flex;
        .weekday {
          width: 40px;
          text-align: center;
        }
      }
      .days {
        display: flex;
        flex-wrap: wrap;
        .day {
          width: 40px;
          text-align: center;
          height: 40px;
          line-height: 30px;
          cursor: pointer;
          padding: 5px;
          .day-inner {
            width: 100%;
            height: 100%;
          }
          .chosen-day {
            //width: 100%;
            //height: 100%;
            border-radius: 20px;
            //border: 1px solid red;
            background-color: #409eff;
            color: #FFFFFF;
          }
        }
        .cannot-choose {
          color: #dddddd;
          cursor: auto;
        }
        .in-range {
          background-color: #f2f6fc;
        }
        .in-pre-range {
          background-color: #f2f6fc;
        }
        .is-start {
          border-radius: 20px 0 0 20px;
        }
        .is-end {
          border-radius: 0 20px 20px 0!important;
        }
        .is-today {
          color: #409eff;
        }
      }
    }
    .month-bar:last-child {
      border-right: none;
    }
  }
}
</style>
上一篇下一篇

猜你喜欢

热点阅读