Files
gym-manage/gym-manage-uniapp/components/recommendCourses/RecommendCourseCard.vue
T

327 lines
8.5 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="recommend-card" :style="cardStyle" @click="handleCardClick">
<!-- 卡片头部图片 + 推荐标题 -->
<view class="card-header">
<!-- 团课封面 -->
<view class="course-image-wrapper">
<image :src="courseImage" mode="aspectFill" class="course-image" />
<view class="image-overlay"></view>
<!-- 状态标签 -->
<text :class="['status-tag', tagType]">{{ tagText }}</text>
</view>
<!-- 推荐标题区 -->
<view class="header-info">
<text class="recommend-title">{{ recommend.recommendTitle || '推荐课程' }}</text>
<text class="recommend-content">{{ recommend.recommendContent }}</text>
</view>
</view>
<!-- 课程信息栏 -->
<view class="info-bar">
<view class="info-item">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/time.png" class="info-icon" />
<text class="info-text">{{ formattedTime }}</text>
</view>
<view class="info-item" v-if="courseLocation">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/intensity.png" class="info-icon" />
<text class="info-text">{{ courseLocation }}</text>
</view>
<view class="info-item">
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/hot.png" class="info-icon" />
<text class="info-text">{{ courseCurrentMembers || 0 }}/{{ courseMaxMembers || 0 }}</text>
</view>
</view>
<!-- 推荐理由 -->
<view class="card-reason" v-if="recommend.recommendReason">
<text class="reason-text">{{ recommend.recommendReason }}</text>
</view>
<!-- 卡片底部操作按钮 -->
<view class="card-footer" @click.stop="handleJoinCourse">
<view class="action-btn">
<text>立即预约</text>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue'
import { getCourseCoverUrl } from '@/utils/request.js'
const props = defineProps({
recommend: {
type: Object,
required: true,
default: () => ({})
},
width: {
type: [Number, String],
default: '100%',
description: '卡片宽度'
},
borderRadius: {
type: [Number, String],
default: 24,
description: '卡片圆角,单位 rpx'
}
})
// 计算卡片样式
const cardStyle = computed(() => {
const style = {}
if (props.width) {
style.width = typeof props.width === 'number' ? `${props.width}rpx` : props.width
}
if (props.borderRadius) {
style.borderRadius = typeof props.borderRadius === 'number' ? `${props.borderRadius}rpx` : props.borderRadius
}
return style
})
// 团课信息
const courseStartTime = computed(() => props.recommend.groupCourse?.startTime)
const courseEndTime = computed(() => props.recommend.groupCourse?.endTime)
const courseMaxMembers = computed(() => props.recommend.groupCourse?.maxMembers)
const courseCurrentMembers = computed(() => props.recommend.groupCourse?.currentMembers)
const courseLocation = computed(() => props.recommend.groupCourse?.location)
const courseId = computed(() => props.recommend.groupCourse?.id)
// 课程封面图片
const courseImage = computed(() => {
const coverImage = props.recommend.groupCourse?.coverImage
const url = getCourseCoverUrl(coverImage) || 'https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=400&q=80'
console.log('[RecommendCourseCard] 封面URL:', {
'courseName': props.recommend.groupCourse?.courseName,
'原始coverImage': coverImage,
'转换后URL': url,
'是否完整URL': url && url.startsWith('http')
})
return url
})
// 课程标签文本
const tagText = computed(() => {
const current = courseCurrentMembers.value || 0
const max = courseMaxMembers.value || 0
if (current >= max) return '已满员'
if (props.recommend.groupCourse?.status === 2) return '已结束'
if (props.recommend.groupCourse?.status === 1) return '已取消'
if (max > 0 && current / max >= 0.8) return '热门'
return '推荐'
})
// 课程标签类型
const tagType = computed(() => {
const current = courseCurrentMembers.value || 0
const max = courseMaxMembers.value || 0
if (current >= max) return 'full'
if (props.recommend.groupCourse?.status === 2) return 'ended'
if (props.recommend.groupCourse?.status === 1) return 'ended'
if (max > 0 && current / max >= 0.8) return 'hot'
return 'default'
})
// 格式化课程时间
const formattedTime = computed(() => {
if (!courseStartTime.value) return ''
const start = new Date(courseStartTime.value)
const date = `${start.getMonth() + 1}${start.getDate()}`
const time = `${String(start.getHours()).padStart(2, '0')}:${String(start.getMinutes()).padStart(2, '0')}`
if (courseEndTime.value) {
const end = new Date(courseEndTime.value)
const endTime = `${String(end.getHours()).padStart(2, '0')}:${String(end.getMinutes()).padStart(2, '0')}`
return `${date} ${time}-${endTime}`
}
return `${date} ${time}`
})
// 处理卡片点击(跳转到详情页)
const handleCardClick = () => {
if (!props.recommend.groupCourse) {
uni.showToast({ title: '课程信息不完整', icon: 'none' })
return
}
uni.navigateTo({ url: `/pages/groupCourse/detail?id=${courseId.value}` })
}
// 处理预约课程点击
const handleJoinCourse = () => {
if (props.recommend.groupCourse?.status === 2) {
uni.showToast({ title: '课程已结束', icon: 'none' })
return
}
if (props.recommend.groupCourse?.status === 1) {
uni.showToast({ title: '课程已取消', icon: 'none' })
return
}
const current = courseCurrentMembers.value || 0
const max = courseMaxMembers.value || 0
if (current >= max) {
uni.showToast({ title: '课程已满员', icon: 'none' })
return
}
uni.navigateTo({ url: `/pages/groupCourse/detail?id=${courseId.value}` })
}
</script>
<style lang="scss">
.recommend-card {
width: 100%;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 8rpx 32rpx rgba(45, 74, 90, 0.1);
border: 1rpx solid rgba(124, 181, 204, 0.2);
margin-bottom: 24rpx;
}
// 卡片头部:左图右文
.card-header {
display: flex;
gap: 24rpx;
padding: 24rpx;
}
.course-image-wrapper {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
position: relative;
flex-shrink: 0;
}
.course-image {
width: 100%;
height: 100%;
}
.image-overlay {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: linear-gradient(to top, rgba(45, 74, 90, 0.3) 0%, transparent 60%);
}
// 状态标签(图片左下角)
.status-tag {
position: absolute;
left: 8rpx;
bottom: 8rpx;
padding: 4rpx 12rpx;
border-radius: 8rpx;
font-size: 18rpx;
font-weight: 600;
color: #ffffff;
background: linear-gradient(135deg, #7CB5CC, #9CCFDF);
&.hot {
background: linear-gradient(135deg, #FF8C69, #FFA07A);
}
&.full {
background: linear-gradient(135deg, #A0B8C8, #B8CCD8);
}
&.ended {
background: linear-gradient(135deg, #C0CCD0, #D0D8DC);
}
}
// 推荐标题区
.header-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
.recommend-title {
font-size: 32rpx;
font-weight: 700;
color: #2D4A5A;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.recommend-content {
font-size: 24rpx;
color: #5A7A8A;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
// 信息栏:时间 | 地点 | 人数
.info-bar {
display: flex;
align-items: center;
gap: 24rpx;
padding: 0 24rpx 16rpx;
}
.info-item {
display: flex;
align-items: center;
gap: 6rpx;
}
.info-icon {
width: 28rpx;
height: 28rpx;
}
.info-text {
font-size: 22rpx;
color: #5A7A8A;
}
// 推荐理由
.card-reason {
margin: 0 24rpx;
padding: 16rpx;
background: rgba(124, 181, 204, 0.06);
border-radius: 12rpx;
border-left: 4rpx solid #7CB5CC;
}
.reason-text {
font-size: 22rpx;
color: #6A8A9A;
line-height: 1.5;
}
// 卡片底部
.card-footer {
padding: 16rpx 24rpx 24rpx;
}
.action-btn {
width: 100%;
height: 72rpx;
background: linear-gradient(135deg, #82DC82, #66CC66);
border-radius: 36rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(130, 220, 130, 0.35);
text {
font-size: 28rpx;
font-weight: 600;
color: #ffffff;
}
}
</style>