亲宝软件园·资讯

展开

Vue实现万年日历的示例详解

qb 人气:0

前言

又是一个老生常谈的功能,接下来会从零实现一个万年日历,从布局到逻辑,再到随处可见的打卡功能。涉及的内容有:

1.日历的布局

<template>
  <div class="calendar">
    <!-- 年份和日期变化 -->
    <div class="select">
      <span class="select-icon select-sub" @click="changeYear(-1)">《</span>
      <span class="select-icon select-sub select-month" @click="changeMonth(-1)"><</span>
      <span>{{ currentMonth }}</span>
      <span class="select-icon select-add select-month" @click="changeMonth(+1)">></span>
      <span class="select-icon select-add" @click="changeYear(+1)">》</span>
    </div>
    <!-- 日历标题 -->
    <ul class="calendar-header">
      <li v-for="(item, index) in headerData" :key="index" class="calendar-header-item">
        <span>{{ item }}</span>
      </li>
    </ul>
    <!-- 日期内容 -->
    <div class="calendar-box">
      <div class="calendar-box-item" :class="{ delMark: !hasRemarkDays.includes(item) }" v-for="(item, index) in baseDateList" :key="index">
        <!-- 最高层级显示圆形日期 -->
        <div class="item-wrap" :class="{ hasMarked: hasRemarkDays.includes(item) }" @click="toRemark(item, currentDate)">
          <span class="item-date">{{ item ? item : "" }}</span>
          <span class="item-patch" v-if="isShowPatch && !hasRemarkDays.includes(item) && item && item < currentDate">补卡</span>
          <span class="item-current" v-if="currentDate == item"><i></i></span>
          <span class="item-continue" v-if="continueMarkObj.maxDay == item">连续{{ continueMarkObj.maxLen }}天</span>
        </div>
        <!-- 全遮罩背景淡橙色背景 -->
        <span class="all-mark" v-if="hasRemarkDays.includes(item)"></span>
        <!-- 左右遮罩,如果左边无打卡,遮住;如果右边无打卡,遮住 -->
        <span class="left-mark" v-if="index % 7 === 0 || !hasRemarkDays.includes(item - 1)"></span>
        <span class="right-mark" v-if="index % 7 === 6 || !hasRemarkDays.includes(item + 1)"></span>
      </div>
    </div>
  </div>
</template>

2.日期数据的产生

  computed: {
    // 返回当前的实际天数,从周一算起
    baseDateList() {
      let date = new Date(this.currentMonth);
      let monthFlag = date.getMonth() + 1;
      let yearFlag = date.getFullYear();
      let currentData = date.getDay();
      let dayLength = new Date(yearFlag, monthFlag, 0).getDate();

      // 周一之前的补0
      let dateBaseData = [];
      for (let i = 0; i < currentData; i++) {
        dateBaseData.push(0);
      }
      // 周一之后的实际填写
      for (let i = 0; i < dayLength; i++) {
        dateBaseData.push(i + 1);
      }

      return dateBaseData;
    },
  }

其中currentData表示今天是周几,在此之前的数组都补充为0,dayLength表示这月共多少天,然后,全部push进日期数据中。通过flex布局中的flex-wrap: wrap;让其自动换行即可。

3.年月的变化

// 修改年:当type = +1时,表示加一年,为-1时反之
changeYear(type) {
  let time = new Date(this.currentMonth);
  let year = time.getFullYear() + type;
  let month = time.getMonth() + 1;
  this.currentMonth = `${year}-${month}`;
},
// 修改月:当type = -1时,表示加一月,为-1时次之
changeMonth(type) {
  let time = new Date(this.currentMonth);
  let year = time.getFullYear();
  let month = time.getMonth() + 1;
  if (month === 12 && type > 0) { // 12月,并且是加的情况,年加1,月变为1
    year++
    month = 1
  } else if(month === 1 && type < 0) { // 1月,并且是减的情况,年减1,月变为12
    year--
    month = 12
  } else { // 其他情况,直接变化变量type的大小
    month += type
  }
  this.currentMonth = `${year}-${month}`;
},

引入type,既是加减的标志位,又是变量的大小。

4.连续最长打卡日期

getMaxDay() {
  let arr = this.hasRemarkDays;
  let buffChild = [];
  let max = [];
  
  for (let i = 0; i < arr.length; i++) {
    // 如果子集不连续了,重新进行赋值和更新
    if (buffChild.length && arr[i] - buffChild[buffChild.length - 1] > 1) {
      max = max.length > buffChild.length ? [...max] : [...buffChild];
      buffChild = [];
      buffChild.push(arr[i]);
      continue;
    }
    // 如果不是,直接进行累计
    buffChild.push(arr[i]);
  }
  return max.length > buffChild.length ? [...max] : [...buffChild];
},

假设max是最大的数组,那么,每次找到连续的数组都max进行对比并更新。在节点中,通过:class="{ hasMarked: hasRemarkDays.includes(item) }"的方式,为其添加hasMarked的样式。

5.补卡日期

补卡逻辑很简单,当前日期之前的日期,均可进行补卡,然后,在触发的方法中,通过$emit的方式向父组件传递需要补卡的日期,并触发补卡逻辑。

总结:日历的实现主要是应用JavaScript中Date对象的api和经典的flex布局,而最长连续打卡是常规算法最长连续子序列的实现方式之一。

加载全部内容

相关教程
猜你喜欢
用户评论