Files
xiuhuwangqiu/components/booking-time.vue
wangxiaowei f59ed2e36d 完善功能
2025-12-04 17:30:32 +08:00

326 lines
9.4 KiB
Vue
Raw Permalink 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>
<view class="time-block">
<view class="time-block-close" @click="showPopup = false">
<image class="" src="@/static/icon_close.png" style="width: 60rpx;height: 60rpx;"> </image>
</view>
<view class="time-title">选择时间</view>
<view class="time-yellow">
示例07:00代表07:01-07:59
</view>
<view>
<view class="booking-time">
<!-- 顶部日期tab -->
<view class="tab-bar-scroll">
<view class="tab-bar">
<view v-for="(item, idx) in day.time" :key="item.display"
class="tab-bar-item"
:class="{active: idx === selectedDay}"
@click="handleTabClick(idx)">
{{ item.display }}
</view>
</view>
</view>
<!-- 时间格子 -->
<view v-if="day.time && day.time[selectedDay]">
<view class="time-grid-wrap">
<view class="time-grid">
<view v-for="item2 in day.time[selectedDay].time_slots" :key="item2.start_time"
class="time-cell"
:class="[
item2.disabled == 0
? 'cell-disabled'
: selectedTime.includes(item2.start_time)
? 'cell-selected'
: 'cell-normal'
]"
@click="item2.disabled == 1 && handleSelectTime(item2.start_time, item2.timestamp, day.time[selectedDay].time_slots)">
{{ item2.start_time }}
</view>
</view>
</view>
<!-- 分割线 -->
<view class="divider"></view>
<!-- 底部按钮 -->
<view class="time-footer">
<view class="btn-reset" @click="handleResetSelectedTime">重置</view>
<view class="btn-confirm" @click="handleConfirmSelectedTime">
<text>确定</text>
<text v-if="countSelectedTime">{{ countSelectedTime }}小时</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'BookingTime',
props: {
value: { // v-model
type: Boolean,
default: false
},
day: {
type: Object,
default: () => ({})
}
},
data() {
return {
OSS: this.$root.OSS || '', // 或 inject 方式
showPopup: this.value,
selectedDay: 0,
selectedTime: [],
selectedTimeStamp: [],
countSelectedTime: 0
}
},
watch: {
value(val) {
this.showPopup = val
},
showPopup(val) {
this.$emit('input', val)
}
},
methods: {
handleTabClick(idx) {
if (this.selectedDay !== idx) {
this.selectedDay = idx;
this.handleChangeTimeTab();
}
},
handleSelectTime(time, timestamp, timeSlots) {
const availableSlots = timeSlots.filter(slot => slot.disabled == 1)
const times = availableSlots.map(slot => slot.start_time)
const timestamps = availableSlots.map(slot => slot.timestamp)
const idx = times.indexOf(time)
const selectedIdxArr = this.selectedTime.map(t => times.indexOf(t)).sort((a, b) => a - b)
if (this.selectedTime.length === 0) {
this.selectedTime = [time]
this.selectedTimeStamp = [timestamp]
} else if (this.selectedTime.includes(time)) {
const minIdx = selectedIdxArr[0]
const maxIdx = selectedIdxArr[selectedIdxArr.length - 1]
if (idx === minIdx) {
this.selectedTime = times.slice(idx + 1, maxIdx + 1)
this.selectedTimeStamp = timestamps.slice(idx + 1, maxIdx + 1)
} else if (idx === maxIdx) {
this.selectedTime = times.slice(minIdx, idx)
this.selectedTimeStamp = timestamps.slice(minIdx, idx)
} else {
this.selectedTime = times.slice(minIdx, idx)
this.selectedTimeStamp = timestamps.slice(minIdx, idx)
}
} else {
const allIdx = selectedIdxArr.concat(idx)
const minIdx = Math.min(...allIdx)
const maxIdx = Math.max(...allIdx)
this.selectedTime = times.slice(minIdx, maxIdx + 1)
this.selectedTimeStamp = timestamps.slice(minIdx, maxIdx + 1)
}
this.countSelectedTime = this.handleCalcContinuousHours(this.selectedTime)
},
handleConfirmSelectedTime() {
if (this.selectedTime.length === 0) {
uni.showToast({
title: '请选择时间',
icon: 'none'
});
return
}
if (this.countSelectedTime < this.day.minimum_time) {
uni.showToast({
title: this.day.minimum_time + '小时起订',
icon: 'none'
});
return
}
const data = [
this.day.time[this.selectedDay].display,
this.selectedTime.sort(),
this.selectedTimeStamp.sort(),
this.countSelectedTime,
this.day.time[this.selectedDay].date,
]
this.$emit('selectedTime', data)
this.showPopup = false
},
handleChangeTimeTab() {
this.selectedTime = []
this.selectedTimeStamp = []
this.countSelectedTime = 0
},
handleCalcContinuousHours(times) {
// 新逻辑:选中多少时间块就是多少小时
return times.length;
},
handleResetSelectedTime() {
this.selectedTime = []
this.selectedTimeStamp = []
}
}
}
</script>
<style lang="scss" scoped>
.booking-time {
/deep/ .wd-tabs__line {
background-color: #4C9F44 !important;
}
}
.tab-bar-scroll {
overflow-x: auto;
background: #fff;
}
.tab-bar {
display: flex;
flex-direction: row;
padding: 0 30rpx;
background: #fff;
}
.tab-bar-item {
flex-shrink: 0;
padding: 0 32rpx;
height: 62rpx;
line-height: 62rpx;
font-size: 32rpx;
color: #303133;
margin: 10rpx 16rpx 10rpx 0;
border-bottom: 4rpx solid transparent;
cursor: pointer;
}
.tab-bar-item.active {
color: #365A9A;
font-weight: bold;
position: relative;
}
.tab-bar-item.active::after {
content: '';
display: block;
width: 60rpx;
height: 10rpx;
background: #365A9A;
border-radius: 6rpx;
position: absolute;
left: 50%;
bottom: -10rpx;
transform: translateX(-50%);
}
.divider {
width: 100%;
height: 2rpx;
background: #E5E5E5;
margin: 32rpx 0 0 0;
}
.time-title {
font-size: 36rpx;
color: #121212;
line-height: 50rpx;
text-align: center;
padding-top: 50rpx;
padding-bottom: 40rpx;
}
.time-grid-wrap {
height: 500rpx;
margin-top: 30rpx;
}
.time-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx 20rpx;
padding: 0 30rpx;
}
.time-cell {
height: 72rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
line-height: 40rpx;
box-sizing: border-box;
background: #F7F7F7;
color: #303133;
transition: background 0.2s, color 0.2s;
}
.cell-disabled {
background: #F7F7F7 !important;
color: #C9C9C9 !important;
}
.cell-selected {
background: #E6EFFF !important;
color: #365A9A !important;
}
.cell-normal {
background: #F7F7F7 !important;
color: #303133 !important;
}
.time-footer {
width: 100%;
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
height: 152rpx;
font-size: 32rpx;
line-height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
z-index: 10;
}
.btn-reset {
width: 330rpx;
height: 90rpx;
background: #F6F7F8;
border-radius: 8rpx;
color: #303133;
margin-right: 30rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
.btn-confirm {
width: 330rpx;
height: 90rpx;
background: #365A9A;
border-radius: 8rpx;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
.time-block {
position: relative;
padding-bottom: 78rpx;
}
.time-block-close {
position: absolute;
top: 18rpx;
right: 30rpx;
}
.time-yellow {
width: 100%;
background: #FFFBE5;
font-weight: 500;
font-size: 26rpx;
color: #E2950F;
line-height: 56rpx;
padding: 10rpx 30rpx;
text-align: left;
}
</style>