【vue】基于element UI周控件实现的单选周和多选周

目前element UI上的周控件只有单选周
前段时间 由于需要 本人仿照element单选周的风格写了一个单选周和多选周控件

最近整理了一下代码 分享给大家 希望大家可以相互交流
呼呼呼呼~

组件介绍:

  • 以每年的1月1号所在的周数为第一周
  • 日历中增加了周数的展示
  • 可设置禁选的周数(可配置)
  • 多选周组件可以设置一开始周为原点的周范围(可配置)

单选周


用法

    

单选周:"dateNow" @change="changeDate" :weekOptions="weekOptions">

复制代码

单选周相关Api

     // 默认值 [开始值,结算值] 或者 []
     dateNow:["2019-02-25", "2019-03-03"],
     // 配置禁选周
      weekOptions: {
        disabledWeeks: {
          '2019':[10,11,12]   //禁用 2019年 第10,11,12 周  
        },
      }
复制代码

要点一 当月所在的日历生成

单选周的主要突破点在于6排8列的日历数组的实现 要绘制出一个月的日历表 我们需要知道:

  • 当月第一天是星期几
  • 上个月有多少天
  • 本月有多少天

以上可以推算出当前月中上月和下月的日期

主要实现代码:

  rows() {
      const date = new Date(this.year, this.month, 1)  // 本月一号日期
      let day = this.getFirstDayOfMonth(date)  // 本月第一天的星期
      day = (day === 0 ? 7 : day)
      const dateCountOfMonth = this.getDayCountOfMonth(date.getFullYear(), date.getMonth()) // 本月的天数
      const dateCountOfLastMonth = this.getDayCountOfPreMonth(date.getFullYear(), (date.getMonth() === 0 ? 11 : date.getMonth() - 1))  // 上个月的天数
      const datesArry = this.tableRows
      let count = 1
      // 获取当前月需要渲染的每个单元格的日期
      for (let i = 0; i < 6; i++) {
        const row = datesArry[i]
        for (let j = 0; j < 7; j++) {
          let cell = row[j]
          if (!cell) {
            //初始化cell
            cell = { row: i, column: j, type: 'normal', isToday: false, dateString: null, limit: false, week: null,year:null }
          }
          cell.type = 'normal'
          // 获取每个cell对应的日期
          if (i >= 0 && i <= 1) {
            if (j + i * 7 >= (day - 1)) {
              cell.text = count++
            } else {
              cell.text = dateCountOfLastMonth - (day - 1) + j  + 1 - i * 7
              cell.type = 'prev-month'
            }
          } else {
            if (count <= dateCountOfMonth) {
              cell.text = count++
            } else {
              cell.text = count++ - dateCountOfMonth
              cell.type = 'next-month'
            }
          }
         this.setCellToday(cell) //设置是否今天
         this.setCellDateString(cell) //设置格式化的日期
         this.setCellLimit(cell)   //是否禁用
         this.setCellYear(cell)   //设置年
         this.setCellWeek(cell)   //设置周数
         this.setDisabledWeeks(cell)   //设置禁用周
         this.$set(row, j, cell)   // 保存
        }
      }
      return (datesArry)
    }
复制代码

这样我们可以得到一个6行7列的日历数组rows
以上代码主要是计算出了rows中每个cell展示的日期数字
当然每个cell中还会存储其他相关属性

通过该函数渲染出来的每个cell包含以下内容:

要点二 日历中切换上月下月以及上年下年

切换的实现方式主要运用了 Vue 中 computed 属性
每次点击切换月份或者年份的箭头时 会改变 计算属性 tableString 内引用的值
故计算属性 tableString 会同时被更新
而日历数组也作为计算属性 在计算的时候受 tableString 的影响 最终也会同步更新

点击切换-> 改变 计算属性 tableString-> 改变 计算属性 rows->自动触发日历的更新

这样就实现了 点击切换箭头时候 日历数组也会同步更新

// 日历table表渲染内容 受year/month/day等变化影响
    tableString: {
      get() {
        let year = this.calender_year
        let month = parseInt(this.calender_month) + 1
        let day
        if (month < 10) {
          month = '0' + month
        }
        if (parseInt(this.calender_day) < 10) {
          day = '0' + this.calender_day
        } else {
          day = this.calender_day
        }
        return year + '-' + month + '-' + day
      },
      set() {
      }
   }
复制代码

要点三 获取任意日期所处月份和年份

//以每年的1月1号为第一周  根据日期获取周数
    getYearWeekByDate(date) {
      if (!date) {
        //清空input框时候
        let curDate = new Date()
        return {
          year: curDate.getFullYear(),
          week: 0
        }
      }
      //讲日期移到周日计算
      var temptDate = new Date(date)
      if (temptDate.getDay() !== 0) {
        date = this.addDate(temptDate, (7 - temptDate.getDay()))
      }
      var da = date; //日期格式2018-12-30
      //当前日期
      var date1 = new Date(da.substring(0, 4), parseInt(da.substring(5, 7)) - 1, da.substring(8, 10));
      //1月1号
      var date2 = new Date(da.substring(0, 4), 0, 1);
      //获取1月1号星期(以周一为第一天,0周一~6周日)
      var dateWeekNum = date2.getDay() - 1;
      if (dateWeekNum < 0) {
        dateWeekNum = 6;
      }
      date2.setDate(date2.getDate() - dateWeekNum);
      var d = Math.round((date1.valueOf() - date2.valueOf()) / 86400000) + 1;
      var week = Math.ceil(d / 7);
      return {
        year: da.substring(0, 4),
        week: week
      }
    }
复制代码

因为我们是以每年的1月1号作为第一周
给组件赋值的时候 需要通过此函数计算默认选中了哪年哪周
如果通过getYear()方法获取默认值的年数和周数的话
跨年的时候
会导致前端展示的选中的周数不全或者错位 日历UI展示的周数和input框对应不上等诸多现象
比如 2018-12-31 直接通过时间函数获取结果是2018年第53周
而我们预期的该日期结果是 2019年第1周

多选周


用法:

    

多选周:"dateNowDouble" @change="changeDateDouble" :weekOptions="weekOptions"> 复制代码

多选周Api:

  // 默认值 [开始值,结算值] 或者 []
  dateNowDouble:["2019-02-11", "2019-02-24"],
    // 配置禁选周
    weekOptions: {
    disabledWeeks: {
    '2019':[10,11,12]   //禁用 2019年 第10,11,12 周  
    },
    // 以选择的开始周为原点的可选周范围
    weekRange: 2  //多周时候 设置可选的前后周范围
    }
   }
复制代码

要点一 初始化单元格背景颜色

多选周是在单选周的基础上改造的
核心代码类似 但是业务逻辑稍微复杂了些
多选周的日历数组分为存储在两个不同的数组中 rows_left 和 rows_right
与单选周相比 每个cell 额外增加了 select 属性 记录当前cell被选中的信息

setSelectValue(datesArry) {
      datesArry.forEach((row) => {
        row.forEach((cell, index) => {
          var select = 0
          if (cell.type == 'normal') {   //本月的才设置选中状态
            if ((row[0].year == row[6].year)) {  
              //如果该周起止日期是同一年  并且选择的结束周 > 开始周
              if (cell.week >= this.weekNumStart && cell.week <= this.weekNumEnd && cell.year === Number(this.weekYearStart)) {
                select = 1
                if (index == 0 && cell.week == this.weekNumStart) {
                  select = 2
                }
                if (index == 6 && cell.week == this.weekNumEnd) {
                  select = 3
                }
              }
              //如果该周跨年了    并且选择的结束周 < 开始周
              if (this.weekYearStart != this.weekYearEnd) {
                if ((cell.week >= this.weekNumStart && cell.year === Number(this.weekYearStart)) || (cell.week <= this.weekNumEnd && cell.year === Number(this.weekYearEnd))) {
                  select = 1
                  if (index == 0 && cell.week == this.weekNumStart) { 
                    select = 2
                  }
                  if (index == 6 && cell.week == this.weekNumEnd) {
                    select = 3
                  }
                }
              }
            } else {
              //如果该周跨年 以该周的周日所在年和周为比较依据  决定该周是否被选中
              if (row[6].week >= this.weekNumStart && row[6].week <= this.weekNumEnd && row[6].year === Number(this.weekYearStart)) {
                select = 1
                if (index == 0) {
                  select = 2
                }
                if (index == 6 && cell.week == this.weekNumEnd) {
                  select = 3
                }
              }
            }
          }
          this.$set(cell, 'select', select)
        })
      })
},
复制代码

要点二 周范围的几种情况

周范围意思多选周组件中 选择起始周后 在哪些范围内能选择结束周
比如 周范围设置成 2
起始周选择了2019年第7周
则只能在以开始周为原点上下2周范围内选择结束周
也就是说结束周只能在6-8周中选择 除此之外的其他周将被禁用

    //设置可选的周范围
    setRngeWeeks(cell) {
      //如果设置了特定的周范围 并且选择了开始周
      if (this.isSetRange && this.selectFlag !== 0) {
        // 获取结束周可选的周范围的起止时间
        var weekNums_begin = this.weekNumStart - (Number(this.weekOptions.weekRange) - 1)
        var weekNums_end = this.weekNumStart + (Number(this.weekOptions.weekRange) - 1)
        //周区间时候  获取开始周的年份的总周数
        var current_year = this.getYearWeekByDate(this.firstSelectValue[1].dateString).year
        var totalWeeksOfCurrentYear = this.getWeekNumsOfYear(current_year).week
        var totalWeeksOfPreYear = this.getWeekNumsOfYear(Number(current_year) - 1).week
        var totalWeeksOfAftYear = this.getWeekNumsOfYear(Number(current_year) + 1).week
        // 对结束周可选的前后区间的取值情况进行多重分类
        if (weekNums_begin >= 1 && weekNums_end <= totalWeeksOfCurrentYear) {
          //结束周可选的前后区间落在 今年
          if ((cell.week < weekNums_begin && cell.year == this.weekYearStart) || (cell.week > weekNums_end && cell.year == this.weekYearStart)) {
            this.$set(cell, 'limit', true)
          }
          if ((cell.year !== this.weekYearStart)) {
            this.$set(cell, 'limit', true)
          }
        }
        if (weekNums_begin < 1 && weekNums_end <= totalWeeksOfCurrentYear) {
          //结束周可选的前后区间落在 今年和前一年 
          weekNums_begin = totalWeeksOfPreYear + weekNums_begin
          if ((cell.week < weekNums_begin && (Number(cell.year) + 1) == this.weekYearStart) || (cell.week > weekNums_end && cell.year == this.weekYearStart)) {
            this.$set(cell, 'limit', true)
          }
          if ((cell.year < this.weekYearStart - 1) || (cell.year > this.weekYearStart + 1)) {
            this.$set(cell, 'limit', true)
          }

        }
        if (weekNums_begin >= 1 && weekNums_end > totalWeeksOfCurrentYear) {
          //结束周可选的前后区间落在 今年和后一年 
          weekNums_end = weekNums_end - totalWeeksOfCurrentYear
          // debugger
          if ((cell.week < weekNums_begin && (cell.year) == this.weekYearStart) || (cell.week > weekNums_end && Number(cell.year - 1) == this.weekYearStart)) {
            this.$set(cell, 'limit', true)
          }
          if ((cell.year < this.weekYearStart) || (cell.year > this.weekYearStart + 1)) {
            this.$set(cell, 'limit', true)
          }
        }
        if (weekNums_begin < 1 && weekNums_end > totalWeeksOfCurrentYear) {
          //结束周可选的前后区间落在 前一年和后一年 
          weekNums_begin = totalWeeksOfPreYear + weekNums_begin
          weekNums_end = weekNums_end - totalWeeksOfCurrentYear
          if ((cell.week < weekNums_begin && (cell.year + 1) == this.weekYearStart) || (cell.week > weekNums_end && Number(cell.year - 1) == this.weekYearStart)) {
            this.$set(cell, 'limit', true)
          }
          if ((cell.year < this.weekYearStart - 1) || (cell.year > this.weekYearStart + 1)) {
            this.$set(cell, 'limit', true)
          }
        }
      }
    }
复制代码

这块逻辑稍微繁琐一点
需要对可选的周范围进行多重分类:

  • 结束周可选的前后区间落在 今年
  • 结束周可选的前后区间落在 今年和后一年
  • 结束周可选的前后区间落在 今年和前一年
  • 结束周可选的前后区间落在 前一年和后一年

区间落在哪一年 就需要知道哪一年的总周数 然后再进行加减计算 禁用相关周数

完整单选周多选周源码请移步: github.com/zenda96/wee…

有问题或者交流可以添加VX(请备注: 掘金)

转载于:https://juejin.im/post/5c948acae51d4572d1055380

你可能感兴趣的:(【vue】基于element UI周控件实现的单选周和多选周)