mrr.sj.front/components/time-picker/time-date-picker.vue

430 lines
12 KiB
Vue
Raw Normal View History

2026-03-24 11:45:13 +08:00
<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;
2026-06-02 11:39:23 +08:00
background: #FF4767;
2026-03-24 11:45:13 +08:00
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>