温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么用uni-popup实现菜鸟上门取件时间选择器

发布时间:2022-08-24 16:45:22 来源:亿速云 阅读:268 作者:iii 栏目:开发技术

这篇文章主要讲解了“怎么用uni-popup实现菜鸟上门取件时间选择器”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用uni-popup实现菜鸟上门取件时间选择器”吧!

兼容

因为目前我的项目只用到这三端,其他的都还没测,所以兼容不保证

  • 支付宝小程序开发者工具popup弹出来会直接滚到最顶部,显示异常,但真机上面没问题,可以不用管

环境兼容
支付宝小程序
微信小程序
H5

菜鸟上门时间选择器

怎么用uni-popup实现菜鸟上门取件时间选择器

需求分析:

1、弹窗从底部弹出

  • 点击蒙层不可关闭

  • 弹窗header左侧title , 右侧关闭按钮

2、左侧日期选择器

  • 显示近3天日期

  • 显示(今天、明天、周一、周二等)

3、右侧时间选择器

  • 可选时间可配置

  • 过期时间显示 “已过期”

  • 选中效果

  • 当前已无可选时间,应该删除今天日期,只可以选未来日期

代码实现:

1.popup弹窗

先做一下基础布局,简单的分成上左右三大块,并做一些基础的配置

怎么用uni-popup实现菜鸟上门取件时间选择器

<template>
	<uni-popup
		mask-background-color="rgba(0, 0, 0, .8)"
		ref="datePickerPop"
		type="bottom"
		background-color="#fff"
		:is-mask-click="false"
	>
		<view class="date_pop">
			<view class="popup_header">
				<view class="pop_title">请选择取件时间</view>
				<view class="pop-close" @click="handleClose('datePop')" />
			</view>
			<!-- 日期 -->
			<view class="date_con">
				<scroll-view scroll-y="true" class="date_box">
				</scroll-view>
				<!-- 时间 -->
				<scroll-view scroll-y="true" class="time_box">
				</scroll-view>
			</view>
		</view>
	</uni-popup>
</template>
<script>
	export default {
		name: 'TimePicker',
		props: {
			visible: {
				required: true,
				default: false
			}
		},
		watch: {
			visible(newVal) {
				if (newVal) {
					if (!this.selectedDate.date_zh) {
						this.selectedDate = this.effectRecentDate[0];
					}
					this.$refs.datePickerPop.open();
				} else {
					this.$refs.datePickerPop.close();
				}
			}
		},
		methods: {
			handleClose() {
				this.$emit('update:visible', false);
			},
		}
	};
</script>
<style scoped lang="scss">
	.date_pop {
		padding: 0;
		height: 750rpx;
		.popup_header {
			display: flex;
			align-items: center;
			justify-content: space-between;
			box-sizing: border-box;
			padding: 60rpx 40rpx;
			.pop_title {
				font-weight: bold;
				font-size: 32rpx;
				width: 90%;
			}
			.pop-close {
				width: 60rpx;
				height: 60rpx;
				background: url('~@/static/images/close.png');
				background-size: 22rpx;
				background-position: center;
				background-repeat: no-repeat;
			}
		}
		.date_con {
			font-size: 28rpx;
			position: relative;
			height: 600rpx;
		}
		.date_box {
			position: absolute;
			top: 0;
			left: 0;
			width: 40%;
			height: 100%;
			background: #f7f7f9;
			overflow-y: scroll;
			.date_item {
				padding: 0 40rpx;
				line-height: 100rpx;
			}
		}
		.time_box {
			position: absolute;
			top: 0;
			right: 0;
			width: 60%;
			height: 100%;
		}
		.date_active {
			background: #fff;
		}
	}
</style>

2.日期+时间选择器

按照需求我重新设计了一下功能及交互

日期选择器

  • 日期可配置,支持显示最近n天日期

  • 显示今天、明天、后台及工作日

  • 默认选中当日(今天)

时间选择器

基础功能

  • 删除过期时间

  • 今日所有可选日期都过期之后删除日期选框(今天)选项

  • 选中时间后面打钩,并关闭弹窗

可选功能

  • 显示已过期时间 (逻辑几个版本之前已经删除了,现在只剩类名,需要的同学可以大概看下代码把它加上或者评论区留个言我把给你找找代码 , 功能样式就类似菜鸟)

  • 直接删除已过期时间

核心逻辑:

1、生成左侧日期列表

// 生成时间选择器 最近n天的时间
/**
*@n {Number} : 生成的天数
*
*/
setRecentData(n) {
	const oneDaySeconds = 60 * 1000 * 60 * 24;
	const today = +new Date();
	let list = [];
	for (let i = 0; i &lt; n; i++) {
		let formatTime = this.formatTime_zh(today + oneDaySeconds * i);
		list.push({
			...formatTime,
			week: i == 0 ? '今天' : i == 1 ? '明天' : formatTime.week
		});
	}
        //设置一下默认选中日期
	this.selectedDate = list[0];
	return list;
},
// 时间处理函数
formatTime_zh(date){
	date = new Date(date);
	const year = date.getFullYear();
	const month = date.getMonth() + 1;
	const day = date.getDate();
	const weekDay = date.getDay();
	const formatNumber = (n) =&gt; {
		n = n.toString();
		return n[1] ? n : '0' + n;
	};
        const numToTxt = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
	return {
		date_zh: `${formatNumber(month)}月${formatNumber(day)}日`,
		date_en: `${year}/${formatNumber(month)}/${formatNumber(day)}`,
		week: numToTxt[weekDay]
	};
},

最终数据格式如图:

怎么用uni-popup实现菜鸟上门取件时间选择器

2、判断时间有没有过期

因为考虑到取件没有那么快,至少要提前半小时下单,所以就有了下面的逻辑(我这里是90分钟)

  • 传入 09:00-10:00 格式时间区间

  • 截取过期时间, 和当前时间做对比

  • 判断已过期 、即将过期 、未过期

/**
* @return {Number} 1:已过期 , 2:即将过期 , 3:未过期
* @time   {String} 09:00-10:00
*/
checkRemainingMinute(time) {
	if (!time) return;
        //过期时间
	const outTime = time.toString().split('-')[1];
        // 这里兼容一下iphone,iphone不支持yyyy-mm-dd hh:mm 格式时间 ,分隔符换为 /
	const fullYearDate = formatMinute(new Date(), '/');
	const now = new Date(fullYearDate);
	const dateTime = this.currentDate + ' ' + outTime;
	const check = new Date(dateTime);
	const difference = check - now;
	const minutes = difference / (1000 * 60);
	// minutes &lt;= 0  : 已过期    --&gt; 1
	// minutes &lt;= 90 : 即将过期  --&gt; 2
	// minutes &gt;  0  : 未过期     --&gt; 3
	return minutes &lt;= 0 ? 1 : minutes &lt;= 90 ? 2 : 3;
}
  /**
   * @description yyyy-mm-dd hh:mm
   * @author wangxinu
   * @export
   * @param {*} cent
   * @returns
   */
  formatMinute: (date, separator = '-') =&gt; {
    date = new Date(date);
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const day = date.getDate();
    const hour = date.getHours();
    const minute = date.getMinutes();
    const second = date.getSeconds();
    const formatNumber = (n) =&gt; {
      n = n.toString();
      return n[1] ? n : '0' + n;
    };
    return `${formatNumber(year)}${separator}${formatNumber(month)}${separator}${formatNumber(
      day,
    )} ${formatNumber(hour)}:${formatNumber(minute)}`;
  },

3、通过计算属性获取有效时间(即右侧列表展示即将过期的和未过期的时间)

data(){
    return {
    	appointment: [
		'08:00-09:00',
		'09:00-10:00',
		'10:00-11:00',
		'11:00-12:00',
		'12:00-13:00',
		'13:00-14:00',
		'14:00-15:00',
		'15:00-16:00',
		'16:00-17:00',
		'17:00-18:00',
		'18:00-19:00',
		'19:00-20:00'
	]
    }
},
computed: {
	// 有效取件时间
   effectAppointmentTime() {
        //取件时间列表
	const appointment = this.appointment;
	// 未来日期返回全部
	if (this.selectedDate.date_en != this.currentDate) {
		return appointment;
	}
        // 当日只返回有效时间
	let list = appointment.filter((item) =&gt; this.checkRemainingMinute(item) != 1);
	// 当天取件时间长度&gt;0 添加立即上门
	if (list.length &gt; 0) {
		list.unshift('立即上门');
	}
	return list;
   }
},

4、通过计算属性获取有效日期

computed: {
	// 有效日期
   effectRecentDate() {
        //查看有效时间列表
	const effectAppointmentTime = this.effectAppointmentTime;
	// 当日取件时间全部失效
            if (effectAppointmentTime.length == 0) {
                //删除(今日)
		this.recentDateList.splice(0, 1);
                //修改默认选中日期
		this.selectedDate = this.recentDateList[0];
		return this.recentDateList;
            } else {
		return this.recentDateList;
            }
	},
},

5、日期或时间选中函数

	// 时间选择器修改函数
	timeChange(date, type) {
		const dateList = this.recentDateList;
		if (type === 'date') {
			// 选择日期
			this.selectedDate = date;
			this.selectedTime = '';
		} else {
			// 选择时间
			this.selectedTime = date;
			if (this.selectedDate.date_zh == '') {
				this.selectedDate = dateList[0];
			}
                        this.handleClose();
                       	this.$emit('selectTime', this.selectedDate, this.selectedTime);
		}
	},

源码及使用

使用:

<template>
	<div class="page">
		<button @click="timePicker_visible = true" type="primary">打开弹窗</button>
		<TimePicker :visible.sync="timePicker_visible" @selectTime="selectTime"/>
	</div>
</template>
<script>
	import TimePicker from './components/TimePicker';
	export default {
		name: 'test',
		components: { TimePicker },
		mixins: [],
		props: {},
		data() {
			return {
				timePicker_visible: false
			};
		},
                methods:{
                    selectTime(date,time){
                        console.log('date',date)
                        console.log('time',time)
                   }
                }
	};
</script>

源码:

<template>
	<uni-popup
		mask-background-color="rgba(0, 0, 0, .8)"
		ref="datePickerPop"
		type="bottom"
		background-color="#fff"
		:is-mask-click="false"
	>
		<view class="date_pop">
			<view class="popup_header">
				<view class="pop_title">请选择取件时间</view>
				<view class="pop-close" @click="handleClose('datePop')" />
			</view>
			<!-- 日期 -->
			<view class="date_con">
				<scroll-view scroll-y="true" class="date_box">
					<view
						v-for="date in effectRecentDate"
						:key="date.date_zh"
						:class="[`date_item`, selectedDate.date_zh == date.date_zh ? `date_active` : ``]"
						@click="timeChange(date, 'date')"
					>
						{{ date.date_zh }}({{ date.week }})
					</view>
				</scroll-view>
				<!-- 时间 -->
				<scroll-view scroll-y="true" class="time_box">
					<view
						v-for="(time, index) in effectAppointmentTime"
						:key="index"
						:class="{
							bottom: true,
							time_item: true,
							time_active: selectedTime === time
						}"
						@click="timeChange(effectAppointmentTime[index], `time`)"
					>
						{{ time }}
					</view>
				</scroll-view>
			</view>
		</view>
	</uni-popup>
</template>
<script>
	import { formatDate, toFixed, formatMinute } from '@/public/utils/utils';
	export default {
		name: 'TimePicker',
		props: {
			visible: {
				required: true,
				default: false
			}
		},
		watch: {
			visible(newVal) {
				if (newVal) {
					if (!this.selectedDate.date_zh) {
						this.selectedDate = this.effectRecentDate[0];
					}
					this.$refs.datePickerPop.open();
				} else {
					this.$refs.datePickerPop.close();
				}
			}
		},
		data() {
			// 生成取件日期
			const recentDayNum = 5;
			this.toFixed = toFixed;
			return {
				currentDate: formatDate(new Date(), '/'),
				selectedTime: '',
				selectedDate: {},
				recentDateList: this.setRecentData(recentDayNum),
				appointment: [
					'08:00-09:00',
					'09:00-10:00',
					'10:00-11:00',
					'11:00-12:00',
					'12:00-13:00',
					'13:00-14:00',
					'14:00-15:00',
					'15:00-16:00',
					'16:00-17:00',
					'17:00-18:00',
					'18:00-19:00',
					'19:00-20:00'
				]
			};
		},
		computed: {
			// 有效日期
			effectRecentDate() {
				const effectAppointmentTime = this.effectAppointmentTime;
				// 当日取件时间全部失效
				if (effectAppointmentTime.length == 0) {
					this.recentDateList.splice(0, 1);
					this.selectedDate = this.recentDateList[0];
					console.log('this.selectedDate: ', this.selectedDate);
					return this.recentDateList;
				} else {
					return this.recentDateList;
				}
			},
			// 有效取件时间
			effectAppointmentTime() {
				const appointment = this.appointment;
				// 未来日期返回全部
				if (this.selectedDate.date_en != this.currentDate) {
					return appointment;
				}
				let list = appointment.filter((item) => this.checkRemainingMinute(item) != 1);
				// 当日只返回有效时间
				if (list.length > 0) {
					list.unshift('立即上门');
				}
				return list;
			}
		},
		methods: {
			handleClose() {
				this.$emit('update:visible', false);
			},
			// 生成时间选择器 最近n天的时间
			setRecentData(n) {
				const oneDayTime = 60 * 1000 * 60 * 24;
				const today = +new Date();
				let list = [];
				for (let i = 0; i < n; i++) {
					let formatTime = this.formatTime_zh(today + oneDayTime * i);
					list.push({
						...formatTime,
						week: i == 0 ? '今天' : i == 1 ? '明天' : formatTime.week
					});
				}
				this.selectedDate = list[0];
				return list;
			},
			// 时间处理函数
			formatTime_zh: (date) => {
				date = new Date(date);
				const year = date.getFullYear();
				const month = date.getMonth() + 1;
				const day = date.getDate();
				const weekDay = date.getDay();
				const formatNumber = (n) => {
					n = n.toString();
					return n[1] ? n : '0' + n;
				};
				const numToTxt = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
				return {
					date_zh: `${formatNumber(month)}月${formatNumber(day)}日`,
					date_en: `${year}/${formatNumber(month)}/${formatNumber(day)}`,
					week: numToTxt[weekDay]
				};
			},
			// 时间选择器修改函数
			timeChange(date, type) {
				const dateList = this.recentDateList;
				if (type === 'date') {
					// 选择日期
					this.selectedDate = date;
					this.selectedTime = '';
				} else {
					// 选择时间
					this.selectedTime = date;
					if (this.selectedDate.date_zh == '') {
						this.selectedDate = dateList[0];
					}
					this.handleClose();
					this.$emit('selectTime', this.selectedDate, this.selectedTime);
				}
			},
			/**
			 * @return {Number} 1:已过期 , 2:即将过期 , 3:未过期
			 */
			checkRemainingMinute(time) {
				console.log('time: ', time);
				if (!time) return;
				const outTime = time.toString().split('-')[1];
				const fullYearDate = formatMinute(new Date(), '/');
				const now = new Date(fullYearDate);
				const dateTime = this.currentDate + ' ' + outTime;
				const check = new Date(dateTime);
				const difference = check - now;
				const minutes = difference / (1000 * 60);
				// minutes <= 0  : 已过期    --> 1
				// minutes <= 90 : 即将过期  --> 2
				// minutes >  0  : 未过期     --> 3
				return minutes <= 0 ? 1 : minutes <= 90 ? 2 : 3;
			}
		}
	};
</script>
<style scoped lang="scss">
	.date_pop {
		padding: 0;
		height: 750rpx;
		.popup_header {
			display: flex;
			align-items: center;
			justify-content: space-between;
			box-sizing: border-box;
			padding: 60rpx 40rpx;
			.pop_title {
				font-weight: bold;
				font-size: 32rpx;
				width: 90%;
			}
			.pop-close {
				width: 60rpx;
				height: 60rpx;
				background: url('~@/static/images/close.png');
				background-size: 22rpx;
				background-position: center;
				background-repeat: no-repeat;
			}
		}
		.date_con {
			font-size: 28rpx;
			position: relative;
			height: 600rpx;
		}
		.date_box {
			position: absolute;
			top: 0;
			left: 0;
			width: 40%;
			height: 100%;
			background: #f7f7f9;
			overflow-y: scroll;
			.date_item {
				padding: 0 40rpx;
				line-height: 100rpx;
			}
                        .date_active {
                            background: #fff;
                        }
		}
		.time_box {
			position: absolute;
			top: 0;
			right: 0;
			width: 60%;
			height: 100%;
			.disabled {
				color: #ccc;
				&::after {
					content: '已过期';
					margin-left: 130rpx;
				}
			}
			.outTime {
				color: #ccc;
				&::after {
					content: '即将过期';
					margin-left: 100rpx;
				}
			}
			.time_item {
				padding: 0 40rpx;
				line-height: 100rpx;
			}
		}
		.time_active {
			color: #ff5b29;
			position: relative;
			&::after {
				position: absolute;
				content: '✔';
				right: 15%;
				margin: auto;
			}
		}
	}
</style>

TODO:

  • 时间区域打开显示对应选中时间位置

  • 右侧时间列表改后台返回

感谢各位的阅读,以上就是“怎么用uni-popup实现菜鸟上门取件时间选择器”的内容了,经过本文的学习后,相信大家对怎么用uni-popup实现菜鸟上门取件时间选择器这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI