Files
gym-manage/gym-manage-coach-uniapp/pages/course-list/course-list.vue
T

293 lines
7.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="course-list-page">
<view class="header-wrap" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-bar" :style="{ height: navBarHeight + 'px' }">
<text class="back-btn" @click="goBack">&#8592;</text>
<text class="nav-title">我的团课</text>
</view>
<!-- 状态筛选 Tab -->
<view class="tab-row">
<view
v-for="(tab, idx) in tabs"
:key="idx"
class="tab-item"
:class="{ active: currentTab === tab.value }"
@click="switchTab(tab.value)"
>
<text>{{ tab.label }}</text>
</view>
</view>
</view>
<scroll-view class="content" scroll-y :style="{ height: 'calc(100vh - ' + totalHeaderHeight + 'px)' }">
<view class="content-inner">
<view v-if="loading" class="loading-wrap">
<text class="loading-text">加载中...</text>
</view>
<view v-else-if="filteredCourses.length === 0" class="empty-wrap">
<text class="empty-text">暂无课程</text>
</view>
<view v-else class="course-list">
<view v-for="course in filteredCourses" :key="course.id" class="course-card" :class="{ grayed: !course._isToday }" @click="goToDetail(course.id)">
<view class="card-header">
<text class="course-name">{{ course.courseName }}</text>
<view class="status-tag" :class="course._statusClass">
<text>{{ course._statusLabel }}</text>
</view>
</view>
<view class="card-info">
<view class="info-row">
<text class="info-label">时间</text>
<text class="info-value">{{ formatDate(course.startTime) }} {{ formatTime(course.startTime) }} - {{ formatTime(course.endTime) }}</text>
</view>
<view class="info-row">
<text class="info-label">地点</text>
<text class="info-value">{{ course.location || '未设置' }}</text>
</view>
<view class="info-row">
<text class="info-label">人数</text>
<text class="info-value">{{ course.currentMembers || 0 }} / {{ course.maxMembers || 0 }}</text>
</view>
</view>
</view>
</view>
<view class="bottom-safe"></view>
</view>
</scroll-view>
</view>
</template>
<script>
const coachApi = require('../../api/coach')
const store = require('../../store/index')
export default {
data() {
return {
statusBarHeight: 0,
navBarHeight: 44,
totalHeaderHeight: 0,
loading: true,
courses: [],
currentTab: 'all',
tabs: [
{ label: '全部', value: 'all' },
{ label: '待开课', value: 'pending' },
{ label: '进行中', value: 'in_progress' },
{ label: '已结束', value: 'ended' }
]
}
},
computed: {
filteredCourses() {
switch (this.currentTab) {
case 'pending':
return this.courses.filter(c => c.status == 0)
case 'in_progress':
return this.courses.filter(c => c.status == 3 || c.status == 7)
case 'ended':
return this.courses.filter(c => c.status == 2 || c.status == 6 || c.status == 1 || c.status == 4 || c.status == 5)
default:
return this.courses
}
}
},
onLoad() {
const systemInfo = uni.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 20
let navBarHeight = 44
// #ifdef MP-WEIXIN
const menuButton = uni.getMenuButtonBoundingClientRect()
if (menuButton) {
navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height
}
// #endif
this.statusBarHeight = statusBarHeight
this.navBarHeight = navBarHeight
// 状态栏 + 导航栏 + tab行高度
this.totalHeaderHeight = statusBarHeight + navBarHeight + 44
},
onShow() {
if (!store.isLoggedIn) {
uni.reLaunch({ url: '/pages/login/login' })
return
}
this.loadCourses()
},
methods: {
goBack() { uni.navigateBack() },
goToDetail(id) {
uni.navigateTo({ url: '/pages/course-detail/course-detail?id=' + id })
},
switchTab(value) { this.currentTab = value },
async loadCourses() {
this.loading = true
try {
const coachId = store.getCoachId()
if (!coachId) return
const result = await coachApi.getCoachCourses(coachId)
const list = Array.isArray(result) ? result : (result && result.content ? result.content : [])
// 预计算状态 class 和 labeluni-app 模板不支持 :class 中调用方法
const classMap = { 0: 'normal', 1: 'cancelled', 2: 'ended', 3: 'in-progress', 4: 'timeout', 5: 'absent', 6: 'auto-ended', 7: 'late' }
const labelMap = { 0: '正常', 1: '已取消', 2: '已结束', 3: '进行中', 4: '超时', 5: '教练缺席', 6: '自动结束', 7: '教练迟到' }
const todayStr = new Date().toISOString().substring(0, 10)
this.courses = list
.map(c => ({
...c,
_statusClass: classMap[c.status] || '',
_statusLabel: labelMap[c.status] || '未知',
_isToday: (c.startTime || '').substring(0, 10) === todayStr
}))
.sort((a, b) => {
if (a._isToday && !b._isToday) return -1
if (!a._isToday && b._isToday) return 1
return 0
})
} catch (e) {
console.error('加载课程失败:', e)
uni.showToast({ title: '加载课程失败', icon: 'none' })
} finally {
this.loading = false
}
},
formatDate(timeStr) {
if (!timeStr) return '--'
return timeStr.substring(0, 10)
},
formatTime(timeStr) {
if (!timeStr) return '--'
return timeStr.substring(11, 16)
},
}
}
</script>
<style scoped>
.course-list-page {
min-height: 100vh;
background: #F5F7FA;
overflow-x: hidden;
}
.header-wrap {
background: #1A1A1A;
}
.nav-bar {
padding: 0 16px;
display: flex;
align-items: center;
}
.back-btn {
font-size: 20px;
color: #FFFFFF;
margin-right: 12px;
}
.nav-title {
font-size: 18px;
font-weight: 700;
color: #FFFFFF;
}
/* Tab 筛选 */
.tab-row {
display: flex;
padding: 8px 16px 10px;
}
.tab-item {
padding: 6px 16px;
border-radius: 20px;
margin-right: 8px;
font-size: 13px;
color: rgba(255,255,255,0.6);
background: rgba(255,255,255,0.1);
}
.tab-item.active {
color: #1A1A1A;
background: #00E676;
font-weight: 600;
}
/* 内容区 */
.content-inner {
padding: 14px 14px 0;
}
.loading-wrap, .empty-wrap {
display: flex;
justify-content: center;
padding: 60px 0;
}
.loading-text, .empty-text {
font-size: 14px;
color: #7A7E84;
}
/* 课程卡片 */
.course-card {
background: #FFFFFF;
border-radius: 16px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.course-name {
font-size: 16px;
font-weight: 700;
color: #1E1E1E;
flex: 1;
}
.status-tag {
padding: 3px 10px;
border-radius: 12px;
font-size: 11px;
font-weight: 600;
}
.status-tag.normal { background: rgba(0,230,118,0.15); color: #00C853; }
.status-tag.in-progress, .status-tag.late { background: rgba(33,150,243,0.15); color: #2196F3; }
.status-tag.ended, .status-tag.auto-ended { background: rgba(158,158,158,0.15); color: #9E9E9E; }
.status-tag.cancelled { background: rgba(255,152,0,0.15); color: #FF9800; }
.status-tag.timeout { background: rgba(244,67,54,0.15); color: #F44336; }
.status-tag.absent { background: rgba(244,67,54,0.15); color: #F44336; }
.card-info {
background: #F5F7FA;
border-radius: 10px;
padding: 10px 12px;
}
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
font-size: 12px;
color: #7A7E84;
}
.info-value {
font-size: 13px;
color: #1E1E1E;
font-weight: 500;
}
/* 操作按钮 - 已移除,仅保留按钮样式供可能复用 */
.course-card.grayed {
opacity: 0.5;
pointer-events: auto;
}
.bottom-safe {
height: 20px;
}
</style>