#項目使用到的
插件鏈接:hbxw-timepicker预约时间选择组件 - DCloud 插件市场
適配環境 :1,微信小程序。2,支付寶小程序。3,h5。
我使用的是uniapp 編譯微信小程序基於vue3開發
先看一下實現出來的效果:
1,講解 :++左邊是明天 後天 這些可以自定義設置的,右邊的時間間隔 也是可以自定義的 ,一小時間隔 三小時間隔 都可以 然後早上開始時間和結束時間都是可以自定義的++ ,
**2,**我自己寫的是三小時間隔 我項目中有多個配送方式 所以我設置的有三小時間隔還有一個小時間隔的,

一,首先html
html
<hbxw-timepicker v-model:isShow="showTimePicker2" :dayRange="2" :anotherNames="['明天', '后天']"
:minHour="9" :maxHour="18" @change="pickerChange"></hbxw-timepicker>
二,javascript部分
javascript
const showTimePicker3 = ref(false);
// 存储所有时间选择器显示状态的数组
const showTimePickers = [showTimePicker0, showTimePicker1, showTimePicker2, showTimePicker3];
// 显示时间选择器的方法
const pickerTime = (index) => {
showTimePickers[index].value = true;
};
let date_time = ref('請選擇')
let date_timelog = ref('請選擇')
const sahrse = ref(true)
// 时间选择器值改变的回调方法
const pickerChange = (result) => {
console.log('pickerChange 接收到的结果:', result);
// 检查是否有有效的结果
if (!result || !result.result) {
console.warn('未选择有效时间,结果为 null 或 undefined');
// 重置为默认值
date_time.value = '請選擇';
date_timelog.value = '請選擇';
sahrse.value = true; // 恢复默认状态
return;
}
// 提取结果中的数据
const {
result: {
year,
month,
day,
hourStart,
hourEnd,
anotherName = '今天', // 只在 anotherName 为 undefined 时生效
hoursStr
} = {} // 防止 result 为 undefined 时的错误
} = result || {}; // 防止 result 为 undefined 时的错误
// 检查关键字段是否存在
if (!year || !month || !day || !hourStart || !hourEnd) {
console.warn('选择的时间信息不完整:', result);
date_time.value = '時間格式錯誤';
date_timelog.value = '時間格式錯誤';
return;
}
// 更新时间显示
date_time.value = `${year}-${month}-${day}-${hourStart}/${hourEnd}`;
date_timelog.value = `${anotherName||'今天'}${hoursStr}`;
sahrse.value = false; // 禁用选择状态
};
三,循環時間
javascript
<template>
<view class="hbxw-timepicker" :class="{'hbxw-timepicker-ani': isAni}"
:style="{'z-index': zIndex, '--opacity': maskOpacity}" v-if="isShow">
<view class="hbxw-timepicker-mask" @click="close"></view>
<view class="hbxw-timepicker-main" :class="{'hbxw-timepicker-main-ani': isAni}">
<!-- 标题 -->
<view class="hbxw-timepicker-title">
<slot name="title" :title="title" :subTitle="subTitle">
<view class="hbxw-timepicker-title-main">{{title}}</view>
<view class="hbxw-timepicker-title-subtitle">{{subTitle}}</view>
</slot>
<image src="../../static/guanbi.png" class="hbxw-timepicker-close" @click="close"></image>
</view>
<!-- 主内容,日期和时间选择区 -->
<view class="hbxw-timepicker-content">
<view class="hbxw-timepicker-day">
<view class="hbxw-timepicker-day-item"
:class="{'hbxw-timepicker-day-item-active': dayActiveIndex === index}"
v-for="(day, index) in days" :key="index" @click="toggleDay(index)">
{{anotherNames[index] ? anotherNames[index].replace('month', day.month).replace('day', day.day) : '2025-'+day.value}}
</view>
</view>
<view class="hbxw-timepicker-time">
<view v-if="hours.length > 0" class="hbxw-timepicker-time-item" v-for="(hour, index) in hours"
:key="index" :class="{'hbxw-timepicker-time-item-active': hourActiveIndex === index}"
@click="toggleHour(index)">
<text class="hbxw-timepicker-time-val">{{hour.hoursStr}}</text>
<view class="hbxw-timepicker-time-icon">
<image src="../../static/gougou.png" class="hbxw-timepicker-time-img"></image>
</view>
</view>
<view class="hbxw-timepicker-notime" v-else>
<text class="hbxw-timerpicker-nodata">{{noDateTips}}</text>
</view>
</view>
</view>
<!-- 底部按钮 -->
<slot name="btn" :result="result" v-if="isBtn">
<view class="hbxw-timerpicker-sure" @click="sure" v-if="btnStr">{{btnStr}}</view>
</slot>
</view>
</view>
</template>
<script>
export default {
props: {
isShow: {
type: Boolean,
default: false
},
businessType: {
type: String,
default: ''
},
created() {
// 组件初始化时读取缓存,如果父组件没传值,则用缓存值
if (!this.businessType) {
const storedType = uni.getStorageSync('type_title');
// 确保缓存值是合法的(散货/板货/包车)
const validTypes = ['散貨', '板貨', '包車'];
if (validTypes.includes(storedType)) {
this.businessType = storedType; // 这里需要注意:props 不建议直接修改,更好的方式是用 data 接收
}
}
},
title: {
type: String,
default: '期望上门时间'
},
subTitle: {
type: String,
// default: '快递1小时内上门取件,请自行做好数据备份'
},
// 是否显示1小时内
isFast: {
type: Boolean,
default: true
},
// 日期别名,用于一些特殊场景,如需要显示今天明天后天...
anotherNames: {
type: Array,
default() {
return ['明天', '后天'];// 这里只保留明天和后天
}
},
dayRange: {
type: Number,
default: 3
},
minHour: {
type: Number,
default: 9
},
maxHour: {
type: Number,
default: 19
},
isBtn: {
type: Boolean,
default: true
},
isAni: {
type: Boolean,
default: true
},
zIndex: {
type: Number,
default: 9999
},
maskOpacity: {
type: Number,
default:.76
},
noDateTips: {
type: String,
default: '今日已暂无服务'
},
isTwo: {
type: Boolean,
default: true
},
isAutoClose: {
type: Boolean,
default: false
}
},
model: {
prop: 'isShow',
event: 'update'
},
watch: {
isShow(newVal, oldVal) {
if (newVal) {
this.init();
}
}
},
data() {
return {
days: [],
dayActiveIndex: 0,
hourActiveIndex: 0,
currentBusinessType: uni.getStorageSync('type_title') || '散貨'
}
},
computed: {
// 可选时间列表
hours() {
let nowDate = new Date();
let hoursResult = [];
let firstHour = this.minHour;
// 根据业务类型设置时间间隔
let interval = this.currentBusinessType === '包車'? 1 : 3;
for (let i = firstHour; i < this.maxHour; i += interval) {
const endHour = i + interval;
if (endHour > this.maxHour) break;
hoursResult.push({
start: i,
end: endHour,
hoursStr: `${this.isTwo? ('0' + i).slice(-2) : i}:00~${this.isTwo? ('0' + endHour).slice(-2) : endHour}:00`
});
}
return hoursResult;
},
// 当前选中结果
result() {
if (this.days.length === 0 || this.hours.length === 0) {
return null;
}
const {
year,
month,
day
} = this.days[this.dayActiveIndex];
const {
start,
end,
hoursStr
} = this.hours[this.hourActiveIndex] || {};
// console.log('---- result ----:', year,month,day, start, end, hoursStr);
return {
year: year,
month: this.isTwo? ('0' + month).slice(-2) : month,
day: this.isTwo? ('0' + day).slice(-2) : day,
hourStart: this.isTwo? ('0' + start).slice(-2) : start,
hourEnd: this.isTwo? ('0' + end).slice(-2) : end,
hoursStr: hoursStr,
anotherName: this.anotherNames[this.dayActiveIndex] || ''
}
},
btnStr() {
let hoursStr = '';
if (!this.result) {
return '待选择';
}
if (this.result.hourStart === this.result.hourEnd) {
hoursStr = `${this.result.hourStart}:00(${this.result.hoursStr})`
} else {
hoursStr = this.result.hoursStr;
}
// return `预约${this.result.year}年${this.result.month}月${this.result.day}日 ${hoursStr}`
}
},
methods: {
// 插件初如化
init() {
let firstDate = new Date();
this.days = [];
// 只生成明天和后天的数据
for (let i = 1; i < 3; i++) {
const dateItem = new Date(firstDate.setDate(firstDate.getDate() + i));
this.days.push({
year: dateItem.getFullYear(),
month: dateItem.getMonth() + 1,
day: dateItem.getDate(),
value: `${dateItem.getMonth() + 1}-${dateItem.getDate()}`
})
}
// console.log('---- hbxw - timepicker ----:', this.days);
},
// 切换月份
toggleDay(index) {
this.dayActiveIndex = index;
this.hourActiveIndex = 0;
this.$nextTick(() => {
this.$emit('change', {
result: this.result,
form: 'day'
});
});
},
// 切换时间
toggleHour(index) {
this.hourActiveIndex = index;
this.$nextTick(() => {
this.$emit('change', {
result: this.result,
form: 'hour'
});
if (this.isAutoClose) {
this.$emit('update:isShow', false);
}
});
this.$emit('change', {
result: this.result,
form: 'close'
});
this.$emit('update:isShow', false);
},
// 确认按钮
sure() {
this.$emit('change', {
result: this.result,
form:'sure'
});
},
// 关闭弹窗方法
close() {
this.$emit('change', {
result: this.result,
form: 'close'
});
this.$emit('update:isShow', false);
}
}
}
</script>
<style lang="scss" scoped>
@keyframes showAni {
0% {
opacity: 0;
transform: translateY(40%);
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.hbxw-timepicker {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999
}
.hbxw-timepicker-mask {
background-color: rgba(0, 0, 0, var(--opacity));
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9998;
}
.hbxw-timepicker-main {
position: absolute;
left: 0;
bottom: 0;
z-index: 9999;
width: 100%;
display: flex;
height: 48%;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
flex-direction: column;
align-items: center;
// border-radius: 40rpx 40rpx 0 0;
// background-color: #F7F9FA;
background-color: white;
transform-origin: center bottom;
opacity: 0;
&.hbxw-timepicker-main-ani {
opacity: 0;
}
}
.hbxw-timepicker-ani {
.hbxw-timepicker-main-ani {
animation: showAni .3s linear 0.1s 1 forwards;
}
}
.hbxw-timepicker-title {
width: 100%;
display: flex;
flex-direction: column;
position: relative;
padding: 39rpx 32rpx 27rpx 32rpx;
box-sizing: border-box;
border-bottom: 1px solid #DCDFE0;
.hbxw-timepicker-title-main {
font-size: 32rpx;
font-weight: bold;
line-height: 54rpx;
color: #000;
}
.hbxw-timepicker-title-subtitle {
font-size: 25rpx;
line-height: 48rpx;
color: #000;
}
.hbxw-timepicker-close {
width: 24rpx;
height: 24rpx;
position: absolute;
top: 27rpx;
right: 27rpx;
}
}
.hbxw-timepicker-content {
display: flex;
flex-direction: row;
width: 100%;
// border-radius: 40rpx;
height: 740rpx;
overflow: hidden;
background-color: white;
}
.hbxw-timerpicker-sure {
width: 686rpx;
height: 100rpx;
background-color: #E44A6C;
border-radius: 50rpx;
text-align: center;
font-size: 32rpx;
line-height: 100rpx;
font-weight: bold;
margin: 51rpx 0 32rpx 0;
color: #000;
}
.hbxw-timepicker-day {
width: 370rpx;
height: 100%;
overflow-y: auto;
background-color: #F7F7F7;
.hbxw-timepicker-day-item {
width: 100%;
height: 110rpx;
font-size: 26rpx;
text-align: center;
line-height: 110rpx;
box-sizing: border-box;
// border-bottom: 1px solid #DCDFE0;
color: #737E85;
&:nth-last-of-type(1) {
border-bottom-color: transparent;
}
}
.hbxw-timepicker-day-item-active {
font-weight: bold;
border-bottom: 1px solid white;
background-color: white;
color: #DA4C43;
;
}
}
.hbxw-timepicker-time {
flex: 1;
padding: 0 32rpx;
height: 100%;
overflow-y: auto;
.hbxw-timepicker-time-item {
height: 110rpx;
flex: none;
box-sizing: border-box;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #EBEDF0;
}
.hbxw-timepicker-time-item-active {
.hbxw-timepicker-time-val {
// color:#549204;
color: #DA4C43;
}
.hbxw-timepicker-time-icon {
// background-color: #AAF24E;
// width: 30px;
// height: 30px;
.hbxw-timepicker-time-img {
opacity: 1;
}
}
}
.hbxw-timepicker-time-val {
font-size: 28rpx;
color: #000;
}
.hbxw-timepicker-time-icon {
width: 32rpx;
height: 32rpx;
box-sizing: border-box;
border-radius: 50%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
// border:1px solid #A2A4A6;
.hbxw-timepicker-time-img {
width: 52rpx;
height: 52rpx;
opacity: 0;
}
}
}
.hbxw-timepicker-notime {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.hbxw-timerpicker-nodata {
font-size: 24rpx;
color: #737E85;
}
</style>