252 lines
6.7 KiB
Vue
252 lines
6.7 KiB
Vue
<template>
|
|
<!-- 今日推荐容器 -->
|
|
<view class="today-recommend">
|
|
<!-- 区域标题栏 -->
|
|
<view class="section-header">
|
|
<!-- 区域标题 -->
|
|
<text class="section-title">今日推荐</text>
|
|
</view>
|
|
|
|
<!-- 推荐列表 -->
|
|
<view class="recommend-list">
|
|
<!-- 推荐项 -->
|
|
<view
|
|
v-for="(item, index) in recommends"
|
|
:key="item.id || index"
|
|
class="recommend-item"
|
|
@click="handleItemClick(item)"
|
|
>
|
|
<!-- 推荐项图片 -->
|
|
<image :src="item.image" mode="aspectFill" class="item-image" />
|
|
<!-- 推荐项内容区域 -->
|
|
<view class="item-content">
|
|
<!-- 推荐项标题 -->
|
|
<text class="item-title">{{ item.title }}</text>
|
|
<!-- 推荐项标签列表 -->
|
|
<view class="item-tags">
|
|
<text v-for="(tag, tagIndex) in item.tags" :key="tagIndex" class="tag">{{ tag }}</text>
|
|
</view>
|
|
<!-- 推荐项描述 -->
|
|
<text class="item-desc">{{ item.desc }}</text>
|
|
</view>
|
|
<!-- 推荐项操作区域 -->
|
|
<view class="item-action">
|
|
<!-- 开始训练按钮 -->
|
|
<view class="start-btn" @click.stop="handleItemClick(item)">
|
|
<text class="start-btn-text">开始训练</text>
|
|
</view>
|
|
<!-- 参与人数 -->
|
|
<text class="participants">{{ item.participants }}人参与</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
import { onShow } from '@dcloudio/uni-app'
|
|
import { getActiveRecommendCourses } from '@/api/groupCourse.js'
|
|
|
|
import { getCourseCoverUrl } from '@/utils/request.js'
|
|
|
|
console.log('[TodayRecommend] 组件脚本已加载')
|
|
|
|
// 今日推荐数据列表
|
|
const recommends = ref([])
|
|
|
|
// 计算课程时长
|
|
const calculateDuration = (startTime, endTime) => {
|
|
if (!startTime || !endTime) return '60分钟'
|
|
const start = new Date(startTime)
|
|
const end = new Date(endTime)
|
|
const diffMs = end - start
|
|
if (diffMs <= 0) return ''
|
|
const durationMinutes = Math.floor(diffMs / (1000 * 60))
|
|
return `${durationMinutes}分钟`
|
|
}
|
|
|
|
// 获取课程难度
|
|
const getCourseLevel = (course) => {
|
|
if (course.courseType === '2') return '中级'
|
|
if (course.courseType === '3') return '高级'
|
|
if (course.courseType === '1') return '初级'
|
|
return '初级'
|
|
}
|
|
|
|
// 处理图片URL - 已由 getOssUrl 工具函数处理
|
|
|
|
// 获取今日推荐
|
|
const fetchTodayRecommend = async () => {
|
|
console.log('[TodayRecommend] fetchTodayRecommend 开始请求')
|
|
try {
|
|
const res = await getActiveRecommendCourses()
|
|
console.log('[TodayRecommend] API 返回:', JSON.stringify(res).substring(0, 200))
|
|
if (res && Array.isArray(res) && res.length > 0) {
|
|
console.log('[TodayRecommend] 第一项 groupCourse.coverImage:', JSON.stringify(res[0]?.groupCourse?.coverImage))
|
|
console.log('[TodayRecommend] 第一项完整 groupCourse:', JSON.stringify(res[0]?.groupCourse))
|
|
}
|
|
if (res && Array.isArray(res)) {
|
|
// 提取推荐中的 groupCourse,只排除已满员的课程
|
|
const filteredCourses = res.filter(recommend => {
|
|
const course = recommend.groupCourse
|
|
if (!course) return false
|
|
const current = Number(course.currentMembers) || 0
|
|
const max = Number(course.maxMembers) || 999
|
|
return current < max
|
|
})
|
|
|
|
recommends.value = filteredCourses.slice(0, 3).map(recommend => {
|
|
const course = recommend.groupCourse
|
|
const rawCover = course.coverImage
|
|
const finalImage = getCourseCoverUrl(rawCover) || 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=300&q=80'
|
|
console.log('[TodayRecommend] 封面URL映射:', {
|
|
'courseName': course.courseName,
|
|
'原始coverImage': rawCover,
|
|
'转换后URL': finalImage,
|
|
'是否为完整URL': finalImage.startsWith('http')
|
|
})
|
|
return {
|
|
id: course.id,
|
|
image: finalImage,
|
|
title: course.courseName || '未知课程',
|
|
tags: [calculateDuration(course.startTime, course.endTime), getCourseLevel(course)],
|
|
desc: course.description || '精彩课程,不容错过',
|
|
participants: String(course.currentMembers || 0)
|
|
}
|
|
})
|
|
console.log('[TodayRecommend] 最终展示 recommends.value 数量:', recommends.value.length)
|
|
} else {
|
|
console.warn('[TodayRecommend] API 返回格式异常:', res)
|
|
}
|
|
} catch (err) {
|
|
console.error('获取今日推荐失败:', err)
|
|
}
|
|
}
|
|
|
|
const handleItemClick = (item) => {
|
|
if (!item.id) { uni.showToast({ title: '课程信息不完整', icon: 'none' }); return }
|
|
uni.navigateTo({ url: `/pages/groupCourse/detail?id=${item.id}` })
|
|
}
|
|
|
|
onMounted(() => { console.log('[TodayRecommend] onMounted 触发'); fetchTodayRecommend() })
|
|
|
|
onShow(() => {
|
|
console.log('[TodayRecommend] onShow 触发,刷新今日推荐')
|
|
fetchTodayRecommend()
|
|
})
|
|
|
|
defineExpose({ fetchTodayRecommend })
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.today-recommend {
|
|
padding: 0 24rpx;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 34rpx;
|
|
font-weight: 700;
|
|
color: #2D4A5A;
|
|
}
|
|
|
|
.recommend-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24rpx;
|
|
}
|
|
|
|
.recommend-item {
|
|
display: flex;
|
|
gap: 24rpx;
|
|
background: rgba(255, 255, 255, 0.6);
|
|
backdrop-filter: blur(16px);
|
|
-webkit-backdrop-filter: blur(16px);
|
|
border-radius: 24rpx;
|
|
padding: 20rpx;
|
|
box-shadow: 0 8rpx 28rpx var(--shadow-blue-light);
|
|
border: 1rpx solid rgba(255, 255, 255, 0.6);
|
|
}
|
|
|
|
.item-image {
|
|
width: 200rpx;
|
|
height: 160rpx;
|
|
border-radius: 16rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.item-content {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
min-width: 0;
|
|
}
|
|
|
|
.item-title {
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
color: #2D4A5A;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.item-tags {
|
|
display: flex;
|
|
gap: 12rpx;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.tag {
|
|
padding: 6rpx 16rpx;
|
|
background: rgba(122, 181, 204, 0.12);
|
|
border-radius: 8rpx;
|
|
font-size: 22rpx;
|
|
color: #6BA8C0;
|
|
}
|
|
|
|
.item-desc {
|
|
font-size: 24rpx;
|
|
color: var(--tabbar-text-inactive);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.item-action {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
gap: 16rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.start-btn {
|
|
padding: 16rpx 28rpx;
|
|
background: rgba(130, 220, 130, 0.9);
|
|
border-radius: 9999rpx;
|
|
box-shadow: 0 6rpx 20rpx rgba(130, 220, 130, 0.4);
|
|
}
|
|
|
|
.start-btn-text {
|
|
font-size: 24rpx;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.participants {
|
|
font-size: 22rpx;
|
|
color: var(--tabbar-text-inactive);
|
|
white-space: nowrap;
|
|
}
|
|
</style> |