用vue实现类ant Design的日历组件

前言

最近在对以前写的vue项目进行优化,并且进行了一些功能的完善,我写了一个周报系统,其中一个完善的功能就是,可以通过日历选择以往的周报。于是我着手ant Design的一些功能,自己实现了一个周报组件。

一些图片

  • 未选择时
    在这里插入图片描述
  • 鼠标移上去
    在这里插入图片描述
  • 选中状态
    在这里插入图片描述

问题

1. input聚焦问题

我的设想是这样的,日历首先是用v-if="show",show=false设置了隐藏,我们在点击input框的时候,日历会显示出来,然后我们在点击非日历的部分的时候,日历会隐藏。

 <div class="showWeek" @focus ="show=true" @blur="show=false" ref="showWeek" v-if="show"> 

为了实现这个功能,我给input输入框绑定了focus事件和blur,当focus事件触发时,日历显示,当blur事件触发时,日历隐藏。
可是这时出现了一个问题,我在对日历上的日期进行选择的时候,input框失去了焦距,触发了blur事件。

后面我想了一个办法,就是只给input框绑定click事件,设置一个stop防止冒泡,点击input之后日历显示,

<div class="showWeek" @click.stop ="show=true" ref="showWeek" v-if="show"> 

然后给document绑定click事件,点击之后日历隐藏。但是document可能会经过很多次的点击,每一次点击都触发日历隐藏显然是不合适的。所以我给show设置了监听,当show的值为false,说明show刚刚隐藏,移除document的点击事件,否则添加。

 'show':function(newValue){
            if(newValue){
                document.addEventListener('click',this.bindEvent);
            }else{
                document.removeEventListener('click',this.bindEvent);
            }
        }

2.日历生成问题

在写这个组件的时候,最麻烦的就是,如何生成日历。我首先想的是,用两个v-for的嵌套,外层是今年的第几周,内层是这周的星期几,然后根据年,周,星期几生成天数。但是可能是因为我的算法还有所欠缺,我并没有实现。
咨询了学长,学长和我说,可以先用二维数组,根据年月生成对应的一个月的时间,存在二维数组中,然后用双层v-for将数据绑定在每一格子中,实现起来会更简单。那么如何做呢

  1. 生成第一行的数据
    第一行和最后一行是比较麻烦的两行,因为我们需要和上个月以及下一个月的数据打交道。
    我们首先要获取几个值
  • 本月第一天是星期几
  • 上一个月最后一天是几号
    举个例子,本月第一天是星期六,上个月最后一天是31号。如果我们想做一个从周一到周天的日历,那么1号之前就有5天
    27 28 29 30 31
    计算公式
上个月最后一天的星期数 = 这个月1号的星期数-1
上个月最后一天 - (上个月最后一天的星期数-1)
31 - (6-1-1) = 27

通过这个公式,我们可以求出在6月份的日历中,第一行的开始日期
在这里插入图片描述
要特别注意的是,因为周天的getDay方法的返回值为0,而我们要写的是周一到周天的日历,而不是从周日到周六的,所以我们需要对getDay方法进行改写

 myGetDay(day){
            var w = day.getDay();
            if(w === 0){
                w = 7;
            }
            return w;
        }

这样,我们就可以根据开始日期,结束日期,生成该周的号数了

 numberList(start, end, flag) {
            let list = []
            for (let i = start; i <= end; i++) {
                list.push({
                    text: i,
                    flag: !!flag
                })
            }
            return list
        }

我们把第一行中上个月的号数存在数组里

 let list = 
                    this.numberList(temp, this.lastMonthEndDate, true)
                    .concat(this.numberList(1, 7 - this.currentMonthFirstDay+1))
                this.list.push(list);

把这个月的天数和上个月的天数进行拼接

                this.list.push(list);

拼接后存入月份数组中,作为第一行
接下来我们要求本月数据,思路就是, 求出本月的总天数减去第一行的本月天数,取一个余,然后用for循环实现。代码如下。

 getDateList() {
                this.list = []
                // 获取日历第一行的数据(需加上第一个星期中所包含上个月的几天)
                //temp计算出上个月最后一天的星期数减去这个月第一天的星期数
                //上个月的最后一天是几号
                console.log(this.lastMonthEndDate);
                //这个月的第一天是星期几
                console.log(this.currentMonthFirstDay);
                //(this.currentMonthFirstDay - 1)得到的是上个月的最后一天是星期几
                //temp的值是在本页呈现的上个月的开始天数
                //上个月最后一天是星期五。31号
                //31-(5) = 26
                //日 一 二  三 四 五
                //26 27 28 29 30 31
                //let temp = this.lastMonthEndDate - (this.currentMonthFirstDay - 1);

                //为了改成星期一到星期五,需要少一天
                let temp = this.lastMonthEndDate - (this.currentMonthFirstDay - 1)+1;
                // 一 二  三 四 五
                // 27 28 29 30 31
                // let list = 
                //     this.numberList(temp, this.lastMonthEndDate, true)
                //     .concat(this.numberList(1, 7 - this.currentMonthFirstDay))
                //剩下的时间就得多填一天
                let list = 
                    this.numberList(temp, this.lastMonthEndDate, true)
                    .concat(this.numberList(1, 7 - this.currentMonthFirstDay+1))
                this.list.push(list);
                //好了,第一行填完了
                //剩下的天数,从第一行结束的天数开始算起
                temp = (7 - this.currentMonthFirstDay) + 2
                /*
                * 剩下的行数
                */
                // 计算除了第一行剩下的天数
                const leftDays = this.currentMonthEndDate - (7 - this.currentMonthFirstDay)-1;
                console.log(leftDays);
                // 剩下的星期数,决定还要写多少行
                const lineNumber = Math.ceil(leftDays / 7)
                // 包含下个月日历的天数
                const nextDays = 7 - (leftDays % 7)
                for (let i = 0; i < lineNumber; i++) {
                    //最后一行如果包含有别的月的情况
                    if (i === lineNumber - 1 && nextDays > 0 && nextDays !== 7) {
                        this.list[lineNumber] = 
                            this.numberList(temp, this.currentMonthEndDate)
                            .concat(this.numberList(1, nextDays, true))
                    } else {
                        this.list.push(this.numberList(temp, temp + 6))
                    }
                    temp = temp + 7
                }
        }

3.点击某一周的一天,选中一整周

这个就是,$event事件,里面有一个path,标志着点击事件的路径,根据点击事件的路径找到点击的行,设置背景色即可。

4.根据选中的周去后台调取数据

因为我是用时间戳来存储时间
然后我又把mysql自己封装了一层(封装的不太好用,我打算用模板字符串优化一下)

let lastSQL = myselfSql.select('content',"*","YEARWEEK(date_format(from_unixtime((weekly_taskData)/1000),'%Y-%m-%d'),1) = "+time+" and user_id="+163+" order by weekly_taskData");
  • data_format是吧时间戳转化为‘yyyy-mm-dd’的格式
  • YEARWEEK(‘yyyy-mm-dd’,1)返回的值是某年某周
    在这里插入图片描述
    如果逗号后的值是0,算作2018年52周,1算作2019年01周
    github地址
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章