Files
learning-system-portal/src/views/portal/course/Detail.vue
2025-11-11 18:13:01 +08:00

773 lines
23 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>
<div>
<div class="xpage-coures-banner">
<portal-header current="course" textColor="#fff" :goSearch="1"></portal-header>
<div class="xcontent">
<div class="banner-crumbs">
<router-link to="/course"><span class="crumbs-first">课程列表</span></router-link>
<span class="crumbs-line"> &gt; </span>
<span class="crumbs-last">课程详情</span>
</div>
<div class="bcourse-title">
<!-- <span>{{courseInfo.name}}</span> -->
<!-- <span>{{courseInfo.summary}}</span> -->
<!-- <span class="bcourse-score">{{toScore(courseInfo.score)}}</span> -->
</div>
</div>
</div>
<div class="portal-content xcontent">
<div class="detail">
<div class="image">
<course-image :course="courseInfo"></course-image>
</div>
<div class="detail-info">
<div class="course-title">
<div class="title"> {{courseInfo.name}}</div>
<interactBar :readonly="!stuStusts || stuStusts==0" :type="1" :data="courseInfo" :praises="true" :comments="false" :views="false"></interactBar>
</div>
<!-- <div class="course-title-right"> -->
<!-- <interactBar :readonly="!stuStusts || stuStusts==0" :type="1" :data="courseInfo" :comments="false" :views="false"></interactBar> -->
<!-- </div> -->
<!-- <div class="label-div">
<el-tag class="label-item" effect="plain" v-for="(item,tagIdx) in tagArray" :key="tagIdx">{{item}}</el-tag>
</div>-->
<div class="label-div">
<div v-for="(item, tagIdx) in tagArray" :key="tagIdx" class="keyword-tag">
{{ item }}
</div>
</div>
<div>
<div class="study-count">{{courseInfo.studys}}人学习</div>
<!-- <div><span style="font-size:20px;color:#ff8e00">{{courseInfo.score ? courseInfo.score.toFixed(1) : 0}}</span><span style="font-size:12px;color:#ff8e00"></span></div> -->
</div>
<!-- <div style="width:160px;height:50px"> -->
<!-- </div> -->
<!-- <div class="label-div">
<el-tag class="label-item" effect="plain">{{courseType(courseInfo.type)}}</el-tag>
</div> -->
<div class="score-div">
<!-- colors="[#FF8E00]" -->
<div v-if="courseInfo.score">
<el-rate disabled v-model="courseInfo.score"></el-rate>
<span class="score-text">{{courseInfo.score ? courseInfo.score.toFixed(1) : 0}}<span style="font-size: 14px;"></span></span>
</div>
<div v-else class="score-no">未评分</div>
</div>
<div class="btn-div" v-if="isCrowd" style="height:30px;margin-top: 15px;">
<el-button v-if="stuStusts == 0" type="primary" @click="startSigningUp()" >开始学习</el-button>
<el-button v-if="stuStusts == 1" type="primary" @click="jumpJearning">{{btnName}}</el-button>
</div>
<div class="erweima" v-if="courseInfo.device == 2 || courseInfo.device == 3">
<img class="detail-img" v-show="!showQrcode" @click="showQrcode = true" src="../../../assets/images/qr.png">
<div v-show="showQrcode" class="qrcode" @click="showQrcode = false">
<div id="qrcode" ref="qrcode"></div>
<!-- <div class="qrcode-title">手机扫码观看</div> -->
</div>
</div>
</div>
</div>
<el-row :gutter="10">
<el-col :span="18">
<el-card :body-style="{ padding: '0px' }" class="info" style="margin-top: 10px;">
<el-tabs v-model="activeName">
<el-tab-pane label="内容简介" name="first">
<div>
<div class="content"><img src="@/assets/images/icon/remark-iocn.png" alt="" srcset="">目标人群<div class="content-text">{{courseInfo.forUsers}}</div></div>
<div class="content"><img src="@/assets/images/icon/remark-iocn.png" alt="" srcset="">课程价值<div class="content-text">{{courseInfo.value}}</div></div>
<div class="content"><img src="@/assets/images/icon/remark-iocn.png" alt="" srcset="">详细介绍<div class="content-text" v-html="courseInfo.summary"></div></div>
</div>
</el-tab-pane>
<el-tab-pane label="课程速览" name="second" v-if="courseInfo.type==20">
<div class="category" v-for="(item) in catalogTree" :key="item.id">
<div class="node">
<div class="title">{{item.section.name}}</div>
<div class="subnode" v-for="(it,itIdx) in item.contents" :key="it.id">
<div class="sub-title">
<div class="tip">
<span class="tip-type">{{getType(it.contentType)}}</span>
<a :href="'studyindex?id='+it.courseId+'&contentId='+it.id"> {{it.contentName}}</a>
</div>
</div>
</div>
</div>
</div>
<div style="height: 30px;"></div>
</el-tab-pane>
<el-tab-pane label="课程速览" name="second" v-if="courseInfo.type==10">
<div class="category" v-for="(item) in contentList" :key="item.id">
<div class="node">
<div class="title">{{item.contentName}}</div>
</div>
</div>
<div style="height: 30px;"></div>
</el-tab-pane>
<el-tab-pane :label="'评论('+commentsTtoal+')'" name="third">
<comments v-if="courseInfo.id!=''" :readonly="!stuStusts || stuStusts==0" :showTop="false" @writeTotal="showCommentsTotal" @success="addCommentSuccess" @delSuccess="delCommentSuccess" :obj-type="1" :to-users="toUsers" :obj-id="courseInfo.id" :obj-title="courseInfo.name"></comments>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
<el-col :span="6">
<el-card :body-style="{ padding: '0px' }" class="teachers">
<div class="top">授课讲师</div>
<div>
<div class="teacher" v-for="(item, idx) in teachers" :key="idx" >
<div class="teacher-avator" style="cursor: pointer;" @click="toUserHome(item)">
<el-avatar v-if="item.avatar !=='' " :src="item.avatar" shape="circle" :size="50"></el-avatar>
<div class="teacher-text" v-if="item.sex !== null && item.avatar ==''">
<div v-if="item.sex === 1 "><img src="../../../../public/images/Avatarman.png" alt=""></div>
<div v-else><img src="../../../../public/images/Avatarwoman.png" alt=""></div>
</div>
</div>
<div class="teacher-info">
<div class="teacher-name">{{ item.teacherName }}</div>
<!-- <div class="teacher-remark" v-html="item.remark"></div> -->
<div class="teacher-remark">{{ cutOrgNamePath(item.orgInfo) }} </div>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<portal-footer></portal-footer>
</div>
</template>
<script>
import QRCode from 'qrcodejs2';
import { mapGetters,mapActions} from 'vuex';
import portalHeader from "@/components/PortalHeader.vue";
import portalFooter from "@/components/PortalFooter.vue";
import comments from "@/components/Portal/comments.vue";
import interactBar from "@/components/Portal/interactBar.vue";
import apiCourse from '@/api/modules/course.js';
import apiUser from '@/api/system/user.js';
import courseStudy from '@/api/modules/courseStudy.js';
import apiCoursePortal from '@/api/modules/coursePortal.js';
import { courseType,getType,toScore,userAvatarText,cutOrgNamePath } from '@/utils/tools.js';
import courseImage from "@/components/Course/courseImage.vue"
export default {
name: "atticle",
components: { portalHeader, portalFooter,interactBar,comments,courseImage},
computed: {
...mapGetters(['resOwnerMap','sysTypeMap','userInfo']),
},
data() {
return {
audiences:'',
cutOrgNamePath,
userAvatarText,
getType,
showQrcode: false,
qrcode: '',
courseType,
toScore,
fileBaseUrl:process.env.VUE_APP_FILE_BASE_URL,
// resOwnerListMap:resOwnerIndexName,
props: {name:'',type:'',sex:null},
tagArray:[],
courseInfo:{id:'',sex:null},
contentList:[],
catalogTree:[],
curContent:{},//当前看的内容目录
teachers:[],
activeName: "first",
stuStusts: 1,
commentsTtoal:0,
toUsers:[],//可以@的用户列表
btnName:'开始学习',
isCrowd:false, //根据受众展示不展示
};
},
mounted() {
// 增加的用户受众id
let localKey = "user_" + this.userInfo.sysId + "_gids";
let hasIds = sessionStorage.getItem(localKey);
this.audiences = hasIds ?? ''
let id = this.$route.query.id;
this.courseInfo.id=id;
this.$watermark.set(this.userInfo.name+this.userInfo.loginName);
//this.stuStusts=0;
this.loadResOwners();
let $this=this;
//页面只支取一次,所以先直接写在这里面
apiCoursePortal.detailPost({
id,
preview:false,
course:true,
audiences:this.audiences
}).then(rs=>{
if(rs.status==200){
if(!rs.result.course.enabled || rs.result.course.deleted){
$this.$message.error('十分抱歉,此课程已停用,如需使用,请联系管理员。');
return;
}
if(!rs.result.isCrowd){
$this.$message.error('您没有查看该课程的权限');
}
this.isCrowd = rs.result.isCrowd
if(rs.result.teachers && rs.result.teachers.length > 0){
let userIds=[];
let ctoUsers=[];
rs.result.teachers.forEach(item=>{
item.avatar = "";
item.sex = null;
userIds.push(item.teacherId);
ctoUsers.push({aid:item.teacherId,name:item.teacherName,sex:item.sex});
})
this.toUsers=ctoUsers;
this.loadAuthorInfo(rs.result.teachers,userIds);
}
this.courseInfo=rs.result.course;
this.courseInfo.score = rs.result.course.score;
if(rs.result.course.tags!=''){
this.tagArray=rs.result.course.tags.split(',');
}
if(!rs.result.course.resOwner3){
this.courseInfo.resOwner = this.resOwnerName(rs.result.course.resOwner2);
} else{
this.courseInfo.resOwner = this.resOwnerName(rs.result.course.resOwner3);
}
this.teachers=rs.result.teachers;
if(this.courseInfo.type == 10) {
this.contentList=rs.result.contents;
} else {
let treeList=[];
rs.result.sections.forEach(sec=>{
let treeNode={section:sec,contents:[]}
rs.result.contents.forEach(c=>{
if(c.csectionId==sec.id){
treeNode.contents.push(c);
}
});
treeList.push(treeNode);
})
this.catalogTree=treeList;
}
this.showQrimage();
this.isSignUp();
}else{
if(rs.status==204){ //无查看此课程的权限
this.courseInfo.name=rs.result.course.name;
}
this.$message.error(rs.message);
}
})
// this.getResOwnerTree().then(rs=>{
// this.resOwnerListMap=rs;
// });
},
methods: {
...mapActions({
getResOwnerTree:'resOwner/getResOwnerTree',
loadResOwners:'resOwner/loadResOwners',
getSysTypeTree:'sysType/getSysTypeTree',
loadSysTypes:'sysType/loadSysTypes'
}),
toUserHome(tea){
this.$router.push({path:this.$xpage.getHomePath(tea.teacherId)})
},
showQrimage(){
let urlPre=window.location.protocol+'//'+window.location.host;
let returnUrl=urlPre+'/mobile/pages/login/loading?returnUrl=/pages/study/courseStudy?id='+this.courseInfo.id;
returnUrl=encodeURIComponent(returnUrl);
this.qrcode =urlPre+'/m?returnUrl='+returnUrl;
this.$nextTick(() => {
this.crateQrcode();
});
},
crateQrcode(){
this.qr = new QRCode('qrcode', {
width: 150,
height: 150, // 高度
text: this.qrcode // 二维码内容
});
this.qr._el.title = '';
},
showCommentsTotal(total){
this.commentsTtoal=total;
},
addCommentSuccess(comm){
this.commentsTtoal++;
},
delCommentSuccess() {
this.commentsTtoal--;
},
resOwnerName(code){
if(code==''){return '';}
return this.resOwnerMap&&this.resOwnerMap.get(code);
},
// sysTypeName(code){
// if(code==''){return '';}
// return this.sysTypeMap.get(code);
// },
jumpJearning() {
this.$router.push({path:'/course/studyindex',query:{id:this.courseInfo.id}});
},
// 开始报名
startSigningUp() {
let pamars = {
courseId: this.courseInfo.id,
courseName: this.courseInfo.name,
courseType: this.courseInfo.type,
signType: 1,
signInfo: ''
}
courseStudy.signup(pamars).then(res=>{
if(res.status == 200) {
this.stuStusts = 1;
//this.activeName = 'second';
this.$router.push('/course/studyindex?id='+this.courseInfo.id);
} else {
this.$message.error(res.message);
}
})
},
loadLastStudy(){
courseStudy.lastStudy().then(rs=>{
if(rs.status==200){
this.btnName="继续学习";
}
});
},
// 判断是否报名
isSignUp() {
courseStudy.hasSignup(this.courseInfo.id).then(res =>{
if(res.result) {
this.stuStusts = 1;
this.activeName = 'second';
this.loadLastStudy();
} else {
this.stuStusts = 0;
//this.startSigningUp();
}
})
},
loadAuthorInfo(list,ids){ //加载作者信息,头像,机构信息
if(ids.length==0){
return;
}
apiUser.getByIds(ids).then(res=>{
if(res.status==200){
list.forEach((item,index)=>{
res.result.some(author=>{
if(author.aid==item.teacherId){
if(author.avatar){
item.avatar=this.fileBaseUrl + author.avatar;
}
item.sex = author.sex;
item.orgInfo = author.orgInfo
return true;
}else{
return false;
}
})
});
}else{
//console.log('加载课程信息失败:'+res.error);
//this.$message.error(res.message);
}
});
},
},
};
</script>
<style scoped lang="scss">
::v-deep .teacher .teacher-avator .teacher-text {
background: none;
border: none;
}
::v-deep .el-tabs__item.is-active {
font-size: 18px;
color: #409EFF;
}
::v-deep .el-tabs__active-bar {
// width: 38px !important;
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background-color: #409EFF;
z-index: 1;
transition: transform .3s cubic-bezier(.645,.045,.355,1);
list-style: none;
}
::v-deep .el-tag {
background-color: #ecf5ff;
border-color: #409EFF;
display: inline-block;
height: 28px;
padding: 0 10px;
line-height: 28px;
font-size: 12px;
color: #3e7fff;
border-width: 1px;
border-style: solid;
border-radius: 0px;
box-sizing: border-box;
white-space: nowrap;
}
.course-title{
position: relative;
height: auto;
display: flex;
justify-content: space-between;
.title {
// height: 70px;
font-size: 22px;
font-weight: 600;
color: #333333;
margin-bottom: 10px;
word-break:break-all;
}
// .course-title-right{
// position: absolute;
// right: 0;
// top: 0
// }
}
.detail {
display: flex;
margin-top: 20px;
background-color: #fff;
.image {
width: 500px;
height: 280px;
}
.detail-info {
position: relative;
flex: 1;
padding: 24px 24px 5px 24px;
// margin-right: 361px;
.study-count {
margin-top: 30px;
font-size: 16px;
color: #444444;
}
/*.label-div {
margin: 5px 0;
min-height: 20px;
.label-item {
padding: 0px 8px;
margin-top: 5px;
float: left;
line-height: 24px;
font-size: 12px;
border-radius: 2px;
margin-right: 8px;
color: #2C68FF;
height: 24px;
background: rgba(44, 104, 255, 0.06);
border: none; // 或者使用 border-color: transparent;
}
}*/
.label-div {
margin: 5px 0;
min-height: 20px;
.keyword-tag {
padding: 0px 10px;
margin-top: 7px;
float: left;
line-height: 24px;
font-size: 12px;
border-radius: 2px;
margin-right: 10px;
color: #2C68FF;
height: 24px;
background: rgba(44, 104, 255, 0.06);
}
}
::v-deep .el-rate__icon {
font-size: 20px;
margin-right: 6px;
color: #FFB30F;
transition: .3s;
}
.score-div{
margin-top: 10px;
.el-rate {
display: inline-block;
}
.score-text{
font-size: 28px;
color: #FFB30F ;
font-family: "Arial";
margin-left: 23px;
// font-weight: 600;
}
// margin-bottom: 19px;
.score-no{
color: #FFB30F;
padding: 5px 0;
background: #efefef;
border-radius: 20px;
width: 65px;
font-size: 12px;
font-weight: 600;
text-align: center;
}
}
.btn-div {
.el-button{
font-size: 16px;
border-radius: 0;
}
}
.erweima{
position: absolute;
bottom: 0;
right: 0;
cursor: pointer;
.detail-img{
vertical-align:bottom;
}
.qrcode{
width: 150px;
height: 150px;
display: inline-block;
margin: 1px;
img {
width: 80px;
height: 80px;
background-color: #fff; //设置白色背景色
padding: 6px; // 利用padding的特性挤出白边
box-sizing: border-box;
}
.qrcode-title{
font-size: 12px;
text-align: center;
line-height: 25px;
margin-top: 1px;
border: 1px solid #666666;
}
}
}
}
}
.info {
background-color: #fff;
margin-top: 20px;
padding: 20px;
.category {
padding: 0 10px;
padding-bottom: 20px;
border-bottom: 1px solid #f1f1f1;
.node {
.title {
margin-top: 10px;
margin-bottom: 10px;
// border-left: 2px solid #0591ff;
padding-left: 5px;
font-weight: 600;
font-size: 14px;
}
.subnode {
padding: 3px 15px 4px 0;
.sub-title {
font-size: 14px;
color: #000;
line-height: 28px;
.tip {
color: #828282;
line-height: 41px;
.tip-type{
width: 36px;
height: 24px;
border: 1px solid #666666;
padding: 3px 5px;
margin-right: 17px;
}
}
.circular {
background: green;
width: 6px;
height: 7px;
display: inline-block;
border-radius: 50%;
}
}
}
}
}
.content {
// line-height: 25px;
padding: 10px 0;
font-size: 18px;
color: #343434;
img{
width: 18px;
margin-right: 6px;
}
div{
margin: 10px 0;
font-size: 14px;
text-indent:2em;
color: #666666;
word-break:break-all;
}
}
.pinglun-div {
background-color: #fff;
padding: 0px 20px 10px 20px;
.title {
font-size: 16px;
font-weight: 600;
line-height: 50px;
border-bottom: 1px solid #dfdfdf;
.tip {
color: #666;
font-size: 12px;
margin: 0 10px;
}
}
.submit-div {
margin: 15px 0;
}
.bottom {
text-align: right;
}
.data {
margin-top: 10px;
background-color: #fff;
padding: 5px 20px 10px 20px;
.data-item {
border-bottom: 1px solid #dfdfdf;
margin-top: 10px;
.top {
font-size: 18px;
font-weight: 600;
span {
font-size: 14px;
color: #666;
font-weight: normal;
}
}
.text {
margin: 15px 0 5px 0;
padding-left: 40px;
line-height: 35px;
}
.huifu {
padding-left: 40px;
padding-bottom: 10px;
border-bottom: 1px solid #dfdfdf;
}
.result-div {
padding-left: 40px;
padding-top: 10px;
.top {
font-size: 16px;
}
}
.up-div {
padding: 0 15px;
.up-btn {
padding: 8px 20px;
color: #000;
}
}
img {
margin-right: 10px;
width: 30px;
border: 1px solid #eee;
border-radius: 50%;
vertical-align: middle;
}
}
}
}
}
.teacher{
background-color: #FFFFFF;
display: flex;
border-top: 1px solid #e7e7e7;
padding: 10px 0px;
.teacher-info{
flex: 1;
.teacher-name{
padding: 0px 5px 10px 10px;
}
.teacher-remark{
padding: 0px 5px 0 10px;
font-size: 12px;
color: #797979;
}
}
}
.teachers {
background-color: #fff;
margin-top: 10px;
padding: 5px 15px 5px 10px;
.teacher-text{
width: 68px;
height: 68px;
text-align: center;
line-height: 68px;
div{
img{
width: 68px;
height: 68px;
}
}
}
::v-deep .el-tabs__nav-wrap::after {
// content: "";
// position: absolute;
// left: 0;
// bottom: 0;
// width: 100%;
// height: 2px;
background-color: #fff;
// z-index: 1;
}
::v-deep .el-tabs__nav-wrap::after {
// content: "";
// position: absolute;
// left: 0;
// bottom: 0;
// width: 100%;
// height: 2px;
background-color: #fff;
// z-index: 1;
}
.top {
font-size: 18px;
color: #353535;
// font-weight: 600;
line-height: 50px;
}
}
::v-deep .el-card{
border: 1px solid #fff;
}
.el-card, .el-message {
border-radius: 0px;
}
</style>