vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > uni popup取件时间选择器

uni-popup手写菜鸟上门取件时间选择器

作者:学不会就躺平噶

这篇文章主要为大家介绍了uni-popup手撸了一个菜鸟上门取件时间选择器,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

 近期做的项目有个需求是做一个类似菜鸟的取件时间选择器,去找了很久没找到合适的,没办法只能自己收撸,经过好几个小版本修改之后也算是定型了,这里总结一篇文档备忘,把源码贴出来后续方便后续copy

技术

uniapp + vue2 + uni-popup

兼容

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

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

菜鸟上门时间选择器

需求分析:

1、弹窗从底部弹出

2、左侧日期选择器

3、右侧时间选择器

代码实现:

1.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.日期+时间选择器

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

日期选择器

时间选择器

基础功能

可选功能

先看效果

🎉🎃核心逻辑:

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]
	};
},

最终数据格式如图:

2、判断时间有没有过期

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

/**
* @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取件时间选择器的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文