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>
|