最后更新于2022年6月22日,变更如下:
2022.06.22
- 修复在苹果手机上日历无法展示的问题(感谢Lysa_snow)
2022.05.31
- 引入dayjs优化日期计算逻辑
- 删除iconfont字体,三角形使用css替代
- 新增月份变化事件(bind:monthChange)和年份变化事件(bind:yearChange)
- 日期变变化事件名由bind:change修改为bind:dateChange
一、先上一个demo的演示二维码
也可以直接拉到底部,有个码云地址
二、属性
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
defaultDate | 日历默认选中的时间 | String | Date | Number(可以被dayjs解析的格式即可) | 今天 |
spot | 底部需要展示小圆点的日期数组 | Array<String | Date | Number | [] |
三、事件
事件 | 说明 | 回调参数 |
---|---|---|
bind:dateChange | 选中的日期变化时触发 | event.detail:{ date, month, year, dateString } |
bind:monthChange | 选中的月份变化时触发 | event.detail:{ date, month, year, dateString } |
bind:yearChange | 选中的年份变化时触发 | event.detail:{ date, month, year, dateString } |
四、实现代码
WXML
<!--components/calendar/calendar.wxml-->
<view class="calendar">
<view class="title flex">
<view class="flex">
<picker value="{{selectDay.year+'-'+(selectDay.month+1)}}" bindchange="editMonth" mode="date" fields="month" class="year-month">{{selectDay.year}}.{{selectDay.month>8?selectDay.month+1:"0"+(selectDay.month+1)}}</picker>
<view class="last-icon" bindtap="lastMonth"> </view>
<view class="next-icon" bindtap="nextMonth"> </view>
</view>
<view catchtap="openChange" class="flex open">
<view>{{open?"收起":"展开"}}</view>
</view>
</view>
<!-- 日历头部 -->
<view class="flex-around calendar-week">
<view class="view">日</view>
<view class="view">一</view>
<view class="view">二</view>
<view class="view">三</view>
<view class="view">四</view>
<view class="view">五</view>
<view class="view">六</view>
</view>
<!-- 日历主体 -->
<view class="calendar-main">
<view class="date-group" style="height:{{open? dateList.length/7*72 : 72}}rpx;">
<view class="flex-start flex-wrap date-transform" style="transform: translateY({{open?0:-72*transform}}rpx);">
<view wx:for="{{dateList}}" wx:key="dateList" class="day">
<view class="bg {{(item.year === selectDay.year && item.month === selectDay.month) ?'': 'other-month'}} {{item.dateString === selectDay.dateString?'select':''}}" catchtap="selectChange" data-date="{{item.date}}" data-year="{{item.year}}" data-month="{{item.month}}" data-date-string="{{item.dateString}}">
{{item.date}}
</view>
<view class="spot" wx:if="{{item.spot}}"></view>
</view>
</view>
</view>
</view>
</view>
WXSS
/* components/calendar/calendar.wxss */
.flex {
display: flex;
justify-content: space-between;
align-items: center;
}
.flex-start {
display: flex;
justify-content: flex-start;
align-items: center;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.flex-end {
display: flex;
justify-content: flex-end;
align-items: center;
}
.flex-around {
display: flex;
justify-content: space-around;
align-items: center;
}
.flex1 {
flex: 1;
}
.flex-wrap {
flex-wrap: wrap;
}
.align-start {
align-items: flex-start;
}
.align-end {
align-items: flex-end;
}
.align-stretch {
align-items: stretch;
}
.calendar {
background-color: #fff;
}
.calendar .title {
font-size: 40rpx;
color: #333;
padding: 30rpx;
line-height: 60rpx;
}
.calendar .title .year-month {
margin-right: 20rpx;
}
.calendar .title .last-icon {
width: 0;
height: 0;
border-right: 24rpx solid #999;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
margin-left: 10rpx;
}
.calendar .title .next-icon {
width: 0;
height: 0;
border-left: 24rpx solid #999;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
margin-left: 40rpx;
}
.calendar .title .open {
background-color: #f6f6f6;
color: #999;
font-size: 22rpx;
line-height: 36rpx;
border-radius: 18rpx;
padding: 0 14rpx;
}
.calendar .calendar-week {
line-height: 40rpx;
padding: 0 25rpx;
font-size: 28rpx;
color: #999;
}
.calendar .calendar-week .view {
width: 100rpx;
text-align: center;
}
.calendar .calendar-main {
padding: 30rpx 25rpx;
}
.calendar .calendar-main .date-group {
transition: all 0.3s;
overflow: hidden;
}
.calendar .calendar-main .date-transform {
transition: all 0.3s;
}
.calendar .calendar-main .day {
position: relative;
width: 100rpx;
color: #666;
text-align: center;
height: 72rpx;
}
.calendar .calendar-main .day .bg {
height: 56rpx;
line-height: 56rpx;
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.calendar .calendar-main .day .select {
width: 56rpx;
border-radius: 50%;
text-align: center;
color: #fff;
background: #409eff;
margin: 0 auto;
}
.calendar .calendar-main .day .other-month {
color: #ccc;
}
.calendar .calendar-main .day .spot {
width: 8rpx;
height: 8rpx;
background-color: #409eff;
border-radius: 50%;
margin: 6rpx auto 0;
}
JS部分
// components/calendar/calendar.js
const dayjs = require("./dayjs")
Component({
/**
* 组件的属性列表
*/
properties: {
//底下需要展示小圆点的日期数组
spot: {
type: Array,
value: []
},
//组件渲染时默认选中的时间
defaultDate: {
type: String,
optionalTypes: [Date, Number],
value: ''
}
},
/**
* 组件的初始数据
*/
data: {
dateList: [], //日历主体渲染数组
selectDay: {}, //选中时间
open: true, //日历是否展开
transform: 0 //收起时日历高度偏移倍数
},
/**
* 组件的方法列表
*/
methods: {
//picker设置月份
editMonth(e) {
const arr = e.detail.value.split("-")
this.setDate(parseInt(arr[0]), parseInt(arr[1]) - 1)
},
//上月切换按钮点击
lastMonth() {
const lastMonth = dayjs(new Date(this.data.selectDay.year, this.data.selectDay.month - 1))
this.setDate(lastMonth.year(), lastMonth.month())
},
//下月切换按钮点击
nextMonth() {
const nextMonth = dayjs(new Date(this.data.selectDay.year, this.data.selectDay.month + 1))
this.setDate(nextMonth.year(), nextMonth.month())
},
//设置选中日期
setDate(paramYear, paramMonth, paramDate) {
const date = Math.min(dayjs(`${paramYear}-${paramMonth + 1}`).daysInMonth(), this.data.selectDay.date)
const time = dayjs(`${paramYear}-${paramMonth + 1}-${paramDate || date}`)
const selectDay = {
year: paramYear,
month: paramMonth,
date: paramDate || date,
dateString: time.format("YYYY-MM-DD"),
}
//设置收起时的日历主体偏移量
let dateListStart = dayjs(`${paramYear}-${paramMonth + 1}`).day(0)
this.setData({
transform: dayjs(`${paramYear}-${paramMonth + 1}-${paramDate || date}`).day(0).diff(dateListStart, 'week')
})
if (paramYear !== this.data.selectDay.year) {
this.setData({
selectDay,
open: true
})
this.dateListInit(paramYear, paramMonth)
this.triggerEvent("dateChange", this.data.selectDay)
this.triggerEvent("monthChange", this.data.selectDay)
this.triggerEvent("yearChange", this.data.selectDay)
return
}
if (paramMonth !== this.data.selectDay.month) {
this.setData({
selectDay,
open: true
})
this.dateListInit(paramYear, paramMonth)
this.triggerEvent("dateChange", this.data.selectDay)
this.triggerEvent("monthChange", this.data.selectDay)
return
}
if (paramDate && paramDate !== this.data.selectDay.date) {
this.setData({
selectDay
})
this.triggerEvent("dateChange", this.data.selectDay)
}
},
//展开收起
openChange() {
this.setData({
open: !this.data.open
})
},
//日历主体的渲染方法
dateListInit(paramYear = this.data.selectDay.year, paramMonth = this.data.selectDay.month) {
let dateList = []; //需要遍历的日历数组数据
let startDate = dayjs(`${paramYear}-${paramMonth + 1}`).day(0) //日历渲染开始日期
let endDate = dayjs(`${paramYear}-${paramMonth + 1}`).endOf('month').day(6) //日历主体渲染结束日期
const timeArr = this.data.spot.map(item => {
return dayjs(item).format('YYYY-MM-DD')
}) //底部小圆点需要展示的数组
while (startDate < endDate) {
const dateString = startDate.format("YYYY-MM-DD")
dateList.push({
date: startDate.date(),
month: startDate.month(),
year: startDate.year(),
dateString,
spot: timeArr.indexOf(dateString) !== -1
})
startDate = startDate.add(1, 'day')
}
this.setData({
dateList: dateList
})
},
//某一天被点击时
selectChange(e) {
const year = e.currentTarget.dataset.year
const month = e.currentTarget.dataset.month
const date = e.currentTarget.dataset.date
this.setDate(year, month, date)
}
},
//组件生命周期
lifetimes: {
attached() {
let now = this.data.defaultDate ? dayjs(this.data.defaultDate) : dayjs()
this.setDate(now.year(), now.month(), now.date())
}
},
//监听参数变化
observers: {
spot: function () {
this.dateListInit()
}
}
})
五、码云地址
https://gitee.com/GaoWeiQiang1996/wx-calendar
如果有帮助到你,给个免费的赞吧!