Browse Source

入馆预约

master
xuhuajiao 1 month ago
parent
commit
e4504d46d1
  1. 454
      subpkg/pages/seat-booking/seat-booking-1.vue
  2. 0
      subpkg/pages/seat-booking/seat-booking-chang.vue
  3. 347
      subpkg/pages/seat-booking/seat-booking.vue

454
subpkg/pages/seat-booking/seat-booking-1.vue

@ -0,0 +1,454 @@
<template>
<view class="booking-detail">
<view class="section">
<view class="section-title">日期选择</view>
<view class="calendar">
<view class="calendar-header">
<text class="month">{{ currentYear }}{{ currentMonth }}</text>
</view>
<view class="calendar-weekdays">
<text v-for="day in weekdays" :key="day" class="weekday">{{ day }}</text>
</view>
<view class="calendar-days">
<view
v-for="(day, index) in calendarDays"
:key="index"
class="day-item"
:class="{
'other-month': !day.currentMonth,
'selected': day.date === selectedDate,
'disabled': !day.available,
'today': day.isToday
}"
@click="selectDate(day)"
>
<text class="day-num">{{ day.day }}</text>
<text v-if="day.available && !day.isToday" class="available-tag">可预约</text>
<text v-if="day.isToday" class="today-tag">今天</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: '',
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
calendarDays: [],
timeSlots: [],
//
reserveConfig: null
};
},
onLoad(options) {
//
this.reserveConfig = {
"frequency": "30",
"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: {
// 1~7 (=1=7)
getWeekNum(dateStr) {
const d = new Date(dateStr);
let day = d.getDay(); // 0,1...6
if(day === 0) return 7
return day
},
generateCalendar() {
const year = this.currentYear;
const month = this.currentMonth;
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
const daysInMonth = lastDay.getDate();
const startWeekday = firstDay.getDay();
const today = new Date();
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
// 1. frequency
let allowDateList = [];
const freq = Number(this.reserveConfig.frequency);
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 days = [];
const prevMonthLastDay = new Date(year, month - 1, 0).getDate();
for (let i = startWeekday - 1; i >= 0; i--) {
days.push({
day: prevMonthLastDay - i,
date: '',
currentMonth: false,
available: false,
isToday: false
});
}
//
for (let i = 1; i <= daysInMonth; i++) {
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(i).padStart(2, '0')}`;
const isToday = dateStr === todayStr;
// frequency
const inFreqRange = allowDateList.includes(dateStr);
let available = false;
if(inFreqRange){
// 1~7
const weekNo = this.getWeekNum(dateStr);
const weekCfg = this.reserveConfig.weekDays.find(w => w.id === weekNo)
if(weekCfg && weekCfg.checked){
available = true
}
}
days.push({
day: i,
date: dateStr,
currentMonth: true,
available: available,
isToday: isToday
});
}
const remainingDays = 42 - days.length;
for (let i = 1; i <= remainingDays; i++) {
days.push({
day: i,
date: '',
currentMonth: false,
available: false,
isToday: false
});
}
this.calendarDays = days;
//
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
}
// times
this.timeSlots = weekCfg.times.map((t, idx)=>{
return {
id: idx + 1,
time: t,
available: true
}
})
},
selectDate(day) {
if (!day.available || !day.currentMonth) 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;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 16px;
}
/* 日历 */
.calendar-header {
text-align: center;
margin-bottom: 12px;
}
.month {
font-size: 16px;
color: #333;
font-weight: bold;
}
.calendar-weekdays {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.weekday {
width: 14.28%;
text-align: center;
font-size: 13px;
color: #999;
}
.calendar-days {
display: flex;
flex-wrap: wrap;
}
.day-item {
width: 14.28%;
aspect-ratio: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 6px;
border-radius: 6px;
position: relative;
}
.day-item.other-month .day-num {
color: #ccc;
}
.day-item.disabled .day-num {
color: #ccc;
}
.day-item.selected {
background-color: #01a4fe;
}
.day-item.selected .day-num {
color: #fff;
font-size: 15px;
font-weight: bold;
}
.day-item.selected .available-tag,
.day-item.selected .today-tag {
color: rgba(255, 255, 255, 0.9);
font-size: 10px;
}
.day-item.today:not(.selected) {
background-color: #fff3cd;
}
.day-item.today:not(.selected) .day-num {
color: #856404;
}
.day-num {
font-size: 14px;
color: #333;
}
.available-tag {
font-size: 10px;
color: #01a4fe;
margin-top: 2px;
}
.today-tag {
font-size: 10px;
color: #856404;
margin-top: 2px;
}
/* 时间选择 */
.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>

0
subpkg/pages/seat-booking/seat-booking copy.vue → subpkg/pages/seat-booking/seat-booking-chang.vue

347
subpkg/pages/seat-booking/seat-booking.vue

@ -1,30 +1,32 @@
<template>
<view class="booking-detail">
<view class="section">
<view class="section-title">日期选择</view>
<view class="calendar">
<view class="calendar-header">
<text class="month">{{ currentYear }}{{ currentMonth }}</text>
<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-weekdays">
<text v-for="day in weekdays" :key="day" class="weekday">{{ day }}</text>
</view>
<view class="calendar-days">
<view class="calendar-days-grid">
<view
v-for="(day, index) in calendarDays"
:key="index"
class="day-item"
class="day-card"
:class="{
'other-month': !day.currentMonth,
'selected': day.date === selectedDate,
'disabled': !day.available,
'today': day.isToday
'card-selected': day.date === selectedDate,
'card-available': day.available,
'card-disabled': !day.available,
'card-today': day.isToday
}"
@click="selectDate(day)"
>
<text class="day-num">{{ day.day }}</text>
<text v-if="day.available && !day.isToday" class="available-tag">开放日</text>
<text v-if="day.isToday" class="today-tag">今天</text>
<!-- 今天左上角纯色块三角无文字 -->
<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>
@ -61,83 +63,150 @@ export default {
currentMonth: new Date().getMonth() + 1,
selectedDate: '',
selectedTime: '',
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
calendarDays: [],
timeSlots: [
{ id: 1, time: '09:15-12:45', available: true },
{ id: 2, time: '13:00-16:30', available: false },
{ id: 3, time: '16:45-20:15', available: true }
]
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 year = this.currentYear;
const month = this.currentMonth;
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
const daysInMonth = lastDay.getDate();
const startWeekday = firstDay.getDay();
const today = new Date();
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
const days = [];
const prevMonthLastDay = new Date(year, month - 1, 0).getDate();
for (let i = startWeekday - 1; i >= 0; i--) {
days.push({
day: prevMonthLastDay - i,
date: '',
currentMonth: false,
available: false,
isToday: false
});
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 todayDate = new Date();
const availableDates = [];
for (let i = 0; i < 7; i++) {
const date = new Date(todayDate);
date.setDate(todayDate.getDate() + i);
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
availableDates.push(dateStr);
}
for (let i = 1; i <= daysInMonth; i++) {
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(i).padStart(2, '0')}`;
const isAvailable = availableDates.includes(dateStr);
const isToday = dateStr === todayStr;
days.push({
day: i,
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,
currentMonth: true,
available: isAvailable,
isToday: isToday
});
}
weekText: this.getWeekText(weekNo),
available: available,
isToday: dateStr === todayStr
})
})
this.calendarDays = list
const remainingDays = 42 - days.length;
for (let i = 1; i <= remainingDays; i++) {
days.push({
day: i,
date: '',
currentMonth: false,
available: false,
isToday: false
});
const firstAvail = this.calendarDays.find(d => d.available);
if(firstAvail){
this.selectedDate = firstAvail.date
this.refreshTimeSlots(this.selectedDate)
}else{
this.selectedDate = ''
this.timeSlots = []
}
},
this.calendarDays = days;
if (availableDates.length > 0) {
this.selectedDate = availableDates[0];
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 || !day.currentMonth) return;
if (!day.available) return;
this.selectedDate = day.date;
this.refreshTimeSlots(day.date)
},
selectTime(slot) {
if (!slot.available) return;
@ -185,93 +254,109 @@ export default {
padding: 16px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 16px;
.calendar-wrap{
background:#ffffff;
border-radius:16px;
// padding:10px;
}
/* 日历 */
.calendar-header {
text-align: center;
.calendar-top{
display:flex;
justify-content:space-between;
align-items:center;
margin-bottom:12px;
}
.month {
.calendar-title{
font-size:17px;
font-weight:bold;
color:#222;
}
.calendar-month{
font-size:16px;
color:#333;
font-weight:bold;
}
.calendar-weekdays {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.weekday {
width: 14.28%;
text-align: center;
font-size: 13px;
color: #999;
}
.calendar-days {
.calendar-days-grid{
display:flex;
flex-wrap:wrap;
}
.day-item {
width: 14.28%;
aspect-ratio: 1;
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;
margin-bottom: 6px;
border-radius: 6px;
position:relative;
overflow: hidden;
}
.day-item.other-month .day-num {
color: #ccc;
}
.day-item.disabled .day-num {
color: #ccc;
/* 左上角纯三角色块,无文字,模仿截图角标 */
.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;
}
.day-item.selected {
background-color: #01a4fe;
.card-week{
font-size:12px;
color:#666;
margin-bottom:4px;
}
.day-item.selected .day-num {
color: #fff;
font-size: 15px;
.card-num{
font-size:18px;
font-weight:bold;
color:#333;
}
.day-item.selected .available-tag,
.day-item.selected .today-tag {
color: rgba(255, 255, 255, 0.9);
font-size: 10px;
.card-status{
font-size:11px;
margin-top:4px;
}
.day-item.today:not(.selected) {
background-color: #fff3cd;
/* 可预约 */
.day-card.card-available{
background:#e8f4fd;
}
.day-item.today:not(.selected) .day-num {
color: #856404;
.day-card.card-available .card-status{
color:#01a4fe;
}
.day-num {
font-size: 14px;
color: #333;
/* 闭馆(不可选) */
.day-card.card-disabled{
background:#f7f8fa;
}
.available-tag {
font-size: 10px;
color: #01a4fe;
margin-top: 2px;
.day-card.card-disabled .card-week,
.day-card.card-disabled .card-num,
.day-card.card-disabled .card-status{
color:#999;
}
.today-tag {
font-size: 10px;
color: #856404;
margin-top: 2px;
/* 选中状态 */
.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;

Loading…
Cancel
Save