<template>
<view>
<u-popup :show="timePopShow" mode="bottom" @close="close" @open="open" :closeOnClickOverlay="true">
<view class="popup-container">
<!-- 自定义时间内容 -->
<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-column>
<view v-for="(hours, index) in hours" :key="index" class="item">{{ hours }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(mins, index) in mins" :key="index" class="item">{{ mins }}</view>
</picker-view-column>
</picker-view>
</view>
<view @click="confirm2" class="confirm-btn2">取消</view>
<view @click="confirm" class="confirm-btn">确认</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
},
remorkStartTime: {
type: String,
default: ''
},
remorkEndTime: {
type: String,
default: ''
}
})
const currentTab = ref('tab2') //选项卡控制变量
const tabList = ref([{
name: '月份选择',
value: 'tab1'
},
{
name: '自定义时间',
value: 'tab2'
}
]) //选项卡内容
const dateHave = (item) => {
const date = moment(item, 'YYYY-MM-DD HH:mm');
return [
date.year() - 2020, // 年份减去2020
date.month(), // 月份从0开始
date.date() - 1, // 日期从0开始
date.hours() - 1, // 小时从0开始
date.minutes() // 分钟从0开始
];
}
const startNowDate = new Date().getTime()
const currentDate = new Date(); //当前时间
const currentYear = currentDate.getFullYear(); //当前年
const currentMonth = currentDate.getMonth(); //当前月
const currentDatehours =currentDate.getHours() - 1;
const currentDatemins = currentDate.getMinutes() + 1;
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 = months == (1 || 3 || 5 || 7 || 8 || 10 || 12) ? Array.from({
length: 31
}, (v, i) => i + 1) : Array.from({
length: 30
}, (v, i) => i + 1) // 生成1到31日
const hours = Array.from({
length: 18
}, (v, i) => i + 1) // 生成1到31日
const mins = Array.from({
length: 60
}, (v, i) => i) // 生成1到31日
const dateValue = ref([currentYear - 2020, currentMonth]) // 默认选中当前年份和月份
const currentSelect = ref('start') // 用于标记当前选择的是开始日期还是结束日期
// 初始化 startDateText
let startDate = moment(props.remorkStartTime, 'YYYY-MM-DD HH:mm');
if (!startDate.isValid() || currentDatehours.value >= 18) {
startDate = moment().add(1, 'days').startOf('day').hour(8).minute(0); // 默认设置为下一天的9:00
}
const startDateText = ref(startDate.format('YYYY-MM-DD HH:mm')) // 开始时间
// 初始化 endDateText
let endDate = moment(props.remorkEndTime, 'YYYY-MM-DD HH:mm');
if (!endDate.isValid() || currentDatehours.value >= 18) {
endDate = moment(startDate).add(1, 'hours'); // 默认设置为开始时间后1小时
}
const endDateText = ref(endDate.format('YYYY-MM-DD HH:mm')) // 截至时间
const dateArrayStart = props.remorkStartTime ? dateHave(props.remorkStartTime) : dateHave(startDateText.value);
const dateArrayEnd = props.remorkEndTime ? dateHave(props.remorkEndTime) : dateHave(endDateText.value);
const pickerValue = ref(dateArrayStart) // 当前选择器的值
const startDateValue = ref(dateArrayStart) // 默认选中当前日期
const endDateValue = ref(dateArrayEnd) // 默认选中截止日期
// 选项卡切换点击
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')} ${hours[value[3]].toString().padStart(2, '0')}:${mins[value[4]].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')} ${hours[value[3]].toString().padStart(2, '0')}:${mins[value[4]].toString().padStart(2, '0')}`;
}
}
}
// 开始/截止时间点击选择
const selectDate = (type) => {
currentSelect.value = type;
// 恢复上一次选中时间
if (type === 'start') {
pickerValue.value = props.remorkStartTime ? dateHave(props.remorkStartTime) : [...startDateValue.value];
} else if (type === 'end') {
pickerValue.value = props.remorkEndTime ? dateHave(props.remorkEndTime) : [...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 startHours = hours[startDateValue.value[3]];
const startMins = mins[startDateValue.value[4]];
const endYear = years[endDateValue.value[0]];
const endMonth = months[endDateValue.value[1]];
const endDay = days[endDateValue.value[2]];
const endHours = hours[endDateValue.value[3]];
const endMins = mins[endDateValue.value[4]];
if (compareDates(startYear, startMonth, startDay, startHours, startMins, endYear, endMonth, endDay, endHours, endMins)) {
emits('emitsCompareDates', compareDates(startYear, startMonth, startDay, startHours, startMins, endYear, endMonth, endDay, endHours, endMins))
}
}
}
//取消按钮
const confirm2 = () => {
close()
}
// 月份数据处理(返回每个月开始/截止时间)
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, startHours, startMins, endYear, endMonth, endDay, endHours, endMins) => {
// 比较两个日期
let startTime = `${startYear}-${startMonth}-${startDay} ${startHours.toString().padStart(2, '0')}:${startMins.toString().padStart(2, '0')}`;
let endTime = `${endYear}-${endMonth}-${endDay} ${endHours.toString().padStart(2, '0')}:${endMins.toString().padStart(2, '0')}`;
const startTime1 = JSON.parse(JSON.stringify(startTime))
const endTime1 = JSON.parse(JSON.stringify(endTime))
// 如果开始时间和结束时间相同,则提示用户
if (startYear === endYear && startMonth === endMonth && startDay === endDay && startHours === endHours && startMins === endMins) {
uni.showToast({
title: '开始时间不能与截止时间一致',
icon: 'none',
})
return
}
if (new Date(startTime1.replace(/-/g, '/')).getTime() < new Date().getTime()) {
uni.showToast({
title: '预约时间不能小于当前时间',
icon: 'none',
})
return
}
if (new Date(endTime1.replace(/-/g, '/')).getTime() < new Date().getTime()) {
uni.showToast({
title: '预约时间不能小于当前时间',
icon: 'none',
})
return
}
if (new Date(endTime1.replace(/-/g, '/')).getTime() < new Date(startTime1.replace(/-/g, '/')).getTime()) {
uni.showToast({
title: '开始时间不能大于截止时间',
icon: 'none',
})
return
}
// 比较日期并交换如果必要
return {
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;
.confirm-btn {
width: 200rpx;
height: 50rpx;
background-color: #0f43a8;
border-radius: 20rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
position: absolute;
bottom: 50rpx;
left: 60%;
}
.confirm-btn2 {
width: 200rpx;
height: 50rpx;
border: 1rpx solid #0f43a8;
border-radius: 20rpx;
color: #0f43a8;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
position: absolute;
bottom: 50rpx;
left: 10%;
}
.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;
position: relative;
.picker-view-column-label {
width: 100%;
position: absolute;
top: 10rpx;
left: 50%;
z-index: 2000;
}
}
}
}
.picker-view {
width: 100%;
height: 400rpx;
margin-top: 20rpx;
position: relative;
}
.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>
该组件在数据初始化以及选择做了限制,如果到当天晚上18点的时候,直接自动转到下一天。