美文网首页
Vue组件开发系列之Calendar组件

Vue组件开发系列之Calendar组件

作者: vue爱好者 | 来源:发表于2019-08-15 07:13 被阅读0次

一个简单实用的日历组件

演示地址:
http://widget-ui.cn/Calendar

组件源码:
https://github.com/AntJavascript/widgetUI/tree/master/Calendar

基本用法:
<wt-calendar @change="change"></wt-calendar>


微信截图_20190807213514.png

参数(isFull)


微信截图_20190807213603.png

参数(section="[2019/8/1-2019/8/14]")


微信截图_20190807214432.png

参数(showBtn)


微信截图_20190807214544.png

组件结构:

<template>
  <div class='wt-calendar'>
    <div class="date mint-content">
      <div class="calendar-content">
        <!-- 年份 月份 -->
      <div class="year-month" :class="{'showBtn':showBtn}">
        <wt-button v-if="showBtn" title="取消" type="default" size="small" style="width: 3rem;" @click.native="cancel"></wt-button>
        <div class="month">
            <i class="icon-back" @click="pickPreNext(currentYear,currentMonth,0)">  </i>
            <span>{{ currentYear }} 年 {{ currentMonth }} 月</span>
            <i class="icon-right" @click="pickPreNext(currentYear,currentMonth,42)">  </i>
        </div>
        <wt-button v-if="showBtn" title="确定" type="primary" size="small" style="width: 3rem;" @click.native="comfirm"></wt-button>
      </div>
      <!-- 星期 -->
      <ul class="weekdays">
        <li>一</li>
        <li>二</li>
        <li>三</li>
        <li>四</li>
        <li>五</li>
        <li>六</li>
        <li>日</li>
      </ul>
      <!-- 日期 -->
      <div class="bodyDiv" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" :class="classMove">
        <ul class="days" v-for="(value,index1) in daysUL" :key="index1">
          <li @click="pick(day,index+index1*7)" v-for="(day, index) in value" :key="index">
            <!--本月-->
            <div class="dateItem"
               :class="{
                'disabled':disabledClassName(day),
                'other': otherClassNme(day),
                'currentDay': currentDayClassName(day),
                'selected': selectedClassNme(day, index, index1)
                }">
              <span v-if="day==='spaces'"></span>
              <span v-else >{{ day.getDate() }}</span>
           </div>
          </li>
        </ul>
      </div>
      </div>
    </div>
  </div>
</template>

代码分析:

components参数:

import Button from '../Button/Button';  // 请查看Button组件  https://www.jianshu.com/p/769ff9c19f8d

components: {
    'wt-button': Button
  }

props参数:

props: {
    isFull: {
      type: String,
      default: () => {
        return undefined;
      }
    },
    section: {
      type: String,
      default: () => {
        return '';
      }
    },
    showBtn: {
      type: Boolean,
      default: () => {
        return false;
      }
    }
  }

data参数:

data () {
    return {
      days: [], // 用于临时存储数据
      daysUL: [],
      isSelected: [],
      currentYear: '',
      currentMonth: '',
      selectIndex: '',
      firstWeek: 1,
      classMove: '',
      startX: 0,
      startY: 0,
      moveX: 0,
      moveY: 0,
      moveing: false,
      sectionBegin: this.section !== '' ? this.section.split('-')[0].replace(/\[/, '') : '',
      sectionEnd: this.section !== '' ? this.section.split('-')[1].replace(/\]/, '') : '',
      beforeSpaces: [], // 前面需要空几天
      currentDate: new Date() // 选中的日期
    };
  }

created 生命周期:

created () {
    var self = this;
    var date = new Date();
    this.currentYear = date.getFullYear();
    this.currentMonth = date.getMonth() + 1;
    self.initData(self.formatDate(this.currentYear, this.currentMonth, 1), true);
  },

methods函数:

methods: {
    // 取消
    cancel () {
      this.$emit('cancel');
    },
    // 确定
    comfirm () {
      this.$emit('comfirm', this.currentDate);
    },
    touchStart () {
      // 开始触摸坐标
      this.startX = event.touches[0].clientX;
      this.startY = event.touches[0].clientY;
    },
    touchMove () {
      // 移动触摸坐标
      this.moveX = event.touches[0].clientX;
      this.moveY = event.touches[0].clientY;
      if (Math.abs(this.startY - this.moveY) > 30) {
        return;
      } else {
        event.preventDefault();
      }
      event.preventDefault();
    },
    touchEnd () {
      // 300毫秒内只允许滑动一次,避免滑动过快
      if (this.moveing) {
        return;
      }
      this.moveing = true;
      setTimeout(() => {
        this.moveing = false;
      }, 300);
      var self = this;
      var distance = this.moveX - this.startX;
      // 如果往左滑 < -50 就生成下一月数据
      if (this.moveX !== 0 && distance < -50) {
        // 如果有section参数
        if (self.section) {
          if (self.currentMonth === 12) {
            self.classMove = 'animated fadeInRight';
            if (new Date((self.currentYear + 1) + '/1/1') < new Date(self.sectionEnd)) {
              self.pickPreNext(self.currentYear, self.currentMonth, 42);
            }
          } else if (new Date(self.currentYear + '/' + (self.currentMonth + 1) + '/1') <= new Date(self.sectionEnd)) {
            self.classMove = 'animated fadeInRight';
            self.pickPreNext(self.currentYear, self.currentMonth, 42);
          }
        } else {
          self.classMove = 'animated fadeInRight';
          self.pickPreNext(self.currentYear, self.currentMonth, 42);
        }
      // 如果往左滑 > 50 就生成上一月数据
      } else if (distance > 50) {
        // 如果有section参数
        if (self.section) {
          if (self.currentMonth === 1) {
            self.classMove = 'animated fadeInLeft';
            if (new Date((self.currentYear - 1) + '/12/1') > new Date(self.sectionBegin)) {
              self.pickPreNext(self.currentYear, self.currentMonth, 0);
            }
          } else if (new Date(self.currentYear + '/' + self.currentMonth + '/1') > new Date(self.sectionBegin)) {
            self.classMove = 'animated fadeInLeft';
            self.pickPreNext(self.currentYear, self.currentMonth, 0);
          }
        } else {
          self.classMove = 'animated fadeInLeft';
          self.pickPreNext(self.currentYear, self.currentMonth, 0);
        }
      }
      setTimeout(function () {
        self.classMove = '';
        self.startX = 0;
        self.moveX = 0;
      }, 200);
    },
    // 格式化日期
    formatDate (year, month, day) {
      const y = year;
      let m = month;
      if (m < 10) m = `0${m}`;
      let d = day;
      if (d < 10) d = `0${d}`;
      return `${y}/${m}/${d}`;
    },
    // 初始化日期
    initData (cur, init) {
      var self = this;
      var paramDate = new Date(cur);
      self.currentYear = paramDate.getFullYear();
      self.currentMonth = paramDate.getMonth() + 1;
      self.days.length = 0;
      // 没有的日期用 "spaces" 代替
      self.firstWeek = self.dayWeek(paramDate.getFullYear(), paramDate.getMonth() + 1);
      var beforeSpaces = new Array(self.firstWeek - 1).fill('spaces', 0, self.firstWeek - 1);
      self.beforeSpaces = beforeSpaces;
      // 如果存在 ifFull 参数说明需要显示42天
      if (self.isFull !== undefined) {
        var prevMonth = '';
        // 如果当前月份是1月
        if (self.currentMonth === 1) {
          prevMonth = new Date(self.currentYear - 1, 12, 0).getDate(); // 取上一个月的最后一天
          beforeSpaces.forEach((item, index) => {
            self.days.push(new Date(self.currentYear - 1 + '/' + '12/' + (prevMonth - (beforeSpaces.length - (index + 1)))));
          });
        } else {
          prevMonth = new Date(self.currentYear, self.currentMonth - 1, 0).getDate(); // 取上一个月的最后一天
          beforeSpaces.forEach((item, index) => {
            self.days.push(new Date(self.currentYear + '/' + (self.currentMonth - 1) + '/' + (prevMonth - (beforeSpaces.length - (index + 1)))));
          });
        }
      } else {
        beforeSpaces.forEach((item, index) => {
          self.days.push(item);
        });
      }
      self.isSevenDay();
      // 循环当月总天数
      var curMonthDays = self.getcurMonthDays(self.currentYear, self.currentMonth);
      for (let i = 0; i <= curMonthDays; i += 1) {
        const d = new Date(self.currentYear + '/' + self.currentMonth + '/' + 1);
        if (i < curMonthDays) {
          // 小于当月最大天数,放入数组
          d.setDate(d.getDate() + i);
          self.days.push(d);
        } else {
          // 不足7天用 "spaces" 代替
          for (let j = self.days.length, l = 1; j < 7; j++, l++) {
            // 如果存在 ifFull 参数说明需要显示42天
            if (self.isFull !== undefined) {
              if (self.currentMonth === 12) {
                self.days.push(new Date((self.currentYear + 1) + '/1/' + l));
              } else {
                self.days.push(new Date(self.currentYear + '/' + (self.currentMonth + 1) + '/' + l));
              }
            } else {
              // self.days.push('spaces');
            }
          }
        }
        self.isSevenDay();
      }
      self.isSevenDay(true);
      // 如果没有42条数据
      if (self.isFull !== undefined && self.daysUL.length < 6) {
        let lastData = self.daysUL[self.daysUL.length - 1][6]; // 最后一条数据
        for (let j = lastData.getDate(); j < (7 + lastData.getDate()); j++) {
          self.days.push(new Date(lastData.getFullYear() + '/' + (lastData.getMonth() + 1) + '/' + j));
        }
        self.isSevenDay();
      }
      // 如果 self.daysUL[0] 没有值
      if (self.isFull !== undefined && self.daysUL[0].length === 0) {
        self.daysUL.splice(0, 1);
        let lastData = self.daysUL[self.daysUL.length - 1][6]; // 最后一条数据
        for (let j = lastData.getDate(); j < (7 + lastData.getDate()); j++) {
          self.days.push(new Date(lastData.getFullYear() + '/' + (lastData.getMonth() + 1) + '/' + j));
        }
        self.isSevenDay();
      }
    },
    // 某月1号是星期几
    dayWeek (year, month) {
      var week = new Date('' + year + '/' + month + '/' + '1').getDay();
      return week === 0 ? 7 : week;
    },
    // 是否有7天数据,有的话push给 "daysUL"
    isSevenDay (flag) {
      if (flag) {
        this.daysUL.push(this.days);
        this.days = [];
        return;
      }
      if (this.days.length % 7 === 0) {
        this.daysUL.push(this.days);
        this.days = [];
      }
    },
    // 是否的当天
    isToDay (year, month, day) {
      var toDate = new Date();
      return toDate.getFullYear() === year && toDate.getMonth() + 1 === month && toDate.getDate() === day;
    },
    // 是否的当月
    isToMonth (year, month) {
      var toDate = new Date();
      var day = toDate.getDate();
      if (toDate.getFullYear() === year && toDate.getMonth() + 1 === month) {
        this.selectIndex = day + this.beforeSpaces.length;
        return true;
      } else {
        return false;
      }
    },
    // 获取当月有多少天
    getcurMonthDays (year, month) {
      var curMonthDays = new Date(year, month, 0).getDate();
      return curMonthDays;
    },
    // 上一個月&下一个月   传入当前年份和月份
    pickPreNext (year, month, num) {
      var sectionBeginDate = new Date(this.sectionBegin);
      var sectionEndDate = new Date(this.sectionEnd);
      const d = new Date(this.formatDate(year, month === 0 ? 1 : month, 1));
      if (num === 42) {
        var max = this.getcurMonthDays(year, month);
        d.setDate(max + 1);
      } else {
        d.setDate(num);
      }
      var curmnth = 0;
      if (month === 0) {
        curmnth = d.getMonth();
      } else {
        curmnth = d.getMonth() + 1;
      }
      // 处理点击超出范围逻辑
      if (this.section !== '') {
        if (!(d > sectionBeginDate) || !(d <= sectionEndDate)) {
          alert();
          return;
        }
      }
      this.daysUL = [];
      this.isSelected = [];
      this.selectIndex = '';
      if (this.isToMonth(d.getFullYear(), curmnth)) {
        this.initData(this.formatDate(d.getFullYear(), curmnth, 1), true);
      } else {
        this.initData(this.formatDate(d.getFullYear(), curmnth, 1), true);
      }
    },
    // 返回今天
    returnNow () {
      this.daysUL = [];
      this.initData(null);
    },
    // 当前选择日期
    pick (date, index) {
      if (this.disabledClassName(date)) {
        return;
      }
      if (date === 'spaces') {
        return;
      }
      this.selectIndex = index;
      this.isSelected = [];
      for (let i = 0; i < 42; i++) {
        if (index === i) {
          this.isSelected.push(true);
          continue;
        }
        this.isSelected.push(false);
      }
      this.currentDate = date;
      // 传值父组件
      this.$emit('change', date);
    },
    // 显示 other 类
    otherClassNme (day) {
      var self = this;
      if (new Date(day).getFullYear() === self.currentYear && (day.getMonth() + 1) === self.currentMonth) {
        return false;
      } else {
        return true;
      }
    },
    // 显示 currentDay 类
    currentDayClassName (day) {
      // 年月日都相等,则显示currentDay类,说明是当天
      if (new Date(day).getFullYear() === new Date().getFullYear() && (day.getMonth() + 1) === (new Date().getMonth() + 1) && day.getDate() === new Date().getDate()) {
        return true;
      } else {
        return false;
      }
    },
    /*
     * 年月日都相等
    */
    selectedClassNme (day, index, index1) {
      var self = this;
      var date = new Date();
      if (self.isSelected[(index + index1 * 7)] || (new Date(day).getFullYear() === date.getFullYear() && (day.getMonth() + 1) === (date.getMonth() + 1) && day.getDate() === date.getDate() && self.isSelected.length === 0)) {
        return true;
      } else {
        return false;
      }
    },
    // 超出规定范围,是否允许点击
    disabledClassName (day) {
      var date = new Date(day);
      var sectionBeginDate = new Date(this.sectionBegin);
      var sectionEndDate = new Date(this.sectionEnd);
      if (date < sectionBeginDate || date > sectionEndDate) {
        return true;
      } else {
        return false;
      }
    }
  }

css代码:

.wt-calendar {
    overflow: hidden;
    .date {
        color: #333;
        float: left;
        width: 100%;
        .calendar-content{
            background: #fff;
            .year-month {
                &.showBtn {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    margin: 0 0.3rem;
                }
                .month {
                    font-size: 0.7rem;
                    text-align: center;
                    // width: 100%;
                    // border-bottom: 1px solid gainsboro;
                    line-height: 2.2rem;
                    display: flex;
                    justify-content: center;
                    i {
                        cursor:pointer;
                        width: 2.2rem;
                        height: 2.2rem;
                        display: block;
                        line-height: 2.2rem;
                    }
                }
            }
            .weekdays {
                opacity: 0.6;
                display: flex;
                font-size: 0.7rem;
                border-bottom: 1px solid #eee;
                background: #eee;
                li{
                    flex: 1;
                    font-size: 0.7rem;
                    width:2.7rem;
                    list-style-type:none;
                    text-align: center;
                    line-height:  2rem;
                    cursor:pointer;
                }
            }
            .bodyDiv{
                // border-bottom: 1px solid gainsboro;
                .days {
                    display: flex;
                    li {
                        // flex: 1;
                        font-size: 0.7rem;
                        list-style-type:none;
                        text-align: center;
                        line-height:  2rem;
                        cursor:pointer;
                        width: 14.285%;
                        .dateItem{
                            position: relative;
                            justify-content: center;
                            align-items: center;
                            &.other {
                                color: #e2e0e0;
                            }
                            &.disabled {
                                background: #e2e0e0;
                                color:#999;
                            }
                            &.currentDay {
                                background: #f6f6f6;
                                // border-radius: 2rem;
                                // // display: inline-block;
                                // width: 2rem;
                                // height: 2rem;
                            }
                            &.selected {
                                // display: inline-block;
                                // width: 2rem;
                                height: 2rem;
                                color: #fff;
                                background-color: #1BB5F1;
                                // border-radius: 2rem;
                            }
                        }
                    }
                }
            }
        }
    }
  }

相关文章

网友评论

      本文标题:Vue组件开发系列之Calendar组件

      本文链接:https://www.haomeiwen.com/subject/hfqzdctx.html