21
This commit is contained in:
parent
05ad1348cf
commit
d085baf581
3
.vs/ProjectSettings.json
Normal file
3
.vs/ProjectSettings.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"CurrentProjectSetting": null
|
||||||
|
}
|
||||||
6
.vs/VSWorkspaceState.json
Normal file
6
.vs/VSWorkspaceState.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"ExpandedNodes": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"PreviewInSolutionExplorer": false
|
||||||
|
}
|
||||||
Binary file not shown.
BIN
.vs/mahjong_group.slnx/v18/.wsuo
Normal file
BIN
.vs/mahjong_group.slnx/v18/.wsuo
Normal file
Binary file not shown.
23
.vs/mahjong_group.slnx/v18/DocumentLayout.json
Normal file
23
.vs/mahjong_group.slnx/v18/DocumentLayout.json
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"Version": 1,
|
||||||
|
"WorkspaceRootPath": "D:\\CodeManage\\Coreshop\\uniapp\\mahjong_group\\",
|
||||||
|
"Documents": [],
|
||||||
|
"DocumentGroupContainers": [
|
||||||
|
{
|
||||||
|
"Orientation": 0,
|
||||||
|
"VerticalTabListWidth": 256,
|
||||||
|
"DocumentGroups": [
|
||||||
|
{
|
||||||
|
"DockedWidth": 200,
|
||||||
|
"SelectedChildIndex": -1,
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"$type": "Bookmark",
|
||||||
|
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
.vs/mahjong_group/v18/workspaceFileList.bin
Normal file
BIN
.vs/mahjong_group/v18/workspaceFileList.bin
Normal file
Binary file not shown.
BIN
.vs/slnx.sqlite
Normal file
BIN
.vs/slnx.sqlite
Normal file
Binary file not shown.
|
|
@ -8,8 +8,8 @@ const development = {
|
||||||
// API基础URL
|
// API基础URL
|
||||||
baseUrl: 'https://sqqp.zpc-xy.com',
|
baseUrl: 'https://sqqp.zpc-xy.com',
|
||||||
host: ['https://sqqp.zpc-xy.com'],
|
host: ['https://sqqp.zpc-xy.com'],
|
||||||
// baseUrl: 'http://192.168.195.15:2401',
|
// baseUrl: 'http://192.168.1.21:2016',
|
||||||
// host: ['http://192.168.195.15:2401'],
|
// host: ['http://192.168.1.21:2016'],
|
||||||
// baseUrl: 'http://192.168.1.24:2016',
|
// baseUrl: 'http://192.168.1.24:2016',
|
||||||
// host: ['http://192.168.1.24:2016'],
|
// host: ['http://192.168.1.24:2016'],
|
||||||
imageUrl: 'https://guyu-1308826010.cos.ap-shanghai.myqcloud.com',
|
imageUrl: 'https://guyu-1308826010.cos.ap-shanghai.myqcloud.com',
|
||||||
|
|
|
||||||
|
|
@ -14,23 +14,40 @@
|
||||||
</view>
|
</view>
|
||||||
</label-field>
|
</label-field>
|
||||||
<view :style="{ height: lineHeight }"></view>
|
<view :style="{ height: lineHeight }"></view>
|
||||||
<label-field label="时间段">
|
<label-field label="开始时间">
|
||||||
<view v-if="roomDetailLoading" class="center" style="padding: 30rpx 0;">
|
<picker mode="time" :value="startTime" @change="onStartTimeChange">
|
||||||
<text style="color: #999; font-size: 24rpx;">加载时段信息中...</text>
|
<view class="picker-value">{{ startTime || '请选择开始时间' }}</view>
|
||||||
</view>
|
</picker>
|
||||||
<uni-data-select v-else v-model="selectedTimeSlot" placeholder="请选择时间段"
|
|
||||||
:localdata="timeSlotOptions" @change="onTimeSlotChange"></uni-data-select>
|
|
||||||
<view v-if="!roomDetailLoading && timeSlotOptions.length === 0" style="padding: 20rpx 0;">
|
|
||||||
<text style="color: #FF0000; font-size: 24rpx;">当前日期该房间暂无可预约时段</text>
|
|
||||||
</view>
|
|
||||||
</label-field>
|
</label-field>
|
||||||
<view :style="{ height: lineHeight }"></view>
|
<view :style="{ height: lineHeight }"></view>
|
||||||
<label-field label="最晚到店时间">
|
<label-field label="结束时间">
|
||||||
<view class="input-wrapper" style="padding: 15rpx 20rpx;" @click="openLatestDateTimePicker">
|
<view class="end-time-row">
|
||||||
<text>{{ getLatestDateTimeDisplayText() }}</text>
|
<picker mode="time" :value="endTime" @change="onEndTimeChange">
|
||||||
|
<view class="picker-value">{{ endTime || '请选择结束时间' }}</view>
|
||||||
|
</picker>
|
||||||
|
<view class="next-day-toggle" v-if="startTime && endTime">
|
||||||
|
<text class="next-day-label" :class="{ active: !isNextDay }" @click="setNextDay(false)">当天</text>
|
||||||
|
<text class="next-day-divider">/</text>
|
||||||
|
<text class="next-day-label" :class="{ active: isNextDay }" @click="setNextDay(true)">次日</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</label-field>
|
</label-field>
|
||||||
</card-container>
|
<!-- 时长和跨时段信息显示 -->
|
||||||
|
<view class="time-info" v-if="startTime && endTime && !timeError">
|
||||||
|
<view class="time-info-item">
|
||||||
|
<text class="time-info-label">预计时长:</text>
|
||||||
|
<text class="time-info-value">{{ calculateDuration() }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="time-info-item">
|
||||||
|
<text class="time-info-label">跨越时段:</text>
|
||||||
|
<text class="time-info-value">{{ calculateCrossSlots() }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 时间错误提示 -->
|
||||||
|
<view class="time-error" v-if="timeError">
|
||||||
|
<text>{{ timeError }}</text>
|
||||||
|
</view>
|
||||||
|
</card-container>
|
||||||
|
|
||||||
<card-container marginTop="30rpx">
|
<card-container marginTop="30rpx">
|
||||||
|
|
||||||
|
|
@ -137,14 +154,9 @@
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<up-datetime-picker :show="datePickerVisible" v-model="datePickerValue" mode="date"
|
<up-datetime-picker :show="datePickerVisible" v-model="datePickerValue" mode="date"
|
||||||
:minDate="datePickerMinDate" :maxDate="datePickerMaxDate" title="选择日期" @confirm="onDatePickerConfirm"
|
:minDate="datePickerMinDate" :maxDate="datePickerMaxDate" title="选择日期" @confirm="onDatePickerConfirm"
|
||||||
@cancel="() => datePickerVisible = false" @close="() => datePickerVisible = false"></up-datetime-picker>
|
@cancel="() => datePickerVisible = false" @close="() => datePickerVisible = false"></up-datetime-picker>
|
||||||
<up-datetime-picker :show="latestDateTimePickerVisible" v-model="latestDateTimePickerValue" mode="time"
|
|
||||||
:minHour="latestDateTimeMinHour" :maxHour="latestDateTimeMaxHour"
|
|
||||||
:minMinute="latestDateTimeMinMinute" :maxMinute="latestDateTimeMaxMinute"
|
|
||||||
title="选择最晚到店时间" @confirm="onLatestDateTimePickerConfirm"
|
|
||||||
@cancel="() => latestDateTimePickerVisible = false" @close="() => latestDateTimePickerVisible = false"></up-datetime-picker>
|
|
||||||
<up-picker title="年龄范围选择" :show="agePickerVisible" :columns="agePickerColumns" :keyName="'text'"
|
<up-picker title="年龄范围选择" :show="agePickerVisible" :columns="agePickerColumns" :keyName="'text'"
|
||||||
:defaultIndex="agePickerDefaultIndex" @confirm="onAgePickerConfirm" @cancel="() => agePickerVisible = false"
|
:defaultIndex="agePickerDefaultIndex" @confirm="onAgePickerConfirm" @cancel="() => agePickerVisible = false"
|
||||||
@close="() => agePickerVisible = false"></up-picker>
|
@close="() => agePickerVisible = false"></up-picker>
|
||||||
|
|
@ -224,25 +236,19 @@
|
||||||
const lineHeight = ref("15rpx")
|
const lineHeight = ref("15rpx")
|
||||||
// 房间页跳转相关
|
// 房间页跳转相关
|
||||||
const selectedDate = ref(null) // 选中的日期时间戳(秒级)
|
const selectedDate = ref(null) // 选中的日期时间戳(秒级)
|
||||||
const selectedTimeSlot = ref(null) // 选中的时段类型 0-3,null表示未选择
|
|
||||||
const roomDetail = ref(null) // 房间详情数据
|
const roomDetail = ref(null) // 房间详情数据
|
||||||
const roomDetailLoading = ref(false) // 房间详情加载状态
|
const roomDetailLoading = ref(false) // 房间详情加载状态
|
||||||
const timeSlotOptions = ref([]) // 时间段选项,从房间详情动态生成
|
// 自由时间选择相关
|
||||||
|
const startTime = ref('') // 开始时间 "HH:mm"
|
||||||
|
const endTime = ref('') // 结束时间 "HH:mm"
|
||||||
|
const timeError = ref('') // 时间错误提示
|
||||||
|
const isNextDay = ref(false) // 结束时间是否为次日(用于通宵预约)
|
||||||
// 日期选择器状态
|
// 日期选择器状态
|
||||||
const datePickerVisible = ref(false)
|
const datePickerVisible = ref(false)
|
||||||
const datePickerValue = ref(Date.now())
|
const datePickerValue = ref(Date.now())
|
||||||
const datePickerMinDate = ref(Date.now()) // 最小日期为今天
|
const datePickerMinDate = ref(Date.now()) // 最小日期为今天
|
||||||
// 最大日期为未来7天(今天+未来6天)
|
// 最大日期为未来7天(今天+未来6天)
|
||||||
const datePickerMaxDate = ref(new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).getTime())
|
const datePickerMaxDate = ref(new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).getTime())
|
||||||
// 最晚到店时间选择器状态
|
|
||||||
const latestDateTimePickerVisible = ref(false)
|
|
||||||
const latestDateTimePickerValue = ref('00:00') // 时间选择器值(HH:MM格式字符串)
|
|
||||||
const latestDateTime = ref(null) // 最晚到店时间(秒级时间戳)
|
|
||||||
// 时间选择器范围限制
|
|
||||||
const latestDateTimeMinHour = ref(0)
|
|
||||||
const latestDateTimeMaxHour = ref(23)
|
|
||||||
const latestDateTimeMinMinute = ref(0)
|
|
||||||
const latestDateTimeMaxMinute = ref(59)
|
|
||||||
//提交表单数据
|
//提交表单数据
|
||||||
const reservationInfo = ref({
|
const reservationInfo = ref({
|
||||||
room_id: 0, //房间id 非空
|
room_id: 0, //房间id 非空
|
||||||
|
|
@ -384,14 +390,15 @@
|
||||||
|
|
||||||
// 更新选中的日期
|
// 更新选中的日期
|
||||||
selectedDate.value = selectedTimestamp;
|
selectedDate.value = selectedTimestamp;
|
||||||
|
|
||||||
// 重置时段选择
|
// 重置时间选择
|
||||||
selectedTimeSlot.value = null;
|
startTime.value = '';
|
||||||
|
endTime.value = '';
|
||||||
|
timeError.value = '';
|
||||||
|
isNextDay.value = false; // 重置跨天标记
|
||||||
reservationInfo.value.start_time = 0;
|
reservationInfo.value.start_time = 0;
|
||||||
reservationInfo.value.end_time = 0;
|
reservationInfo.value.end_time = 0;
|
||||||
// 重置最晚到店时间
|
|
||||||
latestDateTime.value = null;
|
|
||||||
|
|
||||||
// 重新加载房间详情
|
// 重新加载房间详情
|
||||||
if (reservationInfo.value.room_id) {
|
if (reservationInfo.value.room_id) {
|
||||||
console.log('重新加载房间详情, roomId:', reservationInfo.value.room_id, 'date:', selectedTimestamp);
|
console.log('重新加载房间详情, roomId:', reservationInfo.value.room_id, 'date:', selectedTimestamp);
|
||||||
|
|
@ -400,284 +407,204 @@
|
||||||
|
|
||||||
datePickerVisible.value = false;
|
datePickerVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最晚到店时间显示文本
|
* 开始时间变更处理
|
||||||
*/
|
*/
|
||||||
const getLatestDateTimeDisplayText = () => {
|
const onStartTimeChange = (e) => {
|
||||||
if (!latestDateTime.value) {
|
startTime.value = e.detail.value;
|
||||||
return '请选择最晚到店时间';
|
validateTimeRange();
|
||||||
}
|
buildTimeFromPicker();
|
||||||
const date = new Date(latestDateTime.value * 1000);
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取时间段的时间范围限制
|
* 结束时间变更处理
|
||||||
*/
|
*/
|
||||||
const getTimeSlotRange = () => {
|
const onEndTimeChange = (e) => {
|
||||||
if (!selectedTimeSlot.value || selectedTimeSlot.value === null || selectedTimeSlot.value === undefined) {
|
endTime.value = e.detail.value;
|
||||||
return { minHour: 0, maxHour: 23, minMinute: 0, maxMinute: 59 };
|
// 自动判断是否需要切换到次日
|
||||||
}
|
autoDetectNextDay();
|
||||||
|
validateTimeRange();
|
||||||
let minHour = 0, maxHour = 23, minMinute = 0, maxMinute = 59;
|
buildTimeFromPicker();
|
||||||
|
|
||||||
switch (selectedTimeSlot.value) {
|
|
||||||
case 0: // 凌晨 00:00-05:59
|
|
||||||
minHour = 0;
|
|
||||||
maxHour = 5;
|
|
||||||
minMinute = 0;
|
|
||||||
maxMinute = 59;
|
|
||||||
break;
|
|
||||||
case 1: // 上午 06:00-11:59
|
|
||||||
minHour = 6;
|
|
||||||
maxHour = 11;
|
|
||||||
minMinute = 0;
|
|
||||||
maxMinute = 59;
|
|
||||||
break;
|
|
||||||
case 2: // 下午 12:00-17:59
|
|
||||||
minHour = 12;
|
|
||||||
maxHour = 17;
|
|
||||||
minMinute = 0;
|
|
||||||
maxMinute = 59;
|
|
||||||
break;
|
|
||||||
case 3: // 晚上 18:00-23:59
|
|
||||||
minHour = 18;
|
|
||||||
maxHour = 23;
|
|
||||||
minMinute = 0;
|
|
||||||
maxMinute = 59;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { minHour, maxHour, minMinute, maxMinute };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开最晚到店时间选择器
|
* 自动检测是否需要切换到次日(当结束时间小于开始时间时)
|
||||||
*/
|
*/
|
||||||
const openLatestDateTimePicker = () => {
|
const autoDetectNextDay = () => {
|
||||||
if (!selectedDate.value) {
|
if (!startTime.value || !endTime.value) return;
|
||||||
uni.showToast({
|
|
||||||
title: '请先选择日期',
|
const [startH, startM] = startTime.value.split(':').map(Number);
|
||||||
icon: 'none'
|
const [endH, endM] = endTime.value.split(':').map(Number);
|
||||||
});
|
const startMinutes = startH * 60 + startM;
|
||||||
return;
|
const endMinutes = endH * 60 + endM;
|
||||||
|
|
||||||
|
// 如果结束时间小于开始时间,自动切换到次日
|
||||||
|
if (endMinutes <= startMinutes) {
|
||||||
|
isNextDay.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedTimeSlot.value || selectedTimeSlot.value === null || selectedTimeSlot.value === undefined) {
|
|
||||||
uni.showToast({
|
|
||||||
title: '请先选择时间段',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置时间段范围限制
|
|
||||||
const timeRange = getTimeSlotRange();
|
|
||||||
latestDateTimeMinHour.value = timeRange.minHour;
|
|
||||||
latestDateTimeMaxHour.value = timeRange.maxHour;
|
|
||||||
latestDateTimeMinMinute.value = timeRange.minMinute;
|
|
||||||
latestDateTimeMaxMinute.value = timeRange.maxMinute;
|
|
||||||
|
|
||||||
// 如果有选择时间段,使用自动计算的时间
|
|
||||||
updateLatestDateTimeFromSlot();
|
|
||||||
|
|
||||||
// 将时间戳转换为 HH:MM 格式字符串
|
|
||||||
let timeString = null;
|
|
||||||
if (latestDateTime.value) {
|
|
||||||
const date = new Date(latestDateTime.value * 1000);
|
|
||||||
const hours = String(date.getHours()).padStart(2, '0');
|
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
||||||
timeString = `${hours}:${minutes}`;
|
|
||||||
} else {
|
|
||||||
// 默认使用时间段开始时间+30分钟
|
|
||||||
const timeRangeForDefault = getTimeSlotRange();
|
|
||||||
let defaultHour = timeRangeForDefault.minHour;
|
|
||||||
let defaultMinute = 30;
|
|
||||||
if (defaultMinute > timeRangeForDefault.maxMinute) {
|
|
||||||
defaultMinute = timeRangeForDefault.maxMinute;
|
|
||||||
}
|
|
||||||
timeString = `${String(defaultHour).padStart(2, '0')}:${String(defaultMinute).padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保时间字符串格式正确(必须是 "HH:MM" 格式)
|
|
||||||
if (timeString && typeof timeString === 'string' && timeString.match(/^\d{2}:\d{2}$/)) {
|
|
||||||
latestDateTimePickerValue.value = timeString;
|
|
||||||
} else {
|
|
||||||
// 如果格式不正确,使用时间段开始时间作为默认值
|
|
||||||
const timeRangeForFallback = getTimeSlotRange();
|
|
||||||
latestDateTimePickerValue.value = `${String(timeRangeForFallback.minHour).padStart(2, '0')}:00`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保值设置完成后再打开选择器
|
|
||||||
nextTick(() => {
|
|
||||||
latestDateTimePickerVisible.value = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 最晚到店时间选择器确认
|
* 设置结束时间是否为次日
|
||||||
*/
|
*/
|
||||||
const onLatestDateTimePickerConfirm = async (e) => {
|
const setNextDay = (value) => {
|
||||||
// up-datetime-picker mode="time" 时,v-model 绑定的值应该是 "HH:MM" 格式字符串
|
isNextDay.value = value;
|
||||||
// 事件参数 e 可能是对象 { value: "HH:MM", mode: 'time' } 或直接是字符串 "HH:MM"
|
validateTimeRange();
|
||||||
let timeString = latestDateTimePickerValue.value;
|
buildTimeFromPicker();
|
||||||
|
}
|
||||||
// 如果 v-model 的值无效,尝试从事件参数获取
|
|
||||||
if (!timeString || typeof timeString !== 'string') {
|
/**
|
||||||
if (e && typeof e === 'object' && e.value !== undefined) {
|
* 验证时间范围(支持跨天预约)
|
||||||
timeString = e.value;
|
*/
|
||||||
} else if (typeof e === 'string') {
|
const validateTimeRange = () => {
|
||||||
timeString = e;
|
timeError.value = '';
|
||||||
}
|
|
||||||
|
if (!startTime.value || !endTime.value) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证时间字符串格式
|
const [startH, startM] = startTime.value.split(':').map(Number);
|
||||||
if (!timeString || typeof timeString !== 'string' || !timeString.includes(':')) {
|
const [endH, endM] = endTime.value.split(':').map(Number);
|
||||||
console.error('时间选择器返回的值格式无效:', e, 'latestDateTimePickerValue:', latestDateTimePickerValue.value);
|
const startMinutes = startH * 60 + startM;
|
||||||
uni.showToast({
|
let endMinutes = endH * 60 + endM;
|
||||||
title: '时间选择失败,请重试',
|
|
||||||
icon: 'none'
|
// 如果是次日,结束时间加24小时
|
||||||
});
|
if (isNextDay.value) {
|
||||||
latestDateTimePickerVisible.value = false;
|
endMinutes += 24 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算时长(分钟)
|
||||||
|
const durationMinutes = endMinutes - startMinutes;
|
||||||
|
|
||||||
|
if (durationMinutes <= 0) {
|
||||||
|
timeError.value = '结束时间必须晚于开始时间';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (durationMinutes < 60) {
|
||||||
|
timeError.value = '预约时长不能少于1小时';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (durationMinutes > 720) {
|
||||||
|
timeError.value = '预约时长不能超过12小时';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建时间戳(从时间选择器值,支持跨天预约)
|
||||||
|
*/
|
||||||
|
const buildTimeFromPicker = () => {
|
||||||
|
if (!selectedDate.value || !startTime.value || !endTime.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析时间字符串 "HH:MM"
|
if (!validateTimeRange()) {
|
||||||
const timeParts = timeString.split(':');
|
// 清空时间戳
|
||||||
if (timeParts.length !== 2) {
|
reservationInfo.value.start_time = 0;
|
||||||
console.error('时间格式错误:', timeString);
|
reservationInfo.value.end_time = 0;
|
||||||
uni.showToast({
|
|
||||||
title: '时间格式错误',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hours = parseInt(timeParts[0], 10);
|
|
||||||
const minutes = parseInt(timeParts[1], 10);
|
|
||||||
|
|
||||||
if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
|
|
||||||
console.error('时间值无效:', hours, minutes);
|
|
||||||
uni.showToast({
|
|
||||||
title: '时间值无效',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证选择的时间是否在时间段范围内
|
|
||||||
if (selectedTimeSlot.value !== null && selectedTimeSlot.value !== undefined) {
|
|
||||||
const timeRange = getTimeSlotRange();
|
|
||||||
if (hours < timeRange.minHour || hours > timeRange.maxHour) {
|
|
||||||
uni.showToast({
|
|
||||||
title: `时间必须在 ${timeRange.minHour}:00-${timeRange.maxHour}:59 范围内`,
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (hours === timeRange.minHour && minutes < timeRange.minMinute) {
|
|
||||||
uni.showToast({
|
|
||||||
title: `时间必须在 ${timeRange.minHour}:${String(timeRange.minMinute).padStart(2, '0')}-${timeRange.maxHour}:59 范围内`,
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (hours === timeRange.maxHour && minutes > timeRange.maxMinute) {
|
|
||||||
uni.showToast({
|
|
||||||
title: `时间必须在 ${timeRange.minHour}:00-${timeRange.maxHour}:${String(timeRange.maxMinute).padStart(2, '0')} 范围内`,
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有选择日期,使用当前日期
|
|
||||||
if (!selectedDate.value) {
|
|
||||||
uni.showToast({
|
|
||||||
title: '请先选择日期',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将选择的日期和时间组合
|
|
||||||
const date = new Date(selectedDate.value * 1000);
|
const date = new Date(selectedDate.value * 1000);
|
||||||
const year = date.getFullYear();
|
const [startH, startM] = startTime.value.split(':').map(Number);
|
||||||
const month = date.getMonth();
|
const [endH, endM] = endTime.value.split(':').map(Number);
|
||||||
const day = date.getDate();
|
|
||||||
const dateTime = new Date(year, month, day, hours, minutes, 0);
|
const startDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), startH, startM, 0);
|
||||||
|
|
||||||
// 转换为秒级时间戳
|
// 如果是次日,结束时间加1天
|
||||||
const selectedTimestamp = Math.floor(dateTime.getTime() / 1000);
|
let endDateTime;
|
||||||
|
if (isNextDay.value) {
|
||||||
// 验证转换后的时间戳是否有效
|
const nextDay = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1, endH, endM, 0);
|
||||||
if (isNaN(selectedTimestamp) || selectedTimestamp <= 0) {
|
endDateTime = nextDay;
|
||||||
console.error('时间戳转换失败:', dateTime, selectedTimestamp);
|
} else {
|
||||||
uni.showToast({
|
endDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), endH, endM, 0);
|
||||||
title: '时间处理失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新最晚到店时间
|
reservationInfo.value.start_time = Math.floor(startDateTime.getTime() / 1000);
|
||||||
latestDateTime.value = selectedTimestamp;
|
reservationInfo.value.end_time = Math.floor(endDateTime.getTime() / 1000);
|
||||||
|
|
||||||
latestDateTimePickerVisible.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据时间段自动更新最晚到店时间(时间段开始时间+30分钟)
|
* 计算时长显示(支持跨天预约)
|
||||||
*/
|
*/
|
||||||
const updateLatestDateTimeFromSlot = () => {
|
const calculateDuration = () => {
|
||||||
if (!selectedDate.value || selectedTimeSlot.value === null || selectedTimeSlot.value === undefined) {
|
if (!startTime.value || !endTime.value) return '-';
|
||||||
return;
|
|
||||||
|
const [startH, startM] = startTime.value.split(':').map(Number);
|
||||||
|
const [endH, endM] = endTime.value.split(':').map(Number);
|
||||||
|
let endMinutes = endH * 60 + endM;
|
||||||
|
|
||||||
|
// 如果是次日,结束时间加24小时
|
||||||
|
if (isNextDay.value) {
|
||||||
|
endMinutes += 24 * 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将日期时间戳转换为日期对象
|
const diffMinutes = endMinutes - (startH * 60 + startM);
|
||||||
const date = new Date(selectedDate.value * 1000);
|
|
||||||
const year = date.getFullYear();
|
if (diffMinutes <= 0) return '-';
|
||||||
const month = date.getMonth();
|
|
||||||
const day = date.getDate();
|
const hours = Math.floor(diffMinutes / 60);
|
||||||
|
const mins = diffMinutes % 60;
|
||||||
// 根据时段类型设置开始时间
|
|
||||||
let startHour = 0;
|
if (mins === 0) {
|
||||||
let startMinute = 30; // 默认加30分钟
|
return `${hours}小时`;
|
||||||
|
|
||||||
switch (selectedTimeSlot.value) {
|
|
||||||
case 0: // 凌晨 00:00-05:59
|
|
||||||
startHour = 0;
|
|
||||||
startMinute = 30;
|
|
||||||
break;
|
|
||||||
case 1: // 上午 06:00-11:59
|
|
||||||
startHour = 6;
|
|
||||||
startMinute = 30;
|
|
||||||
break;
|
|
||||||
case 2: // 下午 12:00-17:59
|
|
||||||
startHour = 12;
|
|
||||||
startMinute = 30;
|
|
||||||
break;
|
|
||||||
case 3: // 晚上 18:00-23:59
|
|
||||||
startHour = 18;
|
|
||||||
startMinute = 30;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return `${hours}小时${mins}分钟`;
|
||||||
// 创建最晚到店时间(开始时间+30分钟)
|
|
||||||
const latestDateTimeObj = new Date(year, month, day, startHour, startMinute, 0);
|
|
||||||
|
|
||||||
// 转换为秒级时间戳
|
|
||||||
latestDateTime.value = Math.floor(latestDateTimeObj.getTime() / 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算跨越时段(支持跨天预约)
|
||||||
|
*/
|
||||||
|
const calculateCrossSlots = () => {
|
||||||
|
if (!startTime.value || !endTime.value) return '-';
|
||||||
|
|
||||||
|
const [startH] = startTime.value.split(':').map(Number);
|
||||||
|
const [endH] = endTime.value.split(':').map(Number);
|
||||||
|
|
||||||
|
const slots = [];
|
||||||
|
const ranges = [
|
||||||
|
{ name: '凌晨', start: 0, end: 6 },
|
||||||
|
{ name: '上午', start: 6, end: 12 },
|
||||||
|
{ name: '下午', start: 12, end: 18 },
|
||||||
|
{ name: '晚上', start: 18, end: 24 }
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isNextDay.value) {
|
||||||
|
// 跨天预约:当天的时段 + 次日的时段
|
||||||
|
// 当天:从开始时间到24点
|
||||||
|
for (const r of ranges) {
|
||||||
|
if (startH < r.end && 24 > r.start) {
|
||||||
|
if (!slots.includes(r.name)) {
|
||||||
|
slots.push(r.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 次日:从0点到结束时间
|
||||||
|
for (const r of ranges) {
|
||||||
|
if (0 < r.end && endH > r.start) {
|
||||||
|
if (!slots.includes(r.name)) {
|
||||||
|
slots.push(r.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 同一天:正常判断
|
||||||
|
for (const r of ranges) {
|
||||||
|
// 重叠判断:预约开始 < 时段结束 AND 预约结束 > 时段开始
|
||||||
|
if (startH < r.end && endH > r.start) {
|
||||||
|
slots.push(r.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slots.join('、') || '-';
|
||||||
|
}
|
||||||
|
|
||||||
const tipsShow = () => {
|
const tipsShow = () => {
|
||||||
submitPopupRef.value.open()
|
submitPopupRef.value.open()
|
||||||
}
|
}
|
||||||
|
|
@ -807,38 +734,27 @@ const onCustomDepositInput = (val) => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否选择了时段
|
// 检查是否选择了时间
|
||||||
if (selectedTimeSlot.value === null || selectedTimeSlot.value === undefined) {
|
if (!startTime.value || !endTime.value) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请选择时间段',
|
title: '请选择开始和结束时间',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证选择的时段是否仍然可预约
|
// 检查时间是否有效
|
||||||
if (roomDetail.value && roomDetail.value.time_slots) {
|
if (timeError.value) {
|
||||||
const selectedSlot = roomDetail.value.time_slots.find(
|
uni.showToast({
|
||||||
slot => slot.slot_type === selectedTimeSlot.value
|
title: timeError.value,
|
||||||
);
|
icon: 'none'
|
||||||
|
})
|
||||||
if (!selectedSlot || selectedSlot.status !== 'available') {
|
return false
|
||||||
uni.showToast({
|
|
||||||
title: '该时段已不可预约,请重新选择',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 2000
|
|
||||||
});
|
|
||||||
// 重新加载房间详情,更新可预约时段
|
|
||||||
if (reservationInfo.value.room_id && selectedDate.value) {
|
|
||||||
await loadRoomDetailForReservation(reservationInfo.value.room_id, selectedDate.value);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info.start_time || info.start_time === 0) {
|
if (!info.start_time || info.start_time === 0) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请选择时间段',
|
title: '请选择开始时间',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
|
|
@ -846,7 +762,7 @@ const onCustomDepositInput = (val) => {
|
||||||
|
|
||||||
if (!info.end_time || info.end_time === 0) {
|
if (!info.end_time || info.end_time === 0) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请选择时间段',
|
title: '请选择结束时间',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
|
|
@ -947,9 +863,7 @@ const onCustomDepositInput = (val) => {
|
||||||
// 确保信誉限制同步
|
// 确保信誉限制同步
|
||||||
credit_limit: currentValue.value,
|
credit_limit: currentValue.value,
|
||||||
deposit_fee: finalDeposit,
|
deposit_fee: finalDeposit,
|
||||||
important_data: {},
|
important_data: {}
|
||||||
// 添加最晚到店时间参数
|
|
||||||
latestDateTime: latestDateTime.value || null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将玩法类型和具体规则从数字转换为字符串
|
// 将玩法类型和具体规则从数字转换为字符串
|
||||||
|
|
@ -1079,14 +993,15 @@ const onCustomDepositInput = (val) => {
|
||||||
maxPlayerCount.value = savedMaxPlayerCount;
|
maxPlayerCount.value = savedMaxPlayerCount;
|
||||||
peopleRange.value = savedPeopleRange;
|
peopleRange.value = savedPeopleRange;
|
||||||
peopleText.value = savedPeopleText;
|
peopleText.value = savedPeopleText;
|
||||||
|
|
||||||
// 重置时段选择
|
// 重置时间选择
|
||||||
selectedTimeSlot.value = null;
|
startTime.value = '';
|
||||||
|
endTime.value = '';
|
||||||
|
timeError.value = '';
|
||||||
|
isNextDay.value = false; // 重置跨天标记
|
||||||
reservationInfo.value.start_time = 0;
|
reservationInfo.value.start_time = 0;
|
||||||
reservationInfo.value.end_time = 0;
|
reservationInfo.value.end_time = 0;
|
||||||
// 重置最晚到店时间
|
|
||||||
latestDateTime.value = null;
|
|
||||||
|
|
||||||
gameRuleText.value = "请先选择玩法类型"
|
gameRuleText.value = "请先选择玩法类型"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1145,9 +1060,6 @@ const onCustomDepositInput = (val) => {
|
||||||
} else {
|
} else {
|
||||||
peopleText.value = "请选择游玩人数";
|
peopleText.value = "请选择游玩人数";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 time_slots 中过滤出可预约的时段,动态生成时间段选项
|
|
||||||
generateTimeSlotOptions(detail.time_slots);
|
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '获取房间详情失败',
|
title: '获取房间详情失败',
|
||||||
|
|
@ -1164,103 +1076,7 @@ const onCustomDepositInput = (val) => {
|
||||||
roomDetailLoading.value = false;
|
roomDetailLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据房间的 time_slots 动态生成时间段选项
|
|
||||||
* 只显示可预约的时段(status === 'available')
|
|
||||||
*/
|
|
||||||
const generateTimeSlotOptions = (timeSlots) => {
|
|
||||||
if (!timeSlots || !Array.isArray(timeSlots)) {
|
|
||||||
timeSlotOptions.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 时段时间范围映射
|
|
||||||
const timeRangeMap = {
|
|
||||||
0: { text: '凌晨', range: '00:00-05:59' },
|
|
||||||
1: { text: '上午', range: '06:00-11:59' },
|
|
||||||
2: { text: '下午', range: '12:00-17:59' },
|
|
||||||
3: { text: '晚上', range: '18:00-23:59' }
|
|
||||||
};
|
|
||||||
|
|
||||||
// 过滤出可预约的时段并生成选项
|
|
||||||
const availableSlots = timeSlots.filter(slot => slot.status === 'available');
|
|
||||||
|
|
||||||
timeSlotOptions.value = availableSlots.map(slot => {
|
|
||||||
const slotInfo = timeRangeMap[slot.slot_type] || { text: slot.slot_name, range: '' };
|
|
||||||
// 生成显示文本
|
|
||||||
let displayText = slotInfo.text;
|
|
||||||
if (slotInfo.range) {
|
|
||||||
displayText += ` (${slotInfo.range})`;
|
|
||||||
}
|
|
||||||
// 如果有价格信息,可以追加显示
|
|
||||||
if (slot.price_desc_standard) {
|
|
||||||
displayText += ` ${slot.price_desc_standard}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: slot.slot_type,
|
|
||||||
text: displayText
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 时段选择改变事件
|
|
||||||
*/
|
|
||||||
const onTimeSlotChange = (val) => {
|
|
||||||
if (!selectedDate.value) return;
|
|
||||||
selectedTimeSlot.value = val;
|
|
||||||
// 根据日期和时段计算开始时间和结束时间
|
|
||||||
calculateTimeFromSlot();
|
|
||||||
// 自动更新最晚到店时间(时间段开始时间+30分钟)
|
|
||||||
updateLatestDateTimeFromSlot();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据选择的日期和时段计算开始时间和结束时间
|
|
||||||
*/
|
|
||||||
const calculateTimeFromSlot = () => {
|
|
||||||
if (!selectedDate.value || selectedTimeSlot.value === null || selectedTimeSlot.value === undefined) return;
|
|
||||||
|
|
||||||
// 将日期时间戳转换为日期对象
|
|
||||||
const date = new Date(selectedDate.value * 1000);
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = date.getMonth();
|
|
||||||
const day = date.getDate();
|
|
||||||
|
|
||||||
// 根据时段类型设置开始时间
|
|
||||||
let startHour = 0;
|
|
||||||
let endHour = 0;
|
|
||||||
|
|
||||||
switch (selectedTimeSlot.value) {
|
|
||||||
case 0: // 凌晨 00:00-05:59
|
|
||||||
startHour = 0;
|
|
||||||
endHour = 6;
|
|
||||||
break;
|
|
||||||
case 1: // 上午 06:00-11:59
|
|
||||||
startHour = 6;
|
|
||||||
endHour = 12;
|
|
||||||
break;
|
|
||||||
case 2: // 下午 12:00-17:59
|
|
||||||
startHour = 12;
|
|
||||||
endHour = 18;
|
|
||||||
break;
|
|
||||||
case 3: // 晚上 18:00-23:59
|
|
||||||
startHour = 18;
|
|
||||||
endHour = 24;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建开始时间和结束时间
|
|
||||||
const startTime = new Date(year, month, day, startHour, 0, 0);
|
|
||||||
const endTime = new Date(year, month, day, endHour, 0, 0);
|
|
||||||
|
|
||||||
// 转换为秒级时间戳
|
|
||||||
reservationInfo.value.start_time = Math.floor(startTime.getTime() / 1000);
|
|
||||||
reservationInfo.value.end_time = Math.floor(endTime.getTime() / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoad(async (options) => {
|
onLoad(async (options) => {
|
||||||
const config = await getConfigData();
|
const config = await getConfigData();
|
||||||
console.log('config', config);
|
console.log('config', config);
|
||||||
|
|
@ -1416,6 +1232,92 @@ const onCustomDepositInput = (val) => {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 时间选择器样式 */
|
||||||
|
.picker-value {
|
||||||
|
padding: 15rpx 20rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 结束时间行布局(时间选择器 + 当天/次日切换) */
|
||||||
|
.end-time-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 当天/次日切换样式 */
|
||||||
|
.next-day-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-day-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #00AC4E;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-day-divider {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ccc;
|
||||||
|
margin: 0 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 时间信息显示 */
|
||||||
|
.time-info {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-info-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-info-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-info-value {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 时间错误提示 */
|
||||||
|
.time-error {
|
||||||
|
margin: 16rpx 0;
|
||||||
|
padding: 16rpx;
|
||||||
|
background-color: #fff2f0;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 可点击的行(年龄范围显示) */
|
/* 可点击的行(年龄范围显示) */
|
||||||
.clickable-row {
|
.clickable-row {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user