This commit is contained in:
zpc 2025-09-14 23:16:52 +08:00
parent d6a5977cc4
commit 575ccce769
5 changed files with 359 additions and 100 deletions

2
components.d.ts vendored
View File

@ -16,6 +16,8 @@ declare module 'vue' {
MahjongCard: typeof import('./components/index/MahjongCard.vue')['default']
NoData: typeof import('./components/com/page/no-data.vue')['default']
NoEmpty: typeof import('./components/com/index/NoEmpty.vue')['default']
PickerData: typeof import('./components/com/appointment/picker-data.vue')['default']
RadioSelect: typeof import('./components/com/appointment/radio-select.vue')['default']
ReservationEvaluate: typeof import('./components/com/page/reservation-evaluate.vue')['default']
ReservationItem: typeof import('./components/com/page/reservation-item.vue')['default']
ReservationPopup: typeof import('./components/com/index/ReservationPopup.vue')['default']

View File

@ -23,8 +23,13 @@ const props = defineProps({
display: flex;
}
.spacer-20 { width: 20rpx; }
.flex-1 { flex: 1; }
.spacer-20 {
width: 20rpx;
}
.flex-1 {
flex: 1;
}
.label-field__label {
font-size: 26rpx;

View File

@ -0,0 +1,86 @@
<template>
<view class="u-picker-data">
<up-picker
:show="visible"
:columns="optionsInner"
:keyName="labelKey"
:defaultIndex="defaultIndex"
@confirm="confirm"
@cancel="cancel"
@close="close">
</up-picker>
</view>
</template>
<script>
export default {
name: 'com-appointment-picker-data',
data() {
return {
visible: false,
current: '',
defaultIndex: [],
options: [],
valueKey: 'id',
labelKey: 'name',
_resolver: null,
_rejecter: null,
}
},
computed: {
optionsInner() {
return [this.options];
}
},
methods: {
// props
show(params = {}) {
const { options = [], valueKey = 'id', labelKey = 'name', modelValue } = params;
this.options = Array.isArray(options) ? options : [];
this.valueKey = valueKey;
this.labelKey = labelKey;
this.current = '';
this.defaultIndex = [];
if (modelValue !== undefined && modelValue !== null && this.options.length > 0) {
this.options.forEach((ele, index) => {
if (ele[this.valueKey] == modelValue) {
this.current = ele[this.labelKey]
this.defaultIndex = [index]
}
})
}
this.visible = true;
return new Promise((resolve, reject) => {
this._resolver = resolve;
this._rejecter = reject;
})
},
cancel() {
this.visible = false;
if (this._resolver) this._resolver(undefined);
this._resolver = null;
this._rejecter = null;
},
close() {
this.visible = false;
if (this._resolver) this._resolver(undefined);
this._resolver = null;
this._rejecter = null;
},
confirm(e) {
const { columnIndex, value } = e;
this.visible = false;
const selected = value && value[0] ? value[0] : undefined;
this.defaultIndex = columnIndex;
this.current = selected ? selected[this.labelKey] : '';
if (this._resolver) this._resolver(selected);
this._resolver = null;
this._rejecter = null;
}
}
}
</script>
<style lang="scss" scoped>
/* 空样式占位,可按需补充样式 */
</style>

View File

@ -0,0 +1,41 @@
<template>
<radio-group @change="onChange">
<label v-for="(opt, idx) in options" :key="idx" style="font-size: 24rpx; margin-right:16rpx;">
<radio :value="String(opt.value)" color="#00AC4E" style="transform:scale(0.7);" :checked="isChecked(opt.value)" />{{ opt.text }}
</label>
</radio-group>
</template>
<script>
export default {
name: 'com-appointment-radio-select',
props: {
modelValue: {
type: [String, Number],
default: ''
},
options: {
type: Array,
default: () => []
}
},
emits: ['update:modelValue', 'change'],
methods: {
isChecked(val) {
if (typeof this.modelValue === 'number') return Number(this.modelValue) === Number(val);
return String(this.modelValue) === String(val);
},
onChange(e) {
const raw = e && e.detail ? e.detail.value : undefined;
const newVal = typeof this.modelValue === 'number' ? Number(raw) : raw;
this.$emit('update:modelValue', newVal);
this.$emit('change', newVal);
}
}
}
</script>
<style scoped>
</style>

View File

@ -17,41 +17,38 @@
{{ getDayDescription(endTimeStr) }}
</time-select-cell>
<view :style="{ height: lineHeight }"></view>
<time-select-cell label="房间" @select="openUpDatesTimePickerEnd">
<up-picker-data v-model="reservationInfo.room_id" title="请选择房间" :options="[]" valueKey="id"
labelKey="name">
</up-picker-data>
<time-select-cell label="房间" @select="openRoomPicker">
{{ getRoomPickerName() }}
</time-select-cell>
</card-container>
<card-container marginTop="30rpx">
<label-field label="组局名称">
<view style="width:100%; ">
<input class="uni-input" placeholder="请输入组局名称" style="font-size: 24rpx; width: 89%;" />
<view style="border: 1px solid #515151 ;border-radius: 4px;">
<up-input placeholder="请输入内容" border="surround" v-model="reservationInfo.title"></up-input>
</view>
</label-field>
<view :style="{ height: lineHeight }"></view>
<label-field label="人数">
<uni-data-select v-model="peopleValue" :localdata="peopleRange"
@change="changeLog"></uni-data-select>
<uni-data-select v-model="reservationInfo.player_count" :placeholder="peopleText"
:localdata="peopleRange"></uni-data-select>
</label-field>
<view :style="{ height: lineHeight }"></view>
<label-field label="玩法类型">
<uni-data-select v-model="peopleValue" :localdata="peopleRange"
@change="changeLog"></uni-data-select>
<uni-data-select v-model="reservationInfo.game_type" placeholder="请选择玩法类型"
:localdata="gameTypeRange" @change="gameTypeRangeChange"></uni-data-select>
</label-field>
<view :style="{ height: lineHeight }"></view>
<label-field label="具体规则">
<uni-data-select v-model="peopleValue" :localdata="peopleRange"
@change="changeLog"></uni-data-select>
<uni-data-select v-model="reservationInfo.game_rule" :placeholder="gameRuleText"
:localdata="gameRuleRange" @change="gameRuleRangeChange"></uni-data-select>
</label-field>
<view :style="{ height: lineHeight }"></view>
<label-field label="其他补充">
<view
style="width: 89%; background-color: white; border-radius: 10rpx; height: 70rpx; display: flex; align-items: center; padding-left: 20rpx; border: 1rpx solid #515151;">
<input class="uni-input" placeholder="请输入补充信息" style="font-size: 24rpx; width: 100%;" />
<view style="border: 1px solid #515151 ;border-radius: 4px;">
<up-input placeholder="请输入其他补充内容" v-model="reservationInfo.extra_info"></up-input>
</view>
</label-field>
@ -61,28 +58,11 @@
<card-container marginTop="30rpx">
<label-slect-field label="是否禁烟">
<radio-group @change="() => { }">
<label style="font-size: 24rpx;margin-right:16rpx;">
<radio value="r1" color="#00AC4E" style="transform:scale(0.7);" :checked="true" />禁烟
</label>
<label style="font-size: 24rpx; margin-right:16rpx;">
<radio value="r2" color="#00AC4E" style="transform:scale(0.7);" />不禁烟
</label>
</radio-group>
<com-appointment-radio-select :options="smokingOptions" v-model="reservationInfo.is_smoking" />
</label-slect-field>
<view :style="{ height: lineHeight }"></view>
<label-slect-field label="性别">
<radio-group @change="() => { }">
<label style="font-size: 24rpx; margin-right:16rpx;">
<radio value="r1" color="#00AC4E" style="transform:scale(0.7);" :checked="true" />不限
</label>
<label style="font-size: 24rpx; margin-right:16rpx;">
<radio value="r2" color="#00AC4E" style="transform:scale(0.7);" />
</label>
<label style="font-size: 24rpx; margin-right:16rpx;">
<radio value="r3" color="#00AC4E" style="transform:scale(0.7);" />
</label>
</radio-group>
<com-appointment-radio-select :options="genderOptions" v-model="reservationInfo.gender_limit" />
</label-slect-field>
<view :style="{ height: lineHeight }"></view>
<label-slect-field label="信誉">
@ -106,17 +86,7 @@
<card-container marginTop="30rpx">
<label-slect-field label="鸽子费">
<radio-group @change="() => { }">
<label style="font-size: 24rpx;margin-right:16rpx;">
<radio value="r1" color="#00AC4E" style="transform:scale(0.7);" :checked="true" />0
</label>
<label style="font-size: 24rpx;margin-right:16rpx;">
<radio value="r2" color="#00AC4E" style="transform:scale(0.7);" />5
</label>
<label style="font-size: 24rpx;margin-right:16rpx;">
<radio value="r3" color="#00AC4E" style="transform:scale(0.7);" />10
</label>
</radio-group>
<com-appointment-radio-select :options="depositOptions" v-model="reservationInfo.deposit_fee" />
</label-slect-field>
<view :style="{ height: lineHeight }"></view>
<text
@ -134,51 +104,71 @@
</view>
<com-appointment-picker-data ref="roomPickerRef" />
</com-page-container-base>
</template>
<script setup>
import { ref } from 'vue';
import { getDayDescription, ceilMinuteToNext5 } from '@/common/system/timeUtile';
import { union } from 'lodash';
import {
ref,
watch
} from 'vue';
import {
getConfigData
} from '@/common/server/config'
import {
getDayDescription,
ceilMinuteToNext5
} from '@/common/system/timeUtile';
import {
forEach,
union
} from 'lodash';
import TimeSelectCell from '@/components/com/appointment/time-select-cell.vue'
import LabelField from '@/components/com/appointment/label-field.vue'
import LabelSlectField from '@/components/com/appointment/label-slect-field.vue'
import CardContainer from '@/components/com/appointment/card-container.vue'
import TagSelect from '@/components/com/appointment/tag-select.vue'
import ComAppointmentPickerData from '@/components/com/appointment/picker-data.vue'
import ComAppointmentRadioSelect from '@/components/com/appointment/radio-select.vue'
import {
getReservationRoomList
} from '@/common/server/interface/sq'
const _containerBase = ref(null)
const startTimeStr = ref(0)
const endTimeStr = ref(0)
const hours = ref("")
const minutes = ref("")
const roomValue = ref("")
const peopleValue = ref("")
const lineHeight = ref("15rpx")
const timeRange = ref(["2小时", "3小时", "4小时", "自定义"])
const timeRangeValue = ref("")
//
const roomOptions = ref([])
const roomPickerRef = ref(null)
//
const reservationInfo = ref({
room_id: 0,//id
start_time: 0,//
end_time: 0,//
max_age: 0,//
min_age: 0,//
title: '',//
extra_info: '',//
game_rule: '',//
game_type: '',//
gender_limit: 1,//
is_smoking: 0,//
credit_limit: 0,//
deposit_fee: 0,//
player_count: 0,//
room_id: 0, //id
room_name: '请选择房间', //
start_time: 0, //
end_time: 0, //
max_age: 0, //
min_age: 0, //
title: '', //
extra_info: '', //
game_rule: '', //
game_type: '', //
gender_limit: 0, //
is_smoking: 2, //
credit_limit: 0, //
deposit_fee: 0, //
player_count: 0, //
});
//
const onTimeRangeChange = async (val) => {
timeRangeValue.value = val;
console.log('timeRange change:', val)
if (val != "") {
await openUpDatesTimePicker();
await openUpDatesTimePicker(false);
if (startTimeStr.value > 0) {
var str = val;
if (str == "2小时") {
@ -188,23 +178,35 @@ const onTimeRangeChange = async (val) => {
} else if (str == "4小时") {
endTimeStr.value = startTimeStr.value + 1000 * 60 * 60 * 4;
} else {
await openUpDatesTimePickerEnd();
await openUpDatesTimePickerEnd(false);
}
}
}
}
const getRoomPickerName = () => {
return reservationInfo.value.room_name;
}
const openUpDatesTimePicker = async () => {
const openUpDatesTimePicker = async (isManual = true) => {
var now = startTimeStr.value;
var min = Date.now();
min += 1000 * 60 * 30;
min = ceilMinuteToNext5(min).valueOf();
if (startTimeStr.value == 0) {
now = Date.now();
now += 1000 * 60 * 30;
now = ceilMinuteToNext5(now).valueOf();
}
const startTime = await _containerBase.value.openUpDatesTimePicker(now, now, "预约开始时间")
const startTime = await _containerBase.value.openUpDatesTimePicker(now, min, "预约开始时间")
startTimeStr.value = startTime
//
reservationInfo.value.start_time = Math.floor(startTimeStr.value / 1000)
//
if (isManual) {
timeRangeValue.value = "自定义"
}
}
const openUpDatesTimePickerEnd = async () => {
const openUpDatesTimePickerEnd = async (isManual = true) => {
if (startTimeStr.value == 0) {
uni.showToast({
title: '请先选择开始时间',
@ -213,26 +215,110 @@ const openUpDatesTimePickerEnd = async () => {
return;
}
var now = endTimeStr.value;
var min = (startTimeStr.value + 1000 * 60 * 30);
if (now == 0) {
now = (startTimeStr.value + 1000 * 60 * 30);
}
//minDate+1000*60*30 30
const endTime = await _containerBase.value.openUpDatesTimePicker(now, now, "预约结束时间")
const endTime = await _containerBase.value.openUpDatesTimePicker(now, min, "预约结束时间")
endTimeStr.value = endTime
//
reservationInfo.value.end_time = Math.floor(endTimeStr.value / 1000)
//
if (isManual) {
timeRangeValue.value = "自定义"
}
}
const maxPlayerCount = ref(0)
const openRoomPicker = async () => {
// /
if (!reservationInfo.value.start_time || !reservationInfo.value.end_time) {
uni.showToast({
title: '请先选择开始和结束时间',
icon: 'none'
})
return
}
if (reservationInfo.value.end_time <= reservationInfo.value.start_time) {
uni.showToast({
title: '结束时间需晚于开始时间',
icon: 'none'
})
return
}
//
if (!roomOptions.value || roomOptions.value.length === 0) {
await tryLoadRooms()
}
if (!roomOptions.value || roomOptions.value.length === 0) {
uni.showToast({
title: '暂无可预约房间',
icon: 'none'
})
return
}
const selected = await roomPickerRef.value.show({
options: roomOptions.value,
valueKey: 'id',
labelKey: 'name',
modelValue: reservationInfo.value.room_id
})
if (selected) {
reservationInfo.value.room_id = selected.id
reservationInfo.value.room_name = selected.name
maxPlayerCount.value = selected.capacity
}
}
const range = ref([
{ value: 0, text: '808号-大包30元/小时' },
{ value: 1, text: '807号-大包30元/小时' },
{ value: 2, text: '806号-大包30元/小时' },
{ value: 3, text: '805号-大包30元/小时' },
])
const peopleRange = ref([])
const peopleText = ref("请先选择房间");
const gameTypeRange = ref([])
const gameRuleRange = ref([])
const gameRuleText = ref("请先选择玩法类型");
const gameTypeRangeChange = (e) => {
console.log('gameTypeRangeChange:', e)
// gameRuleRange.value
if (e == 0) {
gameRuleRange.value = [];
reservationInfo.value.game_rule = 0;
gameRuleText.value = "请先选择玩法类型";
} else {
var f = gameTypeRange.value.find(it => it.value == e);
console.log('f', f);
gameRuleText.value = "请选择具体规则";
var temp = [];
const peopleRange = ref([
{ value: 0, text: '1人' },
{ value: 1, text: '2人' },
{ value: 2, text: '3人' },
{ value: 3, text: '4人' },
if (f.children != null && f.children.length > 0) {
f.children.forEach(e => {
temp.push({
value: e.id,
text: e.name
})
})
}
gameRuleRange.value = temp;
}
}
const gameRuleRangeChange = (e) => {
console.log('gameRuleRangeChange:', e)
}
const smokingOptions = ref([
{ value: 2, text: '不禁烟' },
{ value: 1, text: '禁烟' },
])
const genderOptions = ref([
{ value: 0, text: '不限' },
{ value: 1, text: '男' },
{ value: 2, text: '女' },
])
const depositOptions = ref([
{ value: 0, text: '0元' },
{ value: 5, text: '5元' },
{ value: 10, text: '10元' },
])
const currentValue = ref(0)
@ -257,26 +343,65 @@ const decrement = () => {
const changeLog = (e) => {
console.log('change事件:', e)
setTime()
}
const changeLog2 = (e) => {
console.log('change事件2:', e)
setTime()
}
//
watch([startTimeStr, endTimeStr], async ([s, e]) => {
reservationInfo.value.start_time = s ? Math.floor(s / 1000) : 0
reservationInfo.value.end_time = e ? Math.floor(e / 1000) : 0
await tryLoadRooms()
})
const setTime = () => {
if (startTimeStr.value === "" || endTimeStr.value === "") {
return
watch(() => reservationInfo.value.room_id, async (newId, oldId) => {
console.log('room_id changed:', oldId, '->', newId);
if (newId == 0) {
reservationInfo.value.player_count = 0;
peopleRange.value = [];
peopleText.value = "请先选择房间";
} else {
var t = [];
peopleText.value = "请选择游玩人数";
for (let i = 2; i <= maxPlayerCount.value; i++) {
t.push({
value: i,
text: i + '人'
});
}
console.log("peopleRange.value ", maxPlayerCount.value, t);
if (reservationInfo.value.player_count > maxPlayerCount.value) {
reservationInfo.value.player_count = 0;
}
peopleRange.value = t;
}
});
//
const tryLoadRooms = async () => {
if (!reservationInfo.value.start_time || !reservationInfo.value.end_time) return
if (reservationInfo.value.end_time <= reservationInfo.value.start_time) return
const list = await getReservationRoomList(reservationInfo.value.start_time, reservationInfo.value.end_time)
// list.push()
if (Array.isArray(list)) {
roomOptions.value = Array.isArray(list) ? list : []
} else {
roomOptions.value = []
}
//
console.log("房间id==>", reservationInfo.value.room_id, "房间列表==>", roomOptions.value);
if (!roomOptions.value.some(it => it.id === reservationInfo.value.room_id)) {
reservationInfo.value.room_id = 0;
reservationInfo.value.room_name = '请选择房间';
}
const startTime = new Date(startTimeStr.value.replace(/-/g, '/'))
const endTime = new Date(endTimeStr.value.replace(/-/g, '/'))
const timeDiff = endTime.getTime() - startTime.getTime()
const totalMinutes = Math.floor(timeDiff / 1000 / 60)
hours.value = Math.floor(totalMinutes / 60)
minutes.value = totalMinutes % 60
console.log(`两个时间相差 ${hours.value} 小时 ${minutes.value} 分钟`)
}
onLoad(async () => {
const config = await getConfigData();
console.log('config', config);
if (config != null && config.config != null) {
gameTypeRange.value = [...config.config.playingMethodOptions];
}
})
</script>
<style lang="scss">