最近在支付宝小程序上需要一个类似飞猪酒店时间选择的功能,但是没有找到合适的组件,没有办法,只能手动造一个使用。
?支付宝小程序效果图如下
组件说明:
1、当前日期之前的时间不可选
2、选择时间范围可跨月选择
在支付宝原生页面中使用
在xxx.json中引入组件
"calendar-select":"../../components/calender-select/calender-select"
axml文件中使用
<button size="default" type="primary" onTap="selectTime">选择时间</button>
<calendar-select
maxDate="2022-6-30"
:addOrRemoveData="{{addOrRemoveData}}"
mode="range"
value="{{showCalendar}}"
:altPrice="{{dataPrice}}"
onCloseFunc="closeFunc"
onTurnMsg="changeDate"></calendar-select>
?js中data数据
closeFunc(){
this.setData({showCalendar:false})
},
selectTime(){
this.setData({showCalendar:true})
},
changeDate(data){
console.log(data,"]]]]]返回数据]]]]]]")
},
?调用方法
closeFunc(){
this.setData({showCalendar:false})
},
selectTime(){
this.setData({showCalendar:true})
},
changeDate(data){
console.log(data,"]]]]]返回数据]]]]]]")
},
?
组件代码
components/calender-select/calender-select.axml
<view a:if="{{value}}" class="calendar-body">
<view class="title-name" onTap="close">
{{title}} <text class="iconfont icon-close-bold"></text>
</view>
<view a:if="{{mode == 'range'}}" class="range-text">
<view class="left">
<view class="alt">入住日期</view>
<view class="time">
<text>{{startMonth}}月{{startDay}}日</text> {{startWeek}}
</view>
</view>
<view class="right">
<view class="alt">离店日期</view>
<view class="time">
<text>{{endMonth}}月{{endDay}}日</text> {{endWeek}}
</view>
</view>
</view>
<scroll-view class="day-list {{ mode == 'range'?'day-range':''}}" scroll-y="true" :scroll-top="{{scrollTop}}" onScrollToLower="changeMonthHandler" style="width: 100%;height:80vh">
<view class="day-item" a:for="{{listData}}" a:for-item="item" a:key="itemIndex">
<view class="day-title">{{item.showTitle}}</view>
<view class="week-list">
<view class="week-day__text" a:for="{{weekDayZh}}" a:for-item="itemZh" a:key="indexZh">{{itemZh}}</view>
</view>
<view class="content">
<!-- 前置空白部分 -->
<block a:for="{{item.weekdayArr}}" a:for-item="itemArr" a:key="indexArr">
<view class="item" ></view>
</block>
<!-- (openDisAbled(item.year, item.month, dayIndex+1)?'day-range' : '') -->
<!-- ( mode== 'date' && activeDate ==`${item.year}-${item.month}-${dayIndex+1}`?'activeData':'')
(mode == 'range' && startDate==`${item.year}-${item.month}-${dayIndex+1}`?'startDate':'')
( mode == 'range' && openStartOrend(item.year, item.month, dayIndex+1)?'startOrend':'')
( mode== 'range' && endDate==`${item.year}-${item.month}-${dayIndex+1}`?'endDate':'') -->
<view a:for="{{item.daysArr}}" a:for-item="dayItem" a:key="dayItem"
class="item {{item.showBoolArr[dayItem-1]==false?'gray-color':''}}
{{mode == 'range' && item.showBoolArr[dayItem]=='false'?'day-range':''}}
{{ mode== 'date' && activeDate ==`${item.year}-${item.month}-${dayItem}`?'activeData':''}}
{{mode == 'range' && startDate==`${item.year}-${item.month}-${dayItem}`?'startDate':''}}
{{ mode == 'range' &&item.newDaysBoolArr[dayItem-1] ?'startOrend':''}}
{{mode== 'range' && endDate==`${item.year}-${item.month}-${dayItem}`?'endDate':''}}"
data-item={{item}}
data-index={{dayItem-1}}
data-faItem={{item.daysArr}}
data-showbool={{item.showBoolArr[dayItem-1]}}
onTap="dateClick">
<view class="number {{item.showBoolArr[dayItem-1]==false?'gray-color':''}}">{{ dayItem }}</view>
<view class="number">{{mode == 'range' && startDate==`${item.year}-${item.month}-${dayItem}`?'入住':'' }}</view>
<view class="number">{{mode == 'range' && endDate==`${item.year}-${item.month}-${dayItem}`?'离店':'' }}</view>
</view>
</view>
</view>
</scroll-view>
</view>
components/calender-select/calender-select.js
Component({
mixins: [],
data: {
scrollTop: 0,
datePrice: [],
// 星期几,值为1-7
weekday: 1,
weekdayArr:[],
// 当前月有多少天
days: 0,
daysArr:[],
showTitle: '',
year: 2020,
month: 0,
day: 0,
startYear: 0,
startMonth: 0,
startDay: 0,
startWeek: '',
endYear: 0,
endMonth: 0,
endDay: 0,
endWeek: '',
today: '',
activeDate: '',
startDate: '',
endDate: '',
isStart: true,
min: null,
max: null,
weekDayZh: ['日', '一', '二', '三', '四', '五', '六'],
listData:[],
startDateTime:"",//当前时间搓
endDateTime:""
},
props: {
// 通过双向绑定控制组件的弹出与收起
value: false,
// date-单个日期选择,range-开始日期+结束日期选择
mode: 'date',
// 日期选择标题
title: '可预约日期查询',
// 最小下单日期(不在范围内日期禁用不可选,包含当天)
minDate: '2022-01-01',
// 最大下单日期(不在范围内日期禁用不可选,包含当天)
maxDate: '2022-05-22',
// 是否禁止周六周日 不可下单 默认不禁止
isWeekend: false,
// 每天显示的提示
altPrice: [{
date: '2022-01-01',
price: ''
}],
// 入住离开日期
addOrRemoveData: []
},
didMount() {
this.data.timestamp =new Date().getTime();
this.init()
},
didUpdate(a,b) {
console.log(a,"=======",b)
// if(a.mode!=b.mode|| a.minDate!=b.minDate||a.maxDate!=b.maxDate){
// this.init()
// }
//数据发生跟新的时候,重新获取入住和离店的时间错
let startDateTime = this.data.startDate.replace(/\-/g, '/');
let endDateTime = this.data.endDate.replace(/\-/g, '/');
this.data.startDateTime = startDateTime;
this.data.endDateTime = endDateTime;
},
deriveDataFromProps(nextProps) {
if (JSON.stringify(this.data.itemData) !== JSON.stringify(this.data.itemData)) {
//业务处理
this.init()
}
},
didUnmount() {},
methods: {
init() {
/// 初始化日期
let now = new Date();
this.setData({
// 初始化日期下的 文字
datePrice : this.props.altPrice,
year : now.getFullYear(),
month : now.getMonth() + 1,
day : now.getDate(),
today : `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`,
activeDate : this.props.today,
min : this.initDate(this.props.minDate),
max :this.initDate(this.props.maxDate || this.props.today),
startDate : "",
startWeek: "",
startYear : 0,
startMonth : 0,
startDay : 0,
endYear : 0,
endMonth :0,
endDay : 0,
endDate : "",
endWeek :"",
isStart : true,
listData : [],
selectList:[]
})
this.changeData();
// 默认显示两个月
let _this = this
setTimeout(() => {
_this.changeMonthHandler(1)
}, 100)
setTimeout(() => {
_this.changeMonthHandler(1)
}, 2000);
// 初始化入住离店时间
if (this.props.addOrRemoveData.length > 0) {
setTimeout(() => {
_this.addOrRemoveChange()
}, 200)
}
},
//日期处理
initDate(date) {
console.log(date,"=====")
let fdate = date.split('-');
return {
year: Number(fdate[0] || 2022),
month: Number(fdate[1] || 1),
day: Number(fdate[2] || 1)
}
},
// 判断可使用日期范围
openDisAbled(year, month, day) {
let bool = true;
let date = `${year}/${month}/${day}`;
let today = this.data.today.replace(/\-/g, '/');
let min = `${this.data.min.year}/${this.data.min.month}/${this.data.min.day}`;
let max = `${this.data.max.year}/${this.data.max.month}/${this.data.max.day}`;
let timestamp = new Date(date).getTime();
console.log(min,max)
if (timestamp < new Date(min).getTime() || timestamp > new Date(max).getTime()) {
bool = false;
my.showToast({content:"选择时间超出范围"})
console.log('选择时间超出范围')
}
// 判断是否大于今日
if (timestamp < new Date(today).getTime()) {
console.log("选择时间不能小于当前时间")
bool = false;
}
// 禁止周六周日下单
if (this.isWeekend && (this.getWeekText(date) === '星期六' || this.getWeekText(date) === '星期日')) {
bool = false;
}
return bool;
},
// 判断是否再选中日期范围内
openStartOrend(year, month, day) {
let bool = false;
let date = `${year}/${month}/${day}`;
let startDate = this.data.startDate.replace(/\-/g, '/');
let endDate = this.data.endDate.replace(/\-/g, '/');
let timestamp = new Date(date).getTime();
if (timestamp > new Date(startDate).getTime() && timestamp < new Date(endDate).getTime()) {
bool = true
}
return bool
},
// 显示日期下的文字提示
viewDayAlt(year, month, day) {
console.log("显示日期下的文字提示")
let txt = ''
let item = this.data.datePrice.find(item => {
let today = item.date.replace(/\-/g, '/');
let date = `${year}/${month}/${day}`;
return new Date(date).getTime() === new Date(today).getTime()
})
if (item) {
return Number.isFinite(item.price) ? '¥' + item.price : item.price
}
},
generateArray(start, end) {
return Array.from(new Array(end + 1).keys()).slice(start);
},
formatNum(num) {
return num < 10 ? '0' + num : num + '';
},
//一个月有多少天
getMonthDay(year, month) {
let days = new Date(year, month, 0).getDate();
return days;
},
getWeekday(year, month) {
let date = new Date(`${year}/${month}/01 00:00:00`);
return date.getDay();
},
checkRange(year) {
let overstep = false;
if (year < this.minYear || year > this.maxYear) {
uni.showToast({
title: "日期超出范围啦~",
icon: 'none'
})
overstep = true;
}
return overstep;
},
//scorllview触底
changeMonthHandler(isAdd) {
console.log(isAdd,"isAddisAddisAdd")
if (isAdd) {
// 根据日期限制显示
if (this.data.year >= this.data.max.year && this.data.month >= this.data.max.month) {
console.log(false)
return false
}
let month = this.data.month + 1;
let year = month > 12 ? this.data.year + 1 : this.data.year;
if (!this.checkRange(year)) {
console.log("checkRangecheckRange")
this.data.month = month > 12 ? 1 : month;
this.data.year = year;
this.changeData();
}
} else {
let month = this.month - 1;
let year = month < 1 ? this.year - 1 : this.year;
if (!this.checkRange(year)) {
console.log("checkRangecheckRangeerrrrrr")
this.data.month = month < 1 ? 12 : month;
this.data.year = year;
this.changeData();
}
}
},
changeYearHandler(isAdd) {
let year = isAdd ? this.year + 1 : this.year - 1;
if (!this.checkRange(year)) {
this.data.year = year;
this.changeData();
}
},
changeData() {
this.data.days = this.getMonthDay(this.data.year, this.data.month);
this.data.daysArr=this.generateArray(1,this.data.days)
this.data.weekday = this.getWeekday(this.data.year, this.data.month);
this.data.weekdayArr=this.generateArray(1,this.data.weekday)
this.data.showTitle = `${this.data.year}年${this.data.month}月`;
if (this.data.isChange && this.data.mode == 'date') {
this.btnFix(true);
}
this.setDayText()
},
// 处理插入数据时间列表
setDayText() {
console.log("插入数==",this.data.listData)
let daysArr = this.data.daysArr;
let year = this.data.year;
let month = this.data.month;
let newDaysBoolArr = [];//控制颜色
let showBoolArr = [];//控制是否可点击
daysArr.map(item=>{
let day = item>9?item:"0"+item;
showBoolArr.push(this.openDisAbledBool(year,month,day))
newDaysBoolArr.push(false)
})
let listData = this.data.listData;
listData.push({
year: this.data.year,
month: this.data.month,
weekdayArr: this.data.weekdayArr,
daysArr: this.data.daysArr,
showTitle: this.data.showTitle,
newDaysBoolArr:newDaysBoolArr,
showBoolArr:showBoolArr
})
this.setData({
listData
})
},
//判断日期是否为不可点击
openDisAbledBool(year, month, day) {
let bool = true;
let date = `${year}/${month}/${day}`;
let timestamp = new Date(date).getTime();
let today = this.data.today.replace(/\-/g, '/');
if (timestamp < new Date(today).getTime()) {
bool = false;
}
return bool;
},
// 处理初始化入住离店时间
addOrRemoveChange() {
let _this = this
this.addOrRemoveData.forEach((item, index) => {
let data = item.replace(/\-/g, '/')
let now = new Date(data);
let year = now.getFullYear();
let month = now.getMonth() + 1;
let day = now.getDate() - 1;
if (index === 0) {
this.dateClick({year: year, month: month }, day)
} else {
setTimeout(() => {
_this.dateClick({year: year, month: month }, day)
}, 300)
}
})
},
// 选择日期操作
dateClick(evt) {
let {item,index:day,showbool} = evt.target.dataset;
let listData = this.data.listData;
if(!showbool) return false;
// item, day
day += 1;
if (this.openDisAbled(item.year, item.month, day)) {
this.data.day = day;
let date = `${item.year}-${item.month}-${day}`;
if (this.data.mode == 'date') {
console.log("只选择一个时间")
this.data.activeDate = date;
this.btnFix(true)
} else {
console.log("选择开始和离店2个时间")
let compare = new Date(date.replace(/\-/g, '/')).getTime() < new Date(this.data.startDate.replace(/\-/g, '/')).getTime()
if (this.data.isStart || compare) {
console.log("选择开始时间")
this.setData({
startDate:date,
listData,
startYear:item.year,
startMonth:item.month,
startDay:day,
endYear:0,
endMonth:0,
endDay:0,
endDate:"",
activeDate:"",
startWeek:this.getWeekText(date),
isStart:false,
datePrice:[{
date: date,
price: '入住'
}]
})
//删除选择区域
this.resetListData(listData,2)
} else {
console.log("选择离店时间")
this.setData({
endDate:date,
endYear:item.year,
endMonth:item.month,
endDay:day,
endWeek:this.getWeekText(date),
isStart:true,
datePrice:this.data.datePrice.push({
date: date,
price: '离开'
})
})
//设置选择区域
this.resetListData(listData,1)
//是否手动关闭时间组件并返回数据
this.btnFix(true)
}
}
}
},
//设置选择区域颜色
resetListData(listData,type){
listData.forEach((firstItem,firstIndex)=>{
if(type==2){
firstItem.newDaysBoolArr.forEach((subItem,subindex)=>{
listData[firstIndex].newDaysBoolArr[subindex]=false;
})
}else{
firstItem.daysArr.forEach((subItem,subindex)=>{
if(this.openStartOrend(firstItem.year,firstItem.month,subItem)){
listData[firstIndex].newDaysBoolArr[subindex]=true;
}
})
}
})
console.log(listData)
this.setData({
listData:listData
})
},
close() {
this.data.scrollTop = 0
// 修改绑定的父组件变量的值为false,从而隐藏日历弹窗
this.props.onCloseFunc(false);
},
getWeekText(date) {
date = new Date(`${date.replace(/\-/g, '/')} 00:00:00`);
let week = date.getDay();
return '星期' + ['日', '一', '二', '三', '四', '五', '六'][week];
},
btnFix(show) {
if (!show) {
this.close();
}
if (this.data.mode == 'date') {
let arr = this.data.activeDate.split('-')
let year = this.data.isChange ? this.data.year : Number(arr[0]);
let month = this.data.isChange ? this.data.month : Number(arr[1]);
let day = this.data.isChange ? this.data.day : Number(arr[2]);
//当前月有多少天
let days = this.getMonthDay(year, month);
let result = `${year}-${this.formatNum(month)}-${this.formatNum(day)}`;
let weekText = this.getWeekText(result);
let isToday = false;
if (`${year}-${month}-${day}` == this.data.today) {
//今天
isToday = true;
}
this.props.onTurnMsg({
year: year,
month: month,
day: day,
days: days,
activeDate: this.data.activeDate,
result: result,
week: weekText,
isToday: isToday
});
} else {
if (!this.data.startDate || !this.data.endDate) return;
let startMonth = this.formatNum(this.data.startMonth);
let startDay = this.formatNum(this.data.startDay);
let startDate = `${this.data.startYear}-${startMonth}-${startDay}`;
let startWeek = this.getWeekText(startDate)
let endMonth = this.formatNum(this.data.endMonth);
let endDay = this.formatNum(this.data.endDay);
let endDate = `${this.data.endYear}-${endMonth}-${endDay}`;
let endWeek = this.getWeekText(endDate);
this.props.onTurnMsg({
startYear: this.data.startYear,
startMonth: this.data.startMonth,
startDay: this.data.startDay,
startDate: startDate,
startWeek: startWeek,
endYear: this.data.endYear,
endMonth: this.data.endMonth,
endDay: this.data.endDay,
endDate: endDate,
endWeek: endWeek
})
}
}
},
});
components/calender-select/calender-select.acss
.calendar-body {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
background-color: #fafafa;
-webkit-animation: fadelogIn-data-v-7570169f 0.4s;
animation: fadelogIn-data-v-7570169f 0.4s;
z-index: 9;
}
.calendar-body .title-name {
font-size: 36rpx;
font-weight: 500;
line-height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 24rpx;
background-color: #fafafa;
}
.calendar-body .title-name .icon-close-bold {
position: absolute;
top: 24rpx;
right: 24rpx;
width: 40rpx;
height: 40rpx;
font-size: 40rpx;
color: #999;
}
.calendar-body .range-text {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
}
.calendar-body .range-text .alt {
font-size: 24rpx;
color: #303030;
}
.calendar-body .range-text .time {
font-size: 24rpx;
color: #666;
}
.calendar-body .range-text .time text {
font-size: 36rpx;
color: #000;
}
.calendar-body .range-text .right {
text-align: right;
}
.calendar-body .day-list {
padding: 24rpx;
height: calc(100vh - 100rpx);
box-sizing: border-box;
}
.calendar-body .day-list .day-item {
background: #fff;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
.calendar-body .day-list .day-title {
font-size: 30rpx;
font-weight: 500;
color: #000;
text-align: center;
padding: 30rpx 0;
}
.calendar-body .day-list .week-list {
display: flex;
border-bottom: 1rpx solid #f0f0f0;
padding: 24rpx;
}
.calendar-body .day-list .week-list .week-day__text {
flex: 1;
text-align: center;
font-size: 24rpx;
color: #000;
width: 14.28%;
}
.calendar-body .day-list .week-list .week-day__text :last-child {
color: #ed7444;
}
.calendar-body .day-list .week-list .week-day__text :first-child {
color: #ed7444;
}
.calendar-body .day-list .content {
display: flex;
flex-wrap: wrap;
padding: 24rpx;
}
.calendar-body .day-list .content .item {
width: 14.28%;
height: 90rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-bottom: 24rpx;
}
.calendar-body .day-list .content .item .number {
font-size: 24rpx;
color: #000;
}
.calendar-body .day-list .content .item .dec {
font-size: 24rpx;
font-weight: 700;
color: #ed7444;
min-height: 30rpx;
opacity: 0;
}
.calendar-body .day-list .content .hoverData .number {
color: #333;
}
.calendar-body .day-list .content .hoverData .dec {
opacity: 1;
}
.calendar-body .day-list .content .activeData {
background: #96292f;
border-radius: 8rpx;
}
.calendar-body .day-list .content .activeData .number {
color: #fff;
}
.calendar-body .day-list .content .activeData .dec {
color: #fff;
}
.calendar-body .day-list .content .startDate {
background: #96292f;
border-radius: 8rpx;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.calendar-body .day-list .content .startDate .number {
color: #fff;
}
.calendar-body .day-list .content .startDate .dec {
color: #fff;
}
.calendar-body .day-list .content .endDate {
background: #96292f;
border-radius: 8rpx;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.calendar-body .day-list .content .endDate .number {
color: #fff;
}
.calendar-body .day-list .content .endDate .dec {
color: #fff;
}
.calendar-body .day-list .content .startOrend {
background-color: #f4eaeb;
border-radius: 0;
}
.calendar-body .day-range {
height: calc(100vh - 180rpx);
}
@-webkit-keyframes fadelogIn-data-v-7570169f {
0% {
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
100% {
-webkit-transform: none;
transform: none;
}
}
@keyframes fadelogIn-data-v-7570169f {
0% {
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
100% {
-webkit-transform: none;
transform: none;
}
}
.calendar-body .day-list .content .item .gray-color {
color: #999;
}
借鉴uni-app插件
同程艺龙-多模式日历插件(开箱即用) - DCloud 插件市场 普通日历选择、酒店入住离开、飞机往返三种模式,可自定义主题色。(代码已精简优化,插件包仅5KB)https://ext.dcloud.net.cn/plugin?id=472?飞猪同款时间范围选择器 - DCloud 插件市场 仿照飞猪的选择进行改良过的酒店日期选择插件https://ext.dcloud.net.cn/plugin?id=6349
uni-app中的组件,只能打包支付宝小程序后可以直接使用,但是在支付宝原生小程序中,不能直接引入使用。
如在使用中出现问题,欢迎大家一起讨论哦~~~~
如在使用中出现问题,欢迎大家一起讨论哦~~~~
如在使用中出现问题,欢迎大家一起讨论哦~~~~
|