vue 日历选择组件

vue 日历选择组件

日历作为手机端一个常用的控件,想必大家多多少少都会用到,现在github上有很多优秀的日历控件,但可能有些时候并不能满足我们日常的业务开发需求,话不多说,下面简单实现一个日历的选择功能!

链接和图片展示

图片示例

(●'◡'●) (●'◡'●)(●'◡'●)(●'◡'●)(●'◡'●)(●'◡'●)演示示例链接

核心代码分析

首先,我们明确一点,日历,就是时间的选择,有了时间对应的数据,就解决了绝大部分问题,简单分析下,无非就是对应12个月份,然后每个月份对应有几个星期,下面代码实现下,首先创造月份数据(下面代码部分日期方法封装在utils中,完整代码里面有):

buildData() {
    let startDate = new Date();
    let endDate = new Date().addMonths(12); // 月份加12
    let dateData = [];
    while (startDate <= endDate) {
      let month = [];
      dateData.push(month);
      startDate.addMonths(1);
    }
    return dateData;
  },   

现在对应的月份数据已经有了,当然都是空数组,没什么用,下面我们再来创造对应月份的数据,分析下,每月的具体日期是从1号开始,然后让他循环加1天,只要判断的他的月份等于这个月就证明他是这个月的日期,同时周一到周日怎么跟我们的日期一一对应呢?在这里,我们明确一个问题,本文日历对应月份只展示该月中的数据(也就是1号到最后一天),不是该月的数据为“”。明确了之后就容易了,首先我们知道日期getDay()这个方法是可以取得对应周几的(注意:周日该方法返回的是0),这样我们先判断该月的1号是周几?这样第一周的数据(跟周几一一对应)就可以出来了,

new Array(time.getDay() === 0 ? 6 : time.getDay() - 1).fill('')

(不是该月份的值为空),该月的1号对应周几确定了后,下面对应日期加1循环判断就可以了, 还有一个问题,就是该月的最后一天结束后加入对应该周的数据不满足7天,我们要补充完整。
下面是完整代码:

  // 创建日期月份数据
  buildMonthDate(time) {
    let monthDate = [];
    let month = time.getMonth();
    let date = new Date(time);
    var t = time.getDay() === 0 ? 6 : time.getDay() - 1;
    let dateList = new Array(t).fill('');

    while (date.getMonth() === month) {
      let dateInfo = {};
      if (dateList.length === 7) {
        monthDate.push(dateList);
        dateList = [];
      }
      dateInfo = {
        date: date.format('yyyy/MM/dd'),
      }
      dateList.push(dateInfo);
      date.addDays(1);
    }

    // 不满7 补充完整
    if (dateList.length) {
      for (let i = dateList.length; i < 7; i++) {
        dateList.push('');
      }
      monthDate.push(dateList);
      dateList = null;
    }
    return {
      month: monthDate,
      title: `${time.getFullYear()}年${time.getMonth() + 1}月`,
      monthId: `${time.getFullYear()}${time.getMonth() + 1}`
    };
  },

现在对应的数据已经有了,剩下就是画下对应的页面,这个本文就不做详细介绍了,感兴趣的话可以看详细代码(末尾有地址)。

其他功能完善

下面我们来完善下其他功能,增加节假日、加班日、日期范围限制不可选等,我们完善下代码如下:

 // 创建日期月份数据
    buildMonthDate(time) {
      let monthDate = [];
      let month = time.getMonth();
      let date = new Date(time);
      var t = time.getDay() === 0 ? 6 : time.getDay() - 1;
      let dateList = new Array(t).fill('');

      while (date.getMonth() === month) {
        let dateInfo = {};
        if (dateList.length === 7) {
          monthDate.push(dateList);
          dateList = [];
        }
        dateInfo = {
          text: date.getDate(),
          dateTime: date.getTime(),
          date: date.format('yyyy/MM/dd'),
          tips: ''
        }
        // 开始日期之前和结束日期之后的日期置灰
        if (date < this.startDate || date > this.endDate) {
          dateInfo.disable = true;
        }
        if (this.mode === 'end' && new Date(date) < new Date(this.checkStart)) {
          // 返程时间必须大于去程时间
          dateInfo.disable = true;
        }

        // 节日处理
        let festival = getFestival(date);
        if (festival) {
          dateInfo.festival = festival;
          dateInfo.text = festival;
        }
        if (getHolidays(dateInfo.date)) {
          // 休息日
          dateInfo.relax = true;
          dateInfo.tips = '休';
        } else if (getWorks(dateInfo.date)) {
          // 工作日
          dateInfo.fillWork = true;
          dateInfo.tips = '班';
        }
        // 最近日期处理  今天明天 后天
        let recentDay = getRecentDay(date);
        if (recentDay) {
          dateInfo.alias = recentDay;
          dateInfo.festival = '';
        }

        dateList.push(dateInfo);
        date.addDays(1);
      }

      // 不满7 补充完整
      if (dateList.length) {
        for (let i = dateList.length; i < 7; i++) {
          dateList.push('');
        }
        monthDate.push(dateList);
        dateList = null;
      }
      return {
        month: monthDate,
        title: `${time.getFullYear()}年${time.getMonth() + 1}月`,
        monthId: `${time.getFullYear()}${time.getMonth() + 1}`
      };
    },

点击选择功能

下面我们来处理下选择日期后的事件,明确下我们支持的几种模式

  • 选择开始模式
  • 选择结束模式
  • 选择范围

分析下对应功能

  • 选择开始时间的话没有特殊处理
  • 选择结束模式的话要确保只能选择开始时间之后的时间
  • 选择范围模式的话,支持点击两次,第一次选择开始时间,第二次选择结束时间(第二次点击需要判断,如果第二次点击小于第一次点击的时间,认为是新的第一次点击)

下面代码实现:

    // 点击事件处理    
    clickHandler(e) {
      if (e.currentTarget.dataset.disabled) return;
      let date = e.currentTarget.dataset.date;
      let t = {
        'start': 'clickHandlerStart',
        'end': 'clickHandlerEnd',
        'range': 'clickHandlerRange'
      }
      
      this.selected = new Date(date);
      let emitData = this[t[this.mode]](date);
      if (!emitData) return;
      
      this.selecteFuc && this.selecteFuc({
          date,
          BE: emitData
      });
    },
    clickHandlerStart(date) {
      this.checkStart = date;
      if (compare(date) >= compare(this.checkEnd)) {
        this.checkEnd = '';
      }
      alert(`选择开始时间为${date},结束时间为${this.checkEnd || '--'}`);
      return [date, this.checkEnd];
    },
    clickHandlerEnd(date) {
      this.checkEnd = date;
      alert(`选择开始时间为${this.checkStart || '--'},结束时间为${date || '--'}`);
      return [this.checkStart, date];
    },
    clickHandlerRange(date) {
      if (!this.twoClick) {
        this.checkStart = date;
        this.checkEnd = '';
      } else {
        // 点击比较 第二次点击值需要大于第一次点击值
        if (compare(date) < compare(this.checkStart)) {
          this.twoClick = 0;
          this.checkStart = date;
        } else {
          this.checkEnd = date;
        }
      }
      ++this.twoClick;
      if (this.twoClick === 1) return false;
      this.twoClick = 0;
      alert(`选择开始时间为${this.checkStart || '--'},结束时间为${this.checkEnd || '--'}`);
      return [this.checkStart, this.checkEnd];
    }

以上就是对应功能实现过程,欢迎大家issue。

链接(😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊😊)

代码地址

演示地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容