修正多处问题
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="scroll-container theme-light">
|
||||
<view class="booking-page">
|
||||
<MemberInfoSubNav title="我的预约" @back="goBack" />
|
||||
<PageHeader title="我的预约" subtitle="" :show-back="true" />
|
||||
|
||||
<view class="booking-page__tabs">
|
||||
<view
|
||||
@@ -11,7 +11,7 @@
|
||||
:class="{ 'booking-page__tab--active': activeTab === tab.key }"
|
||||
hover-class="mi-tap-tab--hover"
|
||||
:hover-stay-time="150"
|
||||
@tap="setActiveTab(tab.key)"
|
||||
@tap="activeTab = tab.key"
|
||||
>
|
||||
<text
|
||||
class="booking-page__tab-text"
|
||||
@@ -26,31 +26,15 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bt-page__action-bar bt-page__action-bar--end">
|
||||
<text
|
||||
class="bt-page__action-link bt-page__action-link--primary"
|
||||
hover-class="mi-tap--hover"
|
||||
:hover-stay-time="150"
|
||||
@tap="goCourseList"
|
||||
>
|
||||
预约课程
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="booking-page__body">
|
||||
<view
|
||||
v-if="activeTab === 'ongoing' && upcomingAlert"
|
||||
class="booking-page__alert"
|
||||
>
|
||||
<image
|
||||
class="booking-page__alert-icon"
|
||||
src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/clock1.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="booking-page__alert-text">{{ upcomingAlert }}</text>
|
||||
<!-- 无数据 -->
|
||||
<view v-if="!displayedBookings.length && !loading" class="booking-page__empty">
|
||||
<text class="booking-page__empty-text">
|
||||
{{ activeTab === 'ongoing' ? '暂无进行中的预约' : '暂无历史预约' }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
<view v-else
|
||||
v-for="item in displayedBookings"
|
||||
:key="item.id"
|
||||
class="bk-card"
|
||||
@@ -59,7 +43,7 @@
|
||||
>
|
||||
<image
|
||||
class="bk-card__banner"
|
||||
:src="item.banner"
|
||||
:src="item.coverImage || item.banner || 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/default-course.png'"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="bk-card__content">
|
||||
@@ -97,7 +81,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bk-card__footer">
|
||||
<view class="bk-card__footer" v-if="item.footerText">
|
||||
<text class="bk-card__footer-info">{{ item.footerText }}</text>
|
||||
<view class="bk-card__actions">
|
||||
<view
|
||||
@@ -122,33 +106,25 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!displayedBookings.length" class="booking-page__empty">
|
||||
<text class="booking-page__empty-text">
|
||||
{{ activeTab === 'ongoing' ? '暂无进行中的预约' : '暂无历史预约' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
|
||||
import { PAGE, navigateToPage, backToMemberCenter } from '@/common/constants/routes.js'
|
||||
import {
|
||||
cancelOngoingBooking,
|
||||
formatUpcomingAlert
|
||||
} from '@/common/memberInfo/store.js'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import PageHeader from '@/components/index/PageHeader.vue'
|
||||
import { getMemberId } from '@/utils/request.js'
|
||||
import { canCancelBooking, canSigninCourse } from '@/common/memberInfo/bookingStore.js'
|
||||
import { signinGroupCourse, cancelBooking as cancelBookingApi } from '@/api/main.js'
|
||||
import { getMemberBookings, signinGroupCourse, cancelBooking as cancelBookingApi } from '@/api/main.js'
|
||||
|
||||
const tabs = ref([])
|
||||
const tabs = ref([
|
||||
{ key: 'ongoing', label: '进行中' },
|
||||
{ key: 'history', label: '历史记录' }
|
||||
])
|
||||
const activeTab = ref('ongoing')
|
||||
const ongoingBookings = ref([])
|
||||
const historyBookings = ref([])
|
||||
const activeTab = ref('ongoing')
|
||||
const loading = ref(false)
|
||||
|
||||
const displayedBookings = computed(() => {
|
||||
return activeTab.value === 'ongoing'
|
||||
@@ -156,30 +132,97 @@ const displayedBookings = computed(() => {
|
||||
: historyBookings.value
|
||||
})
|
||||
|
||||
const upcomingAlert = computed(() => {
|
||||
return formatUpcomingAlert(ongoingBookings.value[0])
|
||||
})
|
||||
|
||||
function refreshFromStore() {
|
||||
const store = loadMemberStore()
|
||||
ongoingBookings.value = store.ongoingBookings.map((item) => ({
|
||||
...item,
|
||||
canCancel: canCancelBooking(item),
|
||||
canSignin: canSigninCourse(item)
|
||||
}))
|
||||
historyBookings.value = store.historyBookings.map((item) => ({ ...item }))
|
||||
async function fetchBookings() {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
console.log('[booking] 开始获取预约记录')
|
||||
|
||||
const memberId = getMemberId()
|
||||
if (!memberId) {
|
||||
console.warn('[booking] 未登录,无法获取预约记录')
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getMemberBookings(memberId)
|
||||
console.log('[booking] getMemberBookings 返回:', JSON.stringify(res, null, 2))
|
||||
const bookings = res?.data || res?.content || []
|
||||
|
||||
if (Array.isArray(bookings) && bookings.length > 0) {
|
||||
const mapped = bookings.map(mapBooking)
|
||||
ongoingBookings.value = mapped.filter((b) => b.status === 'ongoing')
|
||||
historyBookings.value = mapped.filter((b) => b.status !== 'ongoing')
|
||||
} else {
|
||||
ongoingBookings.value = []
|
||||
historyBookings.value = []
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[booking] 获取预约记录失败:', e)
|
||||
ongoingBookings.value = []
|
||||
historyBookings.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
backToMemberCenter()
|
||||
function mapBooking(b) {
|
||||
const status = getBookingStatus(b)
|
||||
return {
|
||||
id: b.id,
|
||||
courseId: b.courseId,
|
||||
title: b.courseName || b.courseTitle || '预约课程',
|
||||
status: status,
|
||||
statusLabel: getBookingStatusLabel(b),
|
||||
schedule: formatBookingTime(b.courseStartTime || b.startTime || b.bookingTime),
|
||||
banner: b.coverImage || '',
|
||||
coach: b.coachName || b.coach || '教练',
|
||||
footerText: status === 'ongoing' ? '请准时参加课程' : '',
|
||||
canSignin: status === 'ongoing',
|
||||
canCancel: status === 'ongoing'
|
||||
}
|
||||
}
|
||||
|
||||
function goCourseList() {
|
||||
navigateToPage(PAGE.COURSE_LIST)
|
||||
function getBookingStatus(b) {
|
||||
if (b.status) {
|
||||
const s = String(b.status).toLowerCase()
|
||||
if (s === 'cancelled' || s === 'canceled') return 'cancelled'
|
||||
if (s === 'completed' || s === 'finished') return 'completed'
|
||||
}
|
||||
const startTime = b.courseStartTime || b.startTime
|
||||
if (startTime) {
|
||||
const now = new Date()
|
||||
const start = new Date(startTime)
|
||||
if (now > start) return 'completed'
|
||||
}
|
||||
return 'ongoing'
|
||||
}
|
||||
|
||||
function setActiveTab(tab) {
|
||||
activeTab.value = tab
|
||||
function getBookingStatusLabel(b) {
|
||||
if (b.status) {
|
||||
const s = String(b.status).toLowerCase()
|
||||
if (s === 'cancelled' || s === 'canceled') return '已取消'
|
||||
if (s === 'completed' || s === 'finished') return '已完成'
|
||||
if (s === 'ongoing' || s === 'confirmed') return '进行中'
|
||||
}
|
||||
const status = getBookingStatus(b)
|
||||
if (status === 'cancelled') return '已取消'
|
||||
if (status === 'completed') return '已完成'
|
||||
return '进行中'
|
||||
}
|
||||
|
||||
function formatBookingTime(timeStr) {
|
||||
if (!timeStr) return ''
|
||||
try {
|
||||
const d = new Date(timeStr)
|
||||
const month = d.getMonth() + 1
|
||||
const day = d.getDate()
|
||||
const hour = d.getHours().toString().padStart(2, '0')
|
||||
const minute = d.getMinutes().toString().padStart(2, '0')
|
||||
return `${month}月${day}日 ${hour}:${minute}`
|
||||
} catch {
|
||||
return timeStr
|
||||
}
|
||||
}
|
||||
|
||||
// 签到团课
|
||||
@@ -203,8 +246,7 @@ async function signinCourse(item) {
|
||||
|
||||
if (result.code === 200 || result.success) {
|
||||
uni.showToast({ title: '签到成功', icon: 'success' })
|
||||
// 更新本地状态
|
||||
refreshFromStore()
|
||||
fetchBookings()
|
||||
} else {
|
||||
uni.showToast({ title: result.message || '签到失败', icon: 'none' })
|
||||
}
|
||||
@@ -219,10 +261,6 @@ async function signinCourse(item) {
|
||||
|
||||
// 取消预约
|
||||
async function cancelBooking(item) {
|
||||
if (!canCancelBooking(item)) {
|
||||
uni.showToast({ title: '距开课不足2小时,无法取消', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.showModal({
|
||||
title: '取消预约',
|
||||
content: `确定要取消「${item.title}」吗?`,
|
||||
@@ -231,18 +269,12 @@ async function cancelBooking(item) {
|
||||
|
||||
uni.showLoading({ title: '取消中...', mask: true })
|
||||
try {
|
||||
// 调用后端API取消预约
|
||||
const result = await cancelBookingApi(Number(item.id))
|
||||
uni.hideLoading()
|
||||
|
||||
if (result.code === 200 || result.success) {
|
||||
// 更新本地状态
|
||||
const store = loadMemberStore()
|
||||
const localResult = cancelOngoingBooking(store, item.id)
|
||||
if (localResult.ok) {
|
||||
refreshFromStore()
|
||||
}
|
||||
uni.showToast({ title: '已取消', icon: 'success' })
|
||||
fetchBookings()
|
||||
} else {
|
||||
uni.showToast({ title: result.message || '取消失败', icon: 'none' })
|
||||
}
|
||||
@@ -255,7 +287,9 @@ async function cancelBooking(item) {
|
||||
})
|
||||
}
|
||||
|
||||
refreshFromStore()
|
||||
onMounted(() => {
|
||||
fetchBookings()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -263,7 +297,6 @@ refreshFromStore()
|
||||
@import '@/common/style/memberInfo/pages/page-reset.css';
|
||||
@import '@/common/style/memberInfo/pages/sub-page-base.css';
|
||||
@import '@/common/style/memberInfo/member-info-component-reset.css';
|
||||
@import '@/common/style/memberInfo/member-info-sub-nav.css';
|
||||
@import '@/common/style/memberInfo/member-info-tap.css';
|
||||
@import '@/common/style/memberInfo/pages/booking-page.css';
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user