uniapp时间选择器(月份选择、区间时间选择)

仿照支付宝时间查询,月份及区间时间组件;

vue setup语法;使用uniapp内置组件picker-view滚动选择器;弹窗使用uview-plus弹出层;搭配moment时间格式转换。

返回参数可根据项目要求调整;月份选择返回参数,当前选中月份第一天和最后一天时间字符串;

效果图如下:

完整代码如下:

<template>
	<view>
		<u-popup :show="timePopShow" mode="bottom" @close="close" @open="open" :closeOnClickOverlay="true">
			<view class="popup-container">
				<u-tabs :list="tabList" @click="clickTab" class="tabs" :scrollable="false"></u-tabs>

				<!-- 月份选择内容 -->
				<view class="content" v-if="currentTab === 'tab1'">
					<picker-view indicator-style="height: 100rpx;" :value="dateValue" @change="pickerChange"
						class="picker-view">
						<picker-view-column>
							<view class="item" v-for="(item,index) in years" :key="index">{{item}}年</view>
						</picker-view-column>
						<picker-view-column>
							<view class="item" v-for="(item,index) in months" :key="index">{{item}}月</view>
						</picker-view-column>
					</picker-view>
				</view>

				<!-- 自定义时间内容 -->
				<view class="date-range-container" v-if="currentTab === 'tab2'">
					<view class="date-input" :class="{ selected: currentSelect === 'start' }"
						@click="selectDate('start')">
						<text>{{ startDateText }}</text>
					</view>
					<text class="date-separator">至</text>
					<view class="date-input" :class="{ selected: currentSelect === 'end' }" @click="selectDate('end')">
						<text>{{ endDateText }}</text>
					</view>
				</view>

				<view class="content-range" v-if="currentTab === 'tab2'">
					<picker-view :value="pickerValue" indicator-style="height: 100rpx;" class="picker-view"
						@change="pickerChange">
						<picker-view-column>
							<view v-for="(year, index) in years" :key="index" class="item">{{ year }}</view>
						</picker-view-column>
						<picker-view-column>
							<view v-for="(month, index) in months" :key="index" class="item">{{ month }}</view>
						</picker-view-column>
						<picker-view-column>
							<view v-for="(day, index) in days" :key="index" class="item">{{ day }}</view>
						</picker-view-column>
					</picker-view>
				</view>
			</view>

			<!-- 确认按钮 -->
			<view class="header-buttons">
				<button @click="confirm" class="button button--confirm">确认</button>
			</view>
		</u-popup>
	</view>
</template>

<script setup>
	import moment from 'moment'
	import {
		ref,
		defineEmits,
		watch
	} from 'vue';

	// 注册组件暴露方法
	const emits = defineEmits(['emitsClose', 'emitsGetMonthDays', 'emitsCompareDates'])

	// 组件传参
	const props = defineProps({
		// 弹窗开启
		timePopShow: {
			type: Boolean,
			default: false
		},
	})

	const currentTab = ref('tab1') //选项卡控制变量
	const tabList = ref([{
			name: '月份选择',
			value: 'tab1'
		},
		{
			name: '自定义时间',
			value: 'tab2'
		}
	]) //选项卡内容
	const currentDate = new Date(); //当前时间
	const currentYear = currentDate.getFullYear(); //当前年
	const currentMonth = currentDate.getMonth(); //当前月
	const years = Array.from({
		length: 180
	}, (v, i) => 2020 + i) // 生成2020到2200年
	const months = Array.from({
		length: 12
	}, (v, i) => i + 1) // 生成1到12月
	const days = Array.from({
		length: 31
	}, (v, i) => i + 1) // 生成1到31日

	const dateValue = ref([currentYear - 2020, currentMonth]) // 默认选中当前年份和月份

	const currentSelect = ref('start') // 用于标记当前选择的是开始日期还是结束日期
	const startDateText = ref(moment().format('YYYY-MM-DD')) // 开始时间
	const endDateText = ref(moment().add(1, 'days').format('YYYY-MM-DD')) //截至时间
	const pickerValue = ref([currentYear - 2020, currentMonth, currentDate.getDate() - 1]) // 当前选择器的值

	const startDateValue = ref([currentYear - 2020, currentMonth, currentDate.getDate() - 1]) // 默认选中当前日期
	const endDateValue = ref([currentYear - 2020, currentMonth, currentDate.getDate()]) // 默认选中截止日期

	// 选项卡切换点击
	const clickTab = (item) => {
		currentTab.value = item.value;
	}

	// 组件开启
	const open = () => {
		currentTab.value = 'tab1';
	}

	// 组件关闭
	const close = () => {
		selectDate(currentSelect.value)
		emits('emitsClose', false)
	}

	// 时间选择监听
	const pickerChange = (e) => {
		const value = e.detail.value;
		if (currentTab.value === 'tab1') {
			// 当 currentTab 为 'tab1' 时,处理月份选择
			dateValue.value = value; // 更新 dateValue
		} else if (currentTab.value === 'tab2') {
			// 当 currentTab 为 'tab2' 时,处理自定义时间
			if (currentSelect.value === 'start') {
				startDateValue.value = value;
				pickerValue.value = value
				startDateText.value =
					`${years[value[0]]}-${months[value[1]].toString().padStart(2, '0')}-${days[value[2]].toString().padStart(2, '0')}`;
			} else if (currentSelect.value === 'end') {
				endDateValue.value = value;
				pickerValue.value = value
				endDateText.value =
					`${years[value[0]]}-${months[value[1]].toString().padStart(2, '0')}-${days[value[2]].toString().padStart(2, '0')}`;
			}
		}
	}

	// 开始/截止时间点击选择
	const selectDate = (type) => {
		currentSelect.value = type;

		// 恢复上一次选中时间
		if (type === 'start') {
			pickerValue.value = [...startDateValue.value];
		} else if (type === 'end') {
			pickerValue.value = [...endDateValue.value];
		}
	}

	// 确认按钮
	const confirm = () => {
		if (currentTab.value === 'tab1') {
			const year = years[dateValue.value[0]];
			const month = months[dateValue.value[1]];
			emits('emitsGetMonthDays', getMonthDays(year, month), year + '-' + month)

		} else if (currentTab.value === 'tab2') {
			const startYear = years[startDateValue.value[0]];
			const startMonth = months[startDateValue.value[1]];
			const startDay = days[startDateValue.value[2]];
			const endYear = years[endDateValue.value[0]];
			const endMonth = months[endDateValue.value[1]];
			const endDay = days[endDateValue.value[2]];

			if (compareDates(startYear, startMonth, startDay, endYear, endMonth, endDay)) {
				emits('emitsCompareDates', compareDates(startYear, startMonth, startDay, endYear, endMonth, endDay))
			}
		}
	}

	// 月份数据处理(返回每个月开始/截止时间)
	const getMonthDays = (year, month) => {
		const firstDayOfMonth = moment([year, month - 1, 1]);
		const lastDayOfMonth = firstDayOfMonth.clone().endOf('month');

		return {
			startTime: firstDayOfMonth.format('YYYY-MM-DD'),
			endTime: lastDayOfMonth.format('YYYY-MM-DD'),
		}
	}

	// 处理区间时间数据
	const compareDates = (startYear, startMonth, startDay, endYear, endMonth, endDay) => {
		// 比较两个日期
		let startTime = `${startYear}-${startMonth}-${startDay}`;
		let endTime = `${endYear}-${endMonth}-${endDay}`;

		// 如果开始时间和结束时间相同,则提示用户
		if (startYear === endYear && startMonth === endMonth && startDay === endDay) {
			return null
			uni.$u.toast('开始时间不能与截止时间一致');
		}

		// 比较日期并交换如果必要
		return swapIfGreater(startTime, endTime);
	}

	// 时间比对参数调换
	const swapIfGreater = (startTime, endTime) => {
		const startTimestamp = moment(startTime).valueOf();
		const endTimestamp = moment(endTime).valueOf();

		if (startTimestamp > endTimestamp) {
			return {
				startTime: endTime,
				endTime: startTime
			};
		}

		return {
			startTime,
			endTime
		};
	}
</script>

<style lang="less" scoped>
	.popup-container {
		position: relative;
		width: 100vw;
		height: 760rpx;
		padding: 40rpx 40rpx;
		box-sizing: border-box;
		background-color: #fff;

		.tabs {
			width: 100%;
			margin-bottom: 40rpx;
		}

		.content {
			margin-top: 40rpx;
			display: flex;
			justify-content: center;
			align-items: center;
			height: 560rpx;
		}

		.date-range-container {
			display: flex;
			justify-content: center;
			align-items: center;
			margin-bottom: 40rpx;
			margin-top: 40rpx;

			.date-separator {
				margin: 0 20rpx;
			}

			.date-input {
				border-bottom: 4rpx solid #ccc;
				padding: 10rpx 20rpx;
				margin: 0 20rpx;
				cursor: pointer;
				text-align: center;
				width: 50%;
				/* 固定宽度确保开始时间和结束时间一致 */
				height: 60rpx;
				/* 设置高度以确保一致 */
				line-height: 60rpx;
				/* 设置行高以居中内容 */
			}

			.date-input.selected {
				border-bottom: 4rpx solid #007bff;
				color: #007bff;
			}

			.content-range {
				margin-top: 40rpx;
				display: flex;
				justify-content: center;
				align-items: center;
				height: 400rpx;
			}
		}
	}

	.picker-view {
		width: 100%;
		height: 400rpx;
		margin-top: 20rpx;
	}

	.header-buttons {
		display: flex;
		justify-content: space-between;
		padding: 0 40rpx;
		box-sizing: border-box;
		margin-bottom: 20rpx;

		.button--confirm {
			text-align: center;
			background-color: #007bff;
			color: #fff;
			width: 100%;
			font-size: 32rpx;
			font-weight: 400;
			line-height: 88rpx; /* 150% */
		}
	}

	.item {
		line-height: 100rpx;
		text-align: center;
	}
</style>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值