469 lines
11 KiB
Vue
469 lines
11 KiB
Vue
<template>
|
||
<!-- 时间选择器弹窗 -->
|
||
<uni-popup ref="popup" type="bottom" :safe-area="false">
|
||
<view class="custom-picker">
|
||
<view class="custom-picker__header">
|
||
<view class="cancel" :style="{ color: canceColor }" >
|
||
<!-- {{ cancelText }} -->
|
||
</view>
|
||
<view class="title">{{ title }}</view>
|
||
<view class="confirm" :style="{ color: confirmColor }" @tap="onCancel">
|
||
<u-icon name="close"></u-icon>
|
||
</view>
|
||
</view>
|
||
<view class="time-tips">
|
||
为保证有骑手接单,请在配送站点营业时间00:00-24:00内选择
|
||
</view>
|
||
<picker-view
|
||
:indicator-class="indicatorClass"
|
||
:indicator-style="indicatorStyle"
|
||
class="picker-view"
|
||
:value="pickerValue"
|
||
@change="bindChange"
|
||
@pickstart="pickstart"
|
||
@pickend="pickend"
|
||
>
|
||
<picker-view-column>
|
||
<view
|
||
:class="['picker-view__item']"
|
||
v-for="(item, index) in rangeList[0]"
|
||
:key="index"
|
||
>
|
||
{{ item }}
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column>
|
||
<view
|
||
class="picker-view__item"
|
||
v-for="(item, index) in rangeList[1]"
|
||
:key="index"
|
||
>
|
||
{{ item }}
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column>
|
||
<view class="picker-view__item">{{ segmentation }}</view>
|
||
</picker-view-column>
|
||
|
||
<picker-view-column>
|
||
<view
|
||
class="picker-view__item"
|
||
v-for="(item, index) in rangeList[2]"
|
||
:key="index"
|
||
>
|
||
{{ item }}
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column>
|
||
<view
|
||
class="picker-view__item"
|
||
v-for="(item, index) in rangeList[3]"
|
||
:key="index"
|
||
>
|
||
{{ item }}
|
||
</view>
|
||
</picker-view-column>
|
||
</picker-view>
|
||
</view>
|
||
<view class="bottom-content">
|
||
<view class="bottom-time">
|
||
<view class="">
|
||
<text>{{ startTime }}</text>
|
||
<text style="padding: 0 6rpx;">至</text>
|
||
<text>{{ endTime }}</text>
|
||
</view>
|
||
<view class="">{{ totalTime }}</view>
|
||
</view>
|
||
<view class="bottom-btn">
|
||
<u-button
|
||
class="btn-time"
|
||
:hairline="true"
|
||
:plain="true"
|
||
shape="circle"
|
||
@tap="onConfirm"
|
||
>
|
||
确认
|
||
</u-button>
|
||
</view>
|
||
|
||
</view>
|
||
</uni-popup>
|
||
</template>
|
||
|
||
<script>
|
||
// 滚动数据
|
||
let range = [[], [], [], []];
|
||
for (let i = 0; i <= 24; i++) {
|
||
range[0].push(i >= 10 ? String(i) : `0${i}`);
|
||
range[2].push(i >= 10 ? String(i) : `0${i}`);
|
||
}
|
||
for (let i = 0; i < 60; i++) {
|
||
range[1].push(i >= 10 ? String(i) : `0${i}`);
|
||
range[3].push(i >= 10 ? String(i) : `0${i}`);
|
||
}
|
||
|
||
export default {
|
||
name: "TimePickerPopup",
|
||
props: {
|
||
// 当前选中的值
|
||
value: {
|
||
type: Array,
|
||
default: () => ["00", "00", "00", "00"],
|
||
},
|
||
// 标题
|
||
title: {
|
||
type: String,
|
||
default: "时间",
|
||
},
|
||
// 取消按钮文字
|
||
cancelText: {
|
||
type: String,
|
||
default: "取消",
|
||
},
|
||
// 取消按钮颜色
|
||
canceColor: {
|
||
type: String,
|
||
default: "#666666",
|
||
},
|
||
// 确定按钮文字
|
||
confirmText: {
|
||
type: String,
|
||
default: "确定",
|
||
},
|
||
// 确定按钮颜色
|
||
confirmColor: {
|
||
type: String,
|
||
default: "#2bb781",
|
||
},
|
||
// 分割符
|
||
segmentation: {
|
||
type: String,
|
||
default: "-",
|
||
},
|
||
// 设置选择器中间选中框的类名 注意页面或组件的style中写了scoped时,需要在类名前写/deep/
|
||
indicatorClass: {
|
||
type: String,
|
||
default: "picker-view__indicator",
|
||
},
|
||
// 设置选择器中间选中框的样式
|
||
indicatorStyle: {
|
||
type: String,
|
||
default: "",
|
||
},
|
||
maxStartHour: {
|
||
type: Number,
|
||
default: 23, // 默认23点
|
||
},
|
||
maxStartMinute: {
|
||
type: Number,
|
||
default: 30, // 默认59分钟
|
||
},
|
||
maxEndHour: {
|
||
type: Number,
|
||
default: 24, // 默认23点
|
||
},
|
||
maxEndMinute: {
|
||
type: Number,
|
||
default: 59, // 默认59分钟
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
rangeList: [],
|
||
pickerValue: [0, 0, 0, 0],
|
||
isScoll: false, // 是否正在滚动
|
||
startTime:"",
|
||
endTime:"",
|
||
totalTime:""
|
||
};
|
||
},
|
||
created() {
|
||
this.generateRangeLists();
|
||
},
|
||
watch: {
|
||
// 监听最大时间限制变化
|
||
maxStartHour() {
|
||
this.generateRangeLists();
|
||
},
|
||
maxStartMinute() {
|
||
this.generateRangeLists();
|
||
},
|
||
maxEndHour() {
|
||
this.generateRangeLists();
|
||
},
|
||
maxEndMinute() {
|
||
this.generateRangeLists();
|
||
},
|
||
},
|
||
methods: {
|
||
generateRangeLists() {
|
||
const ranges = [[], [], [], []];
|
||
|
||
// 生成开始小时范围 (0 - maxStartHour)
|
||
for (let i = 0; i <= this.maxStartHour; i++) {
|
||
ranges[0].push(i >= 10 ? String(i) : `0${i}`);
|
||
}
|
||
|
||
// 生成开始分钟范围 (0 - maxStartMinute)
|
||
for (let i = 0; i <= this.maxStartMinute; i++) {
|
||
ranges[1].push(i >= 10 ? String(i) : `0${i}`);
|
||
}
|
||
|
||
// 生成结束小时范围 (0 - maxEndHour)
|
||
for (let i = 0; i <= this.maxEndHour; i++) {
|
||
ranges[2].push(i >= 10 ? String(i) : `0${i}`);
|
||
}
|
||
|
||
// 生成结束分钟范围 (0 - maxEndMinute)
|
||
// 如果结束小时是24,分钟只能是00
|
||
if (
|
||
this.pickerValue &&
|
||
this.pickerValue[2] === this.rangeList[2]?.length - 1
|
||
) {
|
||
ranges[3] = ["00"];
|
||
} else {
|
||
for (let i = 0; i <= this.maxEndMinute; i++) {
|
||
ranges[3].push(i >= 10 ? String(i) : `0${i}`);
|
||
}
|
||
}
|
||
this.rangeList = ranges;
|
||
},
|
||
/**
|
||
* 开启弹窗
|
||
*/
|
||
open() {
|
||
this.generateRangeLists();
|
||
|
||
if (Array.isArray(this.value) && this.value.length) {
|
||
this.pickerValue = this.value.map((item, index) => {
|
||
const i = this.rangeList[index].findIndex(
|
||
(child) => Number(child) == Number(this.value[index])
|
||
);
|
||
return i > -1 ? i : 0;
|
||
});
|
||
|
||
// If initial end hour is 24, ensure minute is 00
|
||
if (this.pickerValue[2] === this.rangeList[2]?.length - 1) {
|
||
this.pickerValue[3] = 0;
|
||
this.generateRangeLists();
|
||
}
|
||
} else {
|
||
this.pickerValue = [0, 0, 0, 0];
|
||
}
|
||
this.$refs.popup.open();
|
||
},
|
||
/**
|
||
* 关闭弹窗
|
||
*/
|
||
close() {
|
||
this.$refs.popup.close();
|
||
// 重置选中数据
|
||
this.pickerValue = [0, 0, 0, 0];
|
||
},
|
||
/**
|
||
* 点击确定
|
||
*/
|
||
onConfirm() {
|
||
if (!this.isScoll) {
|
||
let data = this.value || ["00", "00", "00", "00"];
|
||
if (this.pickerValue && this.pickerValue.length) {
|
||
data = this.pickerValue.map((item, index) =>
|
||
String(this.rangeList[index][item])
|
||
);
|
||
}
|
||
this.$emit("confirm", data);
|
||
this.close();
|
||
}
|
||
},
|
||
/**
|
||
* 点击取消
|
||
*/
|
||
onCancel() {
|
||
this.close();
|
||
},
|
||
/**
|
||
* 滚动开始
|
||
*/
|
||
pickstart() {
|
||
this.isScoll = true;
|
||
},
|
||
/**
|
||
* 滚动结束
|
||
*/
|
||
pickend() {
|
||
this.isScoll = false;
|
||
},
|
||
/**
|
||
* 选择器改变
|
||
* @param {Object} e
|
||
*/
|
||
bindChange(e) {
|
||
|
||
const newValue = [...e.detail.value];
|
||
|
||
// 更新 pickerValue
|
||
this.pickerValue = newValue;
|
||
|
||
// 计算 startTime (前两列: 小时和分钟)
|
||
const startHour = this.rangeList[0][newValue[0]];
|
||
const startMinute = this.rangeList[1][newValue[1]];
|
||
const startTime = `${startHour}:${startMinute}`;
|
||
|
||
// 计算 endTime (后两列: 小时和分钟)
|
||
const endHour = this.rangeList[3][newValue[3]];
|
||
const endMinute = this.rangeList[4][newValue[4]];
|
||
const endTime = `${endHour}:${endMinute}`;
|
||
|
||
// 计算总时长 totalTime
|
||
const startDate = new Date(2000, 0, 1, parseInt(startHour), parseInt(startMinute));
|
||
const endDate = new Date(2000, 0, 1, parseInt(endHour), parseInt(endMinute));
|
||
|
||
// 处理跨天情况(如23:00到01:00)
|
||
if (endDate < startDate) {
|
||
endDate.setDate(endDate.getDate() + 1);
|
||
}
|
||
|
||
const diffMs = endDate - startDate;
|
||
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||
const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
||
|
||
const totalTime = `共${diffHours}小时${diffMinutes}分`;
|
||
|
||
// 更新到data中以便在模板显示
|
||
this.startTime = startTime;
|
||
this.endTime = endTime;
|
||
this.totalTime = totalTime;
|
||
|
||
// 检查是否需要限制分钟选择(如选择24:00时分钟只能是00)
|
||
if (newValue[3] === this.rangeList[3]?.length - 1) { // 假设24是最后一小时
|
||
newValue[4] = 0; // 设置为00分钟
|
||
this.pickerValue = newValue;
|
||
this.generateRangeLists();
|
||
}
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import "@/styles/variables.scss";
|
||
.custom-picker {
|
||
width: 100%;
|
||
height: 620rpx;
|
||
background-color: #fff;
|
||
padding-bottom: 0;
|
||
padding-bottom: constant(safe-area-inset-bottom);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
|
||
&__header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 30rpx 40rpx;
|
||
|
||
.cancel {
|
||
color: #666;
|
||
}
|
||
|
||
.title {
|
||
font-weight: 500;
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.confirm {
|
||
color: #2bb781;
|
||
}
|
||
}
|
||
}
|
||
|
||
.picker-view {
|
||
width: 100%;
|
||
height: 100%;
|
||
margin-top: 20rpx;
|
||
|
||
&__item {
|
||
line-height: 100rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
::v-deep &__indicator {
|
||
height: 100rpx;
|
||
color: #2bb781;
|
||
}
|
||
|
||
&__segmentation {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.time-tips {
|
||
padding: 24rpx;
|
||
font-size: 26rpx;
|
||
background: #fadbd8;
|
||
}
|
||
|
||
.acitve {
|
||
&::after,
|
||
&::before {
|
||
position: initial;
|
||
}
|
||
position: relative;
|
||
z-index: 1;
|
||
background: #efefef;
|
||
margin: 0 16rpx;
|
||
border-radius: 24rpx;
|
||
height: 80rpx;
|
||
}
|
||
|
||
::v-deep .uni-picker-view-content {
|
||
z-index: 2;
|
||
}
|
||
|
||
::v-deep .uni-picker-view-indicator {
|
||
width: 82%;
|
||
}
|
||
|
||
::v-deep .picker-view__item {
|
||
margin-left: 20rpx;
|
||
width: 80%;
|
||
font-size: 36rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
}
|
||
|
||
::v-deep .picker-view{
|
||
height: 60%;
|
||
}
|
||
|
||
.bottom-content{
|
||
background: #fff;
|
||
box-shadow: 0 0 4rpx 4rpx rgba(0, 0, 0, 0.1);
|
||
|
||
.bottom-time{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 20rpx 28rpx;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.bottom-btn{
|
||
padding: 28rpx 40rpx 80rpx;
|
||
|
||
.btn-time{
|
||
background: $base-color;
|
||
color: #fff;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
</style>
|