微信小程序:手寫日曆組件

1、前言

最近公司要作一個酒店入住的小程序,不可避免的必定會使用到日曆,而小程序沒有內置的日曆組件。在網上看了一下也沒有很是適合需求的日曆,因而本身寫了一個。小程序

2、代碼

1. 原理分析

寫一個日曆只須要知道兩件事情:數組

  1. 一個月有多少天;
  2. 每月的第一天是星期幾。

2. 功能分析

因爲是酒店入住的日曆,因此須要實現以下功能:數據結構

  1. 渲染日曆,通常是從本月開始,到半年以後的日曆
  2. 過去的日期不可選
  3. 實現點擊獲取入住日期、退房日期,以及改變相應日期的顏色和整個時間段的顏色

3. 數據分析

根據最後的功能實現,我生成的每月的數據結構以下:this

{
    year: 2018,
    month: 3,
    fullMonth: '03',
    allDays:[
        {
            day: 1,
            fullDay: '01',
            fullDate: '2018-03-01'
        },
        {
            day: 2,
            fullDay: '02',
            fullDate: '2018-03-02'
        },
        //......
        //(後面的數據同上)
    ]
}

year就是年份,month是月份,day是日期,fullDate是完整日期。
fullMonth和fullDay本來是兩個不須要的數據,可是在點擊日期改變顏色的時候用到了,由於小程序沒有提供很好的處理數據的filter。固然這個問題也和個人我的水平有關,若是有哪位大神有更好的方法,請留言告訴我。我很是想去掉這兩個數據。code

4.代碼分析

// calendar.js文件

Page({
  data: {
    week_list: ['日','一','二','三','四','五','六'],
    startDate: '',
    endDate: '',
    date_click: 0
  },
  // 獲取每個月總天數
  getAllDaysOfMonth(year,month) {
    return new Date(year,month,0).getDate();
  },
  // 獲取每個月第一天是星期幾
  getFirstDayOfMonth(year,month) {
    return new Date(year, month - 1, 1).getDay();
  },
  // 計算本月前空了幾格
  getEmptyGrids(year,month) {
    // FirstDayOfMonth表明本月的第一天是星期幾
    const FirstDayOfMonth = this.getFirstDayOfMonth(year, month);
    let emptyGrids = [];
    
    // 有空格的狀況
    if (FirstDayOfMonth > 0) {
      for (let i = 0; i < FirstDayOfMonth; i++) {
        emptyGrids.push({
          'num': '',
          'fullDate': 'x'  //x是我本身定義的一個值,表明沒有日期
        });
      }
      // 將空格放入數組
      return emptyGrids;
    }else{
      // 不然返回一個新數組
      return [];
    }
  },
  // 計算本月日曆
  getDaysOfThisMonth(year,month) {
    let days = [];
    const AllDaysOfMonth = this.getAllDaysOfMonth(year, month);

    let fullMonth = month.toString().length === 1 ? `0${month}`:month;
    for (let i = 0; i < AllDaysOfMonth; i++) {
      let day = i+1,
          fullDay = day;

      fullDay = fullDay.toString().length === 1 ? `0${day}` : fullDay;
      days.push({
        day,
        fullDay,
        'fullDate': `${year}-${fullMonth}-${fullDay}`
      });
    }
    // 返回每月的具體日期
    return days;
  },
  // 循環渲染日曆
  // 從本月開始渲染,n表明包括本月開始連續渲染幾個月
  fillCalendar(n) {
    let year = this.data.cur_year,
        month = this.data.cur_month,
        fullMonth,
        canlendar_data = [];

    // 計算年月以及具體日曆
    for (let i = this.data.cur_month; i < this.data.cur_month + n; i++) {
      let EmptyGrids = this.getEmptyGrids(year, month);
      let DaysOfThisMonth = this.getDaysOfThisMonth(year, month);

      // 把空格和具體日曆合爲一個數組
      let allDays = [...EmptyGrids, ...DaysOfThisMonth];

      // 對年份和月份的計算作一些判斷
      if (month > 12) {
        year++;
        month = 1;
        fullMonth = '01'
        canlendar_data.push({
          year, 
          month, 
          fullMonth,
          allDays });
        month++;
      }else{
        fullMonth = month.toString().length === 1 ? `0${month}` : month;
        canlendar_data.push({ 
          year, 
          month, 
          fullMonth,
          allDays });
        month++;
        
      }

    }

    this.setData({
      canlendar_data
    })
  },
  onLoad() {
    const date = new Date();
    const cur_year = date.getFullYear();
    const cur_month = date.getMonth() + 1;
    const cur_day = date.getDate();
    this.setData({
      date,
      cur_year,
      cur_month,
      cur_day
    })

    let month = this.data.cur_month.toString().length === 1 ? `0${this.data.cur_month}` : this.data.cur_month;
    let day = this.data.cur_day.toString().length === 1 ? `0${this.data.cur_day}` : this.data.cur_day;
    let nowDate = `${cur_year}-${month}-${day}`;

    this.setData({
      nowDate
    })

    this.fillCalendar(6);
  },
  // 點擊日期
  chooseDate(e) {
    const year_click = e.currentTarget.dataset.year;
    const month_click = e.currentTarget.dataset.month;
    const day_click = e.currentTarget.dataset.day;
    console.log(year_click,month_click,day_click);
    // 若是是空格或者之前的日期就直接返回
    if(day_click === ''||`${year_click}-${month_click}-${day_click}` < this.data.nowDate) {
      return;
    }

    // 獲取點擊對象的id
    let id = e.currentTarget.dataset.id;
    
    // data_click爲0表明選擇的是入住日期,不然就是離店日期
    if (this.data.date_click == 0){
      // 選擇入住日期
      this.setData({
        startDate: `${year_click}-${month_click}-${day_click}`,
        date_click: 1
      })
    }else {
      let newDay = new Date(Date.parse(id));
      let oldDay = new Date(Date.parse(this.data.startDate));

      // 判斷第二次點擊的日期在第一次點擊的日期前面仍是後面
      if (newDay > oldDay) {
        this.setData({
          endDate: `${year_click}-${month_click}-${day_click}`,
          date_click: 2
        })
      }else{
        this.setData({
          startDate: `${year_click}-${month_click}-${day_click}`,
          endDate: '',
          date_click: 1
        })
      }
    }
  }
})
<!-- calendar.wxml文件 -->

<view class="container">
  <view id="week">
    <view class="week-item {{idx===0||idx===6?'relax':''}}" wx:for="{{week_list}}" wx:for-index="idx">{{item}}</view>
  </view>
  <scroll-view scoll-y="true">
    <view class="month-block" wx:for="{{canlendar_data}}" wx:for-item="canlendar_item">
      <view class="month-title">{{canlendar_item.year}}年{{canlendar_item.month}}月</view>
      <view class="month-content">
        <view class="month-day {{item.fullDate<nowDate?'gray':''}} {{startDate===item.fullDate?'startActive':''}} {{endDate===item.fullDate?'endActive':''}} {{item.fullDate>startDate&&item.fullDate<endDate&&startDate!==''&&endDate!==''?'midActive':''}}" bindtap="chooseDate" data-year="{{canlendar_item.year}}" data-month="{{canlendar_item.fullMonth}}" data-day="{{item.fullDay}}" data-id="{{item.fullDate}}" wx:for="{{canlendar_item.allDays}}">{{item.day}}</view>
      </view>
    </view>
  </scroll-view>
</view>

{{idx===0||idx===6?'relax':''}} 是改變週六週日的顏色,
{{item.fullDate<nowDate?'gray':''}} 是改變過去日期的顏色,
{{startDate===item.fullDate?'startActive':''}} 判斷點擊的是入住日期,
{{endDate===item.fullDate?'endActive':''}} 判斷點擊的是離店日期,
{{item.fullDate>startDate&&item.fullDate<endDate&&startDate!==''&&endDate!==''?'midActive':''}} 改變入住日期和離店日期之間的日期顏色xml

4、結語

到此一個簡單的日曆就完成了,固然這個日曆沒法知足全部業務需求,可是基本的日曆渲染功能以及點擊選擇功能都有。因此在業務需求之上對其進行小部分改變就能夠了,但願你們能夠留言指出個人問題,我也會進一步的改善這個日曆代碼。對象

相關文章
相關標籤/搜索