uniapp 仿美团选择预计送达时间组件(1)

README.md

仿美团选择预送达时间组件delivery-time-op 点餐 时间选择 送达时间

[^简单实用的选择时间组件,源码简单易懂注释清楚,可根据需求随意更改方案]

引用组件
import deliveryTimeOp from '@/components/delivery-time-op/delivery-time-op.vue'
注册组件
components: {
   deliveryTimeOp
},
使用组件
 <delivery-time-op
          @dataCallback="dataCallback"
          @timeCallback="timeCallback"
          @close="close"
          :model='model'
          :showYes='showYes'
          :content="content"
          :barHidth='600'
          title="选择预送达时间">
   </delivery-time-op>
参数说明
@dataCallback:选择日期的回调

@timeCallback:选择时间的回调

@close:隐藏事件(点击iocn关闭弹框)

model:显示或隐藏(true/false)

showYes:点击模态框是否关闭弹框(true/false)

content:需要传递展示的日期时间数据(数组)

barHidth:弹出框高度(数字)

title:标题
数据格式
 content:[{
   "timezh": "今天 (周三)",
   "timeformatter": "8-10",
   "timelist": [
       {
        "timestr": "立即送达",
    }, {
        "timestr": "15:35",
    }, {
        "timestr": "16:05",
    }, {
        "timestr": "16:35",
    }, {
        "timestr": "17:05",
    }, {
        "timestr": "17:35",
    }, {
        "timestr": "18:05",
    }, {
        "timestr": "18:35",
    }]
 }]

deliveryTime.vue

html
<template>
  <view>
	  <view class="pinck-box">
	    <view class="picker-up-title">取货时间</view>
	    <view class="pick-item">
	      <view class="block-up-title">{{bhTxt}}</view>
	      <view class="block-up-time" @click="showModel">
	        <text>{{chooseDay}} {{chooseTime}}</text>
	        <uni-icons type="right" />
	      </view>
	    </view>
	  </view>
	  
	  <deliveryTimeOp
	  	  v-if="content && content.length != 0" 
	  	  @dataCallback="dataCallback" 
	  	  @timeCallback="timeCallback" 
	  	  @close="close" 
	  	  :model='model' 
	  	  :showYes="model" 
	  	  :content="content" 
	  	  :barHidth='600' 
	  	  title="选择取货时间"
	  >
	  </deliveryTimeOp>
  </view>
</template>
 
1. js (推荐使用)
<script>
import deliveryTimeOp from '@/components/delivery-time-op.vue'
export default {
  components: {
    deliveryTimeOp,
  },
  data(){
	return{
		
	}
  },
  mounted(){},
  methods{
			// 时间
			getDliveryTime(){
				let today = '';
				let nextDay = '';
				let threeDay = ''
				let timeformatter = ''

				let now = new Date();
				// 获取星期
				let week = now.getDay();
				let deliveryTimeList = [];
				// 年
				let Y = now.getFullYear();
				// 月份
				let M = now.getMonth() + 1;
				M = M < 10 ? "0" + M : M;
				// 日
				let D = now.getDate();
				switch(week){
					case 0:
						today = "今天(周日)";
						nextDay = "明天(周一)";
						threeDay = "后天(周二)";
						break;
					case 1:
						today = "今天(周一)";
						nextDay = "明天(周二)";
						threeDay = "后天(周三)";
						break;
					case 2:
						today = "今天(周二)";
						nextDay = "明天(周三)";
						threeDay = "后天(周四)";
						break;
					case 3:
						today = "今天(周三)";
						nextDay = "明天(周四)";
						threeDay = "后天(周五)";
						break;
					case 4:
						today = "今天(周四)";
						nextDay = "明天(周五)";
						threeDay = "后天(周六)";
						break;
					case 5:
						today = "今天(周五)";
						nextDay = "明天(周六)";
						threeDay = "后天(周日)";
						break;
					case 6:
						today = "今天(周六)";
						nextDay = "明天(周日)";
						threeDay = "后天(周一)";
						break;
				}
				
				// 判断是否是20点以后, 20点以后则默认值为第二天第一个
				let endTimeStamp = new Date(new Date().setHours(20)).getTime()
				let nowStamp = new Date().getTime()
				
				if(nowStamp > endTimeStamp) {
					let todayStr = this.getTimeList('two');
					let nowDayList = this.getTimeObj(todayStr)
					let D1 = new Date(new Date().setDate(new Date().getDate() + 1)).getDate().toString()
					
					let todayYMD = Y + '-'+ M + '-' + D1.padStart(2, '0')
					
					deliveryTimeList.push({ timezh: nextDay, timeformatter: todayYMD, timelist: nowDayList });
					this.content = deliveryTimeList
					
					let nextDayStr = this.getTimeList('three');
					let nextDayList = this.getTimeObj(nextDayStr)
					
					let D2 = new Date(new Date().setDate(new Date().getDate() + 2)).getDate().toString()
					let nextYMD = Y + '-'+ M + '-' + D2.padStart(2, '0')
					deliveryTimeList.push({ timezh: threeDay, timeformatter: nextYMD, timelist: nowDayList });
					this.content = []
					this.content = deliveryTimeList
				} else {
					let todayStr = this.getTimeList('one');
					let nowDayList = this.getTimeObj(todayStr)
					
					let todayYMD = Y + '-'+ M + '-' + D.toString().padStart(2, '0')
					deliveryTimeList.push({ timezh: today, timeformatter: todayYMD, timelist: nowDayList });
					this.content = deliveryTimeList
					
					let nextDayStr = this.getTimeList('two');
					let nextDayList = this.getTimeObj(nextDayStr)
					let D2 = new Date(new Date().setDate(new Date().getDate() + 1)).getDate().toString()
					let nextYMD = Y + '-' + M + '-' + D2.padStart(2, '0')
					deliveryTimeList.push({ timezh: nextDay, timeformatter: nextYMD, timelist: nextDayList });
					this.content = []
					this.content = deliveryTimeList
					
				}
				
				this.chooseDate = this.content[0].timeformatter
				this.chooseDay = this.content[0].timezh
                this.chooseTime = this.content[0].timelist[0].timestr
			},
			// 获取时间段
			getTimeList(type) {
				let timeListTodayAll = [];
				let timeList = []
				let startTime 
				
				let now = new Date()
				let tommorrow = new Date().setDate(new Date().getDate() + 1)
				let three = new Date().setDate(new Date().getDate() + 2)
				let todayStart = new Date().getFullYear() + '-' + (new Date().getMonth()+1).toString().padStart(2, '0') + '-' + new Date().getDate() + ' 09:00:00'
				
				let allTimes = (21 - 9) * 60 / 40
				for(let i = 0; i < allTimes; i++) {
					let setTodayTime = new Date(todayStart).setMinutes(40*i)
					timeListTodayAll.push(formatTime(setTodayTime))
					timeList.push(formatTime(setTodayTime).substring(11, 16))
				}
				if(type == 'one') {
					let nowTimeStamp = new Date().setMinutes(new Date().getMinutes() + 20)
					for(let j = 0; j < timeListTodayAll.length; j++) {
						let itemStamp = new Date(timeListTodayAll[j]).getTime()
						if(itemStamp >= nowTimeStamp) {
							timeList = timeList.slice(j)
							break
						}
					}
				}
				return timeList
			},
			// 根据时间段转为数组对象
			getTimeObj(todayStr){
				// 将数组中的时间段字符串分割
				let todayArr = []
				todayStr.forEach(item => {
					todayArr.push(...item.split('-'))
				})
				// 将分割后的数组去重
				let todayDup = []
				todayArr.forEach(item => {
					if(todayDup.indexOf(item) == -1){
						todayDup.push(item)
					}
				})
				// 将去重后的数组转为数组对象
				let todayList = []
				todayDup.forEach(item => {
					todayList.push({
						timestr: item
					})
				})
				return todayList;
			},

			// 点击显示时间弹窗
			showModel(){
				this.model = true;
			},

			// 选择日期的回调
			dataCallback(item,index){
				this.chooseDay = item.timezh
				this.chooseDate = item.timeformatter
			},

			// 选择时间的回调
			timeCallback(item,index){
				if(this.now == this.formData.chooseDate){
					this.chooseDay = this.content[0].timezh
					this.chooseDate = this.now
				}else{
					this.chooseDay = this.content[1].timezh
					this.chooseDate = this.content[1].timeformatter
				}
				this.chooseTime = item.timestr
				
				if(this.chooseDay != this.content[0].timezh || this.chooseTime != this.content[0].timelist[0].timestr) {
					this.bhTxt = '指定时间'
				} else {
					this.bhTxt = '尽快取货'
				}
				this.model = false;
			},

			// 隐藏事件
			close(){
				this.model = false 
			},
  },

  onshow(){
		let now = new Date();
		let year = now.getFullYear()
		let month = now.getMonth() + 1
		month = month < 10 ? "0" + month : month;
		let date = now.getDate() 
		date = date < 10 ? '0' + date : date
		this.chooseDate = `${year}-${month}-${date}`
		this.now = `${year}-${month}-${date}`
		this.getDliveryTime()
  },

}
</script>
 
2. js
<script>
import deliveryTimeOp from '@/components/delivery-time-op.vue'
export default {
  components: {
    deliveryTimeOp,
  },
  data() {
    return {
      // 控制显示隐藏
      model: false,
      // 需要传递展示的日期时间数据
      content: [
        // {timezh: "",timeformatter: "", timelist: []},
      ],
      chooseTime: '',
      // 时间
      chooseDay: '',
      //星期
	  chooseDate: '', // 年月日
	  bhTxt: '尽快取货', // 取货时间文字
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.getDliveryTime()
    })
  },
  methods: {
    // 时间
    getDliveryTime() {
      let now = new Date();
      // 获取星期
      let week = now.getDay();
      let today = '';
      // 今天星期 文字
      let nextDay = '';
     // 明天星期 文字
      let timeformatter = ''
     // 年月日
      let deliveryTimeList = []
	  // 获取当前的月份
      let M = now.getMonth() + 1
      M = M < 10 ? '0' + M : M
	  // 获取当前的日
      let D = now.getDate()
      switch (week) {
        case 0:
          today = '今天(周日)'
          nextDay = '明天(周一)'
          break
        case 1:
          today = '今天(周一)'
          nextDay = '明天(周二)'
          break
        case 2:
          today = '今天(周二)'
          nextDay = '明天(周三)'
          break
        case 3:
          today = '今天(周三)'
          nextDay = '明天(周四)'
          break
        case 4:
          today = '今天(周四)'
          nextDay = '明天(周五)'
          break
        case 5:
          today = '今天(周五)'
          nextDay = '明天(周六)'
          break
        case 6:
          today = '今天(周六)'
          nextDay = '明天(周日)'
          break
      }

      let timeDate = new Date(now.getTime() + 3600000);
      
	  // 今天的时间段
      let todayStr = this.getTimeList(
        timeDate.getHours(),
        timeDate.getMinutes()
      );
      let nowDayList = this.getTimeObj(todayStr);
	  // 日 补零
	  let dnum = D
	  dnum = dnum < 10 ? '0' + dnum : dnum;
	  // 年 月 日
	  let todayYMD = Y + '-' + 'M' + '-' + dnum;
      deliveryTimeList.push({
        timezh: today,
        timeformatter: todayYMD,
        timelist: nowDayList,
      });
      this.content = deliveryTimeList;

      // 明天的时间段
      let nextDayStr = this.getTimeList(9, 0);
      let nextDayList = this.getTimeObj(nextDayStr);
	  // 日 补零
	  let DNum = D + 1;
	  DNum = DNum < 10 ? '0' + DNum : DNum;
	  // 年 月 日
	  let nextYMD = Y + '-' + M + '-' + DNum;
      deliveryTimeList.push({
        timezh: nextDay,
        timeformatter: nextYMD,
        timelist: nextDayList,
      });
      this.content = [];
      this.content = deliveryTimeList;
		
	  this.chooseDay = this.content[0].timezh
      this.chooseTime = this.content[0].timelist[0].timestr

    },
    // 获取时间段
    getTimeList(hour, minut) {
      let timeList = [];
	  let times = 0;
	  let timei = 0;
      let startTime = hour < 9 ? 9 : hour;
      if (minut < 20) {
        for (let i = 0; i < 21 - startTime; i++) {
          for (let j = 0; j < 2; j++) {
            if (j % 2 === 0) {
              times = startTime + i;
			  times = times < 10 ? '0' + times : times;
			  timeList.push(
				 times + ":00" + "-" + (times) + ":20"
			  );

            } else {
              times = times = startTime + i;
			  times = times < 10 ? '0' + times : times;
			  timei = startTime + i + 1;
			  timei = timei < 10 ? '0' + timei : timei;
			  timeList.push(
				 times + ":40" + "-" + (timei) + ":00"
			   );

            }
          }
        }
      } else {
		times = 0;
		timei = 0;
        for (let i = 0; i < 21 - startTime; i++) {
          for (let j = 0; j < 2; j++) {
            if (j % 2) {
              if (i !== 20 - startTime) {
                times = startTime + i;
				times = times < 10 ? '0' + times : times;
				timei = startTime + 1 + i;
				timei = timei < 10 ? '0' + timei : timei;
				timeList.push(
					timei + ":00" + "-" + (timei) + ":20"
				);

              }
            } else {
              times = startTime + i;
			  times = times < 10 ? '0' + times : times;
			  timei = timei = startTime + i + 1;
			  timei < 10 ? '0' + timei : timei;
			  timeList.push(
				 times + ":40" + "-" + (timei) + ":00"
			  );
            }
          }
        }
      }
      return timeList
    },
    // 根据时间段转为数组对象
    getTimeObj(todayStr) {
      // 将数组中的时间段字符串分割
      let todayArr = []
      todayStr.forEach((item) => {
        todayArr.push(...item.split('-'))
      })
      // 将分割后的数组去重
      let todayDup = []
      todayArr.forEach((item) => {
        if (todayDup.indexOf(item) == -1) {
          todayDup.push(item)
        }
      })
      // 将去重后的数组转为数组对象
      let todayList = []
      todayDup.forEach((item) => {
        todayList.push({
          timestr: item,
        })
      })
      return todayList
    },
    // 控制弹窗显示
    showModel() {
        this.model = true
    },
    // 选择日期的回调
    dataCallback(item, index) {
      this.chooseDay = item.timezh
	  this.chooseDate = item.timeformatter
    },
    // 选择时间的回调
    timeCallback(item, index) {
       if(this.now == this.content[0].timeformatter){
			this.chooseDay = this.content[0].timezh
			this.chooseDate = this.now
		}else{
			this.chooseDay = this.content[1].timezh
			this.chooseDate = this.content[1].timeformatter
		}
		this.chooseTime = item.timestr
		
		if(this.chooseDay != this.content[0].timezh || this.chooseTime != this.content[0].timelist[0].timestr) {
			this.bhTxt = '指定时间'
		} else {
			this.bhTxt = '尽快取货'
		}
		this.model = false;
    },
    // 隐藏事件
    close() {
      this.model = false
    },
  },
  onshow(){
		let now = new Date();
		let year = now.getFullYear()
		let month = now.getMonth() + 1
		month = month < 10 ? "0" + month : month;
		let date = now.getDate() 
		date = date < 10 ? '0' + date : date
		this.chooseDate = `${year}-${month}-${date}`
		this.now = `${year}-${month}-${date}`
		this.getDliveryTime()
  },
}
</script>

 
css
<style lang="less" scoped>
.pinck-box {
  background: #ffffff;
  padding: 0 32rpx 17rpx 32rpx;
  .picker-up-title {
    font-size: 32rpx;
    font-weight: 500;
    color: #000000;
    padding: 20rpx 0;
  }
  .pick-item {
    height: 88rpx;
    background: #f4f4f4;
    border-radius: 24rpx 24rpx 24rpx 24rpx;
    padding: 0 32rpx;
    display: flex;
    align-items: center;
    justify-content: space-between;
    .block-up-title {
      font-size: 28rpx;
      font-weight: 400;
      color: #ff502f;
    }
    .block-up-time {
      font-size: 24rpx;
      font-weight: 400;
      color: #999999;
      display: flex;
      align-items: center;
      /deep/.uni-icons {
        font-size: 24rpx !important;
        color: #999999 !important;
      }
    }
  }
}
</style>
 

components文件

delivery-time-op.vue

html
<template>
	<view>
		<!-- 模态框 -->
		<view @click="Modal" :class="{mask:model}"></view>
		<!-- 弹窗主体 -->
		<view :style="{'height':barHidth+'rpx'}" class="active" :class="{add:model}">
			<view class="title">{{title}} <text @click="showModal">X</text></view>
			<view class="cont" :style="{height:barHidth-80 +'rpx'}">
				<view class="day">
					<view 
					:class="index === isIndex ? 'active_copy' : ''" 
					v-for="(item,index) in content" 
					:key="index"
					@click="dataCallback(item,index)"
					>{{item.timezh}}</view>
				</view>

				<scroll-view class="content" :scroll-y="true" :scroll-top="scrollTop">
					<view class="appoint" 
					:class="index === Indexes ? 'longActive' : ''" 
					@click="timeCallback(item,index)"
					v-for="(item,index) in Days" 
					:key="index"
					>
						{{item.timestr}}
						<text :class="index === Indexes ? 'cuIcon-check' : ''"></text>
					</view>
				</scroll-view>
			</view>
		</view>

	</view>
</template>
 
js
<script>
	export default {
		props: {
			//控制弹窗的隐藏显示
			model: {
				type: Boolean,
				default: false
			},
			//弹窗标题
			title: {
				type: String,
				default: '弹窗测试'
			},
			//弹窗内容
			content: {
				type: Array,
				default: [{
					content: '我是弹窗内容'
				}]
			},
			//弹窗 窗口高度
			barHidth: {
				type: Number,
				default: 400
			},
			//点击模态框是否能关闭弹窗
			showYes: {
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				scrollTop: 0,
				isIndex: 0,
				Indexes: 0,
				Days: [],
			}
		},
		mounted(){
			// 初始化
			this.Days = this.content[0].timelist;
		},

		methods: {
			// 关闭窗口
			showModal() {
				this.$emit('close', false)
			},
			// 点击模态框关闭窗口
			Modal() {
				if (this.showYes) {
					this.$emit('close', false)
				}
			},
			//配送时间切换回顶
			gotop() {
				this.scrollTop = 1;
				this.$nextTick(function() {
					this.scrollTop = 0;
				});
			},

			//切换日期
			dataCallback(item,index) {
				this.isIndex = index;
				this.Days = this.content[index].timelist;
				this.Indexes = null;
				this.gotop();
				this.$emit('dataCallback', item, index)
			},

			//选择时间
			timeCallback(item,index) {
				this.Indexes = index;
				this.modalName = null;
				this.$emit('timeCallback', item, index)
			},
		}
	}
</script>

 
css
<style scoped>
	.mask {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		background-color: #000;
		animation: getInto .5s 1;
		opacity: 0.5;
		z-index: 999;
	}


	@keyframes getInto {
		0% {
			opacity: 0;
		}

		100% {
			opacity: 0.5;
		}

	}

	.active {
		position: fixed;
		bottom: 0;
		left: 0;
		z-index: 1000;
		width: 100%;
		height: 400rpx;
		border-top-left-radius: 16rpx;
		border-top-right-radius: 16rpx;
		overflow: hidden;
		transform: translateY(100%);
		transition: .4s;
	}

	.add {
		transform: translateY(0);
	}


	.title {
		position: relative;
		text-align: center;
		background-color: #fff;
		padding: 20rpx 0;
		border-bottom: 2rpx solid #eee;
	}

	.title>text {
		position: absolute;
		right: 14rpx;
		width: 40rpx;
		height: 40prx;
		background-color: #ccc;
		color: #666;
		border-radius: 50%;
		font-size: 32rpx;
	}

	.cont {
		display: flex;
		background-color: #fff;
		overflow-y: scroll;
	}

	.day {
		flex: 2;
		background-color: #F3F4F5;
		border-right: 2rpx solid #f8f8f8;
		text-align: center;

	}

	.day view {
		padding: 30rpx 12rpx;
		font-size: 28rpx;
		box-sizing: border-box;
	}

	.content {
		flex: 4;
		font-size: 28rpx;
		border-bottom: 40rpx solid #fff;
		background-color: #fff;
	}

	.appoint {
		text-align: left;
		padding: 30rpx;
		border-bottom: 2rpx solid #f8f8f8;
	}


	.appoint text {
		margin-right: 30rpx;
	}

	.active_copy {
		position: relative;
		background-color: #fff;
		color: #000000;
		box-sizing: border-box;

	}

	.active_copy::after {
		content: '';
		width: 5rpx;
		height: 94rpx;
		background: #fff;
		position: absolute;
		top: 0;
		right: 0;

	}

	.longActive {
		color: #FF502F;
		display: flex;
		align-items: center;
		justify-content: space-between;
		font-size: 32rpx;
		font-weight: 500;
	}
	
	.cuIcon-check{
		width: 30rpx;
		height: 16rpx;
		border-bottom: 4rpx solid #FF502F;
		border-left: 4rpx solid #FF502F;
		transform: rotate(-45deg);
	}
</style>