客户原始需求:用户需要知道在选中的月份中,哪些日期是有客户预约的,并且显示当天预约人数,点击有预约的日期则进入预约信息详细页,详细页内可以新建预约;点击没有预约的日期则直接进入新建预约页面。
因为项目用的是vant的小程序ui组件,所以刚开始想的是用vant的日历组件来实现此需求。经过几番尝试后,始终实现不了 formatter 的动态数据渲染,最后放弃了(如有大佬已实现此功能,麻烦留言分享下你的解决方案,谢谢),然后自己写了一个日历组件。
2. 点击单个日期,返回具体年月日,并且被点击日期变色
3. 根据用户数据,动态渲染日历标注。传入的日期变更,或者是用户数据变更,都会重新渲染日历及标注。上面效果图下方的【改日期】和 【改数据】就是用来测试这个功能的。
<view class="calendar-box">
<view class="head-box">
<view class="title-box">{{title}}</view>
<view class="week-box">
<view class="week-item">日</view>
<view class="week-item">一</view>
<view class="week-item">二</view>
<view class="week-item">三</view>
<view class="week-item">四</view>
<view class="week-item">五</view>
<view class="week-item">六</view>
</view>
</view>
<view class="date-box">
<view
bindtap="clickItem"
data-date="{{day.date}}"
class="date-item {{currentDate == day.date ? 'active' : ''}}"
wx:for="{{daysArray}}" wx:for-item="day"
wx:key="index">
<view class="top-text" wx:if="{{day.topText}}">{{day.topText}}</view>
{{day.date}}
<view class="bottom-text" wx:if="{{day.bottomText}}">{{day.bottomText}}</view>
</view>
</view>
</view>
// components/calendar/calendar.js
Component({
/**
* 组件的属性列表
*/
properties: {
defaultDate: {
type: String,
observer () {
this.getCurrentDaysAndWeekStart();
this.renderDate();
this.triggerEvent('formatter', this.data.daysArray);
}
},
isDataChange: {
type: Boolean,
value: false,
observer () {
this.triggerEvent('formatter', this.data.daysArray);
}
},
daysData: {
type: Array,
observer (newVal) {
if (newVal.length > 0) {
this.setData({ daysArray: newVal });
}
}
}
},
/**
* 组件的初始数据
*/
data: {
Y: '', // 年
M: '', // 月
D: '', // 日
W: '', // 星期
firstDayWeek: '', // 当前月第一天星期几
lastDayWeek: '', // 当前月最后一天星期几
daysCount: 0, // 总天数
daysArray: [], // 日历中天数数组
title: '',
currentDate: '0'
},
/**
* 组件的方法列表
*/
methods: {
// 获取当前月的天数,以及当前月第一天是星期几,最后一天是星期几
getCurrentDaysAndWeekStart () {
let dateString = this.properties.defaultDate;
let today = new Date();
if (dateString) {
today = new Date(dateString);
}
let Y = today.getFullYear();
let M = today.getMonth() + 1;
let D = today.getDate();
let daysCount = new Date(Y, M, 0).getDate(); // 当前月最后一天日期,即当前月的天数
let firstDayWeek = new Date(Y, M - 1, 1).getDay(); // 第一天星期几
let lastDayWeek = new Date(Y, M, 0).getDay(); // 最后一天星期几
this.setData({
Y: Y,
M: M,
D: D,
firstDayWeek: firstDayWeek,
lastDayWeek: lastDayWeek,
daysCount: daysCount,
title: `${Y}年${M}月`
});
},
// 根据总天数和第一天星期几,最后一天星期几,渲染日历天数数组
renderDate () {
let firstDayWeek = this.data.firstDayWeek;
let lastDayWeek = this.data.lastDayWeek;
let daysCount = this.data.daysCount;
let days = []; // 当前月总天数数组
for (let i = 1; i <= daysCount; i++) {
days.push({
date: i.toString(),
topText: '',
bottomText: ''
});
}
// 补全前面 (因为一周七天,如果第一天是星期三,则需要把星期一和星期二数据补全)
for (let i = 0; i < firstDayWeek; i++) {
days.unshift({
date: '',
topText: '',
bottomText: ''
});
}
// 补全后面 (因为一周七天,如果最后一天是星期五,则需要把星期六和星期天数据补全)
for (let i = lastDayWeek; i <= 7; i++) {
days.push({
date: '',
topText: '',
bottomText: ''
});
}
this.setData({ daysArray: days });
},
// 点击单个日期事件
clickItem (event) {
let date = event.currentTarget.dataset.date;
if (date) {
this.setData({ currentDate: date });
this.triggerEvent('select', `${this.data.Y}-${this.data.M}-${date}`);
}
}
}
})
/* components/calendar/calendar.wxss */
.calendar-box {
background-color: #ffffff;
padding: 10rpx;
color: #323233;
}
.head-box {
box-shadow: 0 2px 10px rgb(125 126 128 / 16%);
}
.title-box {
padding: 20rpx 0 40rpx 0;
text-align: center;
font-size: 14px;
}
.week-box {
display: flex;
justify-content: space-between;
font-size: 12px;
padding-bottom: 20rpx;
}
.week-item {
width: 100%;
text-align: center;
}
.date-box {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.date-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 125rpx;
width: 14.285%;
border-radius: 10rpx;
font-size: 12px;
}
.top-text, .bottom-text {
font-size: 8px;
color: red;
}
.active {
background-color: pink;
}