merchapp/java-mall-app-shop-admin/pages/my/storeBusinessStatus/oz-timePicker/oz-timePicker.vue
2025-06-30 15:28:55 +08:00

469 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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