会员个人中心页面初步完成

This commit is contained in:
时舟年
2026-06-04 14:18:53 +08:00
committed by liwentao
parent 1fa2fbd3f3
commit a0026b1da5
170 changed files with 18092 additions and 35 deletions
@@ -0,0 +1,209 @@
<template>
<view class="scroll-container theme-light">
<view class="booking-page">
<MemberInfoSubNav title="我的预约" @back="goBack" />
<view class="booking-page__tabs">
<view
v-for="tab in tabs"
:key="tab.key"
class="booking-page__tab"
:class="{ 'booking-page__tab--active': activeTab === tab.key }"
hover-class="mi-tap-tab--hover"
:hover-stay-time="150"
@tap="setActiveTab(tab.key)"
>
<text
class="booking-page__tab-text"
:class="{ 'booking-page__tab-text--active': activeTab === tab.key }"
>
{{ tab.label }}
</text>
<view
v-if="activeTab === tab.key"
class="booking-page__tab-indicator"
></view>
</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="/static/images/clock1.png"
mode="aspectFit"
/>
<text class="booking-page__alert-text">{{ upcomingAlert }}</text>
</view>
<view
v-for="item in displayedBookings"
:key="item.id"
class="bk-card"
hover-class="mi-tap-card--hover"
:hover-stay-time="150"
>
<image
class="bk-card__banner"
:src="item.banner"
mode="aspectFill"
/>
<view class="bk-card__content">
<view class="bk-card__header">
<text class="bk-card__title">{{ item.title }}</text>
<view
class="bk-card__status"
:class="'bk-card__status--' + item.status"
>
<text
class="bk-card__status-text"
:class="'bk-card__status-text--' + item.status"
>
{{ item.statusLabel }}
</text>
</view>
</view>
<view class="bk-card__meta">
<view class="bk-card__meta-row">
<image
class="bk-card__meta-icon"
src="/static/images/clock0.png"
mode="aspectFit"
/>
<text class="bk-card__meta-text">{{ item.schedule }}</text>
</view>
<view class="bk-card__meta-row">
<image
class="bk-card__meta-icon"
src="/static/images/user0.png"
mode="aspectFit"
/>
<text class="bk-card__meta-text">{{ item.coach }}</text>
</view>
</view>
<view class="bk-card__footer">
<text class="bk-card__footer-info">{{ item.footerText }}</text>
<view
v-if="item.canCancel"
class="bk-card__cancel"
hover-class="mi-tap-btn--hover"
:hover-stay-time="150"
@tap.stop="cancelBooking(item)"
>
<text class="bk-card__cancel-text">取消预约</text>
</view>
</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>
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
import { bookingMock } from '@/common/memberInfo/mockData.js'
import {
loadMemberStore,
cancelOngoingBooking,
formatUpcomingAlert
} from '@/common/memberInfo/store.js'
import { canCancelBooking } from '@/common/memberInfo/bookingStore.js'
import { subPageMixin } from '@/common/memberInfo/mixins.js'
export default {
components: { MemberInfoSubNav },
mixins: [subPageMixin],
data() {
return {
tabs: bookingMock.tabs,
ongoingBookings: [],
historyBookings: [],
activeTab: 'ongoing'
}
},
computed: {
displayedBookings() {
return this.activeTab === 'ongoing'
? this.ongoingBookings
: this.historyBookings
},
upcomingAlert() {
return formatUpcomingAlert(this.ongoingBookings[0])
}
},
onShow() {
this.refreshFromStore()
},
methods: {
refreshFromStore() {
const store = loadMemberStore()
this.ongoingBookings = store.ongoingBookings.map((item) => ({
...item,
canCancel: canCancelBooking(item)
}))
this.historyBookings = store.historyBookings.map((item) => ({ ...item }))
},
goCourseList() {
navigateToPage(PAGE.COURSE_LIST)
},
setActiveTab(tab) {
this.activeTab = tab
},
cancelBooking(item) {
if (!canCancelBooking(item)) {
uni.showToast({ title: '距开课不足2小时,无法取消', icon: 'none' })
return
}
uni.showModal({
title: '取消预约',
content: `确定要取消「${item.title}」吗?`,
success: (res) => {
if (!res.confirm) return
const store = loadMemberStore()
const result = cancelOngoingBooking(store, item.id)
if (!result.ok) {
uni.showToast({ title: result.message, icon: 'none' })
return
}
this.refreshFromStore()
uni.showToast({ title: '已取消', icon: 'success' })
}
})
}
}
}
</script>
<style>
@import '@/common/style/base.css';
@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>