小程序中实现 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