完整代码参见github:vue-calendar-board

讲解组件封装之前,介绍时间对象Date相关的api:参见:MDN

new Date()入参字符串和多个参数不同

实例:2019-03-03返回值是默认是日期的8:00:00,传入时间后按时间返回

2019,02,03返回日期是零点00:00:00,传入小时后按时间返回

//Sun Mar 03 2019 08:00:00 (中国标准时间)
const dt = new Date('2019-03-01') 

// Sun Mar 03 2019 00:00:00 GMT+0800 (中国标准时间) 
const st = new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()) 


//Sun Mar 03 2019 02:00:00 GMT+0800 (中国标准时间)
const df = new Date('2019-03-03 2:00:00');

Date.setDate(day):

设置当前时间的日期,传0表示上个月的最后一天,传负数从上个月末开始算日期,传大于31的数字,表示下个月的日期

获取当前月的最后一天和第一天:

let today = new Date();
let last = new Date(today.getFullYear(), today.getMonth() + 1, 0);//获取当前月最后一天时间
let first = new Date(today.getFullYear(), today.getMonth(), 1)//获取当前月第一天

Date.getDate()

获取当前时间的日期

Date.getDay()

返回0,1,2,3,4,5,6对应数组:['周日', '周一','周二','周三','周四','周五','周六'],

日历组件封装思路:

1,根据日期获取日历行数(显示几个星期);

2,获取当月第一天是星期几,计算出日历第一行第一列显示上月的日期

3, 日历每天添加事件

4,日历组件添加默认事件和受控事件

组件传递props,子组件需要修改props后使用的情况:

1,仅显示格式需要:使用filter

2,需要复杂使用,可以重新再子组件内部定义一变量代替props,然后通过watch监听props的变量,更改时改变当前变量

组件传递数据和事件:

使用$attrs和$listeners

组件结构:

calendar组件 header组件  body组件

核心代码实例  完整代码参见github:vue-calendar-board

<template>
  <div class = "weeks-box">
    <div class="weeks-title">
      <strong class="week-day-title" v-for="(week,index) in weekTitles" :key="index">{{week}}</strong>
    </div>
    <div class="weeks-bg">
      <div class="week-row" v-for = "(week, weekIndex) in currentDates" :key = "weekIndex">
        <div
          class="day-cell"
          v-for = "(day, dayIndex) in week"
          :key ="dayIndex"
          @click = "onDayClick(day)"
        >
          <p :class="{ 'today':day.isToday }">{{day.monthDay}}</p>
          <div class="event-box" v-show = " day.events.length > 0">
            <div class="event-item" v-for = "(event,eventIndex) in day.events" :key = "eventIndex">
              {{event.title}}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import { getStartDate, getDay  } from './utils'
  import config from './config'
  export default {
    props:{
      currentDate:{
        default(){
          return new Date()
        }
      },
      startDay:{
        default:0
      },
      language:{
        default:'zh'
      },
      events:{
        default(){
          return []
        }
      }
    },
    data(){
      return {
        weekTitles:[]
      }
    },
    created(){
      this.getWeekTitles();
    },
    beforeUpdate(){
    },
    upDate(){

    },
    computed:{
      currentDateMock(){
        const currentDate = [];
        for(let i =0; i<5; i++){
          currentDate[i] = [];
          for(let k =0; k<7; k++){
            currentDate[i].push({
              monthDay:i.toString()+k,
              event:[
                {
                  title:'测试'
                }
              ]
            })
          }
        }
        return currentDate;
      },
      currentDates() {
        return this.getCalendar() //获取日历
      }
    },
    watch:{
      startDay(val){
        const { weekNames } = config[this.language];
        this.weekTitles = weekNames.slice(this.startDay).concat(weekNames.slice(0,this.startDay))
      }
    },
    methods:{
      getCalendar(){
        const self = this;
        let now = new Date();
        //获取当前日期所在月份的第一天
        let current = new Date(this.currentDate)
        let startDate = getStartDate(current);

        //获取当前日期的第一天的星期
        let curWeek = getDay(startDate)

        //根据开始日期是周几,第一列展示周几,计算出当前月份从哪一列开始展示 当月第一天向右偏移几天
        let deviationNum = parseInt(this.startDay) - curWeek;
        if(deviationNum>0){
          deviationNum = deviationNum -7;
        }
        //设置第一行第一列的日期,循环时逐个增加一天
        startDate.setDate(startDate.getDate() + deviationNum)
        //计算出当前月份展示完成需要几个星期(几行)
        let weeks = this.getWeeks(curWeek,current);

        let calendar = [];
        //给每一个星期(每一行添加数据)
        for (let perWeek = 0; perWeek < weeks; perWeek++) {
          calendar[perWeek] = [];
          for(let i = 0;i<7;i++){
            calendar[perWeek].push({
              monthDay:startDate.getDate(),
              isToday:now.toDateString() == startDate.toDateString(),
              isCurMonth: startDate.getMonth() == current.getMonth(),
              weekDay: i,
              date:new Date(startDate),
              events: self.filterEvents(startDate)
            })
            startDate.setDate(startDate.getDate()+1)
          }
        }
        return calendar

      },
      //获取要展示的星期数,可能5行,也可能6行
      getWeeks(curWeek,current){
        //获取当前月份的天数(即获取本月的最后一天)
        let curDayNum = new Date(current.getFullYear(),current.getMonth()+1,0).getDate()
        let weeks;
        //获取第一行展示当前月的天数 并计算显示几个星期(几行)
        if(this.startDay == curWeek){
          weeks = Math.ceil((31-7)/7)+1
        }else if(this.startDay < curWeek){
          weeks = Math.ceil((curWeek - this.startDay -7 + 31)/7)+1
        }else{
          weeks = Math.ceil((curWeek - this.startDay + 31)/7)+1
        }
        return weeks;
      },
      getWeekTitles(){
        const { weekNames } = config[this.language];
        this.weekTitles = weekNames.slice(this.startDay).concat(weekNames.slice(0,this.startDay))
      },
      filterEvents(date){
        let thisDayEvents = this.events.filter(day => {
          let dt = new Date(day.start)
          let st = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate())
          let ed = day.end ? new Date(day.end) : st
          return date >= st && date <= ed
        })
        return thisDayEvents
      },
      onDayClick(day){
        this.$listeners.onDayClick && this.$listeners.onDayClick()
      }
    }
  }
</script>

完整代码参见github:vue-calendar-board

可以直接下载 npm install vue-calendar-board 使用

Logo

前往低代码交流专区

更多推荐