1633 lines
37 KiB
Vue
1633 lines
37 KiB
Vue
<template>
|
||
<view class="course-detail-page">
|
||
<!-- 自定义导航栏 -->
|
||
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="nav-content">
|
||
<view class="nav-left" @click="goBack">
|
||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||
</view>
|
||
<view class="nav-title">{{ courseDetail.title || '肝硬化与重症肝病' }}</view>
|
||
<view class="nav-right" @click="goShare">
|
||
<uni-icons type="redo" size="24" color="#333"></uni-icons>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 页面内容 -->
|
||
<view class="page-content" :style="{ paddingTop: navBarHeight + 'px' }">
|
||
<!-- 主横幅 -->
|
||
<view class="main-banner">
|
||
<image :src="courseDetail.index_img || '/static/lunbo_bg.png'" mode="aspectFill" class="banner-image"></image>
|
||
|
||
</view>
|
||
|
||
<!-- 课程信息 -->
|
||
<view class="course-info">
|
||
<view class="course-header">
|
||
<text class="course-title">{{ courseDetail.title || '肝硬化与重症肝病' }}</text>
|
||
<view class="course-meta">
|
||
<text class="lesson-count" v-if="courseDetail.study_num > 0">{{ courseDetail.study_num || 0 }}人学·</text>
|
||
<text class="lesson-count">{{ courseDetail.video_num || 0 }}节课·{{ courseDetail.status === 1 ? '已完结' : '进行中' }}</text>
|
||
<text class="lesson-count" v-if="courseDetail.fuli_num > 0 && courseDetail.special_type_name == '福利课堂'">·福利剩余{{ courseDetail.fuli_remaining || 0 }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="course-tags">
|
||
<view class="tag-item" v-if="courseDetail.fuli_bon > 0">
|
||
<text class="tag-text">福利课堂</text>
|
||
</view>
|
||
<text class="reward-info" v-if="courseDetail.fuli_bon > 0">课程学完预期将返还{{ courseDetail.fuli_bon }}积分</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 限时优惠模块 -->
|
||
<view class="limited-offer" v-if="showLimitedOffer">
|
||
<view class="offer-left">
|
||
<view class="price-info">
|
||
<text class="current-price">¥{{ currentPriceYuan.toFixed(2) }}</text>
|
||
<text class="original-price" v-if="originalPriceYuan > 0">¥{{ originalPriceYuan.toFixed(2) }}</text>
|
||
</view>
|
||
<view class="offer-tag">
|
||
<text class="tag-text">限时优惠</text>
|
||
</view>
|
||
</view>
|
||
<view class="offer-right">
|
||
<text class="countdown-text">距离优惠结束还剩</text>
|
||
<view class="countdown-timer">
|
||
<text class="days">{{ countdown.days }}天</text>
|
||
<view class="time-segments">
|
||
<view class="time-box">{{ countdown.hours }}</view>
|
||
<text class="time-separator">:</text>
|
||
<view class="time-box">{{ countdown.minutes }}</view>
|
||
<text class="time-separator">:</text>
|
||
<view class="time-box">{{ countdown.seconds }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 永久优惠模块(样式同限时优惠,但无倒计时) -->
|
||
<view class="limited-offer permanent" v-if="showPermanentOffer">
|
||
<view class="offer-left">
|
||
<view class="price-info">
|
||
<text class="current-price">¥{{ currentPriceYuan.toFixed(2) }}</text>
|
||
<text class="original-price" v-if="originalPriceYuan > 0">¥{{ originalPriceYuan.toFixed(2) }}</text>
|
||
</view>
|
||
<view class="offer-tag">
|
||
<text class="tag-text">永久优惠</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 标签导航 -->
|
||
<view class="tab-nav">
|
||
<view class="tab-item" :class="{ active: activeTab === 'intro' }" @click="switchTab('intro')">
|
||
<text class="tab-text">课程介绍</text>
|
||
</view>
|
||
<view class="tab-item" :class="{ active: activeTab === 'catalog' }" @click="switchTab('catalog')">
|
||
<text class="tab-text">课程目录</text>
|
||
</view>
|
||
<view class="tab-item" :class="{ active: activeTab === 'reviews' }" @click="switchTab('reviews')" data-tab="reviews">
|
||
<text class="tab-text">评价({{ courseDetail.comment_num || 0 }})</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 课程介绍内容 -->
|
||
<view class="course-intro" v-if="activeTab === 'intro'">
|
||
|
||
|
||
<!-- 课程描述 -->
|
||
<view class="course-description" v-if="courseDetail.descs">
|
||
<view class="description-content" v-html="courseDetail.descs"></view>
|
||
</view>
|
||
|
||
|
||
</view>
|
||
|
||
<!-- 课程目录内容 -->
|
||
<view class="course-catalog" v-if="activeTab === 'catalog'">
|
||
<!-- 课程概览 -->
|
||
<!-- <view class="catalog-overview">
|
||
<view class="overview-item">
|
||
<text class="overview-label">总课时</text>
|
||
<text class="overview-value">{{ courseDetail.video_num || 0 }}节课</text>
|
||
</view>
|
||
<view class="overview-item">
|
||
<text class="overview-label">总时长</text>
|
||
<text class="overview-value">约{{ Math.ceil((courseDetail.video_num || 0) * 0.75) }}小时</text>
|
||
</view>
|
||
<view class="overview-item">
|
||
<text class="overview-label">学习进度</text>
|
||
<text class="overview-value">{{ courseDetail.study_status || 0 }}%</text>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 课程章节列表 -->
|
||
<view class="chapter-list">
|
||
<view class="chapter-item" v-for="(chapter, index) in chapterList" :key="index" @click="toggleChapter(index)">
|
||
<view class="chapter-header">
|
||
<view class="chapter-info">
|
||
<view class="chapter-title">
|
||
<uni-icons :type="chapter.expanded ? 'down' : 'right'" size="16" color="#666"></uni-icons>
|
||
<text class="chapter-name">{{ chapter.title }}</text>
|
||
</view>
|
||
<!-- <view class="chapter-meta">
|
||
<text class="lesson-count">{{ chapter.lessons.length }}节课</text>
|
||
<text class="duration">{{ chapter.duration }}</text>
|
||
</view> -->
|
||
</view>
|
||
<!-- <view class="chapter-status">
|
||
<text class="status-text" :class="chapter.status">{{ chapter.statusText }}</text>
|
||
</view> -->
|
||
</view>
|
||
|
||
<!-- 课时列表 -->
|
||
<view class="lesson-list" v-if="chapter.expanded">
|
||
<view class="lesson-item" v-for="(lesson, lessonIndex) in chapter.lessons" :key="lessonIndex" @click="goLesson(lesson)">
|
||
<view class="lesson-info">
|
||
<view class="lesson-title">
|
||
<uni-icons type="play-circle" size="20" color="#999"></uni-icons>
|
||
<text class="lesson-name">{{ lesson.title }}</text>
|
||
</view>
|
||
<view class="lesson-meta">
|
||
<text class="lesson-duration">{{ lesson.duration }}</text>
|
||
<!-- <text class="lesson-teacher">{{ lesson.teacher }}</text> -->
|
||
</view>
|
||
</view>
|
||
<view class="lesson-status">
|
||
<text class="status-text" :class="lesson.status">{{ lesson.statusText }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 评价内容 -->
|
||
<view class="course-reviews" v-if="activeTab === 'reviews'">
|
||
<view class="review-header">
|
||
<text class="review-title">课程评价</text>
|
||
<view class="review-actions-header">
|
||
<text class="review-count">共{{ reviewTotal || courseDetail.comment_num || 0 }}条评价</text>
|
||
<button class="write-review-btn" @click="goToReview" v-if="courseDetail.is_buy === 1">
|
||
{{ courseDetail.is_commented === 1 ? '追加评论' : '写评价' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="review-list" v-if="courseDetail.comment_num > 0">
|
||
<view class="review-item" v-for="(review, index) in reviewList" :key="index">
|
||
<view class="review-user">
|
||
<image :src="review.avatar" mode="aspectFill" class="user-avatar"></image>
|
||
<view class="user-info">
|
||
<text class="user-name">{{ review.userName }}</text>
|
||
<view class="user-rating">
|
||
<uni-icons v-for="star in 5" :key="star"
|
||
:type="star <= review.rating ? 'star-filled' : 'star'"
|
||
size="16"
|
||
:color="star <= review.rating ? '#FFB800' : '#E0E0E0'">
|
||
</uni-icons>
|
||
</view>
|
||
</view>
|
||
<text class="review-time">{{ review.time }}</text>
|
||
</view>
|
||
<view class="review-content">
|
||
<text class="review-text">{{ review.content }}</text>
|
||
</view>
|
||
|
||
<!-- 回复按钮 -->
|
||
<view class="review-actions">
|
||
<view class="reply-btn" @click="showReplyInput(review.id)">
|
||
<uni-icons type="chat" size="16" color="#666"></uni-icons>
|
||
<text class="reply-text">回复</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 二级评论列表 -->
|
||
<view class="reply-list" v-if="review.replies && review.replies.length > 0">
|
||
<view class="reply-item" v-for="(reply, replyIndex) in review.replies" :key="reply.id">
|
||
<view class="reply-user">
|
||
<image :src="reply.avatar" mode="aspectFill" class="reply-avatar"></image>
|
||
<view class="reply-info">
|
||
<view class="reply-header">
|
||
<text class="reply-name">{{ reply.userName }}</text>
|
||
<text class="reply-time">{{ reply.time }}</text>
|
||
<text v-if="reply.isAuthor" class="author-tag">讲师</text>
|
||
</view>
|
||
<text class="reply-content">{{ reply.content }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 回复输入框 -->
|
||
<view class="reply-input-container" v-if="activeReplyId === review.id">
|
||
<view class="reply-input-wrapper">
|
||
<input
|
||
class="reply-input"
|
||
v-model="replyText"
|
||
placeholder="写下你的回复..."
|
||
:focus="activeReplyId === review.id"
|
||
/>
|
||
<view class="reply-input-actions">
|
||
<button class="cancel-btn" @click="cancelReply">取消</button>
|
||
<button class="submit-btn" @click="submitReply(review.id)">发送</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view class="load-more" v-if="reviewPage < reviewPages && reviewList.length > 0">
|
||
<button class="load-more-btn" @click="loadMoreReviews" :disabled="reviewLoading">
|
||
<text v-if="!reviewLoading">加载更多</text>
|
||
<text v-else>加载中...</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 已加载全部 -->
|
||
<view class="load-all" v-if="reviewPage >= reviewPages && reviewList.length > 0">
|
||
<text class="load-all-text">已加载全部评论</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空评价状态 -->
|
||
<view class="empty-reviews" v-if="reviewList.length === 0 && !reviewLoading">
|
||
<view class="empty-icon">
|
||
<uni-icons type="chat" size="80" color="#E0E0E0"></uni-icons>
|
||
</view>
|
||
<text class="empty-text">暂无评价</text>
|
||
<text class="empty-desc" v-if="courseDetail.is_buy === 1 && courseDetail.is_commented === 1">
|
||
您已经评价过此课程,可以追加评论
|
||
</text>
|
||
<text class="empty-desc" v-else-if="courseDetail.is_buy === 1">
|
||
成为第一个评价的人吧
|
||
</text>
|
||
<text class="empty-desc" v-else>
|
||
购买课程后即可评价
|
||
</text>
|
||
</view>
|
||
|
||
<!-- 加载中状态 -->
|
||
<view class="loading-reviews" v-if="reviewLoading && reviewList.length === 0">
|
||
<view class="loading-icon">
|
||
<uni-icons type="spinner-cycle" size="80" color="#E0E0E0"></uni-icons>
|
||
</view>
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部购买栏 -->
|
||
<view class="bottom-bar" v-if="courseDetail.is_buy === 0">
|
||
<view class="price-section">
|
||
<text class="price" >¥{{ currentPriceYuan.toFixed(2) }}</text>
|
||
</view>
|
||
<view class="buy-section">
|
||
<button class="buy-btn" @click="goBuy" >立即购买</button>
|
||
</view>
|
||
</view>
|
||
<view class="bottom-bar" v-else>
|
||
<button class="buy-btn" @click="goStudy" style=" width: 100%;background-color: #4CAF50;color: #fff;">开始学习</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted } from 'vue'
|
||
import course_api from "@/api/course_api.js"
|
||
|
||
const courseId = ref(0)
|
||
onMounted(() => {
|
||
// 获取页面参数
|
||
const pages = getCurrentPages()
|
||
const currentPage = pages[pages.length - 1]
|
||
const options = currentPage.options || {}
|
||
|
||
courseId.value = options.id
|
||
console.log('课程ID:', options.id)
|
||
})
|
||
|
||
|
||
// 响应式数据
|
||
const statusBarHeight = ref(0)
|
||
const navBarHeight = ref(88)
|
||
const activeTab = ref('intro') // 默认激活课程介绍标签
|
||
const courseDetail = ref({})
|
||
// 倒计时与价格展示
|
||
const countdown = ref({
|
||
days: 0,
|
||
hours: '00',
|
||
minutes: '00',
|
||
seconds: '00'
|
||
})
|
||
let countdownTimer = null // 倒计时定时器
|
||
const showLimitedOffer = ref(false)
|
||
const showPermanentOffer = ref(false)
|
||
const currentPriceYuan = ref(0)
|
||
const originalPriceYuan = ref(0)
|
||
// 课程目录数据
|
||
const chapterList = ref([])
|
||
// 回复相关状态
|
||
const activeReplyId = ref(null)
|
||
const replyText = ref('')
|
||
|
||
|
||
// 方法
|
||
const getSystemInfo = () => {
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
statusBarHeight.value = systemInfo.statusBarHeight
|
||
// #ifdef MP-WEIXIN
|
||
navBarHeight.value = systemInfo.statusBarHeight + 44
|
||
// #endif
|
||
// #ifdef APP-PLUS
|
||
navBarHeight.value = systemInfo.statusBarHeight + 44
|
||
// #endif
|
||
}
|
||
|
||
|
||
const startCountdown = (endTime) => {
|
||
if (!endTime) return
|
||
countdownTimer = setInterval(() => {
|
||
const now = new Date().getTime()
|
||
const distance = endTime - now
|
||
|
||
if (distance < 0) {
|
||
// 倒计时结束
|
||
clearInterval(countdownTimer)
|
||
countdown.value = {
|
||
days: 0,
|
||
hours: '00',
|
||
minutes: '00',
|
||
seconds: '00'
|
||
}
|
||
return
|
||
}
|
||
|
||
// 计算剩余时间
|
||
const days = Math.floor(distance / (1000 * 60 * 60 * 24))
|
||
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
|
||
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))
|
||
const seconds = Math.floor((distance % (1000 * 60)) / 1000)
|
||
|
||
// 更新倒计时数据
|
||
countdown.value = {
|
||
days: days,
|
||
hours: hours.toString().padStart(2, '0'),
|
||
minutes: minutes.toString().padStart(2, '0'),
|
||
seconds: seconds.toString().padStart(2, '0')
|
||
}
|
||
}, 1000)
|
||
}
|
||
|
||
const goBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
const goShare = () => {
|
||
uni.showToast({
|
||
title: '分享功能',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
const goBuy = () => {
|
||
uni.redirectTo({
|
||
url:'/pages_course/course_payment/course_payment?id='+courseId.value
|
||
})
|
||
}
|
||
|
||
const goStudy = () => {
|
||
uni.showToast({
|
||
title: '开始学习',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
const reviewList = ref([])
|
||
const reviewPage = ref(1)
|
||
const reviewTotal = ref(0)
|
||
const reviewPages = ref(1)
|
||
const reviewLoading = ref(false)
|
||
|
||
const getReviewList = () => {
|
||
if (reviewLoading.value) return
|
||
|
||
reviewLoading.value = true
|
||
console.log('获取评论列表', courseId.value, reviewPage.value)
|
||
|
||
course_api.listExcellencourseComment(courseId.value, reviewPage.value).then(res => {
|
||
console.log('评论列表数据:', res)
|
||
if (res.code == 200) {
|
||
const data = res.data
|
||
|
||
// 更新分页信息
|
||
reviewTotal.value = data.total || 0
|
||
reviewPages.value = data.pages || 1
|
||
|
||
// 处理评论数据
|
||
if (data.list && data.list.length > 0) {
|
||
// 如果是第一页,直接替换;否则追加
|
||
if (reviewPage.value === 1) {
|
||
reviewList.value = data.list.map(item => ({
|
||
id: item.id,
|
||
userName: item.user_name || '匿名用户',
|
||
avatar: '/static/icon_home_my_patient.png', // 默认头像
|
||
rating: Math.floor(item.star / 2), // 将10分制转换为5分制
|
||
time: formatDate(item.create_date),
|
||
content: item.comment || '',
|
||
replies: item.excellentcourse_comment_list ? item.excellentcourse_comment_list.map(reply => ({
|
||
id: reply.id,
|
||
userName: reply.user_name || '匿名用户',
|
||
avatar: '/static/icon_home_my_patient.png',
|
||
time: formatDate(reply.create_date),
|
||
content: reply.comment || '',
|
||
isAuthor: false
|
||
})) : []
|
||
}))
|
||
} else {
|
||
// 追加数据
|
||
const newReviews = data.list.map(item => ({
|
||
id: item.id,
|
||
userName: item.user_name || '匿名用户',
|
||
avatar: '/static/icon_home_my_patient.png',
|
||
rating: Math.floor(item.star / 2),
|
||
time: formatDate(item.create_date),
|
||
content: item.comment || '',
|
||
replies: item.excellentcourse_comment_list ? item.excellentcourse_comment_list.map(reply => ({
|
||
id: reply.id,
|
||
userName: reply.user_name || '匿名用户',
|
||
avatar: '/static/icon_home_my_patient.png',
|
||
time: formatDate(reply.create_date),
|
||
content: reply.comment || '',
|
||
isAuthor: false
|
||
})) : []
|
||
}))
|
||
reviewList.value.push(...newReviews)
|
||
}
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: res.msg || '获取评论失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
console.error('获取评论列表失败:', err)
|
||
uni.showToast({
|
||
title: '获取评论失败',
|
||
icon: 'none'
|
||
})
|
||
}).finally(() => {
|
||
reviewLoading.value = false
|
||
})
|
||
}
|
||
|
||
// 格式化日期
|
||
const formatDate = (dateStr) => {
|
||
if (!dateStr) return ''
|
||
|
||
try {
|
||
const date = new Date(dateStr)
|
||
const now = new Date()
|
||
const diff = now - date
|
||
|
||
// 小于1分钟
|
||
if (diff < 60 * 1000) {
|
||
return '刚刚'
|
||
}
|
||
// 小于1小时
|
||
if (diff < 60 * 60 * 1000) {
|
||
return `${Math.floor(diff / (60 * 1000))}分钟前`
|
||
}
|
||
// 小于1天
|
||
if (diff < 24 * 60 * 60 * 1000) {
|
||
return `${Math.floor(diff / (60 * 60 * 1000))}小时前`
|
||
}
|
||
// 小于7天
|
||
if (diff < 7 * 24 * 60 * 60 * 1000) {
|
||
return `${Math.floor(diff / (24 * 60 * 60 * 1000))}天前`
|
||
}
|
||
|
||
// 超过7天显示具体日期
|
||
return date.toLocaleDateString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit'
|
||
})
|
||
} catch (e) {
|
||
return dateStr
|
||
}
|
||
}
|
||
|
||
// 加载更多评论
|
||
const loadMoreReviews = () => {
|
||
if (reviewPage.value < reviewPages.value && !reviewLoading.value) {
|
||
reviewPage.value++
|
||
getReviewList()
|
||
}
|
||
}
|
||
|
||
// 刷新评论列表
|
||
const refreshReviews = () => {
|
||
reviewPage.value = 1
|
||
reviewList.value = []
|
||
getReviewList()
|
||
}
|
||
|
||
const switchTab = (tab) => {
|
||
activeTab.value = tab
|
||
console.log('切换到标签:', tab)
|
||
if (tab == 'reviews') {
|
||
refreshReviews()
|
||
}
|
||
}
|
||
|
||
const toggleChapter = (index) => {
|
||
chapterList.value[index].expanded = !chapterList.value[index].expanded
|
||
console.log('切换章节:', chapterList.value[index].title)
|
||
}
|
||
|
||
const goLesson = (lesson) => {
|
||
console.log('进入课时:', lesson.title)
|
||
if (courseDetail.value.is_buy === 0) {
|
||
uni.showToast({
|
||
title: '请先购买课程',
|
||
icon: 'none'
|
||
})
|
||
} else {
|
||
// 跳转到视频播放页面
|
||
uni.navigateTo({
|
||
url: `/pages_course/video/video?vid=${lesson.vid}&title=${encodeURIComponent(lesson.title)}`
|
||
})
|
||
}
|
||
}
|
||
|
||
const showReplyInput = (reviewId) => {
|
||
activeReplyId.value = reviewId
|
||
replyText.value = ''
|
||
}
|
||
|
||
const cancelReply = () => {
|
||
activeReplyId.value = null
|
||
replyText.value = ''
|
||
}
|
||
|
||
const submitReply = (reviewId) => {
|
||
if (!replyText.value.trim()) {
|
||
uni.showToast({
|
||
title: '请输入回复内容',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
|
||
|
||
// 找到对应的评价
|
||
const review = reviewList.value.find(item => item.id === reviewId)
|
||
|
||
|
||
if (review) {
|
||
course_api.addExcellencourseComment({
|
||
excellentcourse_id: courseId.value,
|
||
comment: replyText.value.trim(),
|
||
p_id: reviewId,
|
||
star:0,
|
||
type:0
|
||
}).then(res => {
|
||
console.log('评价提交结果:', res)
|
||
if(res.code == 200){
|
||
uni.hideLoading()
|
||
// 添加新回复
|
||
const newReply = {
|
||
id: Date.now(),
|
||
userName: '我',
|
||
avatar: '/static/icon_home_my_patient.png',
|
||
time: new Date().toLocaleDateString(),
|
||
content: replyText.value.trim(),
|
||
isAuthor: false
|
||
}
|
||
|
||
if (!review.replies) {
|
||
review.replies = []
|
||
}
|
||
review.replies.push(newReply)
|
||
|
||
// 重置状态
|
||
activeReplyId.value = null
|
||
replyText.value = ''
|
||
|
||
uni.showToast({
|
||
title: '回复成功',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
})
|
||
|
||
|
||
}
|
||
}
|
||
|
||
const goToReview = () => {
|
||
uni.navigateTo({
|
||
url: '/pages_course/course_review/course_review?id=' + courseId.value + '&is_commented=' + courseDetail.value.is_commented
|
||
})
|
||
}
|
||
|
||
const getCourseDetail = () => {
|
||
course_api.excellencourseDetail(courseId.value).then(res => {
|
||
console.log('课程详情数据:', res)
|
||
if(res.code == 200){
|
||
const data = res.data
|
||
courseDetail.value = data
|
||
|
||
// 调试 is_commented 字段
|
||
console.log('is_commented 字段值:', data.is_commented)
|
||
console.log('is_buy 字段值:', data.is_buy)
|
||
|
||
// 更新页面数据
|
||
updatePageData(data)
|
||
// 优惠价格与倒计时
|
||
const now = Date.now()
|
||
const beginTs = data.discount_begin_date ? new Date(data.discount_begin_date).getTime() : null
|
||
const endTs = data.discount_end_date ? new Date(data.discount_end_date).getTime() : null
|
||
const originalCents = data.account || 0
|
||
let finalCents = originalCents
|
||
showLimitedOffer.value = false
|
||
showPermanentOffer.value = false
|
||
if (data.discount_type === 1 && data.discount_price > 0) {
|
||
finalCents = data.discount_price
|
||
showPermanentOffer.value = true
|
||
} else if (data.discount_type === 2 && data.discount_price > 0) {
|
||
const inWindow = (!beginTs || beginTs <= now) && (!!endTs && endTs > now)
|
||
if (inWindow) {
|
||
finalCents = data.discount_price
|
||
showLimitedOffer.value = true
|
||
startCountdown(endTs)
|
||
}
|
||
}
|
||
currentPriceYuan.value = (finalCents || 0) / 100
|
||
originalPriceYuan.value = (originalCents || 0) / 100
|
||
} else {
|
||
uni.showToast({
|
||
title: res.msg || '获取课程详情失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
console.error('获取课程详情失败:', err)
|
||
uni.showToast({
|
||
title: '获取课程详情失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
}
|
||
|
||
// 更新页面数据
|
||
const updatePageData = (data) => {
|
||
// 更新课程标题
|
||
if (data.title) {
|
||
// 更新页面标题
|
||
uni.setNavigationBarTitle({
|
||
title: data.title
|
||
})
|
||
}
|
||
|
||
// 更新课程目录数据
|
||
if (data.excellentcourse_first_title_list && data.excellentcourse_first_title_list.length > 0) {
|
||
chapterList.value = data.excellentcourse_first_title_list.map((chapter, index) => {
|
||
// 计算该章节的总时长
|
||
let totalDuration = 0
|
||
if (chapter.excellentcourse_second_title_list) {
|
||
chapter.excellentcourse_second_title_list.forEach(lesson => {
|
||
if (lesson.time) {
|
||
const timeParts = lesson.time.split(':')
|
||
if (timeParts.length === 2) {
|
||
totalDuration += parseInt(timeParts[0]) + parseInt(timeParts[1]) / 60
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
return {
|
||
id: chapter.id,
|
||
title: chapter.title,
|
||
duration: totalDuration > 0 ? `约${Math.ceil(totalDuration)}分钟` : '约45分钟',
|
||
expanded: false,
|
||
status: data.is_buy === 1 ? 'unlocked' : 'locked',
|
||
statusText: data.is_buy === 1 ? '已解锁' : '未解锁',
|
||
lessons: chapter.excellentcourse_second_title_list ? chapter.excellentcourse_second_title_list.map((lesson, lessonIndex) => {
|
||
// 从标题中提取讲师姓名
|
||
let teacherName = '未知讲师'
|
||
if (lesson.title && lesson.title.includes('-')) {
|
||
teacherName = lesson.title.split('-')[0]
|
||
}
|
||
|
||
return {
|
||
id: lesson.id,
|
||
title: lesson.title,
|
||
duration: lesson.time || '未知时长',
|
||
teacher: teacherName,
|
||
status: (data.is_buy === 1 || lesson.type === 0) ? 'unlocked' : 'locked',
|
||
statusText: (data.is_buy === 1 || lesson.type === 0) ? (lesson.type === 0 ? '试播' : '已解锁') : '未解锁',
|
||
vid: lesson.vid,
|
||
type: lesson.type
|
||
}
|
||
}) : []
|
||
}
|
||
})
|
||
}
|
||
|
||
// 更新评价数量
|
||
if (data.comment_num !== undefined) {
|
||
// 评价数量已经在模板中通过响应式数据绑定,无需手动更新DOM
|
||
console.log('评价数量:', data.comment_num)
|
||
}
|
||
|
||
// 更新学习状态
|
||
if (data.study_status !== undefined) {
|
||
// 可以根据学习状态更新UI
|
||
console.log('学习状态:', data.study_status)
|
||
}
|
||
|
||
// 更新价格信息
|
||
if (data.discount_price !== undefined && data.discount_price > 0) {
|
||
// 如果有优惠价格,更新价格显示
|
||
console.log('优惠价格:', data.discount_price)
|
||
}
|
||
|
||
// 更新福利信息
|
||
if (data.fuli_bon !== undefined) {
|
||
console.log('福利积分:', data.fuli_bon)
|
||
}
|
||
}
|
||
|
||
// 生命周期
|
||
onMounted(() => {
|
||
getSystemInfo()
|
||
startCountdown()
|
||
getCourseDetail()
|
||
// 初始化评论列表
|
||
getReviewList()
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
// 清除定时器
|
||
if (countdownTimer) {
|
||
clearInterval(countdownTimer)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.course-detail-page {
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
padding-bottom: 120rpx;
|
||
}
|
||
|
||
// 导航栏
|
||
.navbar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 999;
|
||
background-color: #fff;
|
||
border-bottom: 1rpx solid #e5e5e5;
|
||
|
||
.nav-content {
|
||
height: 88rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 32rpx;
|
||
|
||
.nav-left, .nav-right {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #FF4757;
|
||
}
|
||
}
|
||
}
|
||
|
||
.page-content {
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
// 主横幅
|
||
.main-banner {
|
||
position: relative;
|
||
height: 400rpx;
|
||
overflow: hidden;
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.banner-content {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
text-align: center;
|
||
|
||
.banner-title {
|
||
font-size: 48rpx;
|
||
color: #1976D2;
|
||
font-weight: bold;
|
||
text-shadow: 0 2rpx 4rpx rgba(255,255,255,0.8);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 课程信息
|
||
.course-info {
|
||
background-color: #fff;
|
||
padding: 32rpx 24rpx;
|
||
|
||
.course-header {
|
||
margin-bottom: 24rpx;
|
||
|
||
.course-title {
|
||
display: block;
|
||
font-size: 36rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.course-meta {
|
||
.lesson-count {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.course-tags {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
.tag-item {
|
||
.tag-text {
|
||
background-color: #FF4757;
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
}
|
||
|
||
.reward-info {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
flex: 1;
|
||
margin-left: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 限时优惠模块
|
||
.limited-offer {
|
||
background: linear-gradient(135deg, #FF6B6B, #FF8E53);
|
||
margin: 24rpx;
|
||
border-radius: 20rpx;
|
||
padding: 32rpx 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 107, 0.3);
|
||
|
||
.offer-left {
|
||
flex: 2;
|
||
|
||
.price-info {
|
||
position: relative;
|
||
margin-bottom: 16rpx;
|
||
|
||
.current-price {
|
||
font-size: 48rpx;
|
||
color: #fff;
|
||
font-weight: bold;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.original-price {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
text-decoration: line-through;
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
|
||
.offer-tag {
|
||
display: inline-block;
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 8rpx 16rpx;
|
||
|
||
.tag-text {
|
||
font-size: 22rpx;
|
||
color: #FF6B6B;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
|
||
.offer-right {
|
||
flex: 1;
|
||
text-align: center;
|
||
|
||
.countdown-text {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
color: #fff;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.countdown-timer {
|
||
.days {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.time-segments {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
|
||
.time-box {
|
||
background-color: #FF8E53;
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
padding: 8rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
min-width: 40rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.time-separator {
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 永久优惠优化:去除右侧留白、更紧凑的左区布局
|
||
.limited-offer.permanent {
|
||
justify-content: flex-start;
|
||
|
||
.offer-left {
|
||
flex: initial;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.price-info {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.offer-right { display: none; }
|
||
}
|
||
|
||
// 标签导航
|
||
.tab-nav {
|
||
background-color: #fff;
|
||
border-bottom: 1rpx solid #e5e5e5;
|
||
display: flex;
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
padding: 24rpx 0;
|
||
text-align: center;
|
||
position: relative;
|
||
|
||
.tab-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
&.active {
|
||
.tab-text {
|
||
color: #FF4757;
|
||
font-weight: 600;
|
||
}
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 60rpx;
|
||
height: 4rpx;
|
||
background-color: #FF4757;
|
||
border-radius: 2rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 课程介绍内容
|
||
.course-intro {
|
||
background-color: #fff;
|
||
padding: 32rpx 24rpx;
|
||
|
||
.intro-banner {
|
||
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
|
||
padding: 24rpx;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 32rpx;
|
||
text-align: center;
|
||
|
||
.intro-title {
|
||
font-size: 32rpx;
|
||
color: #1976D2;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.course-description {
|
||
margin-bottom: 32rpx;
|
||
|
||
.description-title {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.description-content {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
|
||
img {
|
||
max-width: 100%;
|
||
height: auto;
|
||
border-radius: 8rpx;
|
||
margin: 16rpx 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.instructors {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200rpx, 1fr));
|
||
gap: 32rpx;
|
||
|
||
.instructor-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
|
||
.instructor-avatar {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
margin-bottom: 16rpx;
|
||
border: 4rpx solid #f0f0f0;
|
||
}
|
||
|
||
.instructor-info {
|
||
.instructor-name {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-bottom: 8rpx;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.instructor-hospital {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: #666;
|
||
line-height: 1.3;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 课程目录内容
|
||
.course-catalog {
|
||
background-color: #fff;
|
||
padding: 32rpx 24rpx;
|
||
|
||
.catalog-overview {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
padding: 24rpx;
|
||
background: linear-gradient(135deg, #F8F9FA, #E9ECEF);
|
||
border-radius: 16rpx;
|
||
margin-bottom: 32rpx;
|
||
|
||
.overview-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
|
||
.overview-label {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.overview-value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
|
||
.chapter-list {
|
||
.chapter-item {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 16rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
|
||
|
||
.chapter-header {
|
||
padding: 24rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
.chapter-info {
|
||
flex: 1;
|
||
|
||
.chapter-title {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
|
||
.chapter-name {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-left: 12rpx;
|
||
}
|
||
}
|
||
|
||
.chapter-meta {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
margin-left: 28rpx;
|
||
|
||
.lesson-count, .duration {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.chapter-status {
|
||
.status-text {
|
||
font-size: 24rpx;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 12rpx;
|
||
|
||
&.locked {
|
||
background-color: #F5F5F5;
|
||
color: #999;
|
||
}
|
||
|
||
&.unlocked {
|
||
background-color: #E8F5E8;
|
||
color: #4CAF50;
|
||
}
|
||
|
||
&.completed {
|
||
background-color: #E3F2FD;
|
||
color: #2196F3;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.lesson-list {
|
||
background-color: #FAFAFA;
|
||
|
||
.lesson-item {
|
||
padding: 20rpx 24rpx 20rpx 48rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.lesson-info {
|
||
flex: 1;
|
||
|
||
.lesson-title {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 8rpx;
|
||
|
||
.lesson-name {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
margin-left: 12rpx;
|
||
}
|
||
}
|
||
|
||
.lesson-meta {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
margin-left: 32rpx;
|
||
|
||
.lesson-duration, .lesson-teacher {
|
||
font-size: 22rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.lesson-status {
|
||
.status-text {
|
||
font-size: 22rpx;
|
||
padding: 6rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
|
||
&.locked {
|
||
background-color: #F5F5F5;
|
||
color: #999;
|
||
}
|
||
|
||
&.unlocked {
|
||
background-color: #E8F5E8;
|
||
color: #4CAF50;
|
||
}
|
||
|
||
&.completed {
|
||
background-color: #E3F2FD;
|
||
color: #2196F3;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 评价内容
|
||
.course-reviews {
|
||
background-color: #fff;
|
||
padding: 32rpx 24rpx;
|
||
|
||
.review-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 32rpx;
|
||
|
||
.review-title {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.review-actions-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 24rpx;
|
||
|
||
.review-count {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.write-review-btn {
|
||
background-color: #FF4757;
|
||
color: #fff;
|
||
border: none;
|
||
padding: 12rpx 24rpx;
|
||
border-radius: 20rpx;
|
||
font-size: 24rpx;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.review-list {
|
||
.review-item {
|
||
padding: 24rpx 0;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.review-user {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
|
||
.user-avatar {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border-radius: 50%;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.user-info {
|
||
flex: 1;
|
||
|
||
.user-name {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.user-rating {
|
||
display: flex;
|
||
gap: 4rpx;
|
||
}
|
||
}
|
||
|
||
.review-time {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.review-content {
|
||
.review-text {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
|
||
// 回复按钮
|
||
.review-actions {
|
||
margin-top: 16rpx;
|
||
|
||
.reply-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
padding: 12rpx 20rpx;
|
||
background-color: #f8f9fa;
|
||
border-radius: 20rpx;
|
||
|
||
.reply-text {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 二级评论列表
|
||
.reply-list {
|
||
margin-top: 16rpx;
|
||
padding-left: 32rpx;
|
||
border-left: 2rpx solid #f0f0f0;
|
||
|
||
.reply-item {
|
||
padding: 16rpx 0;
|
||
|
||
.reply-user {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
|
||
.reply-avatar {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
margin-right: 12rpx;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.reply-info {
|
||
flex: 1;
|
||
|
||
.reply-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
margin-bottom: 8rpx;
|
||
|
||
.reply-name {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.reply-time {
|
||
font-size: 20rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.author-tag {
|
||
font-size: 20rpx;
|
||
color: #fff;
|
||
background-color: #FF4757;
|
||
padding: 4rpx 8rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
}
|
||
|
||
.reply-content {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
line-height: 1.5;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 回复输入框
|
||
.reply-input-container {
|
||
margin-top: 16rpx;
|
||
padding: 16rpx;
|
||
background-color: #f8f9fa;
|
||
border-radius: 12rpx;
|
||
|
||
.reply-input-wrapper {
|
||
.reply-input {
|
||
width: 100%;
|
||
height: 60rpx;
|
||
padding: 0 16rpx;
|
||
background-color: #fff;
|
||
border: 1rpx solid #e0e0e0;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.reply-input-actions {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
justify-content: flex-end;
|
||
|
||
.cancel-btn {
|
||
padding: 8rpx 20rpx;
|
||
background-color: #f0f0f0;
|
||
color: #666;
|
||
border: none;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.submit-btn {
|
||
padding: 8rpx 20rpx;
|
||
background-color: #FF4757;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加载更多
|
||
.load-more {
|
||
padding: 32rpx 0;
|
||
text-align: center;
|
||
|
||
.load-more-btn {
|
||
background-color: #f8f9fa;
|
||
color: #666;
|
||
border: 1rpx solid #e0e0e0;
|
||
padding: 16rpx 48rpx;
|
||
border-radius: 24rpx;
|
||
font-size: 26rpx;
|
||
|
||
&:disabled {
|
||
opacity: 0.6;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 已加载全部
|
||
.load-all {
|
||
padding: 32rpx 0;
|
||
text-align: center;
|
||
|
||
.load-all-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
// 加载中状态
|
||
.loading-reviews {
|
||
padding: 80rpx 0;
|
||
text-align: center;
|
||
|
||
.loading-icon {
|
||
margin-bottom: 24rpx;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
@keyframes spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
// 空评价状态
|
||
.empty-reviews {
|
||
padding: 80rpx 0;
|
||
text-align: center;
|
||
|
||
.empty-icon {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.empty-desc {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
color: #ccc;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部购买栏
|
||
.bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 120rpx;
|
||
background-color: #fff;
|
||
border-top: 1rpx solid #e5e5e5;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 24rpx 20rpx 24rpx;
|
||
z-index: 999;
|
||
|
||
.price-section {
|
||
flex: 1;
|
||
|
||
.price {
|
||
font-size: 48rpx;
|
||
color: #FF4757;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
|
||
.buy-section {
|
||
.buy-btn {
|
||
background-color: #FF4757;
|
||
color: #fff;
|
||
border: none;
|
||
padding: 24rpx 48rpx;
|
||
border-radius: 12rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
</style>
|