|
|
<template> <view class="booking-detail"> <view class="section"> <view class="calendar-wrap"> <view class="calendar-top"> <text class="calendar-title">选择入馆日期</text> <text class="calendar-month">{{ currentYear }}.{{ String(currentMonth).padStart(2,'0') }}</text> </view> <view class="calendar-days-grid"> <view v-for="(day, index) in calendarDays" :key="index" class="day-card" :class="{ 'card-selected': day.date === selectedDate, 'card-available': day.available, 'card-disabled': !day.available, 'card-today': day.isToday }" @click="selectDate(day)" > <!-- 今天左上角纯色块三角,无文字 --> <view v-if="day.isToday" class="corner-block-today"></view> <!-- 如果是今天就显示“今天”,否则显示星期 --> <text class="card-week">{{ day.isToday ? '今天' : day.weekText }}</text> <text class="card-num">{{ day.day }}</text> <text class="card-status"> {{ day.available ? '可预约' : '' }} </text> </view> </view> </view> </view>
<view class="section"> <view class="section-title">时间选择</view> <view class="time-slots"> <view v-for="slot in timeSlots" :key="slot.id" class="time-slot" :class="{ 'selected': selectedTime === slot.time }" @click="selectTime(slot)" > <text class="time-text">{{ slot.time }}</text> <text v-if="slot.available" class="available-badge">可选</text> <text v-else class="unavailable-badge">已满</text> </view> </view> </view>
<view class="bottom-bar"> <button class="btn-primary" @click="confirmBooking">确认预约</button> </view> </view></template>
<script>export default { data() { return { currentYear: new Date().getFullYear(), currentMonth: new Date().getMonth() + 1, selectedDate: '', selectedTime: '', calendarDays: [], timeSlots: [], reserveConfig: null }; }, onLoad(options) { this.reserveConfig = { "frequency": "15", "weekDays": [ { "id": 1, "name": "周一", "checked": true, "times": [ "00:00 - 23:59" ], "showPicker": false, "newTime": "" }, { "id": 2, "name": "周二", "checked": false, "times": [], "showPicker": false, "newTime": "" }, { "id": 3, "name": "周三", "checked": false, "times": [], "showPicker": false, "newTime": "" }, { "id": 4, "name": "周四", "checked": false, "times": [], "showPicker": false, "newTime": "" }, { "id": 5, "name": "周五", "checked": false, "times": [], "showPicker": false, "newTime": "" }, { "id": 6, "name": "周六", "checked": false, "times": [], "showPicker": false, "newTime": "" }, { "id": 7, "name": "周日", "checked": false, "showPicker": false, "newTime": "" } ] } this.generateCalendar(); }, methods: { getWeekNum(dateStr) { const d = new Date(dateStr); const day = d.getDay(); return day === 0 ? 7 : day }, getWeekText(weekNo){ const map = {1:'周一',2:'周二',3:'周三',4:'周四',5:'周五',6:'周六',7:'周日'} return map[weekNo] },
generateCalendar() { const today = new Date(); const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; const freq = Number(this.reserveConfig.frequency); const allowDateList = []; for(let i = 0; i < freq; i++){ const temp = new Date(today); temp.setDate(today.getDate() + i); const ds = `${temp.getFullYear()}-${String(temp.getMonth()+1).padStart(2,'0')}-${String(temp.getDate()).padStart(2,'0')}` allowDateList.push(ds) }
const list = [] allowDateList.forEach(dateStr=>{ const weekNo = this.getWeekNum(dateStr) const weekCfg = this.reserveConfig.weekDays.find(w => w.id === weekNo) const available = !!weekCfg?.checked const d = new Date(dateStr) list.push({ day: d.getDate(), date: dateStr, weekText: this.getWeekText(weekNo), available: available, isToday: dateStr === todayStr }) }) this.calendarDays = list
const firstAvail = this.calendarDays.find(d => d.available); if(firstAvail){ this.selectedDate = firstAvail.date this.refreshTimeSlots(this.selectedDate) }else{ this.selectedDate = '' this.timeSlots = [] } },
refreshTimeSlots(dateStr){ this.selectedTime = '' if(!dateStr) { this.timeSlots = [] return } const weekNo = this.getWeekNum(dateStr) const weekCfg = this.reserveConfig.weekDays.find(w => w.id === weekNo) if(!weekCfg || !weekCfg.checked){ this.timeSlots = [] return } this.timeSlots = weekCfg.times.map((t, idx)=>{ return { id: idx + 1, time: t, available: true } }) },
selectDate(day) { if (!day.available) return; this.selectedDate = day.date; this.refreshTimeSlots(day.date) }, selectTime(slot) { if (!slot.available) return; this.selectedTime = slot.time; }, confirmBooking() { if (!this.selectedDate) { uni.showToast({ title: '请选择日期', icon: 'none' }); return; } if (!this.selectedTime) { uni.showToast({ title: '请选择时间段', icon: 'none' }); return; } uni.showLoading({ title: '预约中...' }); setTimeout(() => { uni.hideLoading(); uni.showModal({ title: '预约成功', content: `您已成功预约\n日期:${this.selectedDate}\n时间:${this.selectedTime}`, showCancel: false, confirmText: '确定', success: () => { uni.navigateBack({ delta: 2 }); } }); }, 1500); } }};</script>
<style lang="scss" scoped>.booking-detail { min-height: 100vh; background-color: #f5f5f5; padding-bottom: 70px;}
.section { background-color: #fff; margin: 10px; border-radius: 12px; padding: 16px;}
.calendar-wrap{ background:#ffffff; border-radius:16px; // padding:10px;
}.calendar-top{ display:flex; justify-content:space-between; align-items:center; margin-bottom:12px;}.calendar-title{ font-size:17px; font-weight:bold; color:#222;}.calendar-month{ font-size:16px; color:#333; font-weight:bold;}
.calendar-days-grid{ display:flex; flex-wrap:wrap; gap:8px;}.day-card{ width: calc((100% - 48px) / 7); // aspect-ratio: 0.85;
background:#f7f8fa; border-radius: 4px; padding: 4px 0; display:flex; flex-direction:column; align-items:center; justify-content:center; position:relative; overflow: hidden;}
/* 左上角纯三角色块,无文字,模仿截图角标 */.corner-block-today{ position:absolute; top:0; left:0; width: 0; height: 0; border-top: 12px solid #01a4fe; border-right: 12px solid transparent; border-bottom-left-radius: 0;}
.card-week{ font-size:12px; color:#666; margin-bottom:4px;}.card-num{ font-size:18px; font-weight:bold; color:#333;}.card-status{ font-size:11px; margin-top:4px;}
/* 可预约 */.day-card.card-available{ background:#e8f4fd;}.day-card.card-available .card-status{ color:#01a4fe;}
/* 闭馆(不可选) */.day-card.card-disabled{ background:#f7f8fa;}.day-card.card-disabled .card-week,.day-card.card-disabled .card-num,.day-card.card-disabled .card-status{ color:#999;}
/* 选中状态 */.day-card.card-selected{ background:#01a4fe;}.day-card.card-selected .card-week,.day-card.card-selected .card-num,.day-card.card-selected .card-status{ color:#ffffff;}
/* 时间选择 */.section-title { font-size: 16px; font-weight: bold; color: #333; margin-bottom: 16px;}.time-slots { display: flex; flex-wrap: wrap; gap: 10px;}.time-slot { width: calc(50% - 5px); display: flex; align-items: center; justify-content: space-between; padding: 12px; background-color: #f8f8f8; border-radius: 8px; border: 1px solid transparent;}.time-slot.selected { background-color: #e8f4fd; border-color: #01a4fe;}.time-text { font-size: 14px; color: #333;}.available-badge { font-size: 12px; color: #01a4fe; background-color: #e8f4fd; padding: 2px 8px; border-radius: 10px;}.unavailable-badge { font-size: 12px; color: #999; background-color: #f0f0f0; padding: 2px 8px; border-radius: 10px;}
/* 底部按钮 */.bottom-bar { position: fixed; bottom: 0; left: 0; right: 0; padding: 10px; background-color: #fff; box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.1);}.btn-primary { width: 100%; height: 44px; line-height: 44px; background-color: #01a4fe; color: #fff; border-radius: 22px; font-size: 15px; border: none;}.btn-primary::after { border: none;}</style>
|