小程序中实现 calendar 组件

通过上面截图可以看出
- 点击上月和下月切换月份
- 点击具体的日期可以在页面上显示选中日期
- 点击确定将会获得选中日期
- 点击灰色的日期可以跳转对应月份并高亮日期
结构
日历结构主要分为 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 进去就能知道有多少天了。
newDate(2020, 5, 2).getDate()   // 2newDate(2020, 5, 1).getDate()   // 1
newDate(2020, 5, 0).getDate()   // 31
通过 new Date().getDay() 可以获取 date 是星期几,它的值是 0~6,正好可以用 0 代表星期日。
- 代码 - getThisDateWeek({year, month, date}: YearMonthDate) {- returnnewDate(Date.UTC(year, month, date)).getDay() - }, - getThisMonthDays(year: number, month: number) { - returnnewDate(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() - } elseif (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 = newDate().getFullYear() - month = newDate().getMonth() - date = newDate().getDate() - this.data.selectedDate = this.formatDate({year, month, date}) - this.setData({year, month, date}) - }, - onClickMask() { - this.setData({show: false, value: '-1'}) - }, - } 
以上是 小程序中实现 calendar 组件 的全部内容, 来源链接: utcz.com/a/33524.html


