327 lines
7.1 KiB
Vue
327 lines
7.1 KiB
Vue
<template>
|
||
<view class="container">
|
||
|
||
<!-- 信息栏 -->
|
||
<view class="info-bar">
|
||
<view class="info-title">住房档案变更(房屋坐落变更)</view>
|
||
<view class="info-dept">公积金管理中心</view>
|
||
</view>
|
||
|
||
<!-- 日历 -->
|
||
<view class="calendar-wrap">
|
||
<view class="calendar-header">
|
||
<text class="week-item" v-for="week in weekList" :key="week">{{ week }}</text>
|
||
</view>
|
||
|
||
<view class="date-row" v-for="(row, index) in calendarList" :key="index">
|
||
<view class="date-item" :class="{
|
||
active: item && item.active,
|
||
has: item && item.isAvailable,
|
||
disabled: !item || !item.isAvailable
|
||
}" v-for="(item, idx) in row" :key="idx" @click="selectDate(item)">
|
||
|
||
<text class="date-num">{{ item ? getDay(item.date) : '无' }}</text>
|
||
<text class="date-status" v-if="item">{{ item.isAvailable ? '有' : '无' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 时段 -->
|
||
<view class="time-list">
|
||
<view class="time-item" :class="{ selected: item.selected, disabled: item.remainingCount <= 0 }"
|
||
v-for="item in currentTimeSlots" :key="item.time" @click="selectTime(item)">
|
||
<view>{{ item.time }}</view>
|
||
<view>可预约 {{ item.remainingCount }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部 -->
|
||
<view class="footer-bar">
|
||
<view class="selected-info">已选:{{ selectedDate }} {{ selectedTime }}</view>
|
||
<view class="btn-box">
|
||
<button class="btn-gray" @click="handleGoBack">返回</button>
|
||
<button class="btn-blue" @click="handleNext">下一步</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
availableDate
|
||
} from '@/api/reserve.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
weekList: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
||
dateList: [],
|
||
calendarList: [],
|
||
currentTimeSlots: [],
|
||
selectedDate: "",
|
||
selectedTime: "",
|
||
itemData: {}
|
||
};
|
||
},
|
||
onLoad(options) {
|
||
this.itemData = JSON.parse(decodeURIComponent(options.data))
|
||
this.fetchData()
|
||
},
|
||
methods: {
|
||
fetchData() {
|
||
availableDate({
|
||
itemId: this.itemData.id
|
||
}).then(res => {
|
||
if (res.code === 200) {
|
||
this.dateList = res.data
|
||
this.initCalendar()
|
||
// 兼容无数据/首条不可选情况
|
||
const firstAva = this.dateList.find(item => item.isAvailable)
|
||
firstAva && this.selectDate(firstAva)
|
||
}
|
||
})
|
||
},
|
||
|
||
// 核心重写:100%贴合需求 - 按weekDayName对列+跨周换行+今日为起点前后空
|
||
initCalendar() {
|
||
// 初始化两行(最多跨周展示两行,满足业务场景)
|
||
const row1 = Array(7).fill(null) // 第一行(今日所在周)
|
||
const row2 = Array(7).fill(null) // 第二行(跨周后的下一周)
|
||
// 星期转列索引映射(固定匹配表头)
|
||
const weekToCol = {
|
||
"星期一": 0,
|
||
"星期二": 1,
|
||
"星期三": 2,
|
||
"星期四": 3,
|
||
"星期五": 4,
|
||
"星期六": 5,
|
||
"星期日": 6
|
||
}
|
||
// 今日是列表第一条,获取今日所在星期列,作为展示起点
|
||
const today = this.dateList[0]
|
||
if (!today) {
|
||
this.calendarList = [row1, row2]
|
||
return
|
||
}
|
||
const todayCol = weekToCol[today.weekDayName]
|
||
// 遍历所有日期,按规则分配到对应行/列
|
||
this.dateList.forEach(item => {
|
||
const col = weekToCol[item.weekDayName]
|
||
if (col === undefined) return
|
||
// 规则:今日所在列及右侧 → 第一行;今日所在列左侧(跨周)→ 第二行
|
||
if (col >= todayCol) {
|
||
row1[col] = item
|
||
} else {
|
||
row2[col] = item
|
||
}
|
||
})
|
||
// 最终日历列表(仅保留有数据的行,无数据则不展示)
|
||
this.calendarList = [row1]
|
||
if (row2.some(item => item)) {
|
||
this.calendarList.push(row2)
|
||
}
|
||
},
|
||
|
||
getDay(date) {
|
||
if (!date) return "";
|
||
return date.split("-")[2];
|
||
},
|
||
|
||
formatDate(date) {
|
||
const arr = date.split("-");
|
||
return arr[0] + "-" + arr[1] + "-" + arr[2];
|
||
},
|
||
|
||
selectDate(item) {
|
||
if (!item || !item.isAvailable) return;
|
||
this.selectedTime = ''
|
||
this.dateList.forEach(i => (i.active = false));
|
||
item.active = true;
|
||
this.selectedDate = this.formatDate(item.date);
|
||
this.currentTimeSlots = (item.timeSlots || []).map(t => {
|
||
return {
|
||
...t,
|
||
selected: false
|
||
};
|
||
});
|
||
if (this.currentTimeSlots.length > 0) {
|
||
this.selectTime(this.currentTimeSlots[0]);
|
||
}
|
||
},
|
||
|
||
selectTime(item) {
|
||
if (item.remainingCount <= 0) return;
|
||
this.currentTimeSlots.forEach(t => (t.selected = false));
|
||
item.selected = true;
|
||
this.selectedTime = item.time;
|
||
},
|
||
|
||
handleGoBack() {
|
||
uni.navigateBack();
|
||
},
|
||
|
||
handleNext() {
|
||
if (!this.selectedDate || !this.selectedTime) {
|
||
uni.showToast({
|
||
title: "请选择完整",
|
||
icon: "none"
|
||
});
|
||
return;
|
||
}
|
||
|
||
const data = JSON.stringify(this.itemData)
|
||
const time = this.selectedDate + " " + this.selectedTime
|
||
uni.navigateTo({
|
||
url: `/pages/reserve/confirm?data=${encodeURIComponent(data)}&time=${time}`
|
||
});
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
background: #f5f7fa;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.info-bar {
|
||
background: linear-gradient(90deg, rgba(49, 134, 255, 1), #53a8ff);
|
||
padding: 30rpx;
|
||
color: #fff;
|
||
}
|
||
|
||
.info-title {
|
||
font-size: 34rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.info-dept {
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.calendar-wrap {
|
||
background: #fff;
|
||
margin: 20rpx;
|
||
border-radius: 16rpx;
|
||
padding: 20rpx;
|
||
border: 2rpx solid rgba(49, 134, 255, 1);
|
||
}
|
||
|
||
.calendar-header {
|
||
display: flex;
|
||
text-align: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.week-item {
|
||
flex: 1;
|
||
font-size: 30rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.date-row {
|
||
display: flex;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.date-item {
|
||
flex: 1;
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.date-item.has {
|
||
color: rgba(49, 134, 255, 1);
|
||
}
|
||
|
||
.date-item.active {
|
||
background: rgba(49, 134, 255, 1);
|
||
color: #fff;
|
||
}
|
||
|
||
.date-item.disabled {
|
||
color: #ccc;
|
||
}
|
||
|
||
.date-num {
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.date-status {
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.time-list {
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.time-item {
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 16rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 30rpx;
|
||
}
|
||
|
||
.time-item.selected {
|
||
background: #e8f3ff;
|
||
border: 2rpx solid rgba(49, 134, 255, 1);
|
||
}
|
||
|
||
.time-item.disabled {
|
||
color: #ccc;
|
||
}
|
||
|
||
.footer-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
background: #fff;
|
||
padding: 20rpx 30rpx 40rpx;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.selected-info {
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.btn-box {
|
||
display: flex;
|
||
gap: 30rpx;
|
||
}
|
||
|
||
.btn-gray {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
background-color: rgba(242, 247, 255, 1);
|
||
color: rgba(49, 134, 255, 1);
|
||
border-radius: 44rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
border: 2rpx solid rgba(208, 227, 255, 1);
|
||
}
|
||
|
||
.btn-blue {
|
||
flex: 2;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
background-color: rgba(49, 134, 255, 1);
|
||
color: #ffffff;
|
||
border-radius: 44rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
border: 2rpx solid rgba(208, 227, 255, 1);
|
||
}
|
||
</style> |