Files
tra-app/src/pages/courseDetail/index.vue
T
2025-08-30 20:31:58 +08:00

512 lines
12 KiB
Vue

<template>
<view class="max_page">
<nav-bar :is_seat="false" :backgroundColor="bgOpacity"></nav-bar>
<scroll-view
:scroll-y="true"
class="scroll detail-main"
@scroll="scroll_fun"
@scrolltolower="scrolltolower"
:show-scrollbar="false"
>
<view class="bg_div">
<image-preview class="img_100" :imgId="form.imgId" mode="widthFix"></image-preview>
</view>
<view class="back_div" @click="common.navigateBack()">
<view class="back-icon"></view>
</view>
<view class="detail-body">
<view class="head-title font_pf">
<view class="title-text">
{{ form.crsName || '' }}
</view>
<view class="title-num">
<view class="num-item">
<image
class="title-icon"
src="/src/static/images/courseDetail/zhishidian.png"
mode="heightFix"
></image>
<view class="mr-10">知识点</view>
<view class="c-000">{{ form.pgrphNum || 0 }}</view>
</view>
<view class="num-item">
<image
class="title-icon"
src="/src/static/images/courseDetail/yixue.png"
mode="heightFix"
></image>
<view class="mr-10">已学</view>
<view class="c-000">{{ form.styParaGraphNum || 0 }}</view>
</view>
<view
class="num-item examination"
@click="common.navigateTo(`/pages/examRanking/index?crsId=${crsId}`)"
>
<image
class="title-icon"
src="/src/static/images/courseDetail/charts.png"
mode="heightFix"
></image>
<view>考试排行榜</view>
<view class="back-icon"></view>
</view>
<view class="fl1"></view>
<view class="num-item" @click="collect_click(form.collectId)">
<image
class="collect-icon"
:src="form.collectId ? collectedImg : notCollectedImg"
mode="widthFix"
></image>
</view>
</view>
</view>
<view class="body-box">
<uv-sticky :customNavHeight="statusBarHeight" class="sticky">
<view class="body-tab">
<uv-tabs
class="font_pf"
:current="tabAct"
:list="tabList"
@change="tabSwitch"
:scrollable="false"
line-color="#0066FF"
:active-style="{ color: '#323E57', 'font-weight': 600, 'font-size': '30rpx' }"
:inactive-style="{ color: '#666666', 'font-size': '30rpx' }"
></uv-tabs>
</view>
</uv-sticky>
<!-- <swiper class="body-swiper" @change="swiper_change" :current="tabAct"><swiper-item> -->
<view class="course-info" v-if="tabAct == 0">
<introduceVue v-model="form"></introduceVue>
</view>
<!--</swiper-item><swiper-item>-->
<view class="point" v-if="tabAct == 1">
<knowledge v-model="form" @toStudyPrgh="toStudyPrgh"></knowledge>
</view>
<!--</swiper-item><swiper-item>-->
<view class="evaluate" v-if="tabAct == 2 && form">
<comment ref="commentRef" :crsId="crsId" :imgId="form.imgId"></comment>
</view>
<!--</swiper-item></swiper>-->
</view>
</view>
</scroll-view>
<view class="btn-box btn-box-height font_pf">
<template v-if="[0, 1].includes(tabAct)">
<view class="fl1">
<uv-button type="primary" @click="goto_study()">去学习</uv-button>
</view>
<view class="fl1" @click="goto_practice()">
<uv-button type="primary" :disabled="practiceButtonDisabled" >去练习</uv-button>
</view>
<view class="fl1" @click="goto_examination()">
<uv-button type="primary" :disabled="examinationButtonDisabled" >
去考试
</uv-button>
</view>
</template>
<template v-if="tabAct === 2">
<uv-button
type="primary"
class="send_evaluate"
:disabled="isPreview === 'preview'"
@click="common.navigateTo('/pages/courseDetail/submit?crsId=' + crsId + '&imgId=' + form.imgId)"
>
发布评价
</uv-button>
</template>
</view>
</view>
</template>
<script setup>
import { reactive, ref, computed, onMounted } from 'vue';
import { onShow, onLoad, onUnload } from '@dcloudio/uni-app';
import { getCourseDetailApi } from '@/api/courseDetail.js';
import { startExamByCrs } from '@/api/examination.js';
import { insertTraPersonalCollectionTask, deleteTraPersonalCollectionById } from '@/api/favorites.js';
import { startPracticeByCrs } from '@/api/practice.js';
import common from '@/common/common';
import { setPageCache } from '@/common/common';
import introduceVue from './components/introduce.vue';
import knowledge from './components/knowledge.vue';
import comment from './components/comment.vue';
import collectedImg from '@/static/images/courseDetail/out_collect.png';
import notCollectedImg from '@/static/images/courseDetail/in_collect.png';
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight;
// tab栏
const tabList = ref([{ name: '课程介绍' }, { name: '知识点' }, { name: '评价' }]);
const commentRef = ref(null);
const form = ref({});
const crsId = ref('');
const collectId = ref(''); // 记录初始的收藏id
const isPreview = ref(''); // 记录是否是预览
const tabAct = ref(0);
const scrollTop = ref(0);
const practiceButtonDisabled = computed(() => isPreview.value === 'preview' || form.value.examStat === '02');
const examinationButtonDisabled = computed(() => isPreview.value === 'preview' || form.value.examStat !== '00');
const tabSwitch = (item) => {
tabAct.value = item.index;
};
// 上拉加载
const scrolltolower = (item) => {
if (tabAct.value == 2) {
commentRef.value?.getData();
}
};
const getData = async () => {
const { body } = await getCourseDetailApi({
crsId: crsId.value
});
form.value = body;
};
const toStudyPrgh = (info) => {
common.navigateTo(
'/pages/course/study?crsNum=' +
form.value.crsNum +
'&crsId=' +
crsId.value +
'&pgrphId=' +
info.pgrphId +
'&imgId=' +
form.imgId
);
};
const bgOpacity = computed(() => {
if (scrollTop.value <= 20) return `rgba(255,255,255,0)`;
const opacity = Math.min(1, (scrollTop.value - 20) / 80);
return `rgba(255,255,255,${opacity})`;
});
const scroll_fun = (e) => {
scrollTop.value = e.detail.scrollTop;
};
const swiper_change = (res) => {
tabAct.value = res.detail.current;
};
// 去考试
const goto_examination = async () => {
if (isPreview.value === 'preview') return common.msg('预览课程不允许考试');
if (form.value.examStat === '01') return common.msg('该课程下暂无试题');
if (form.value.examStat === '02') return common.msg('该课程下的试题未设置考试规则');
common.loading();
startExamByCrs({
crsId: crsId.value
})
.then((res) => {
setPageCache('examination', {
...res.body,
name: form.value.crsName,
crsId: crsId.value
});
common.navigateTo('/pages/examination/index');
})
.finally(() => {
common.hideLoading();
});
};
// 去学习
const goto_study = async () => {
common.navigateTo(
'/pages/course/study?crsId=' + crsId.value + '&isPreview=' + isPreview.value + '&imgId=' + form.value.imgId
);
};
// 去练习
const goto_practice = async () => {
if (isPreview.value === 'preview') return common.msg('预览课程不允许练习');
if (form.value.examStat !== '00') return common.msg('该课程下暂无试题');
common.loading();
startPracticeByCrs({
crsId: crsId.value
})
.then((res) => {
console.log('res', res);
setPageCache('practice', {
...res.body,
name: form.value.crsName,
crsId: crsId.value
});
common.navigateTo('/pages/practice/index');
})
.finally(() => {
common.hideLoading();
});
};
const collect_click = async (id) => {
try {
if (!id) {
common.loading('收藏中');
const result = await insertTraPersonalCollectionTask({
collectTyp: '01',
collectBusId: crsId.value
});
console.log('result.body', result.body);
form.value.collectId = result.body;
common.msg('收藏成功');
} else {
common.loading('取消收藏中');
await deleteTraPersonalCollectionById({
collectId: id
});
form.value.collectId = null;
common.msg('取消成功');
}
} catch (e) {
common.hideLoading();
}
};
onMounted(async () => {
uni.$on('refresh_comment_data', () => {
commentRef.value?.getData('submit');
tabAct.value = 2;
});
});
onLoad((params) => {
crsId.value = params.crsId;
collectId.value = params.collectId || '';
isPreview.value = params.isPreview || '';
});
onShow(() => {
getData();
});
onUnload(() => {
uni.$off('refresh_comment_data');
if (form.value.collectId !== collectId.value) {
const pages = getCurrentPages();
if (pages.length >= 2) {
const prevPage = pages[pages.length - 2];
if (prevPage.route === 'pages/favorites/index') {
// 更新
uni.$emit('refresh_favorites_data', {
type: 'course',
state: !!form.value.collectId, // 最终是收藏了还是取消了
old: collectId.value, // 进入时候的收藏id
new: form.value.collectId // 新的收藏id
});
}
}
}
});
</script>
<style lang="scss" scoped>
.back_div {
margin-right: 10rpx;
position: fixed;
left: 25rpx;
top: var(--status-bar-height);
// background-color: red;
// width: 60rpx;
// height: 60rpx;
padding: 20rpx;
.back-icon {
position: relative;
width: 40rpx;
height: 40rpx;
cursor: pointer;
}
.back-icon::before,
.back-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.back-icon::after {
top: 25%;
left: 25%;
width: 40%;
height: 40%;
border-top: 4rpx solid #ffffff;
border-left: 4rpx solid #ffffff;
transform: rotate(-45deg);
}
}
.detail-main {
background-color: #f5f5f5;
position: relative;
overflow: hidden;
}
.scroll {
height: 100vh;
}
.detail-body {
position: absolute;
top: 256rpx;
border-radius: 23rpx 23rpx 0rpx 0rpx;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.body-head {
height: 460rpx;
background-color: red;
}
.bg_div {
position: fixed;
width: calc(100% + 4rpx);
left: -4rpx;
.back-image {
width: 100%;
}
}
.head-title {
border-radius: 24rpx 24rpx 0rpx 0rpx;
// height: 162rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
padding-top: 36rpx;
padding-left: 42rpx;
padding-bottom: 24rpx;
min-height: 158rpx;
justify-content: space-between;
}
.title-text {
font-size: 34rpx;
color: #333333;
font-weight: 600;
margin-bottom: 20rpx;
}
.title-icon {
height: 30rpx;
margin-right: 10rpx;
}
.collect-icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.title-num {
display: flex;
flex-wrap: nowrap;
.examination {
color: #0066ff;
.back-icon {
position: relative;
width: 40rpx;
height: 40rpx;
cursor: pointer;
}
.back-icon::before,
.back-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.back-icon::after {
top: 25%;
left: 25%;
width: 40%;
height: 40%;
border-top: 4rpx solid #0066ff;
border-left: 4rpx solid #0066ff;
transform: rotate(135deg);
}
}
}
.body-box {
background-color: #f5f5f5;
flex: 1;
padding-left: 16rpx;
padding-right: 16rpx;
padding-top: 20rpx;
padding-bottom: 245rpx;
}
.body-tab {
background-color: #ffffff;
border-radius: 22rpx;
height: 92rpx;
padding-bottom: 22rpx;
margin-bottom: 24rpx;
}
.course-info,
.point,
.evaluate {
min-height: calc(100vh - 92rpx - 22rpx - 245rpx - var(--status-bar-height));
}
.btn-box-height {
height: 187rpx;
}
.btn-box {
position: fixed;
bottom: 0;
width: 100%;
display: flex;
padding-top: 37rpx;
justify-content: space-around;
background-color: #ffffff;
border: 2rpx solid #e5e5e5;
z-index: 1000;
:deep(.uv-button-wrapper) {
flex: 1;
&.send_evaluate {
margin: 0 60rpx;
}
&:not(.send_evaluate) {
margin: 0 30rpx;
}
.uv-button {
border-radius: 14rpx;
width: 100%;
height: 92rpx;
}
}
}
.num-item {
display: flex;
align-items: center;
font-size: 30rpx;
margin-right: 26rpx;
color: #666666;
height: 30rpx;
}
.learn {
color: #1dd17a;
}
.unlearn {
color: #666666;
}
.c-000 {
font-weight: 600;
color: #000000;
}
</style>