乐趣区

关于前端:小程序中实现-calendar-组件

通过下面截图能够看出

  1. 点击上月和下月切换月份
  2. 点击具体的日期能够在页面上显示选中日期
  3. 点击确定将会取得选中日期
  4. 点击灰色的日期能够跳转对应月份并高亮日期

构造

日历构造次要分为 mask 和 calender

  • 代码

    <view class="mask {{show ?'show':''}}"catch:tap="onClickMask"></view>
    <view class="calender {{onceShow ?'show':''}} {{show ? 'enter' : 'leave'}}">
      <view class="header">
        <view class="header-title">
          <view catch:tap="onToggleMonthClick" data-type="prev"> 上月 </view>
          <view class="title">{{year}}年 {{month+1}} 月 </view>
          <view catch:tap="onToggleMonthClick" data-type="next"> 下月 </view>
        </view>
            <!-- 星期 -->
        <view class="weekdays">
          <view class="weekday"> 日 </view>
          <view class="weekday"> 一 </view>
          <view class="weekday"> 二 </view>
          <view class="weekday"> 三 </view>
          <view class="weekday"> 四 </view>
          <view class="weekday"> 五 </view>
          <view class="weekday"> 六 </view>
        </view>
      </view>
      <view class="body">
        <view class="month">
                <!-- 日期 -->
          <view class="dates">
            <block wx:for="{{thisMonthDates}}" wx:key="*this">
              <view
                  class="date {{item.isToday ?'today':''}} {{item.isSelectedDate ? 'selected-date' : ''}} {{item.isEmptyDate ?'empty-date':''}}"data-index="{{index}}"data-type="{{item.monthState}}"data-formatDate="{{item.formatDate}}"catch:tap="onSelectDateClick"
              >{{item.date}}
              </view>
            </block>
          </view>
        </view>
        <view class="footer"> 确定 </view>
      </view>
    </view>

mask 是遮罩层,当日历弹出时,显示在日历前面,calender 之所以没有放在外面,是因为组件弹出时须要做动画,组件显示的时候 mask 须要立刻呈现。

calender 分为有两种操作

  • 上月和下月切换
  • 日期点击高亮

依据日历构造,写出日历款式

  • 日期的款式分为以后月、上月、下月,上月下月为灰色
  • 高亮当天
  • 切换日期时,当天的色彩是金色
  • 款式代码

外围代码

日历组件最重要的是如何计算当月多少天,以及对应星期几。

好在 js 提供了 new Date().getDate() 能够获取明天是几号,Date 可承受 3 个参数 year、month、date,如果 date 大于这个月的最大天数,就会主动换算成下个月的几号,同理如何小于 1 号,就会主动换算出上个月的几号。利用这个个性,date 传递 0 进去就能晓得有多少天了。

new Date(2020, 5, 2).getDate()   // 2
new Date(2020, 5, 1).getDate()   // 1
new Date(2020, 5, 0).getDate()   // 31

通过 new Date().getDay() 能够获取 date 是星期几,它的值是 0~6,正好能够用 0 代表星期日。

  • 代码

    getThisDateWeek({year, month, date}: YearMonthDate) {return new Date(Date.UTC(year, month, date)).getDay()},
    getThisMonthDays(year: number, month: number) {return new Date(year, month + 1, 0).getDate()}

晓得了当月多少天,怎么晓得当月后面和前面有多少天呢?

  • 代码

    methods: {
        // 当月
        createDays() {const thisMonthDates: any[] = []
          const {year, month} = this.data
          const thisMonthTotalDate = this.getThisMonthDays(year, month)
          for (let date = 1; date <= thisMonthTotalDate; date++) {const monthGrid = this.formatMonthGrid({year, month, date})
            thisMonthDates.push(monthGrid)
          }
          this.data.thisMonthDates = thisMonthDates
          this.setData({thisMonthTotalDate})
        },
        // 单月的上月和下月有多少天
        createEmptyDays() {let {year, month, thisMonthDates} = this.data
            // 当月第一天是星期几
        const firstDayWeek = this.getThisDateWeek({year, month, date: 1})
        const emptyGridsBefore = this.getBeforeMonthEmpty(firstDayWeek)
        const emptyGridsAfter = this.getAfterMonthEmpty(firstDayWeek)
        thisMonthDates = [...emptyGridsBefore, ...thisMonthDates, ...emptyGridsAfter]
        this.setData({thisMonthDates})
      },
        
        // 上月
        getBeforeMonthEmpty(firstDayWeek: number) {const {year, month} = this.data
        const emptyGridsBefore: any[] = []
        const {prevYear, prevMonth} = this.prevYear(year, month)
        const prevMonthDay = this.getThisMonthDays(year, prevMonth)
    
            // 上月 补满 1 号后面的日期
        for (let i = 1; i <= firstDayWeek; i++) {const date = prevMonthDay - (firstDayWeek - i)
          const monthGrid = this.formatMonthGrid({year: prevYear, month: prevMonth, date}, MonthState.Prev)
          emptyGridsBefore.push(monthGrid)
        }
        return emptyGridsBefore
      },
    
        // 下月
      getAfterMonthEmpty(firstDayWeek: number) {const emptyGridsAfter: any[] = []
        const {thisMonthTotalDate, year, month} = this.data
        const {nextYear, nextMonth} = this.nextYear(year, month)
    
            // 日期默认显示 42 天,-7 是为了如果 35 天能显示全的话就用 35 天
        const nextMonthDay = 42 - thisMonthTotalDate - firstDayWeek - 7 >= 0 ?
            42 - thisMonthTotalDate - firstDayWeek - 7 :
            42 - thisMonthTotalDate - firstDayWeek
            
            // 下月补满当月最初一天前面的日期
        for (let date = 1; date <= nextMonthDay; date++) {const monthGrid = this.formatMonthGrid({year: nextYear, month: nextMonth, date}, MonthState.Next)
          emptyGridsAfter.push(monthGrid)
        }
        return emptyGridsAfter
      },
    
        // 如果是 1 月,上月就是上年 12 月
        prevYear(year: number, month: number) {
        const prevYear = month === 0 ? year - 1 : year
        const prevMonth = month === 0 ? 11 : month - 1
        return {prevYear, prevMonth}
      },
    
        // 如果是 12 月,下月就是下年 1 月
      nextYear(year: number, month: number) {
        const nextYear = month === 11 ? year + 1 : year
        const nextMonth = month === 11 ? 0 : month + 1
        return {nextYear, nextMonth}
      },
    }

性能代码

  • 代码

    methods: {
        // state 是用来判断上月还是下月
        formatMonthGrid({year, month, date}: YearMonthDate, state?: string) {const formatDate = this.formatDate({year, month, date})
          const isSelectedDate = this.defaultSelectedDateGrid(formatDate, state)
          const isEmptyDate = this.isEmptyDateGrid(month)
          const week = this.getThisDateWeek({year, month, date})
          const isToday = this.isToday(formatDate)
          return {
            year,
            month,
            date,
            formatDate,
            week,
            isSelectedDate,
            isEmptyDate,
            isToday,
            monthState: state
          }
        },
    
        // 点击日期高亮
        setSelectedDateGrid(formatDate: string) {const {thisMonthDates} = this.data
          let selectedDate: string = ''
          thisMonthDates.forEach((thisMonthDate: any) => {if (thisMonthDate.formatDate === formatDate) {
              thisMonthDate.isSelectedDate = true
              selectedDate = thisMonthDate.formatDate
            } else {thisMonthDate.isSelectedDate = false}
          })
          this.data.selectedDate = selectedDate
          this.setData({thisMonthDates})
        },
    
        // 默认日期高亮
        defaultSelectedDateGrid(formatDate: string, state: string) {const {selectedDate} = this.data
          // 只高亮当月,上月和下月的不高亮
          return formatDate === selectedDate && !state
        },
        
        // 明天
        isToday(formatDate: string) {const {value} = this.data
          return value === formatDate
        },
        
        // 是不是当月
        isEmptyDateGrid(month: number) {const {month: thisMonth} = this.data
          return month !== thisMonth
        },
        
        formatDate({year, month, date}: YearMonthDate) {return `${year}-${month + 1}-${date}`
        },
    }

事件

  • 代码

    methods: {onSelectDateClick(e: Event) {const {formatdate, type} = e.currentTarget.dataset
          const {selectedDate} = this.data
          if (formatdate === selectedDate) return
          this.toggleMonth(type)
          this.setSelectedDateGrid(formatdate)
        },
        
        // 上月和下月切换
        onToggleMonthClick(e: Event) {const {type} = e.currentTarget.dataset
          this.toggleMonth(type)
        },
        toggleMonth(type: string) {if (type === MonthState.Prev) {this.prevMonth()
          } else if (type === MonthState.Next) {this.nextMonth()
          }
        },
        prevMonth() {const {year, month} = this.data
          const {prevYear, prevMonth} = this.prevYear(year, month)
          this.setData({year: prevYear, month: prevMonth}, () => {this.createDays()
            this.createEmptyDays()})
        },
        nextMonth() {const {year, month} = this.data
          const {nextYear, nextMonth} = this.nextYear(year, month)
          this.setData({year: nextYear, month: nextMonth}, () => {this.createDays()
            this.createEmptyDays()})
        },
    }

Properties

  • 代码

    properties = {
      show: {
        type: Boolean,
        value: false,
        observer(show: boolean) {if (show)
            this.setData({enter: show, onceShow: true})
        }
      },
      value: {
        type: String,
        value: '-1',
        observer(value: string) {
          // 2020-6-15
          const year = +(value.split("-")[0])
          const month = +(value.split("-")[1]) - 1
          const date = +(value.split("-")[2])
          this.data.selectedDate = value
          this.setData({year, month, date}, () => {this.initDate()
            this.createDays()
            this.createEmptyDays()})
        }
      }
    },
    methods = {initDate() {let {year, month, date} = this.data
            if (year && month && date) return
            year = new Date().getFullYear()
            month = new Date().getMonth()
            date = new Date().getDate()
            this.data.selectedDate = this.formatDate({year, month, date})
            this.setData({year, month, date})
        },
        onClickMask() {this.setData({show: false, value: '-1'})
        },
    }
退出移动版