首先非常感谢mx-datepicker作者,这个时间组件非常漂亮且好用。
由于我们的需求是,在一个可用的日期范围内选择一个日期,作者并没有支持这种场景,于是在作者的基础上进行了修改。
具体的需求如下:
- 可传入一个可选范围,打开选择器时,只有可选范围内的日期可点击选中,其他日期不可点击
- 动态改变翻月翻年按钮可用性,如果上月数据中没有可选日期则向上翻月按钮不可点击。
文末有完整代码
下面来说一下我的改造过程。
1.在props中增加一个参数,用于接收调用者传进来的可用日期范围enableRange
,接收一个数组
props: {
//颜色
color: {
type: String,
default: '#409eff'
},
//是否显示秒 针对type为datetime或time时生效
showSeconds: {
type: Boolean,
default: false
},
//初始的值
value: [String, Array],
//可用日期范围
enableRange: [Array],
//类型date time datetime range rangetime
type: {
type: String,
default: 'range'
},
//是否显示
show: {
type: Boolean,
default: false
},
//初始格式
format: {
type: String,
default: ''
},
//显示公历节日
showHoliday: {
type: Boolean,
default: false
},
//显示提示
showTips: {
type: Boolean,
default: false
},
//开始文案 针对type为范围选择时生效
beginText: {
type: String,
default: '开始'
},
//结束文案 针对type为范围选择时生效
endText: {
type: String,
default: '结束'
}
},
2.找到关键方法refreshCalendars
和procCalendar
refreshCalendars用于刷新日历,首次打开和进行上下翻页时会调用,所以这个方法就是让我们控制是否可以进行翻页的关键。
refreshCalendars(refresh = false) {
let date = new Date(this.date);
let before = DateTools.getDateToMonth(date, date.getMonth() - 1);
let after = DateTools.getDateToMonth(date, date.getMonth() + 1);
if (this.calendarIndex == 0) {
if (refresh) this.calendars.splice(0, 1, DateTools.getCalendar(date, this.procCalendar));
this.calendars.splice(1, 1, DateTools.getCalendar(after, this.procCalendar));
this.calendars.splice(2, 1, DateTools.getCalendar(before, this.procCalendar));
} else if (this.calendarIndex == 1) {
this.calendars.splice(0, 1, DateTools.getCalendar(before, this.procCalendar));
if (refresh) this.calendars.splice(1, 1, DateTools.getCalendar(date, this.procCalendar));
this.calendars.splice(2, 1, DateTools.getCalendar(after, this.procCalendar));
} else if (this.calendarIndex == 2) {
this.calendars.splice(0, 1, DateTools.getCalendar(after, this.procCalendar));
this.calendars.splice(1, 1, DateTools.getCalendar(before, this.procCalendar));
if (refresh) this.calendars.splice(2, 1, DateTools.getCalendar(date, this.procCalendar));
}
this.title = DateTools.format(this.date, 'yyyy年mm月');
},
3.解决上月
、上年
、下月
、下年
四个按钮的可用性需求
要改造这个方法首先要定义四个变量,来控制是上月
、上年
、下月
、下年
四个按钮的可用性(是否可点击,如果不可点击就置灰)
在data中定义四个变量
canLastYear: false,
canNextYear: false,
canLastMonth: false,
canNextMonth: false
在template中根据变量来控制按钮状态
<view class="picker-modal-header">
<view
:class="[canLastYear ? '' : 'disabled-color', 'picker-icon picker-icon-zuozuo']"
:hover-stay-time="100"
hover-class="picker-icon-active"
@click="canLastYear ? onSetYear('-1') : null"
></view>
<view
:class="[canLastMonth ? '' : 'disabled-color', 'picker-icon picker-icon-zuo']"
:hover-stay-time="100"
hover-class="picker-icon-active"
@click="canLastMonth ? onSetMonth('-1') : null"
></view>
<text class="picker-modal-header-title">{
{ title }}</text>
<view
:class="[canNextMonth ? '' : 'disabled-color', 'picker-icon picker-icon-you']"
class=""
:hover-stay-time="100"
hover-class="picker-icon-active"
@click="canNextMonth ? onSetMonth('+1') : null"
></view>
<view
:class="[canNextYear ? '' : 'disabled-color', 'picker-icon picker-icon-youyou']"
:hover-stay-time="100"
hover-class="picker-icon-active"
@click="canNextYear ? onSetYear('+1') : null"
></view>
</view>
在refreshCalendars
方法中进行日期对比判断:
首先找到calendars
这个二维数组,这里面存放着三页的数据,上一页
,当前页
,下一页
,既然我们要控制上下两页,那么就从这个数组中拿出calendars[0]
和calendars[2]
.
判断上月
和上一年
的逻辑代码如下:
let lastMonth = this.calendars[0]; //取出上一页的所有数据
let enableStartDate = this.enableRange[0]; // 取出调用者传入的起始可用日期
for (var i = 0; i < lastMonth.length; i++) {
let date = lastMonth[i].dateObj;
if (DateTools.compareDate(date, enableStartDate) == 1 || DateTools.compareDate(date, this.enableRange[0]) == 0) {
this.canLastMonth = true;
if (DateTools.compareYear(enableStartDate, date) == -1) {
this.canLastYear = true;
break;
} else {
this.canLastYear = false;
}
break;
} else {
this.canLastMonth = false;
this.canLastYear = false;
}
}
判断下月
和下一年
的逻辑代码如下:`
let nextMonth = this.calendars[2];
let enableEndDate = this.enableRange[1];
for (var i = 0; i < nextMonth.length; i++) {
let date = nextMonth[i].dateObj;
if (DateTools.compareDate(date, enableEndDate) == -1 || DateTools.compareDate(date, enableEndDate) == 0) {
this.canNextMonth = true;
if (DateTools.compareYear(enableEndDate, date) == 1) {
this.canNextYear = true;
break;
} else {
this.canNextYear = false;
}
break;
} else {
this.canNextMonth = false;
this.canNextYear = false;
}
}
4.解决起始日期之外的日期不可点击的需求
从refreshCalendars
方法中可以看出,刷新每个日期空间的方法是procCalendar
,可以看出此方法会带入一个item对象,此方法中就是根据一些特定的条件去处理每个日期的显示样式。我们要实现某些日期不可点击并置灰显示,也需要在这个方法中进行。
procCalendar
未修改的代码:
procCalendar(item) {
//定义初始样式
item.statusStyle = {
opacity: 1,
color:item.isOtherMonth ? '#abcfff' : '#000',
background: 'transparent'
};
item.bgStyle = {
type: '',
background: 'transparent'
};
item.dotStyle = {
opacity: 1,
background: 'transparent'
};
item.tips = '';
//标记今天的日期
if (DateTools.isSameDay(new Date(), item.dateObj)) {
item.statusStyle.color = this.color;
if (item.isOtherMonth) item.statusStyle.opacity = 0.3;
}
//标记选中项
this.checkeds.forEach(date => {
if (DateTools.isSameDay(date, item.dateObj)) {
item.statusStyle.background = this.color;
item.statusStyle.color = '#fff';
item.statusStyle.opacity = 1;
if (this.isMultiSelect && this.showTips) item.tips = this.beginText;
}
});
//节假日或今日的日期标点
if (item.statusStyle.background != this.color) {
let holiday = this.showHoliday ? DateTools.getHoliday(item.dateObj) : false;
if (holiday || DateTools.isSameDay(new Date(), item.dateObj)) {
item.title = holiday || item.title;
item.dotStyle.background = this.color;
if (item.isOtherMonth) item.dotStyle.opacity = 0.2;
}
} else {
item.title = item.dateObj.getDate();
}
//有两个日期
if (this.checkeds.length == 2) {
if (DateTools.isSameDay(this.checkeds[0], item.dateObj)) {
//开始日期
item.bgStyle.type = 'bgbegin';
}
if (DateTools.isSameDay(this.checkeds[1], item.dateObj)) {
//结束日期
if (this.isMultiSelect && this.showTips) item.tips = item.bgStyle.type ? this.beginText + ' / ' + this.endText : this.endText;
if (!item.bgStyle.type) {
//开始日期不等于结束日期
item.bgStyle.type = 'bgend';
} else {
item.bgStyle.type = '';
}
}
if (!item.bgStyle.type && (+item.dateObj > +this.checkeds[0] && +item.dateObj < +this.checkeds[1])) {
//中间的日期
item.bgStyle.type = 'bg';
item.statusStyle.color = this.color;
}
if (item.bgStyle.type) {
item.bgStyle.background = this.color;
item.dotStyle.opacity = 1;
item.statusStyle.opacity = 1;
}
}
},
要想实现按钮根据不同的状态展示不同的样式和相应不同的操作,我们还需要给在item中增加一个属性disable
在procCalendar
中增加判断:
//如果起始日期大于该日期
//可用范围
let startresult = DateTools.compareDate(this.enableRange[0], item.dateObj) == -1 || DateTools.compareDate(this.enableRange[0], item.dateObj) == 0;
let endresult = DateTools.compareDate(item.dateObj, this.enableRange[1]) == -1 || DateTools.compareDate(item.dateObj, this.enableRange[1]) == 0;
if (startresult && endresult) {
item.disable = false;
} else {
item.disable = true;
}
将样式定义的部分改成如下:
//定义初始样式
item.statusStyle = {
opacity: 1,
color: item.disable ? '#ddd' : item.isOtherMonth ? '#abcfff' : '#000',
background: 'transparent'