Files
taimed-international-app/components/course/ChapterList.vue

321 lines
7.3 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="chapter-list">
<!-- 目录状态信息 -->
<view v-if="catalogue" class="catalogue-status">
<view v-if="catalogue.isBuy === 1 || userVip" class="purchased-info">
<view class="info-row">
<text v-if="userVip">
VIP畅学权益有效期截止到{{ userVip.endTime }}
</text>
<template v-else>
<text v-if="!catalogue.startTime">
当前目录还未开始学习
</text>
<text v-else>
课程有效期截止到{{ catalogue.endTime }}
</text>
<!-- <wd-button
v-if="catalogue.startTime"
size="small"
@click="handleRenew"
>
续费
</wd-button> -->
</template>
</view>
</view>
<!-- 未购买状态 -->
<view v-else-if="catalogue.type === 0" class="free-course">
<wd-button type="success" @click="handleGetFreeCourse">
{{ $t('courseDetails.free') }}
</wd-button>
</view>
<view v-else class="unpurchased-info">
<text class="tip-text">
{{ $t('courseDetails.unpurchasedTip') }}
</text>
<view class="action-btns">
<wd-button size="small" type="warning" @click="handlePurchase">
{{ $t('courseDetails.purchase') }}
</wd-button>
<wd-button
v-if="showRenewBtn"
size="small"
type="success"
@click="handleRenew"
>
{{ $t('courseDetails.relearn') }}
</wd-button>
<wd-button size="small" type="primary" @click="goToVip">
{{ $t('courseDetails.openVip') }}
</wd-button>
</view>
</view>
</view>
<view v-if="chapters.length > 0" class="chapter-content">
<!-- VIP标识 -->
<view v-if="userVip" class="vip-badge">
<text>VIP畅学权益生效中</text>
</view>
<!-- 章节列表 -->
<view
v-for="(chapter, index) in chapters"
:key="chapter.id"
class="chapter-item"
@click="handleChapterClick(chapter)"
>
<view class="chapter-content-wrapper">
<view :class="['chapter-info', !canAccess(chapter) ? 'locked' : '']">
<text class="chapter-title">{{ chapter.title }}</text>
<!-- 试听标签 -->
<wd-tag
v-if="chapter.isAudition === 1 && !isPurchased && !userVip"
type="success"
plain
size="small"
custom-class="chapter-tag"
>
试听
</wd-tag>
<!-- 学习状态标签 -->
<template v-if="isPurchased || userVip">
<wd-tag
v-if="chapter.isLearned === 0"
type="primary"
plain
size="small"
custom-class="chapter-tag"
>
未学
</wd-tag>
<wd-tag
v-else
type="success"
plain
size="small"
custom-class="chapter-tag"
>
已学
</wd-tag>
</template>
</view>
<!-- 锁定图标 -->
<view v-if="!canAccess(chapter)" class="lock-icon">
<wd-icon name="lock-on" size="24px" color="#258feb" />
</view>
</view>
</view>
</view>
<!-- 暂无章节 -->
<view v-else class="no-chapters">
<text>暂无章节内容</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { IChapter, ICatalogue, IVipInfo } from '@/types/course'
interface Props {
chapters: IChapter[]
catalogue: ICatalogue
userVip: IVipInfo | null
showRenewBtn?: boolean
}
const props = defineProps<Props>()
const emit = defineEmits<{
click: [chapter: IChapter],
purchase: [catalogue: ICatalogue],
renew: [catalogue: ICatalogue],
toVip: [catalogue: ICatalogue],
}>()
/**
* 判断目录是否已购买
*/
const isPurchased = computed(() => {
return props.catalogue.isBuy === 1
})
// 购买
const handlePurchase = () => {
emit('purchase', props.catalogue)
}
// 去开通vip
const goToVip = () => {
emit('toVip', props.catalogue)
}
// 续费/复读
const handleRenew = () => {
emit('renew', props.catalogue)
}
// 领取免费课程
const handleGetFreeCourse = () => {
emit('purchase', props.catalogue)
}
/**
* 判断章节是否可以访问
*/
const canAccess = (chapter: IChapter): boolean => {
// VIP用户可以访问所有章节
if (props.userVip) return true
// 已购买目录可以访问所有章节
if (isPurchased.value) return true
// 试听章节可以访问
if (chapter.isAudition === 1) return true
// 免费课程可以访问
if (props.catalogue.type === 0) return true
return false
}
/**
* 点击章节
*/
const handleChapterClick = (chapter: IChapter) => {
if (!canAccess(chapter)) {
if (props.catalogue.type === 0) {
uni.showToast({
title: '请先领取课程',
icon: 'none'
})
} else {
uni.showToast({
title: '请先购买课程',
icon: 'none'
})
}
return
}
emit('click', chapter)
}
</script>
<style lang="scss" scoped>
.chapter-list {
padding: 20rpx;
}
.catalogue-status {
padding: 20rpx;
margin-bottom: 20rpx;
background-color: #fff;
.purchased-info {
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 26rpx;
line-height: 50rpx;
}
}
.free-course {
text-align: center;
}
.unpurchased-info {
.tip-text {
display: block;
font-size: 26rpx;
color: #666;
margin-bottom: 20rpx;
line-height: 1.6;
}
.action-btns {
display: flex;
gap: 20rpx;
justify-content: center;
}
}
}
.chapter-content {
position: relative;
padding: 20rpx;
border: 4rpx solid #fffffc;
background: linear-gradient(52deg, #e8f6ff 0%, #e3f2fe 50%);
box-shadow: 0px 0px 10px 0px #89c8e9;
border-top-right-radius: 40rpx;
border-bottom-left-radius: 40rpx;
.vip-badge {
display: inline-block;
font-size: 24rpx;
background: linear-gradient(90deg, #6429db 0%, #0075ed 100%);
color: #fff;
padding: 10rpx 20rpx;
border-radius: 0 50rpx 50rpx 0;
z-index: 1;
}
.chapter-item {
padding: 20rpx 0;
border-bottom: 1px solid #fff;
&:last-child {
border-bottom: none;
}
.chapter-content-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
.chapter-info {
flex: 1;
display: flex;
align-items: center;
gap: 10rpx;
&.locked {
opacity: 0.6;
}
.chapter-title {
flex: 1;
font-size: 28rpx;
color: #1e2f3e;
line-height: 1.5;
}
.chapter-tag {
flex-shrink: 0;
}
}
.lock-icon {
margin-left: 20rpx;
flex-shrink: 0;
}
}
}
}
.no-chapters {
text-align: center;
padding: 80rpx 0;
color: #999;
font-size: 28rpx;
}
</style>