diff --git a/miniprogram/.env b/miniprogram/.env index 7fda9c6..e405b71 100644 --- a/miniprogram/.env +++ b/miniprogram/.env @@ -1,2 +1,2 @@ VITE_ICE_API_BASE_URL=https://venue.test.chatgqt.top -VITE_COMPREHENSIVE_API_BASE_URL=https://venue2.test.chatgqt.top \ No newline at end of file +VITE_COMPREHENSIVE_API_BASE_URL=https://8002.frp.chatgqt.top \ No newline at end of file diff --git a/miniprogram/src/modules/comprehensive/api/privateBooking.js b/miniprogram/src/modules/comprehensive/api/privateBooking.js index dd2e852..c789585 100644 --- a/miniprogram/src/modules/comprehensive/api/privateBooking.js +++ b/miniprogram/src/modules/comprehensive/api/privateBooking.js @@ -46,10 +46,8 @@ const toPrivateBookingOptions = (backendData = {}) => ({ court_type_label: backendData.court_type_label || '', open_time: backendData.open_time || '', close_time: backendData.close_time || '', - unit_price: backendData.unit_price || '0.00', - duration_units: toNumber(backendData.duration_units, 2), + duration_units: toNumber(backendData.duration_units, 1), duration_minutes: toNumber(backendData.duration_minutes, 60), - amount_per_court: backendData.amount_per_court || '0.00', total_court_count: toNumber(backendData.total_court_count), time_options: toArray(backendData.time_options).map(toPrivateTimeOption) }) @@ -67,7 +65,8 @@ const toGridSlot = (backendSlot = {}) => ({ const toGridOccupation = (backendOcc = {}) => ({ court_id: backendOcc.court_id ?? null, start_at: backendOcc.start_at || '', - end_at: backendOcc.end_at || '' + end_at: backendOcc.end_at || '', + source_type: backendOcc.source_type || '' }) const toPrivateBookingGrid = (backendData = {}) => ({ @@ -76,10 +75,10 @@ const toPrivateBookingGrid = (backendData = {}) => ({ court_type_label: backendData.court_type_label || '', open_time: backendData.open_time || '', close_time: backendData.close_time || '', - unit_price: backendData.unit_price || '0.00', courts: toArray(backendData.courts).map(toGridCourt), time_slots: toArray(backendData.time_slots).map(toGridSlot), - occupations: toArray(backendData.occupations).map(toGridOccupation) + occupations: toArray(backendData.occupations).map(toGridOccupation), + slot_prices: backendData.slot_prices || {} }) export const privateBookingApi = { diff --git a/miniprogram/src/pages/comprehensive/private/confirm.vue b/miniprogram/src/pages/comprehensive/private/confirm.vue index 5642555..01a27da 100644 --- a/miniprogram/src/pages/comprehensive/private/confirm.vue +++ b/miniprogram/src/pages/comprehensive/private/confirm.vue @@ -21,7 +21,7 @@ 费用明细 - 半小时段 + 时段 {{ quote.unit_count }} 段 diff --git a/miniprogram/src/pages/comprehensive/private/index.vue b/miniprogram/src/pages/comprehensive/private/index.vue index 243b474..e944762 100644 --- a/miniprogram/src/pages/comprehensive/private/index.vue +++ b/miniprogram/src/pages/comprehensive/private/index.vue @@ -61,7 +61,9 @@ class="grid-cell" :class="getCellClass(court.id, si)" @tap="toggleCell(court.id, si)" - > + > + ¥{{ getCellPrice(court.id, si) }} + { for (const court of grid.value.courts) { status[court.id] = {} const occs = occupationMap.value[court.id] || [] + const courtPrices = grid.value?.slot_prices?.[court.id] || [] for (let si = 0; si < grid.value.time_slots.length; si++) { const slot = grid.value.time_slots[si] @@ -157,13 +160,23 @@ const cellStatus = computed(() => { continue } - const isOccupied = occs.some((occ) => { + const overlappingOcc = occs.find((occ) => { const occStart = dayjs(occ.start_at) const occEnd = dayjs(occ.end_at) return occStart.isBefore(slotEnd) && occEnd.isAfter(slotStart) }) - status[court.id][si] = isOccupied ? 'occupied' : 'available' + if (overlappingOcc) { + status[court.id][si] = overlappingOcc.source_type === 'lock' ? 'locked' : 'occupied' + continue + } + + if (!courtPrices[si]) { + status[court.id][si] = 'no_price' + continue + } + + status[court.id][si] = 'available' } } return status @@ -180,7 +193,7 @@ const currentTimeTop = computed(() => { const closeMin = parseTimeToMinutes(grid.value.close_time) const currentMin = now.value.hour() * 60 + now.value.minute() if (currentMin < openMin || currentMin > closeMin) return -1 - return HEADER_HEIGHT + ((currentMin - openMin) / 30) * CELL_HEIGHT + return HEADER_HEIGHT + ((currentMin - openMin) / 60) * CELL_HEIGHT }) const selectedCourtCount = computed(() => Object.keys(selectedCells.value).length) @@ -203,8 +216,15 @@ const selectionTimeText = computed(() => { }) const selectionAmount = computed(() => { - const priceVal = parseFloat(grid.value?.unit_price || '0') - return (priceVal * selectedTotalSlots.value).toFixed(2) + let total = 0 + for (const [courtId, slots] of Object.entries(selectedCells.value)) { + const prices = grid.value?.slot_prices?.[courtId] || [] + for (const slotIndex of slots) { + const p = parseFloat(prices[slotIndex] || '0') + total += p + } + } + return total.toFixed(2) }) onMounted(loadGrid) @@ -249,6 +269,11 @@ function getCellClass(courtId, slotIndex) { return { [`cell-${status}`]: true } } +function getCellPrice(courtId, slotIndex) { + const prices = grid.value?.slot_prices?.[courtId] || [] + return prices[slotIndex] || '' +} + function toggleCell(courtId, slotIndex) { const status = cellStatus.value[courtId]?.[slotIndex] if (status !== 'available') return @@ -530,6 +555,19 @@ defineExpose({ height: 107rpx; border-right: 1rpx solid $bg-color-hover; border-bottom: 1rpx solid $bg-color-hover; + display: flex; + align-items: center; + justify-content: center; +} + +.cell-price { + font-size: 22rpx; + font-weight: 700; + color: $text-color-muted; +} + +.cell-selected .cell-price { + color: #FFFFFF; } .cell-available { @@ -544,6 +582,26 @@ defineExpose({ background: $bg-color-hover; } +.cell-locked { + background: $bg-color-hover; + position: relative; +} + +.cell-locked::after { + content: '锁'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 20rpx; + font-weight: 700; + color: $text-color-light; +} + +.cell-no_price { + background: $bg-color-hover; +} + .cell-selected { background: $color-primary; }