230 lines
5.8 KiB
Vue
230 lines
5.8 KiB
Vue
<!-- pages/recommendCourses/index.vue -->
|
|
<template>
|
|
<!-- 页面容器 -->
|
|
<view class="recommend-page">
|
|
<!-- 页面标题 -->
|
|
<PageHeader title="推荐课程" subtitle="精选热门团课,等你来练" :show-back="true" />
|
|
|
|
<!-- 骨架屏 -->
|
|
<view v-if="loading" class="skeleton-container">
|
|
<view class="skeleton-card" v-for="i in 3" :key="i">
|
|
<view class="skeleton-header"></view>
|
|
<view class="skeleton-body">
|
|
<view class="skeleton-img"></view>
|
|
<view class="skeleton-text-group">
|
|
<view class="skeleton-text"></view>
|
|
<view class="skeleton-text-short"></view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 推荐课程列表 -->
|
|
<scroll-view v-else class="courses-container" scroll-y="true">
|
|
<view class="courses-list">
|
|
<RecommendCourseCard
|
|
v-for="recommend in recommendCourses"
|
|
:key="recommend.id"
|
|
:recommend="recommend"
|
|
@click="handleCardClick(recommend)"
|
|
/>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-if="!loading && recommendCourses.length === 0" class="empty-state">
|
|
<text class="empty-text">暂无推荐课程</text>
|
|
</view>
|
|
|
|
<!-- 底部占位 -->
|
|
<view class="bottom-placeholder"></view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import PageHeader from '@/components/index/PageHeader.vue'
|
|
import RecommendCourseCard from '@/components/recommendCourses/RecommendCourseCard.vue'
|
|
import { getGroupCourseRecommendList } from '@/api/groupCourse.js'
|
|
|
|
// 推荐课程列表数据
|
|
const recommendCourses = ref([])
|
|
// 加载状态
|
|
const loading = ref(true)
|
|
|
|
// 从缓存加载数据
|
|
function loadFromCache() {
|
|
try {
|
|
const cached = uni.getStorageSync('recommend_courses_cache')
|
|
if (cached && Date.now() - cached.time < 5 * 60 * 1000) {
|
|
recommendCourses.value = cached.data
|
|
loading.value = false
|
|
return true
|
|
}
|
|
} catch (e) {
|
|
console.error('[Recommend Courses Page] 读取缓存失败', e)
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 从网络加载数据
|
|
async function loadFromNetwork() {
|
|
loading.value = true
|
|
console.log('[Recommend Courses Page] 开始从后端获取团课推荐列表...')
|
|
|
|
try {
|
|
// 调用获取所有团课推荐接口
|
|
console.log('[Recommend Courses Page] 发起 API 请求: GET /groupCourse/recommend/list')
|
|
const res = await getGroupCourseRecommendList({
|
|
sortBy: 'priority',
|
|
sortOrder: 'desc'
|
|
})
|
|
|
|
console.log('[Recommend Courses Page] API 响应数据:', res)
|
|
|
|
if (res && Array.isArray(res)) {
|
|
recommendCourses.value = res
|
|
console.log('[Recommend Courses Page] 获取到', res.length, '条推荐课程数据')
|
|
|
|
// 更新缓存
|
|
uni.setStorageSync('recommend_courses_cache', {
|
|
data: res,
|
|
time: Date.now()
|
|
})
|
|
} else {
|
|
console.log('[Recommend Courses Page] API 响应为空或格式不正确')
|
|
recommendCourses.value = []
|
|
}
|
|
} catch (err) {
|
|
console.error('[Recommend Courses Page] 加载失败:', err)
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
recommendCourses.value = []
|
|
} finally {
|
|
loading.value = false
|
|
console.log('[Recommend Courses Page] 数据加载完成,loading:', loading.value)
|
|
}
|
|
}
|
|
|
|
// 获取推荐课程列表
|
|
async function fetchRecommendCourses() {
|
|
// 优先从缓存加载
|
|
const hasCache = loadFromCache()
|
|
if (!hasCache) {
|
|
// 从网络加载
|
|
await loadFromNetwork()
|
|
} else {
|
|
// 后台静默更新
|
|
setTimeout(() => {
|
|
loadFromNetwork()
|
|
}, 100)
|
|
}
|
|
}
|
|
|
|
// 处理卡片点击
|
|
const handleCardClick = (recommend) => {
|
|
if (!recommend.groupCourse) {
|
|
uni.showToast({ title: '课程信息不完整', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
const status = recommend.groupCourse.status
|
|
if (status === 2) {
|
|
uni.showToast({ title: '课程已结束', icon: 'none' })
|
|
return
|
|
}
|
|
if (status === 1) {
|
|
uni.showToast({ title: '课程已取消', icon: 'none' })
|
|
return
|
|
}
|
|
const current = recommend.groupCourse.currentMembers || 0
|
|
const max = recommend.groupCourse.maxMembers || 0
|
|
if (current >= max) {
|
|
uni.showToast({ title: '课程已满员', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
// 跳转到课程详情页
|
|
uni.navigateTo({ url: `/pages/groupCourse/detail?id=${recommend.groupCourse.id}` })
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchRecommendCourses()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.recommend-page {
|
|
min-height: 100vh;
|
|
background: #F5F7FA;
|
|
}
|
|
|
|
.courses-container {
|
|
height: calc(100vh - 160rpx);
|
|
padding: 0;
|
|
}
|
|
|
|
.courses-list {
|
|
padding: 0 24rpx;
|
|
}
|
|
|
|
.empty-state {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 120rpx 0;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 28rpx;
|
|
color: #8CA0B0;
|
|
}
|
|
|
|
.bottom-placeholder {
|
|
height: 40rpx;
|
|
}
|
|
|
|
/* 骨架屏样式 */
|
|
.skeleton-container {
|
|
padding: 0 24rpx;
|
|
}
|
|
|
|
.skeleton-card {
|
|
background: #fff;
|
|
border-radius: 24rpx;
|
|
overflow: hidden;
|
|
box-shadow: 0 8rpx 28rpx rgba(45, 74, 90, 0.08);
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.skeleton-img {
|
|
width: 100%;
|
|
height: 280rpx;
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
|
background-size: 200% 100%;
|
|
animation: skeleton-loading 1.5s infinite;
|
|
}
|
|
|
|
.skeleton-text {
|
|
height: 32rpx;
|
|
margin: 20rpx;
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
|
background-size: 200% 100%;
|
|
animation: skeleton-loading 1.5s infinite;
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
.skeleton-text-short {
|
|
height: 24rpx;
|
|
margin: 0 20rpx 20rpx;
|
|
width: 60%;
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
|
background-size: 200% 100%;
|
|
animation: skeleton-loading 1.5s infinite;
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
@keyframes skeleton-loading {
|
|
0% { background-position: 200% 0; }
|
|
100% { background-position: -200% 0; }
|
|
}
|
|
</style> |