全年日历展示并设置节假日

1,737 阅读4分钟

「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战

大家好,我是 摸鱼小公举,真正的强者,不会怨天尤人,如果想不被别人看轻,你就只有付出比别人多十倍百倍的努力,才能站的比别人更高!上一篇文章是 JS中的闭包和递归函数的使用,主要是讲JS中闭包函数和递归函数的使用。今天我们来写一篇关于全年日历展示并可以设置节假日和工作日的文章,此功能是以前写的,这个全年日历显示是在网上找到一个小demo然后进行改造。添加了年份切换和节假日,工作日设置的功能也重新优化了ui。

此功能效果展示

date.gif

创建一个关于日期方法的 moment.js 文件

此文件是借鉴了人家大佬的,我没有做太多修改,底层原理的实现是人家封装好的,我只是根据我的需求做了点修改。其实这个需求很少用到吧,这是需求还是我2020年的时候做的。

以下是针对这个js文件的讲解:

对Date的扩展,将 Date 转化为指定格式的String 。接下来我们看看日期格式的方法,通过以下代码我们可以看到大概出现五种日期格式。

月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 个占位符 ;年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
eg:
"yyyy-MM-dd hh:mm:ss.S" ==> 2022-02-02 09:09:04.423
"yyyy-M-d h:m:s.S" ==> 2022-2-2 9:9:4.18
"yyyy-MM-dd E HH:mm:ss" ==> 2022-01-10 二 21:09:04
"yyyy-MM-dd EE hh:mm:ss" ==> 2022-02-10 周二 07:09:04
"yyyy-MM-dd EEE hh:mm:ss" ==> 2022-02-10 星期三 09:09:04

Moment.prototype.format = function (format) {
  var date = this.date;
  /*
      var r= /^(\d{4})-(\d{1,2})-(\d{1,2})$/; //正则表达式 匹配出生日期(简单匹配)     
      r.exec('1985-10-15');
      s1=RegExp.$1;s2=RegExp.$2;s3=RegExp.$3;//结果为1985 10 15
      */
  if (typeof date === 'string')
    date = this.parse(date);
  var o = {
    "M+": date.getMonth() + 1, //月份 
    "(d+|D+)": date.getDate(), //日 
    "(h+|H+)": date.getHours(), //小时 
    "m+": date.getMinutes(), //分 
    "s+": date.getSeconds(), //秒 
    "q+": Math.floor((date.getMonth() + 3) / 3), //季度 
    "S": date.getMilliseconds() //毫秒 
  };
  var week = {
    "0": "/u65e5",
    "1": "/u4e00",
    "2": "/u4e8c",
    "3": "/u4e09",
    "4": "/u56db",
    "5": "/u4e94",
    "6": "/u516d"
  };
  if (/(y+|Y+)/.test(format))
    format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
  if (/(E+)/.test(format))
    format = format.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[date.getDay() + ""]);
  for (var k in o) {
    if (new RegExp("(" + k + ")").test(format))
      format = format.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  }
  return format;
}

这是计算两个日期差方法和设置日期解决跨月的相关方法

Moment.prototype.differ = function (date) {
  var time1 = this.date.getTime();
  if (typeof date === 'string')
    date = new Date(date);

  var time2 = date.getTime();
  var differ = Math.ceil((time1 - time2) / (1000 * 3600 * 24));//除不尽时,向上取整
  return differ;
}

Moment.prototype.add = function (num, optionType) {
  var date = this.date;
  if ('day' === optionType) {
    date.setDate(date.getDate() + num);
  }
  if ('month' === optionType) {
    date.setMonth(date.getMonth() + num);
  }
  if ('year' === optionType) {
    date.setFullYear(date.getFullYear() + num);
  }
  this.date = date;
  return this;
}

然后这些就是初始化一个Moment对象,以及该对象的一些处理日期数据的方法

var Moment = function (date) {
  if (date)
    this.date = new Date(date);
  else
    this.date = new Date();
  return this;
};

Moment.prototype.parse = function () {
  return this.date;
}

Moment.prototype.before = function (date) {
  return this.date.getTime() < new Date(date).getTime()
}
Moment.prototype.after = function (date) {
  return this.date.getTime() > date.getTime()
}

module.exports = function (date) {
  return new Moment(date);
}

日历数据渲染页面过程 (实现过程代码中写有注释)

这里渲染日期数据是有两层:第一层是月份,第二层数据是具体日期哪天。

首先讲讲大体的HTML的构成

(1)最大的盒子 .calender-box

(2)头部的年份切换以及节假日标签的显示

   <div class="yearSwitch">
      <span>年份切换:</span>
      <el-input-number
        v-model="num"
        controls-position="right"
        :min="2021"
        :max="3000"
      ></el-input-number>
      <div class="holiday">
        <span>节假日:</span>
        <span class="holidayBgc"></span>
      </div>
      <div class="working">
        <span>工作日:</span>
        <span class="workingBgc"></span>
      </div>
    </div>

(3)12个月的日历数据的渲染,一共又三层数据的展示。第一层是月份(dateList的循环遍历),第二层是星期(weekStr的循环遍历),第三层是具体到日(days的循环遍历),但是这三层的HTML的结构都是同级的。

 <div class="month-box" v-for="(item, index) in dateList" :key="index">
    <!-- 月份 -->
    <div class="date-year-month" style="text-align: center;font-size:35rpx;">
     {{ item.year }}年{{ item.month }}月
    </div>
    <!-- 星期 -->
    <div class="weeks">
     <div class="week" :class="index == 0 || index == 6 ? 'week-active ' : ''"
      v-for="(item, index) in weekStr" :key="index">
      <span>{{ item }}</span>
     </div>
    </div>
    <!-- 具体日期哪天 -->
    <div class="days">
      <div class="day" :class="item.week ? 'week-active' : ''"
      v-for="(item, index) in item.days" :key="index" @click="onPressDate(item)">
       <div :class="[{ active1: item.clickactive1, active2: item.clickactive2 },
       item.active1,item.active2]"
       :style="item.day > 0 ? 'border: 1px solid #fff' : ''">
        <span :style="item.day < 1 ? 'display:none' : ''">{{item.day}}</span>
       </div>
      </div>
    </div>
 </div>

(4)设置节假日的HTML代码,这个代码是放到具体日期哪天的span标签下面的,这里是之前根据需求加的,不需要的直接拿上面的代码就好

<div class="setWorkDay" v-if="isShowOne == item.date">
 <span v-if="item.clickactive1 == false && item.clickactive2 == false &&
 item.active1 == '' && item.active2 == ''" 
 @click="workClick(1, item)">节假日</span>
 <span v-if="item.clickactive1 == false && item.clickactive2 == false &&
 item.active1 == '' && item.active2 == '' " 
 @click="workClick(2, item)">工作日</span>
 <span v-if="item.clickactive1 == true ||item.clickactive2 == true ||item.active1 ||
 item.active2 " :style=" item.clickactive1 == true || item.clickactive2 == true ||
 item.active1 ||item.active2 ? 'line-height:54px;border-radius:6px': '' "
 @click="closeBtn(item)">取消</span>
 <strong @click="closeClick(item)">X</strong>
</div>

下面是一些JS相关逻辑代码

(1) 首先是引入的一些js文件和data里初始化的一些日期数据;还有年份切换的监听,以及created初始化时调用日期数据渲染的方法。

var Moment = require("@/utils/moment.js");
import { getLunarDateFun } from "@/utils/utils";

data() {
    return {
      num: 2021, //年份
      maxMonth: 12, //渲染月数
      dateList: [], //全年日历的数据
      isShow: "", //设置节假日类型
      getDayArr: [],
      dateNum: "",
      isShowOne: "",
      weekStr: ["日", "一", "二", "三", "四", "五", "六"]
    };
  },
  
   watch: {
    num() {
      this.createDateListData();
    }
  },
  created() {
    this.createDateListData();
  },

(2) 下面是methods里的一些对日期数据操作的方法。

首先是日期数据渲染的方法

设置日期为 年-月-01,否则可能会出现跨月的问题 比如:2017-01-31为now ,月份直接+1(now.setMonth(now.getMonth()+1)),则会直接跳到跳到2017-03-03月份.原因是由于2月份没有31号,顺推下去变成了了03-03。这里描述的是Moment(now).add()方法的调用。

-week是为了使当月第一天的日期可以正确的显示到对应的周几位置上,比如星期三(week = 2),则当月的1号是从列的第三个位置开始渲染的,前面会占用-2,-1,0的位置,从1开正常渲染。

从以下的代码中我们可以看到对日期数据处理的时候是特地对月份,星期,日处理过的。days数组里的对象是自己可以自定义修改的,像我之前是为了样式的显隐,就特地在里面极了active相关属性,方便数据渲染。

createDateListData() {
  var dateList = [];
  var now = new Date(this.num + "-04-16 12:00:00");
  
  now = new Date(now.getFullYear(), 0, 1);
  for (var i = 0; i < this.maxMonth; i++) {
   var momentDate = Moment(now).add(this.maxMonth - (this.maxMonth - i), "month").date;
   var year = momentDate.getFullYear();
   var month = momentDate.getMonth() + 1;
   var days = [];
   var totalDay = this.getTotalDayByMonth(year, month);
   var week = this.getWeek(year, month, 1);
   var week_day = false; 
   
   for (var j = -week + 1; j <= totalDay; j++) {
     var week_active = false;
     var DATE_LUNAR = {};
     if (j > 0) {
       DATE_LUNAR = getLunarDateFun(year + "-" + month + "-" + j);
       week_day = this.getWeeks(year, month, j);
       if (week_day == 6 || week_day == 5) {
         week_active = true;
       }
      }
      
      days.push(
      Object.assign(DATE_LUNAR, {
      year: year,
      month: `${JSON.stringify(month).length < 2 ? "0" + month : month}`,
      day: `${JSON.stringify(j).length < 2 ? "0" + j : j}`,date:year +"-" +
      `${JSON.stringify(month).length < 2 ? "0" + month : month}` +"-" +
      `${JSON.stringify(j).length < 2 ? "0" + j : j}`,
      week: week_active,
      clickactive: false,
      clickactive1: false,
      clickactive2: false,
      active1: "",
      active2: ""
      })
      );
      }
      var dateItem = {id: year +"-" +
      `${JSON.stringify(month).length < 2 ? "0" + month : month}`,
      year: year,
      month: `${JSON.stringify(month).length < 2 ? "0" + month : month}`,
      days: days
     };
     dateList.push(dateItem);
     }
     //这里之前是用来回显设置好的节假日或者工作日 (后台的数据)
      this.dateList = dateList
    },

这里是获取月的总天数,周末,以及月的第一天是星期几的相关方法

getTotalDayByMonth(year, month) {
  month = parseInt(month, 10);
  var d = new Date(year, month, 0);
  return d.getDate();
},
getWeeks(year, month, day) {
  var d = new Date(year, month - 1, day - 1);
  return d.getDay();
},
getWeek(year, month, day) {
  var d = new Date(year, month - 1, day);
  return d.getDay();
},

日历的点击事件

这里是每一天日期的点击方法,像我设置节假日那些的操作都是在这里做的。同样的只是简单日历展示的就不需要设置节假日的那些代码。不过可以参考渲染点击样式的代码也可以实现你需要的操作。

onPressDate(e) {
  this.dateNum = e.date.replace(/-/g, "");
  this.isShowOne = e.date;
  if (e.day <= 0) {
    this.isShowOne = "";
  }
  var { year, month, day } = e;
  //渲染点击样式  (设置节假日/工作日)
  for (var i = 0; i < this.dateList.length; i++) {
    var dateItem = this.dateList[i];
    var id = dateItem.id;
    if (id === year + "-" + month) {
      var days = dateItem.days;
      for (var j = 0; j < days.length; j++) {
        var tempDay = days[j].day;
        if (tempDay == day) {
          if (this.isShow == 2) {
            if (days[j].clickactive != true) {
              days[j].clickactive2 = true;
              days[j].clickactive1 = false;
              days[j].active1 = "";
              this.isShow = "";
              this.isShowOne = "";
            } else {
              this.isShow = "";
              this.isShowOne = "";
              days[j].clickactive = false;
            }
          }
          if (this.isShow == 1) {
            days[j].clickactive1 = true;
            days[j].clickactive2 = false;
            days[j].active2 = "";
            this.isShow = "";
            this.isShowOne = "";
          }
        }
      }
    }
  }
},

这里是针对设置节假日的小弹框所做的一些操作

节假日弹框显隐,取消设置的节假日,关闭弹窗事件等;这里设置显隐的状态值搞得有点乱,要有耐心去看哦。

workClick(id) {
  this.isShow = id;
},
// 取消设置的节假日类型 我这里是本地取消,实际要调用后台接口
closeBtn(item) {
  this.dateList.forEach(items => {
    items.days.forEach(item1 => {
      if (items.month == item.month && item1.date == item.date) {
        console.log("11231ewedw");
        if (item1.clickactive || item1.clickactive1 || item1.clickactive2) {
          item1.clickactive1 = false;
          item1.clickactive2 = false;
          item1.clickactive = true;
          this.isShow = 2;
          this.isShowOne = "";
        }
      }
    });
  });
},
closeClick(e) {
  this.isShow = 2;
  e.clickactive = true;
  this.isShowOne = "";
}
全年日历数据打印出来如下(数据太多只是截了数据结构)

image.png

这里是点击详情日期每天的对象数据

image.png

CSS样式设置

这里分了两部分,首先我们来看看年份切换和日期渲染的CSS样式,如果知识想要日期展示不做其它操作,设置节假日的代码可以不用。

这是头部年份切换,节假日标签以及下面日历展示的CSS样式

.yearSwitch {
  width: 100%;
  margin-bottom: 30px;
  margin-left: 50px;
}
.calender-box {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 14px;
  background-color: #fff;
}
.date-year-month {
  padding: 15px 0;
}
.month-box {
  width: 30%;
  margin-right: 30px;
  margin-bottom: 30px;
}
.month {
  line-height: 30px;
}
.weeks {
  background: #f5f5f5;
  display: flex;
  align-items: center;
}
.weeks .week {
  width: 14.2%;
  padding: 10px 0;
}
.weeks .week {
  text-align: center;
}
.days {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.days .day {
  width: 14.2%;
  height: 53px;
  margin-top: 1px;
}
.days .day span {
  padding: 15px 0;
  display: inline-block;
  padding-left: 40%;
}

这里是设置节假日的CSS样式

<style scoped>

.active1 {
  background: #5e7a !important;
  width: 100%;
  color: #fff;
  height: 54px;
}
.active2 {
  background: #409eff;
  width: 100%;
  color: #fff;
  height: 54px;
}
.actives {
  border: 1px solid #fff !important;
}
.setWorkDay {
  border: 1px solid #409eff;
  border-radius: 4px;
  width: 50px;
  height: 54px;
  color: #409eff;
  position: relative;
  margin-top: -30%;
  z-index: 999 !important;
}
.setWorkDay span {
  line-height: 25px;
  padding: 0 !important;
  background-color: #fff;
  width: 100%;
}
.setWorkDay span:first-child {
  border-bottom: 1px solid #409eff;
}
.holiday,
.working {
  display: inline-block;
  margin-left: 30px;
}
.holiday span:first-child,
.working span:first-child {
}
.holidayBgc {
  width: 40px;
  height: 25px;
  background-color: #5e7a;
  float: right;
}
.workingBgc {
  width: 40px;
  height: 25px;
  background-color: #409eff;
  float: right;
  margin-left: 8px;
}

.day span {
  text-align: center;
}
strong {
  position: absolute;
  top: -45%;
  left: 0;
  color: #ccc;
}
</style>

结语

这个功能我是在网上找了日期展示的demo,然后根据自己的需求修改样式,添加功能等。其实很少会有12个月要全部展示的需求,基本上都是展示一个月比较多。如果有这样需求的小伙伴,代码齐全可以复制代码作为自己的小demo。

这里代码太多了,需要有耐心看完,我是分开的好多步来写的,感兴趣的朋友可以自己研究研究。 好了文章到这就结束了,欢迎大家( 点赞+评论+关注 ) 有问题可以来互相交流一下;希望这篇文章对大家有用,也希望大家多多支持我。 今天是我参与2022首次更文挑战的第25天,加油!