430 lines
12 KiB
Vue
430 lines
12 KiB
Vue
<template>
|
||
<uni-popup ref="popup" type="bottom" border-radius="20rpx 20rpx 0 0" background-color="#fff" style="z-index: 20000">
|
||
<!-- 时间选择滚轮 -->
|
||
<view class="region-body">
|
||
<image class="region-body__close" @click="close"
|
||
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/693c54b5-f5a5-4aa0-8d7b-6123250f9839">
|
||
</image>
|
||
<view class="region-body__header"> 请选择时间段 </view>
|
||
<picker-view v-model="pickerValue" @change="handleTimeChange" class="picker-scroll"
|
||
:indicator-style="indicatorStyle" immediate-change>
|
||
<picker-view-column v-if="pickerShowList[0]">
|
||
<view class="picker-item" v-for="(year, index) in years" :key="index">
|
||
{{ year }}年
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column v-if="pickerShowList[1]">
|
||
<view class="picker-item" v-for="(month, index) in months" :key="index">
|
||
{{ month }}月
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column v-if="pickerShowList[2]">
|
||
<view class="picker-item" v-for="(date, index) in dates[pickerValue[0]][pickerValue[1]]"
|
||
:key="index">
|
||
{{ date }}日
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column v-if="pickerShowList[3]">
|
||
<view class="picker-item" v-for="(hour, index) in hours" :key="index">
|
||
{{ hour }}时
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column v-if="pickerShowList[4]">
|
||
<view class="picker-item" v-for="(minute, index) in minutes" :key="index">
|
||
{{ minute }}分
|
||
</view>
|
||
</picker-view-column>
|
||
</picker-view>
|
||
<view class="region-body__btn" :class="{'noSure':timeCompareComputed}" @tap="confirmTime">确认
|
||
{{ `(${formatTime})` }}
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
sureTime: null,
|
||
default_times: "",
|
||
business_time: "",
|
||
indicatorStyle: "height: 68rpx;",
|
||
pickerValue: [0, 0, 0, 0, 0],
|
||
pickerShowList: [1, 1, 1, 1, 1],
|
||
years: [],
|
||
months: Array.from({
|
||
length: 12
|
||
}, (_, i) => i + 1),
|
||
dates: [],
|
||
hours: Array.from({
|
||
length: 24
|
||
}, (_, i) => i),
|
||
minutes: Array.from({
|
||
length: 60
|
||
}, (_, i) => i),
|
||
};
|
||
},
|
||
props: {
|
||
value: String,
|
||
timeIndex: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
timeList: {
|
||
type: Array,
|
||
},
|
||
type: {
|
||
type: String,
|
||
default: 'datetimerange', //datetimerange:年月日时分;daterange:年月日;yearMonth年月;
|
||
},
|
||
startYear:{
|
||
type: Number,
|
||
default: 1980,
|
||
},
|
||
endDistanceYear:{
|
||
type: Number,
|
||
default: 20,//结束时间,与当前时间点相距的时间
|
||
},
|
||
},
|
||
created() {
|
||
//初始化滑块显示行数
|
||
if (this.type == 'datetimerange') {
|
||
this.pickerShowList = [1, 1, 1, 1, 1]
|
||
this.pickerValue = [0, 0, 0, 0, 0]
|
||
} else if (this.type == 'daterange') {
|
||
this.pickerShowList = [1, 1, 1]
|
||
this.pickerValue = [0, 0, 0]
|
||
} else if (this.type == 'yearMonth') {
|
||
this.pickerShowList = [1, 1]
|
||
this.pickerValue = [0, 0]
|
||
}
|
||
//初始化时间数组
|
||
this.setTimesList();
|
||
},
|
||
computed: {
|
||
//获取当月最大天数
|
||
formatTime() {
|
||
if (this.type == 'datetimerange') {
|
||
// 将“/”替换为“-”,并保持时间补零逻辑
|
||
this.sureTime = `${this.years[this.pickerValue[0]]}-${this.months[
|
||
this.pickerValue[1]
|
||
]
|
||
.toString()
|
||
.padStart(2, "0")}-${this.dates[this.pickerValue[0]][
|
||
this.pickerValue[1]
|
||
][this.pickerValue[2]]
|
||
?.toString()
|
||
.padStart(2, "0")} ${this.hours[this.pickerValue[3]]
|
||
.toString()
|
||
.padStart(2, "0")}:${this.minutes[this.pickerValue[4]]
|
||
.toString()
|
||
.padStart(2, "0")}:00`;
|
||
} else if (this.type == 'daterange') {
|
||
this.sureTime = `${this.years[this.pickerValue[0]]}-${this.months[
|
||
this.pickerValue[1]
|
||
]
|
||
.toString()
|
||
.padStart(2, "0")}-${this.dates[this.pickerValue[0]][
|
||
this.pickerValue[1]
|
||
][this.pickerValue[2]]
|
||
?.toString()
|
||
.padStart(2, "0")}`;
|
||
} else if (this.type == 'yearMonth') {
|
||
this.sureTime = `${this.years[this.pickerValue[0]]}-${this.months[
|
||
this.pickerValue[1]
|
||
]
|
||
.toString()
|
||
.padStart(2, "0")}`;
|
||
}
|
||
return this.sureTime;
|
||
},
|
||
timeCompareComputed() {
|
||
if ((this.timeIndex == 0 && this.timeList[1]) || (this.timeIndex == 1 && this.timeList[0]) && this
|
||
.formatTime) {
|
||
if (this.timeIndex == 0) {
|
||
// 开始时间
|
||
if (!this.timeCompare(this.formatTime, this.timeList[1])) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
} else {
|
||
// 结束时间
|
||
if (!this.timeCompare(this.timeList[0], this.formatTime)) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
},
|
||
watch: {
|
||
value: {
|
||
handler(newVal) {
|
||
if (newVal) {
|
||
this.setPickerValue();
|
||
}
|
||
},
|
||
immediate: true,
|
||
},
|
||
},
|
||
methods: {
|
||
setTimesList() {
|
||
// 初始化时间数组
|
||
const currentYear = new Date().getFullYear();
|
||
const startYear = this.startYear; // 开始年份
|
||
const endYear = currentYear + this.endDistanceYear; // 结束年份
|
||
const yearCount = endYear - startYear + 1; // 总年数
|
||
|
||
for (let i = 0; i < yearCount; i++) {
|
||
const year = startYear + i;
|
||
this.years.push(year);
|
||
this.dates[i] = [];
|
||
for (let n = 1; n <= 12; n++) {
|
||
let num = this.getMonthDay(year, n); // 使用当前循环的年份计算天数
|
||
this.dates[i][n - 1] = Array.from({
|
||
length: num
|
||
},
|
||
(_, idx) => idx + 1
|
||
);
|
||
}
|
||
}
|
||
|
||
// 无value时设置默认当前时间
|
||
if (!this.value) {
|
||
this.setDefaultPickerValue();
|
||
}
|
||
},
|
||
|
||
// 新增:设置默认当前时间的索引
|
||
setDefaultPickerValue() {
|
||
const now = new Date();
|
||
const currentYear = now.getFullYear();
|
||
const currentMonth = now.getMonth() + 1; // 转换为1-12
|
||
const currentDate = now.getDate();
|
||
const currentHour = now.getHours();
|
||
const currentMinute = now.getMinutes();
|
||
|
||
// 计算各维度索引
|
||
const yearIndex = this.years.findIndex(y => y === currentYear);
|
||
const monthIndex = this.months.findIndex(m => m === currentMonth);
|
||
const dayIndex = this.dates[yearIndex]?.[monthIndex]?.findIndex(d => d === currentDate) ?? 0;
|
||
const hourIndex = this.hours.findIndex(h => h === currentHour);
|
||
const minuteIndex = this.minutes.findIndex(m => m === currentMinute);
|
||
|
||
this.pickerValue = [yearIndex, monthIndex, dayIndex, hourIndex, minuteIndex];
|
||
},
|
||
|
||
setPickerValue() {
|
||
if (this.value) {
|
||
// 统一将输入的日期字符串中的“-”替换为“/”,兼容解析
|
||
const normalizedValue = this.value.replace(/-/g, '/');
|
||
|
||
if (this.type == 'datetimerange') {
|
||
// 解析时间字符串 '2024/05/27 11:31:00'
|
||
const [datePart, timePart] = normalizedValue.split(" ");
|
||
const [year, month, day] = datePart.split("/").map(Number);
|
||
// 时间部分用 : 分隔,取前两位(小时和分钟),忽略秒数
|
||
const [hour, minute] = timePart.split(":").map(Number);
|
||
|
||
// 计算各部分在数组中的索引
|
||
const yearIndex = this.years.findIndex((y) => y === year);
|
||
const monthIndex = this.months.findIndex((m) => m === month);
|
||
const dayIndex = this.dates[yearIndex]?.[monthIndex]?.findIndex((d) => d === day) ?? 0;
|
||
const hourIndex = this.hours.findIndex((h) => h === hour);
|
||
const minuteIndex = this.minutes.findIndex((m) => m === minute);
|
||
|
||
// 设置pickerValue
|
||
this.pickerValue = [
|
||
yearIndex,
|
||
monthIndex,
|
||
dayIndex,
|
||
hourIndex,
|
||
minuteIndex,
|
||
];
|
||
} else if (this.type == 'daterange') {
|
||
// 解析时间字符串 '2024/05/27'
|
||
const [year, month, day] = normalizedValue.split("/").map(Number);
|
||
|
||
// 计算各部分在数组中的索引
|
||
const yearIndex = this.years.findIndex((y) => y === year);
|
||
const monthIndex = this.months.findIndex((m) => m === month);
|
||
const dayIndex = this.dates[yearIndex]?.[monthIndex]?.findIndex((d) => d === day) ?? 0;
|
||
|
||
// 设置pickerValue
|
||
this.pickerValue = [
|
||
yearIndex,
|
||
monthIndex,
|
||
dayIndex,
|
||
];
|
||
} else if (this.type == 'yearMonth') {
|
||
// 解析时间字符串 '2024/05'
|
||
const [year, month] = normalizedValue.split("/").map(Number);
|
||
|
||
// 计算各部分在数组中的索引
|
||
const yearIndex = this.years.findIndex((y) => y === year);
|
||
const monthIndex = this.months.findIndex((m) => m === month);
|
||
|
||
// 设置pickerValue
|
||
this.pickerValue = [
|
||
yearIndex,
|
||
monthIndex
|
||
];
|
||
}
|
||
|
||
}
|
||
},
|
||
getMonthDay(year, month) {
|
||
let days = new Date(year, month, 0).getDate();
|
||
return days;
|
||
},
|
||
// 处理时间变化
|
||
handleTimeChange(e) {
|
||
this.pickerValue = e.detail.value;
|
||
},
|
||
openTimePopup() {
|
||
this.$refs.popup.open();
|
||
},
|
||
close() {
|
||
this.$refs.popup.close();
|
||
},
|
||
// 确认选择
|
||
confirmTime() {
|
||
if ((this.timeIndex == 0 && this.timeList[1]) || (this.timeIndex == 1 && this.timeList[0])) {
|
||
if (this.timeIndex == 0) {
|
||
// 开始时间
|
||
if (!this.timeCompare(this.formatTime, this.timeList[1])) {
|
||
uni.showToast({
|
||
title: "开始时间不能大于结束时间",
|
||
icon: "none",
|
||
});
|
||
return;
|
||
}
|
||
} else {
|
||
// 结束时间
|
||
if (!this.timeCompare(this.timeList[0], this.formatTime)) {
|
||
uni.showToast({
|
||
title: "结束时间不能小于开始时间",
|
||
icon: "none",
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
this.$emit("input", this.sureTime);
|
||
this.$emit("confirmTime", this.sureTime);
|
||
this.$refs.popup.close();
|
||
},
|
||
//比较开始时间结束时间大小
|
||
timeCompare(dateStr1, dateStr2) {
|
||
if (this.timeIndex >= 0) {
|
||
// 处理日期格式:将“-”替换为“/”,纯日期补充为当天0点
|
||
const formatDate = (str) => {
|
||
// 先将所有“-”替换为“/”,兼容iOS解析
|
||
const normalized = str.replace(/-/g, '/');
|
||
// 判断是否包含时间部分(是否有空格)
|
||
if (!normalized.includes(' ')) {
|
||
return normalized + ' 00:00:00'; // 纯日期补全为当天0点
|
||
}
|
||
return normalized;
|
||
};
|
||
|
||
try {
|
||
// 格式化并转换为时间戳
|
||
const time1 = new Date(formatDate(dateStr1)).getTime();
|
||
const time2 = new Date(formatDate(dateStr2)).getTime();
|
||
// 时间戳比较(避免字符串比较的潜在问题)
|
||
return time2 > time1;
|
||
} catch (error) {
|
||
console.error('日期格式错误:', error);
|
||
return false; // 格式错误时返回默认值
|
||
}
|
||
} else {
|
||
return true;
|
||
}
|
||
}
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
/* 样式部分不变 */
|
||
.region-body {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
border-radius: 20rpx 20rpx 0 0;
|
||
|
||
&__close {
|
||
width: 22rpx;
|
||
height: 22rpx;
|
||
position: absolute;
|
||
top: 44rpx;
|
||
right: 30rpx;
|
||
}
|
||
|
||
&__header {
|
||
padding: 40rpx 0;
|
||
font-weight: 500;
|
||
font-size: 32rpx;
|
||
color: #333333;
|
||
line-height: 45rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
&__btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 690rpx;
|
||
height: 80rpx;
|
||
background: #E8101E;
|
||
border-radius: 38rpx;
|
||
font-weight: 400;
|
||
font-size: 30rpx;
|
||
color: #ffffff;
|
||
line-height: 39rpx;
|
||
text-align: right;
|
||
font-style: normal;
|
||
margin: 0 30rpx;
|
||
}
|
||
|
||
.noSure {
|
||
background-color: #ccc;
|
||
}
|
||
}
|
||
|
||
.region-scroll {
|
||
height: 100%;
|
||
}
|
||
|
||
.picker-scroll {
|
||
height: 400rpx;
|
||
}
|
||
|
||
.picker-item {
|
||
height: 68rpx;
|
||
line-height: 68rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.region-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 24rpx 0;
|
||
border-bottom: 1rpx solid #eeeeee;
|
||
}
|
||
|
||
.region-item.checkbox-item {
|
||
padding: 20rpx 0;
|
||
width: 100%;
|
||
justify-content: flex-start !important;
|
||
align-items: center !important;
|
||
}
|
||
</style> |