zoukankan      html  css  js  c++  java
  • 微信小程序:手写日历组件

    一、前言

    最近公司要做一个酒店入住的小程序,不可避免的一定会使用到日历,而小程序没有内置的日历组件。在网上看了一下也没有非常适合需求的日历,于是自己写了一个。

    二、代码

    1. 原理分析

    写一个日历只需要知道两件事情:

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

    2. 功能分析

    由于是酒店入住的日历,所以需要实现如下功能:

    1. 渲染日历,一般是从本月开始,到半年之后的日历
    2. 过去的日期不可选
    3. 实现点击获取入住日期、退房日期,以及改变相应日期的颜色和整个时间段的颜色

    3. 数据分析

    根据最后的功能实现,我生成的每个月的数据结构如下:

    {
        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。当然这个问题也和我的个人水平有关,如果有哪位大神有更好的方法,请留言告诉我。我非常想去掉这两个数据。

    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':''}} 改变入住日期和离店日期之间的日期颜色

    四、结语

    到此一个简单的日历就完成了,当然这个日历无法满足所有业务需求,但是基本的日历渲染功能以及点击选择功能都有。所以在业务需求之上对其进行小部分改变就可以了,希望大家可以留言指出我的问题,我也会进一步的改善这个日历代码。

  • 相关阅读:
    leetcode Remove Linked List Elements
    leetcode Word Pattern
    leetcode Isomorphic Strings
    leetcode Valid Parentheses
    leetcode Remove Nth Node From End of List
    leetcode Contains Duplicate II
    leetcode Rectangle Area
    leetcode Length of Last Word
    leetcode Valid Sudoku
    leetcode Reverse Bits
  • 原文地址:https://www.cnblogs.com/10manongit/p/12722340.html
Copyright © 2011-2022 走看看