226 lines
9.9 KiB
Vue
226 lines
9.9 KiB
Vue
<template>
|
|
<view class="scroll-container theme-light">
|
|
<view class="bt-page">
|
|
<MemberInfoSubNav title="我的课程" @back="goBack" />
|
|
<view class="mi-mod-tabs">
|
|
<view
|
|
v-for="tab in tabs"
|
|
:key="tab.key"
|
|
class="bt-tab"
|
|
:class="{ 'bt-tab--active': activeTab === tab.key }"
|
|
hover-class="mi-tap-tab--hover"
|
|
@tap="activeTab = tab.key"
|
|
>
|
|
<text class="bt-tab__text">{{ tab.label }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="bt-page__body">
|
|
<!-- 团课 -->
|
|
<template v-if="activeTab === 'group'">
|
|
<view class="mi-mod-subtabs">
|
|
<text
|
|
class="mi-mod-subtab"
|
|
:class="{ 'mi-mod-subtab--on': groupSub === 'ongoing' }"
|
|
@tap="groupSub = 'ongoing'"
|
|
>进行中</text>
|
|
<text
|
|
class="mi-mod-subtab"
|
|
:class="{ 'mi-mod-subtab--on': groupSub === 'completed' }"
|
|
@tap="groupSub = 'completed'"
|
|
>已完成</text>
|
|
</view>
|
|
<view
|
|
v-for="item in groupList"
|
|
:key="item.id"
|
|
class="mi-mod-course-card"
|
|
hover-class="mi-tap-card--hover"
|
|
@tap="onGroupCourse(item)"
|
|
>
|
|
<image class="mi-mod-course-card__banner" :src="item.banner" mode="aspectFill" />
|
|
<view class="mi-mod-course-card__content">
|
|
<text class="mi-mod-course-card__title">{{ item.title }}</text>
|
|
<text class="mi-mod-course-card__coach">{{ item.coach }}</text>
|
|
<view class="mi-mod-course-card__progress">
|
|
<view class="mi-mod-course-card__progress-bar">
|
|
<view class="mi-mod-course-card__progress-fill" :style="{ width: pct(item) + '%' }"></view>
|
|
</view>
|
|
<text class="mi-mod-course-card__progress-text">{{ item.progress }}/{{ item.total }}</text>
|
|
</view>
|
|
<text class="mi-mod-course-card__meta">{{ item.schedule }} · {{ item.location }}</text>
|
|
<view v-if="groupSub === 'ongoing' && item.canCancel" class="mi-mod-course-card__action" @tap.stop="goBooking">
|
|
<text>取消预约</text>
|
|
</view>
|
|
<view v-if="groupSub === 'completed' && item.canEvaluate" class="mi-mod-course-card__action" @tap.stop="evaluate(item)">
|
|
<text>去评价</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<!-- 私教 -->
|
|
<template v-else-if="activeTab === 'private'">
|
|
<view class="bt-card">
|
|
<text class="bt-card__title">剩余课时 {{ privateData.remaining }} 节</text>
|
|
<view class="mi-detail-coach">
|
|
<image class="mi-detail-coach__avatar" :src="privateData.coachAvatar" mode="aspectFill" />
|
|
<view>
|
|
<text class="mi-detail-coach__name">{{ privateData.coach }}</text>
|
|
<text class="mi-detail-coach__rating">下次 {{ privateData.nextClass }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="bt-card">
|
|
<text class="bt-card__title">已约课程</text>
|
|
<view v-for="b in privateData.bookings" :key="b.id" class="mi-mod-session">
|
|
<text class="mi-mod-session__title">{{ b.title }}</text>
|
|
<text class="mi-mod-session__meta">{{ b.time }} · {{ b.location }} · {{ b.status }}</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<!-- 线上课 -->
|
|
<template v-else-if="activeTab === 'online'">
|
|
<view
|
|
v-for="item in onlineList"
|
|
:key="item.id"
|
|
class="mi-mod-course-card"
|
|
hover-class="mi-tap-card--hover"
|
|
@tap="goOnline(item)"
|
|
>
|
|
<image class="mi-mod-course-card__banner" :src="item.cover" mode="aspectFill" />
|
|
<view class="mi-mod-course-card__content">
|
|
<text class="mi-mod-course-card__title">{{ item.title }}</text>
|
|
<text class="mi-mod-course-card__meta">{{ item.duration }} · 进度 {{ item.progress }}%</text>
|
|
<text v-if="item.type === 'live'" class="mi-mod-course-card__next">直播 {{ item.liveTime }}</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<!-- 训练营 -->
|
|
<template v-else>
|
|
<view
|
|
v-for="item in packageList"
|
|
:key="item.id"
|
|
class="mi-mod-course-card"
|
|
>
|
|
<image class="mi-mod-course-card__banner" :src="item.banner" mode="aspectFill" />
|
|
<view class="mi-mod-course-card__content">
|
|
<text class="mi-mod-course-card__title">{{ item.title }}</text>
|
|
<text class="mi-mod-course-card__coach">{{ item.coach }}</text>
|
|
<view class="mi-mod-course-card__progress">
|
|
<view class="mi-mod-course-card__progress-bar">
|
|
<view class="mi-mod-course-card__progress-fill" :style="{ width: pct(item) + '%' }"></view>
|
|
</view>
|
|
<text class="mi-mod-course-card__progress-text">{{ item.progress }}/{{ item.total }} 节</text>
|
|
</view>
|
|
<text class="mi-mod-course-card__meta">{{ item.schedule }}</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<view v-if="isEmpty" class="bt-empty">
|
|
<text class="bt-empty__text">暂无课程</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
|
|
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
|
|
import { moduleMock, getMyCoursesData } from '@/common/memberInfo/moduleStore.js'
|
|
import { loadMemberStore } from '@/common/memberInfo/store.js'
|
|
import { subPageMixin } from '@/common/memberInfo/mixins.js'
|
|
|
|
export default {
|
|
components: { MemberInfoSubNav },
|
|
mixins: [subPageMixin],
|
|
data() {
|
|
return {
|
|
tabs: moduleMock.myCourseTabs,
|
|
activeTab: 'group',
|
|
groupSub: 'ongoing',
|
|
privateData: {},
|
|
onlineList: [],
|
|
packageList: [],
|
|
groupData: { ongoing: [], completed: [] }
|
|
}
|
|
},
|
|
computed: {
|
|
groupList() {
|
|
return this.groupData[this.groupSub] || []
|
|
},
|
|
isEmpty() {
|
|
if (this.activeTab === 'group') return !this.groupList.length
|
|
if (this.activeTab === 'private') return !this.privateData.bookings?.length
|
|
if (this.activeTab === 'online') return !this.onlineList.length
|
|
return !this.packageList.length
|
|
}
|
|
},
|
|
onShow() { this.refresh() },
|
|
methods: {
|
|
refresh() {
|
|
const store = loadMemberStore()
|
|
this.groupData = getMyCoursesData(store, 'group')
|
|
this.privateData = getMyCoursesData(store, 'private')
|
|
this.onlineList = getMyCoursesData(store, 'online').list || []
|
|
this.packageList = getMyCoursesData(store, 'package').list || []
|
|
},
|
|
pct(item) {
|
|
return item.total ? Math.min(100, Math.round((item.progress / item.total) * 100)) : 0
|
|
},
|
|
goBooking() { navigateToPage(PAGE.BOOKING) },
|
|
goOnline(item) { navigateToPage(`${PAGE.ONLINE_COURSE}?id=${item.id}`) },
|
|
evaluate(item) {
|
|
navigateToPage(`${PAGE.COURSE_EVALUATE}?title=${encodeURIComponent(item.title)}`)
|
|
},
|
|
onGroupCourse(item) {
|
|
uni.showModal({
|
|
title: item.title,
|
|
content: `${item.coach}\n${item.schedule}\n${item.location}`,
|
|
showCancel: false
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</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/body-test-common.css';
|
|
@import '@/common/style/memberInfo/pages/module-pages-common.css';
|
|
|
|
.mi-mod-subtabs {
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 16px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.mi-mod-subtab {
|
|
font-size: 14px;
|
|
color: var(--text-muted, #5E6F8D);
|
|
}
|
|
|
|
.mi-mod-subtab--on {
|
|
color: var(--primary-dark, #0B2B4B);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.mi-mod-course-card__action {
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.mi-mod-course-card__action text {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--accent-orange, #FF6B35);
|
|
}
|
|
</style>
|