218 lines
9.2 KiB
Vue
218 lines
9.2 KiB
Vue
<template>
|
|
<view class="scroll-container theme-light">
|
|
<view class="bt-page mi-course-list">
|
|
<MemberInfoSubNav title="预约课程" @back="goBack" />
|
|
<view class="mi-course-list__filters">
|
|
<view class="mi-course-list__date-bar">
|
|
<view
|
|
class="mi-course-list__mode"
|
|
hover-class="mi-tap--hover"
|
|
@tap="toggleDateMode"
|
|
>
|
|
<text class="mi-course-list__mode-text">{{ dateMode === 'day' ? '按天' : '按周' }}</text>
|
|
</view>
|
|
<scroll-view scroll-x class="mi-course-list__dates">
|
|
<view
|
|
v-for="d in dateOptions"
|
|
:key="d.value"
|
|
class="mi-course-list__date"
|
|
:class="{ 'mi-course-list__date--active': selectedDate === d.value }"
|
|
@tap="selectedDate = d.value"
|
|
>
|
|
<text class="mi-course-list__date-week">{{ d.week }}</text>
|
|
<text class="mi-course-list__date-day">{{ d.day }}</text>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
<scroll-view scroll-x class="mi-course-list__chips">
|
|
<view
|
|
v-for="t in typeOptions"
|
|
:key="t.key"
|
|
class="bt-tab"
|
|
:class="{ 'bt-tab--active': courseType === t.key }"
|
|
@tap="courseType = t.key"
|
|
>
|
|
<text class="bt-tab__text">{{ t.label }}</text>
|
|
</view>
|
|
</scroll-view>
|
|
<view class="mi-course-list__row">
|
|
<picker :range="coaches" @change="onCoachChange">
|
|
<view class="mi-course-list__picker">
|
|
<text>{{ coach }}</text>
|
|
<image class="mi-course-list__arrow" src="/static/images/chevronright3.png" mode="aspectFit" />
|
|
</view>
|
|
</picker>
|
|
<picker :range="periodLabels" @change="onPeriodChange">
|
|
<view class="mi-course-list__picker">
|
|
<text>{{ periodLabel }}</text>
|
|
<image class="mi-course-list__arrow" src="/static/images/chevronright3.png" mode="aspectFit" />
|
|
</view>
|
|
</picker>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="bt-page__body">
|
|
<view
|
|
v-for="course in displayCourses"
|
|
:key="course.id"
|
|
class="mi-course-card"
|
|
hover-class="mi-tap-card--hover"
|
|
@tap="openDetail(course)"
|
|
>
|
|
<image class="mi-course-card__banner" :src="course.banner" mode="aspectFill" />
|
|
<view class="mi-course-card__body">
|
|
<view class="mi-course-card__head">
|
|
<text class="mi-course-card__title">{{ course.title }}</text>
|
|
<view class="mi-course-card__type" :class="'mi-course-card__type--' + course.type">
|
|
<text>{{ course.type === 'group' ? '团课' : '私教' }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="mi-course-card__coach">
|
|
<image class="mi-course-card__avatar" :src="course.coachAvatar" mode="aspectFill" />
|
|
<text>{{ course.coach }}</text>
|
|
</view>
|
|
<text class="mi-course-card__meta">
|
|
{{ course.startTime }}-{{ course.endTime }} · {{ course.location }}
|
|
</text>
|
|
<view class="mi-course-card__capacity">
|
|
<view class="mi-course-card__bar">
|
|
<view class="mi-course-card__bar-fill" :style="{ width: course.percent + '%' }"></view>
|
|
</view>
|
|
<text class="mi-course-card__cap-text">{{ course.enrolled }}/{{ course.capacity }}</text>
|
|
<text v-if="course.scarcityLabel" class="mi-course-card__scarcity">{{ course.scarcityLabel }}</text>
|
|
</view>
|
|
<view class="mi-course-card__footer">
|
|
<text class="mi-course-card__price">{{ course.price }}</text>
|
|
<view
|
|
class="mi-course-card__btn"
|
|
:class="{ 'mi-course-card__btn--disabled': course.full }"
|
|
@tap.stop="quickBook(course)"
|
|
>
|
|
<text>{{ course.full ? '已约满' : '预约' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-if="!displayCourses.length" class="bt-empty">
|
|
<text class="bt-empty__text">暂无符合条件的课程</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view
|
|
class="mi-course-list__fab"
|
|
hover-class="mi-tap-btn--hover"
|
|
@tap="goMyBooking"
|
|
>
|
|
<image class="mi-course-list__fab-icon" src="/static/images/clock.png" mode="aspectFit" />
|
|
<text class="mi-course-list__fab-text">我的预约</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
|
|
import { PAGE, navigateToPage } from '@/common/constants/routes.js'
|
|
import { loadMemberStore, persistMemberStore } from '@/common/memberInfo/store.js'
|
|
import {
|
|
courseCatalogMock,
|
|
filterCourses,
|
|
enrichCourseForDisplay,
|
|
getWeekDates,
|
|
bookCourse
|
|
} from '@/common/memberInfo/bookingStore.js'
|
|
import { subPageMixin } from '@/common/memberInfo/mixins.js'
|
|
|
|
const WEEK = ['日', '一', '二', '三', '四', '五', '六']
|
|
|
|
export default {
|
|
components: { MemberInfoSubNav },
|
|
mixins: [subPageMixin],
|
|
data() {
|
|
return {
|
|
dateMode: 'day',
|
|
selectedDate: '2024-07-15',
|
|
courseType: 'all',
|
|
coach: '全部',
|
|
period: 'all',
|
|
coaches: courseCatalogMock.coaches,
|
|
typeOptions: courseCatalogMock.typeOptions,
|
|
periodOptions: courseCatalogMock.periodOptions,
|
|
courses: []
|
|
}
|
|
},
|
|
computed: {
|
|
periodLabels() {
|
|
return this.periodOptions.map((p) => p.label)
|
|
},
|
|
periodLabel() {
|
|
return this.periodOptions.find((p) => p.key === this.period)?.label || '全部时段'
|
|
},
|
|
weekDates() {
|
|
return getWeekDates(this.selectedDate)
|
|
},
|
|
dateOptions() {
|
|
const dates = this.dateMode === 'week' ? this.weekDates : this.weekDates
|
|
return dates.map((value) => {
|
|
const d = new Date(value.replace(/-/g, '/'))
|
|
return {
|
|
value,
|
|
week: WEEK[d.getDay()],
|
|
day: `${d.getMonth() + 1}/${d.getDate()}`
|
|
}
|
|
})
|
|
},
|
|
displayCourses() {
|
|
const filters = {
|
|
date: this.dateMode === 'day' ? this.selectedDate : '',
|
|
weekDates: this.dateMode === 'week' ? this.weekDates : [],
|
|
type: this.courseType,
|
|
coach: this.coach,
|
|
period: this.period
|
|
}
|
|
return filterCourses(this.courses, filters).map(enrichCourseForDisplay)
|
|
}
|
|
},
|
|
onShow() {
|
|
const store = loadMemberStore()
|
|
this.courses = store.courseCatalog.map((c) => ({ ...c }))
|
|
if (!this.selectedDate && this.courses.length) {
|
|
this.selectedDate = this.courses[0].date
|
|
}
|
|
},
|
|
methods: {
|
|
toggleDateMode() {
|
|
this.dateMode = this.dateMode === 'day' ? 'week' : 'day'
|
|
},
|
|
onCoachChange(e) {
|
|
this.coach = this.coaches[e.detail.value]
|
|
},
|
|
onPeriodChange(e) {
|
|
this.period = this.periodOptions[e.detail.value].key
|
|
},
|
|
openDetail(course) {
|
|
navigateToPage(`${PAGE.COURSE_DETAIL}?id=${course.id}`)
|
|
},
|
|
quickBook(course) {
|
|
if (course.full) return
|
|
navigateToPage(`${PAGE.COURSE_DETAIL}?id=${course.id}`)
|
|
},
|
|
goMyBooking() {
|
|
navigateToPage(PAGE.BOOKING)
|
|
}
|
|
}
|
|
}
|
|
</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';
|
|
@import '@/common/style/memberInfo/pages/course-list-page.css';
|
|
</style>
|