You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1455 lines
45 KiB
1455 lines
45 KiB
<template> |
|
<div class="wrap"> |
|
<breadcrumb ref="breadcrumb" |
|
:data="'实验台/' + (curriculumName || courseName)"></breadcrumb> |
|
<div class="flex"> |
|
<div class="cover" |
|
:class="{'is-word': showMask1}"> |
|
<!--实验台不显示课程封面--> |
|
<img v-if="coverUrl" |
|
:src="coverUrl" |
|
alt="" |
|
width="100%" |
|
height="100%"> |
|
<template v-else-if="iframeSrc"> |
|
<iframe class="inner fileIframe" |
|
id="fileIframe" |
|
:src="iframeSrc" |
|
frameborder="0"></iframe> |
|
<template v-if="showMask"> |
|
<div class="mask" |
|
style="top: 0;width:100%;height: 48px;"></div> |
|
<div class="mask" |
|
style="top: 53px;width:100%;height: 30px;"></div> |
|
<div class="mask" |
|
style="bottom: 0;right: 0;width:280px;height: 22px;background-color: #444;"></div> |
|
</template> |
|
<template v-if="showMask1"> |
|
<div class="word-mask" |
|
style="height: 40px;"></div> |
|
<div class="word-mask2" |
|
style="top: 55px;left: 28%;width: 44%;height: calc(100% - 80px);"></div> |
|
</template> |
|
<template v-if="showMask2"> |
|
<div class="excel-mask1" |
|
style="height: 48px;"></div> |
|
</template> |
|
</template> |
|
<div class="pdf" |
|
v-else-if="pdfSrc"> |
|
<img class="full" |
|
src="@/assets/img/screen/full.png" |
|
alt="" |
|
@click="fullScreen"> |
|
<p class="arrow"> |
|
<span @click="changePdfPage(0)" |
|
class="turn el-icon-arrow-left" |
|
:class="{grey: currentPage==1}"></span> |
|
{{ currentPage }} / {{ pageCount }} |
|
<span @click="changePdfPage(1)" |
|
class="turn el-icon-arrow-right" |
|
:class="{grey: currentPage==pageCount}"></span> |
|
</p> |
|
<div style='color:#393A3D;height: 30px;background: #393A3D;'></div> |
|
<pdf class="pdf-wrap" |
|
:src="pdfSrc" |
|
:page="currentPage" |
|
@num-pages="pageCount=$event" |
|
@page-loaded="currentPage=$event" |
|
@loaded="loadPdfHandler"> |
|
</pdf> |
|
</div> |
|
<div class="inner" |
|
v-else-if="playAuth"> |
|
<div class="video_wid" |
|
id="player"></div> |
|
</div> |
|
<div class="inner" |
|
v-else-if="videoSrc"> |
|
<video class="video" |
|
width="100%" |
|
height="100%" |
|
autoplay |
|
controls> |
|
<source :src="videoSrc" |
|
type="video/mp4"> |
|
您的浏览器不支持 video 标签。 |
|
</video> |
|
</div> |
|
</div> |
|
<div class="catalog"> |
|
<el-button v-if="!overdue" |
|
class="renew" |
|
type="primary" |
|
size="small" |
|
@click="showBuy">续费</el-button> |
|
<a v-else |
|
class="entry" |
|
@click="entry">进入实验</a> |
|
<div class="list"> |
|
<h4 class="title">{{ curriculumName || courseName }}</h4> |
|
<div> |
|
<h6 class="pro-title">我的学习进度</h6> |
|
<el-progress :stroke-width="12" |
|
:percentage="schedule" |
|
:format="percentFormat" |
|
:text-color="'#fff'"></el-progress> |
|
</div> |
|
<div class="types"> |
|
<div :class="['item', { active: type === 1 }]" |
|
@click="typeChange(1)"> |
|
<i class="icon el-icon-notebook-2"></i> 目录 |
|
</div> |
|
<div :class="['item', { active: type === 2 }]" |
|
@click="typeChange(2)"> |
|
<i class="icon el-icon-timer"></i> 学习进度 |
|
</div> |
|
</div> |
|
<div class="chapters"> |
|
<template v-if="type === 1"> |
|
<template v-if="chapterList.length"> |
|
<div class="chapter" |
|
v-for="(item,index) in chapterList" |
|
:key="index"> |
|
<div class="chapterName">{{ item.name }}</div> |
|
<div class="section" |
|
v-if="item.subsectionList.length"> |
|
<p class="sectionName" |
|
:class="{active: curLink === `${item.name}${section.name}`}" |
|
v-for="(section,i) in item.subsectionList" |
|
:key="i" |
|
@click="preview(section, item.name)"> |
|
<img v-if="section.fileType === 'pptx'" |
|
src="@/assets/img/exts/ppt.png" |
|
alt=""> |
|
<img v-else-if="section.fileType === 'mp4'" |
|
src="@/assets/img/exts/video.png" |
|
alt=""> |
|
<img v-else-if="section.fileType === 'doc' || section.fileType === 'docx'" |
|
src="@/assets/img/exts/word.png" |
|
alt=""> |
|
<img v-else-if="section.fileType === 'txt'" |
|
src="@/assets/img/exts/txt.png" |
|
alt=""> |
|
<img v-else-if="section.fileType === 'pdf'" |
|
src="@/assets/img/exts/pdf.png" |
|
alt=""> |
|
<img v-else |
|
src="@/assets/img/exts/pic.png" |
|
alt=""> |
|
{{ section.name }} |
|
</p> |
|
</div> |
|
</div> |
|
</template> |
|
</template> |
|
<div v-else |
|
class="section"> |
|
<p class="sectionName" |
|
v-for="(item, i) in progressList" |
|
:key="i" |
|
:title="item.projectName"> |
|
{{ i + 1 }}. |
|
{{ item.projectName }} |
|
<i :class="['icon', item.whetherToStudyOrNot ? 'el-icon-success' : 'circle']" |
|
@click.stop="studySection(item)"></i> |
|
</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="tabs"> |
|
<a class="item" |
|
v-for="(item, i) in tabs" |
|
:key="i" |
|
:class="{active: item.id == active}" |
|
@click="tabChange(item)">{{ item.name }}</a> |
|
</div> |
|
<div class="flex"> |
|
<div class="tab-content"> |
|
<div v-if="active === 1" |
|
class="page"> |
|
<div class="des" |
|
v-html="briefIntroduction"></div> |
|
</div> |
|
<div v-if="active === 2" |
|
class="page"> |
|
<template v-if="showNoteAdd || !notes.length"> |
|
<el-form class="form" |
|
ref="form" |
|
label-width="120px" |
|
center> |
|
<el-form-item class="required" |
|
label="笔记标题"> |
|
<el-input placeholder="请输入笔记标题" |
|
v-model="noteForm.noteName" |
|
maxlength="30" |
|
style="width: 400px"></el-input> |
|
</el-form-item> |
|
<el-form-item class="required" |
|
label="笔记内容"> |
|
<Editor api-key='rnk6zw9v267xqz7pf98twt1vmrvltmd436je7a642pckltda' |
|
v-model="noteForm.noteContent" |
|
:init="editorConfig" /> |
|
</el-form-item> |
|
</el-form> |
|
<div class="text-right"> |
|
<el-button v-if="notes.length" |
|
@click="showNoteAdd = false">取消</el-button> |
|
<el-button type="primary" |
|
@click="submitNote">保存</el-button> |
|
</div> |
|
</template> |
|
<template v-else> |
|
<div class="tool"> |
|
<ul class="filter"> |
|
<li> |
|
<label>搜索:</label> |
|
<el-input placeholder="请输入标题" |
|
v-model="noteKeyword" |
|
clearable></el-input> |
|
</li> |
|
</ul> |
|
<div> |
|
<el-button type="primary" |
|
@click="addNote">新增笔记</el-button> |
|
</div> |
|
</div> |
|
<el-timeline class="timeline"> |
|
<el-timeline-item placement="top" |
|
v-for="(item, i) in notes" |
|
:key="i"> |
|
<p class="sign">{{ item.createTime }}</p> |
|
<div class="ver"> |
|
{{ item.noteName }} |
|
<span class="action"> |
|
<i class="el-icon-edit-outline" |
|
@click="editNote(item)"></i> |
|
<i class="el-icon-delete" |
|
@click="delNote(item)"></i> |
|
</span> |
|
</div> |
|
<div class="des" |
|
v-html="item.noteContent"></div> |
|
</el-timeline-item> |
|
</el-timeline> |
|
</template> |
|
</div> |
|
<div v-if="active === 4" |
|
class="page"> |
|
<quill ref="quill" |
|
:border="true" |
|
v-model="comment" |
|
:height="150" |
|
:toTop="false" /> |
|
<div class="m-t-10 text-right"> |
|
<el-button type="primary" |
|
size="mini" |
|
@click="submitComment(0)">提交</el-button> |
|
</div> |
|
<ul class="msg"> |
|
<li v-for="(item, i) in comments" |
|
:key="i" |
|
:id="'comment' + item.commentId"> |
|
<div class="li-wrap"> |
|
<img :src="item.createUserAvatars" |
|
alt="" |
|
class="avatar"> |
|
<div class="texts"> |
|
<h6> |
|
<span class="name">{{ item.createUsername }}</span> |
|
发表于 {{ item.createTime }} |
|
</h6> |
|
<div class="m-t-8" |
|
v-html="item.content"></div> |
|
</div> |
|
<div class="action"> |
|
<i class="icon el-icon-chat-dot-square" |
|
@click="showReplay(item)"></i> |
|
<!-- 当前用户是管理员 || 当前用户的account==该条评论的创建人accountId --> |
|
<i v-if="(item.currentRoleName && item.currentRoleName.includes('管理员')) || item.currentAccountId == item.createAccountId" |
|
class="icon el-icon-delete" |
|
@click="delComment(item)"></i> |
|
<!-- <div class="like-wrap" |
|
@click="like($event, item)"> |
|
<img v-if="item.isLike" |
|
class="icon" |
|
src="@/assets/img/like-up.png" |
|
alt=""> |
|
<img v-else |
|
class="icon" |
|
src="@/assets/img/like.png" |
|
alt=""> |
|
{{ item.likeCount || '' }} |
|
</div> --> |
|
</div> |
|
</div> |
|
<div class="reply" |
|
v-if="item.showReply"> |
|
<quill :border="true" |
|
v-model="item.replyContent" |
|
:toTop="false" |
|
:height="150" /> |
|
<div class="m-t-10 text-right"> |
|
<el-button type="primary" |
|
size="mini" |
|
@click="submitComment(item)">提交</el-button> |
|
</div> |
|
</div> |
|
<ul class="msg children" |
|
v-if="item.showChildren"> |
|
<li v-for="(reply,i) in item.children" |
|
:key="i" |
|
:id="'comment' + reply.commentId"> |
|
<div class="li-wrap"> |
|
<img class="avatar" |
|
:src="reply.createUserAvatars" |
|
alt=""> |
|
<div class="texts"> |
|
<div class="name">{{reply.createUsername}}</div> |
|
<div class="flex m-v-8"> |
|
<template v-if="reply.level !== 2"> |
|
回复 |
|
<span class="reply-name">@{{ reply.replyUsername }}:</span> |
|
</template> |
|
<div class="des" |
|
v-html="reply.content"></div> |
|
</div> |
|
<div class="date">{{reply.createTime}}</div> |
|
</div> |
|
<div class="action"> |
|
<i class="icon el-icon-chat-dot-square" |
|
@click="showReplay(reply)"></i> |
|
<!-- 当前用户是管理员 || 当前用户的account==该条评论的创建人accountId --> |
|
<i v-if="(reply.currentRoleName && reply.currentRoleName.includes('管理员')) || reply.currentAccountId == reply.createAccountId" |
|
class="icon el-icon-delete" |
|
@click="delComment(reply)"></i> |
|
<!-- <div class="like-wrap" |
|
@click="like($event, item, reply)"> |
|
<img v-if="reply.isLike" |
|
class="icon" |
|
src="@/assets/img/like-up.png" |
|
alt=""> |
|
<img v-else |
|
class="icon" |
|
src="@/assets/img/like.png" |
|
alt=""> |
|
{{ reply.likeCount || '' }} |
|
</div> --> |
|
</div> |
|
</div> |
|
<div class="reply" |
|
v-if="reply.showReply"> |
|
<quill :border="true" |
|
v-model="reply.replyContent" |
|
:toTop="false" |
|
:height="150" /> |
|
<div class="m-t-10 text-right"> |
|
<el-button type="primary" |
|
size="mini" |
|
@click="submitComment(item, reply)">提交</el-button> |
|
</div> |
|
</div> |
|
</li> |
|
</ul> |
|
<div v-if="item.children" |
|
class="toggle"><span @click="item.showChildren = !item.showChildren">{{item.showChildren ? '收起所有回复' : `查看所有${item.children.length}条回复`}} <i class="el-icon-arrow-down"></i></span></div> |
|
</li> |
|
</ul> |
|
</div> |
|
</div> |
|
<div class="rank"> |
|
<h6 class="title">练习成绩排行</h6> |
|
<el-select style="width: 100%" |
|
v-model="archProject" |
|
placeholder="请选择" |
|
@change="getMaximumScore"> |
|
<el-option v-for="(item, i) in progressList" |
|
:key="i" |
|
:label="item.projectName" |
|
:value="item.projectId"></el-option> |
|
</el-select> |
|
<div v-if="maximumScores.length" |
|
class="arches"> |
|
<div v-for="(item, i) in maximumScores" |
|
:key="i" |
|
class="line"> |
|
<div> |
|
<img :src="item.userAvatars" |
|
alt="" |
|
class="avatar"> |
|
<span class="name">{{ item.userName }}</span> |
|
</div> |
|
<span class="point">{{ item.highestScore }}分</span> |
|
</div> |
|
</div> |
|
<p v-else |
|
class="none">暂无数据</p> |
|
</div> |
|
</div> |
|
|
|
<el-dialog title="请选择项目" |
|
v-loading="loading" |
|
:visible.sync="projectVisible" |
|
width="828px" |
|
custom-class="project-dia" |
|
:close-on-click-modal="false"> |
|
<ul class="projects"> |
|
<li v-for="(item, i) in projects" |
|
:key="i" |
|
:class="{active: curProject == item.projectId}" |
|
@click="selectProject(item)"> |
|
<img src="@/assets/img/project.png" |
|
alt=""> |
|
<span>{{ i + 1 }}. {{ item.projectName }}</span> |
|
</li> |
|
</ul> |
|
|
|
<span slot="footer" |
|
class="dialog-footer"> |
|
<el-button size="small" |
|
@click="projectVisible = false">取 消</el-button> |
|
<el-button size="small" |
|
type="primary" |
|
@click="toSub">确 定</el-button> |
|
</span> |
|
</el-dialog> |
|
|
|
<!-- 购买弹框 --> |
|
<el-dialog title="温馨提示" |
|
:visible.sync="buyVisible" |
|
width="420px" |
|
center |
|
:close-on-click-modal="false"> |
|
<div class="buy"> |
|
<p class="tips">该课程订阅期限已到期,请联系院校管理员续费</p> |
|
</div> |
|
</el-dialog> |
|
|
|
<pdfDia :key="pdfVisible" |
|
:visible.sync="pdfVisible" |
|
:src.sync="pdfSrc" /> |
|
</div> |
|
</template> |
|
|
|
<script> |
|
import util from "@/libs/util"; |
|
import Setting from "@/setting"; |
|
import { mapState } from "vuex"; |
|
import pdf from "vue-pdf"; |
|
import quill from "@/components/quill"; |
|
import breadcrumb from '@/components/breadcrumb' |
|
import pdfDia from '@/components/pdf' |
|
import Editor from '@tinymce/tinymce-vue' |
|
import editorConfig from '@/components/editor' |
|
export default { |
|
components: { pdf, breadcrumb, pdfDia, quill, Editor }, |
|
data () { |
|
return { |
|
mallId: '', |
|
courseId: '', |
|
curriculumName: '', |
|
commentId: '', // 从右上角消息通知点进来用来定位评论的 |
|
notifyId: '', |
|
editorConfig, |
|
startTime: Date.now(), // 页面进来的时间 |
|
systemIds: '', |
|
type: 1, |
|
video: "http://liuwanr.oss-cn-shenzhen.aliyuncs.com/mp4/20200519/1589871025648.mp4", |
|
chapterList: [], // 章节列表 |
|
progressList: [], |
|
coverUrl: "", // 课程封面 |
|
courseName: "", // 课程名称 |
|
briefIntroduction: "", // 课程简介 |
|
teachingObjectives: "", // 课程目标 |
|
assessmentList: "", // 考核列表 |
|
curLink: "", // 当前选中 |
|
active: 1, |
|
tabs: [ |
|
{ |
|
id: 1, |
|
name: '课程介绍' |
|
}, |
|
{ |
|
id: 2, |
|
name: '课程笔记' |
|
}, |
|
// { |
|
// id: 3, |
|
// name: '全国排行榜' |
|
// }, |
|
{ |
|
id: 4, |
|
name: '教学互动' |
|
} |
|
], |
|
playAuth: "", |
|
player: null, |
|
previewImg: "", |
|
iframeSrc: "", |
|
isWord: false, |
|
isPPT: false, |
|
isExcel: false, |
|
showMask: false, |
|
showMask1: false, |
|
showMask2: false, |
|
closePosi: { |
|
top: "80px" |
|
}, |
|
pdfVisible: false, |
|
pdfSrc: "", |
|
currentPage: 0, // pdf文件页码 |
|
pageCount: 0, // pdf文件总页数 |
|
fileType: "pdf", // 文件类型 |
|
videoSrc: '', |
|
desShrink: false, |
|
projectVisible: false, |
|
projects: [], |
|
loading: false, |
|
curProject: '', |
|
overdue: 0, //是否过期 |
|
buyVisible: false, |
|
schedule: 0, |
|
plan: '', |
|
noteKeyword: '', |
|
searchTimer: null, |
|
notes: [], |
|
showNoteAdd: false, |
|
noteContent: '', |
|
noteForm: { |
|
cid: +this.$route.query.courseId, |
|
noteContent: '', |
|
noteName: '', |
|
}, |
|
comments: [], |
|
comment: '', |
|
deleteIdentity: false, |
|
archProject: '', |
|
curReplyId: '', |
|
maximumScores: [] |
|
}; |
|
}, |
|
computed: { |
|
...mapState("user", [ |
|
"userId", "studentId", "roleId", "userName" |
|
]) |
|
}, |
|
watch: { |
|
'$route': { |
|
handler () { |
|
this.mallId = +this.$route.query.mallId || '' |
|
this.courseId = +this.$route.query.courseId |
|
this.curriculumName = this.$route.query.curriculumName |
|
this.commentId = this.$route.query.commentId // 从右上角消息通知点进来用来定位评论的 |
|
this.notifyId = this.$route.query.notifyId |
|
this.init() |
|
}, |
|
immediate: true |
|
}, |
|
noteKeyword: function (val) { |
|
clearTimeout(this.searchTimer); |
|
this.searchTimer = setTimeout(() => { |
|
this.getNote(); |
|
}, 500); |
|
} |
|
}, |
|
mounted () { |
|
|
|
}, |
|
destroyed () { |
|
// 记录播放时长 |
|
this.$post(this.api.playRecordSave, { |
|
courseId: this.courseId, |
|
courseType: 0, |
|
playTime: Math.ceil((Date.now() - this.startTime) / 1000 / 60) |
|
}).then(({ data }) => { }).catch(res => { }) |
|
}, |
|
methods: { |
|
goBack () { |
|
this.$router.push('/station') |
|
}, |
|
init () { |
|
this.insertScript(); |
|
this.getData(); |
|
this.addRecord() |
|
}, |
|
async getData () { |
|
let { data } = await this.$post(`${this.api.curriculumDetail}?cid=${this.courseId}&mallId=${this.mallId}`); |
|
this.courseName = data.curriculumName; |
|
// this.$refs.breadcrumb.update('实验台/' + data.curriculumName) |
|
// this.coverUrl = data.coverUrl; |
|
this.briefIntroduction = data.briefIntroduction; |
|
this.teachingObjectives = data.teachingObjectives; |
|
this.assessmentList = data.assessmentConfig; |
|
this.systemIds = data.systemIds |
|
this.queryProject() |
|
this.getStatus() |
|
}, |
|
// 记录最近使用 |
|
addRecord () { |
|
this.$post(`${this.api.recordRecentUsage}?mallId=${this.mallId}`).then(res => { }).catch(res => { }) |
|
}, |
|
// 查询是否过期 |
|
getStatus () { |
|
this.$get(this.api.whetherToRenewTheFee, { |
|
mallId: this.mallId |
|
}).then(({ isRenew }) => { |
|
// 1正常显示资源,0显示续费 |
|
this.overdue = isRenew |
|
this.getChapter() |
|
this.getProgress() |
|
}).catch(res => { }) |
|
}, |
|
// 获取章节小节 |
|
async getChapter () { |
|
let res = await this.$get(`${this.api.curriculumChapter}/${this.courseId}`); |
|
this.chapterList = res.chapterList; |
|
if (this.chapterList.length && this.chapterList[0].subsectionList && this.chapterList[0].subsectionList.length && !this.commentId) { |
|
this.preview(this.chapterList[0].subsectionList[0], this.chapterList[0].name, 1); |
|
} |
|
}, |
|
// 获取学习进度 |
|
async getProgress () { |
|
let res = await this.$post(`${this.api.courseLearningProgress}?courseId=${this.courseId}&systemId=${this.systemIds}&mallId=${this.mallId}`); |
|
this.progressList = res.list |
|
if (res.list.length) this.archProject = res.list[0].projectId |
|
this.maximumScores.length || this.getMaximumScore() |
|
this.plan = res.plan |
|
this.schedule = +(res.schedule.replace('%', '')) |
|
}, |
|
// 获取最高分列表 |
|
async getMaximumScore () { |
|
let { message } = await this.$post(`${this.api.maximumPracticeScoreList}?projectId=${this.archProject}&pageNum=1&pageSize=1000&mallId=${this.mallId}&cid=${this.courseId}`); |
|
const list = message.records |
|
list.forEach(e => { |
|
if (!e.userAvatars) e.userAvatars = 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' |
|
}) |
|
this.maximumScores = list |
|
}, |
|
// 进度数字自定义 |
|
percentFormat (val) { |
|
return this.plan |
|
}, |
|
// 目录和进度切换 |
|
typeChange (id) { |
|
this.getChapter() |
|
this.type = id |
|
}, |
|
// 获取课程笔记 |
|
getNote () { |
|
this.$get(this.api.curriculumNoteList, { |
|
cid: this.courseId, |
|
search: this.noteKeyword |
|
}).then(res => { |
|
this.notes = res.curriculumNotes |
|
}).catch(res => { }) |
|
}, |
|
// 新增笔记 |
|
addNote () { |
|
this.noteForm = { |
|
cid: this.courseId, |
|
noteContent: '', |
|
noteName: '', |
|
} |
|
this.showNoteAdd = true |
|
}, |
|
// 保存笔记 |
|
submitNote () { |
|
const form = this.noteForm |
|
if (!form.noteName) return util.errorMsg('请输入笔记标题') |
|
if (!form.noteContent) return util.errorMsg('请输入笔记内容') |
|
this.$post(this.api[form.noteId ? 'updateNote' : 'addNote'], form).then(res => { |
|
this.getNote() |
|
util.successMsg(form.noteId ? '修改成功' : '添加成功') |
|
this.showNoteAdd = false |
|
this.noteForm = { |
|
cid: this.courseId, |
|
noteContent: '', |
|
noteName: '', |
|
} |
|
}).catch(res => { }) |
|
}, |
|
// 删除笔记 |
|
delNote (row) { |
|
this.$confirm("此删除操作不可逆,是否确认删除选中项?", "提示", { |
|
type: "warning" |
|
}).then(() => { |
|
this.$post(`${this.api.deleteNotes}?noteId=${row.noteId}`).then(res => { |
|
util.successMsg("删除成功"); |
|
this.getNote(); |
|
}).catch(res => { }) |
|
}).catch(() => { }) |
|
}, |
|
// 编辑笔记 |
|
editNote (row) { |
|
this.noteForm = JSON.parse(JSON.stringify(row)) |
|
this.showNoteAdd = true |
|
}, |
|
|
|
// 教学互动列表 |
|
getComment () { |
|
this.$get(this.api.commentTreeList, { |
|
mallId: this.mallId, |
|
}).then(res => { |
|
this.deleteIdentity = res.deleteIdentity |
|
const list = res.rootComments |
|
this.handleComments(list) |
|
this.comments = list |
|
this.$nextTick(async () => { |
|
if (this.curReplyId) { |
|
const el = document.querySelector('#comment' + this.curReplyId) |
|
el && el.scrollIntoView(false) |
|
} |
|
if (this.commentId) { |
|
this.active = 4 |
|
const el = document.getElementById('comment' + this.commentId) |
|
el && el.scrollIntoView(false) |
|
this.commentId = '' |
|
} |
|
}) |
|
}).catch(res => { }) |
|
}, |
|
// 递归处理评论参数 |
|
handleComments (list, parent = {}) { |
|
list.forEach((e, i) => { |
|
if (!e.createUserAvatars) e.createUserAvatars = 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' |
|
e.showChildren = false |
|
if (e.commentId == this.commentId) parent.showChildren = true |
|
if (e.commentId == this.curReplyId) e.showChildren = true |
|
e.showReply = false |
|
e.replyContent = '' |
|
if (e.children && e.children.length && parent.children) { |
|
e.children.forEach(n => n.showReplyName = true) |
|
parent.children.push(...e.children) |
|
} |
|
e.children && this.handleComments(e.children, parent.commentId ? parent : e) |
|
}) |
|
}, |
|
// 回复 |
|
showReplay (row) { |
|
row.showReply = !row.showReply |
|
}, |
|
// 删除评论 |
|
delComment (row) { |
|
this.$confirm("此删除操作不可逆,是否确认删除选中项?", "提示", { |
|
type: "warning" |
|
}).then(() => { |
|
this.$post(`${this.api.deleteAComment}?commentId=${row.commentId}`).then(res => { |
|
util.successMsg("删除成功"); |
|
this.getComment(); |
|
}).catch(res => { }) |
|
}).catch(() => { }) |
|
}, |
|
// 保存评论 |
|
submitComment (row, reply) { |
|
const content = reply ? reply.replyContent : row ? row.replyContent : this.comment |
|
if (!content) return util.errorMsg('请输入内容!') |
|
this.$post(this.api.addComment, { |
|
mallId: this.mallId, |
|
content, |
|
pid: reply ? reply.commentId : row ? row.commentId : 0, |
|
replyAccountId: reply ? reply.createAccountId : row.createAccountId || '', |
|
status: row ? 2 : 1, |
|
replyCommentId: reply ? reply.commentId : row ? row.commentId : '' |
|
}).then(res => { |
|
this.comment = '' |
|
this.curReplyId = row ? row.commentId : '' |
|
this.getComment() |
|
}).catch(res => { }) |
|
}, |
|
// 评论点赞 |
|
like (e, row, reply) { |
|
this.$post(`${this.api[(reply ? reply.isLike : row.isLike) ? 'unlike' : 'commentAndLike']}?commentId=${reply ? reply.commentId : row.commentId}`).then(res => { |
|
this.curReplyId = row.commentId |
|
this.getComment() |
|
}).catch(res => { }) |
|
}, |
|
|
|
// 插入阿里云播放器脚本 |
|
insertScript () { |
|
const linkTag = document.createElement("link"); |
|
linkTag.id = "aliplayerLink"; |
|
linkTag.rel = "stylesheet"; |
|
linkTag.href = "https://g.alicdn.com/de/prismplayer/2.8.2/skins/default/aliplayer-min.css"; |
|
document.body.appendChild(linkTag); |
|
|
|
const scriptTag = document.createElement("script"); |
|
scriptTag.id = "aliplayerScript"; |
|
scriptTag.type = "text/javascript"; |
|
scriptTag.src = "https://g.alicdn.com/de/prismplayer/2.8.2/aliplayer-min.js"; |
|
document.body.appendChild(scriptTag); |
|
this.$once("hook:beforeDestroy", function () { |
|
document.body.removeChild(document.querySelector("#aliplayerLink")); |
|
document.body.removeChild(document.querySelector("#aliplayerScript")); |
|
}); |
|
}, |
|
// 显示咨询弹框 |
|
showBuy () { |
|
this.buyVisible = true |
|
}, |
|
transferType (ext) { |
|
const suf = ext.toLowerCase() |
|
if ('jpg,jpeg,png,gif,svg,psd'.includes(suf)) return '图片' |
|
if ('mp4,3gp,mov,m4v,avi,dat,mkv,flv,vob,rmvb,rm,qlv'.includes(suf)) return '视频' |
|
return suf |
|
}, |
|
// tab切换 |
|
tabChange ({ id }) { |
|
this.active = id |
|
id == 2 && this.getNote() |
|
id == 4 && this.getComment() |
|
}, |
|
// 勾选/取消勾选学习进度 |
|
studySection (item) { |
|
item.whetherToStudyOrNot ? |
|
this.$post(`${this.api.deleteLearningProgress}?id=${item.learningProgressId}`).then(res => { |
|
this.getProgress() |
|
}).catch(res => { }) : |
|
this.$post(this.api.saveLearningProgress, { |
|
cid: this.courseId, |
|
projectId: item.projectId, |
|
}).then(res => { |
|
this.getProgress() |
|
}).catch(res => { }) |
|
}, |
|
preview (row, chapterName, showDia = 0) { |
|
// 如果没过期,则正常预览,否则显示购买弹框 |
|
if (this.overdue || showDia) { |
|
this.curLink = `${chapterName}${row.name}`; // 章节名称+小节名称,小节名称有重复的情况,如果只用小节名称判断,会有同时选中多个的情况 |
|
this.playAuth = '' |
|
this.coverUrl = '' |
|
this.pdfSrc = '' |
|
this.iframeSrc = '' |
|
this.videoSrc = '' |
|
this.isPPT = false |
|
this.isWord = false |
|
this.isExcel = false |
|
if (this.transferType(row.fileType) == "视频") { |
|
// 阿里云视频点播 |
|
if (row.fileId) { |
|
this.$get(`${this.api.getPlayAuth}/${row.fileId}`).then(res => { |
|
this.playAuth = res.data.playAuth; |
|
// 如果已经产生了实例,则销毁重新创建,不然播放器会播放上一个视频 |
|
if (this.player) { |
|
this.player.dispose() |
|
this.player = null |
|
} |
|
this.$nextTick(() => { |
|
if (this.player) { |
|
this.player.replayByVidAndPlayAuth(row.fileId, this.playAuth); |
|
} else { |
|
this.player = new Aliplayer({ |
|
id: "player", |
|
width: "100%", |
|
autoplay: false, |
|
vid: row.fileId, |
|
playauth: this.playAuth, |
|
encryptType: 1 //当播放私有加密流时需要设置。 |
|
}); |
|
} |
|
}); |
|
}).catch(res => { }); |
|
} else { |
|
this.videoSrc = row.fileUrl |
|
} |
|
} else if (this.transferType(row.fileType) == "图片") { |
|
this.coverUrl = row.fileUrl |
|
} else if (row.fileType == "pdf") { |
|
this.pdfSrc = row.fileUrl |
|
} else { |
|
this.$get(`${this.api.curriculumGetSubsection}/${row.id}`).then(res => { |
|
if (row.fileType == "pptx") this.isPPT = true |
|
if (row.fileType == "doc" || row.fileType == "docx") this.isWord = true |
|
if (row.fileType == "xls" || row.fileType == "xlsx") this.isExcel = true |
|
|
|
if (this.isPPT) { |
|
this.showMask = true; |
|
} else { |
|
this.showMask = false; |
|
} |
|
if (this.isWord) { |
|
this.showMask1 = true; |
|
} else { |
|
this.showMask1 = false; |
|
} |
|
if (this.isExcel) { |
|
this.showMask2 = true; |
|
} else { |
|
this.showMask2 = false; |
|
} |
|
this.iframeSrc = res.previewUrl; |
|
}).catch(err => { }); |
|
} |
|
} else { |
|
this.showBuy() |
|
} |
|
}, |
|
closeIframe () { |
|
this.iframeSrc = '' |
|
this.showMask = false; |
|
this.showMask1 = false; |
|
}, |
|
closePdf () { |
|
this.pdfSrc = '' |
|
this.currentPage = 1; |
|
}, |
|
changePdfPage (val) { |
|
if (val === 0 && this.currentPage > 1) { |
|
this.currentPage--; |
|
} |
|
if (val === 1 && this.currentPage < this.pageCount) { |
|
this.currentPage++; |
|
} |
|
}, |
|
loadPdfHandler (e) { |
|
this.currentPage = 1; |
|
}, |
|
// pdf全屏 |
|
fullScreen () { |
|
this.pdfVisible = true |
|
}, |
|
// 选择项目 |
|
selectProject (item) { |
|
this.curProject = item.projectId |
|
}, |
|
// 展示选择项目弹框 |
|
showProjectDia () { |
|
console.log(123) |
|
this.projectVisible = true |
|
}, |
|
// 查询项目 |
|
queryProject () { |
|
this.$get(this.api.getProjectBySystemId, { |
|
systemId: this.systemIds, |
|
cId: this.courseId, // 课程id |
|
mallId: this.mallId, |
|
permissions: 0 // 0: 练习,1: 考核 |
|
}).then(({ projects }) => { |
|
this.projects = projects |
|
}).catch(res => { }) |
|
}, |
|
// 进入实验 |
|
entryProject (projectId) { |
|
this.$confirm('是否要继续上次的实验?', '提示', { |
|
confirmButtonText: '是', |
|
cancelButtonText: '否', |
|
type: 'success', |
|
closeOnClickModal: false |
|
}).then(() => { |
|
this.curProject = projectId |
|
this.toSub() |
|
}).catch(() => { |
|
this.showProjectDia() |
|
}) |
|
}, |
|
// 进入实验 |
|
entry () { |
|
// 查询上次做的项目(python跟沙盘接口不一样) |
|
if (this.systemIds.split(',').includes('19')) { // 沙盘 |
|
this.$post(`${this.api.getSandTableLastCache}?cid=${this.courseId}`).then(res => { |
|
// 返回了data则提示是否继续,否则,显示选择项目的弹框 |
|
if (res.getLastCache) { |
|
this.entryProject(+res.getLastCache) |
|
} else { |
|
this.showProjectDia() |
|
} |
|
}).catch(res => { |
|
this.showProjectDia() |
|
}) |
|
} else { // python |
|
this.$get(this.api.getTheMostRecentlyRunProject, { |
|
cid: this.courseId |
|
}).then(({ data }) => { |
|
// 返回了data则提示是否继续,否则,显示选择项目的弹框 |
|
if (data && data.length) { |
|
this.entryProject(data[0].projectId) |
|
} else { |
|
this.showProjectDia() |
|
} |
|
}).catch(res => { |
|
this.showProjectDia() |
|
}) |
|
} |
|
}, |
|
// 进入python系统 |
|
toPython (projectId) { |
|
const id = this.systemIds |
|
let token = util.local.get(Setting.tokenKey); |
|
util.cookies.set('assessmentId', '', -1) |
|
util.cookies.set('startTime', '', -1) |
|
util.cookies.set('stopTime', '', -1) |
|
projectId ? util.cookies.set('projectId', projectId) : util.cookies.set('projectId', '', -1) |
|
util.cookies.set('token', token) |
|
util.cookies.set('courseId', this.courseId) |
|
util.cookies.set('curriculumName', encodeURIComponent(this.curriculumName)) |
|
util.cookies.set('systemId', id) |
|
util.cookies.set('mallId', this.mallId) |
|
util.cookies.set('isSubmit', '', -1) |
|
util.cookies.set('fromManager', '', -1) |
|
util.cookies.set('competitionId', '', -1) |
|
util.cookies.set('language', '', -1) |
|
util.cookies.set('className', '', -1) |
|
// 8个python子系统都跳这个地址,子系统会通过cookie里的systemId识别展示哪套系统 |
|
location.href = process.env.NODE_ENV === 'development' ? |
|
`http://${location.hostname}:8085/#/` : |
|
Setting.isPro ? |
|
`https://${location.hostname}/pyTrials` : |
|
`${location.origin}/pyTrials` |
|
}, |
|
// 进入子系统 |
|
toSub () { |
|
let curProject = this.projects.find(e => e.projectId == this.curProject) |
|
if (!curProject && this.projects.length) { |
|
curProject = this.projects[0] |
|
this.curProject = curProject.projectId |
|
} |
|
const { systemId } = curProject |
|
let token = util.local.get(Setting.tokenKey); |
|
if (systemId == 11) { |
|
// 银行系统 |
|
location.href = `${Setting.systemPath}/#/index/list?curriculumName=${this.curriculumName}&token=${token}&cid=${this.courseId}&mallId=${this.mallId}&systemId=${this.systemIds}&projectId=${this.curProject}&assessmentId=&classId=&stopTime=&test=true` |
|
} else if (systemId == 12) { |
|
// 众筹系统 |
|
window.open(`http://${Setting.zcPath}?systemId=${systemId}&courseId=${this.courseId}&projectId=${this.curProject}&token=${token}&userId=${this.userId}&classId=1&mallId=${this.mallId}${Setting.isTest ? '&beta=1' : ''}`); |
|
} else if (systemId == 19) { |
|
// 沙盘 |
|
location.href = `${Setting.sandPath}/#/?curriculumName=${this.curriculumName}&token=${token}&cid=${this.courseId}&mallId=${this.mallId}&systemId=${this.systemIds}&projectId=${this.curProject}&assessmentId=&classId=&stopTime=&referrer=${encodeURIComponent(location.href)}` |
|
} else { |
|
// python系统 |
|
this.toPython(this.curProject) |
|
} |
|
}, |
|
} |
|
}; |
|
</script> |
|
|
|
<style lang="scss" scoped> |
|
@import '../../../styles/page/station.scss'; |
|
$height: 700px; |
|
.video_wid, |
|
.cover { |
|
position: relative; |
|
width: 76%; |
|
height: $height !important; |
|
border: 0; |
|
} |
|
.page { |
|
/deep/.des { |
|
overflow: auto; |
|
div, |
|
p, |
|
span { |
|
font-family: PingFangSC-Regular !important; |
|
} |
|
} |
|
} |
|
.l-title { |
|
font-size: 17px; |
|
} |
|
.cover { |
|
flex: 1; |
|
background-color: #252528; |
|
img { |
|
border-radius: 8px; |
|
} |
|
&.is-word { |
|
overflow: hidden; |
|
} |
|
} |
|
.fileIframe { |
|
height: $height !important; |
|
} |
|
.video_wid, |
|
.inner { |
|
width: 100%; |
|
height: 100% !important; |
|
border: 0; |
|
overflow: auto; |
|
} |
|
.cover.is-word { |
|
.inner { |
|
height: calc(100% + 38px) !important; |
|
margin-top: -38px; |
|
} |
|
} |
|
.video_wid:focus { |
|
outline: none; |
|
} |
|
.catalog { |
|
width: 296px; |
|
padding: 16px; |
|
margin-left: 12px; |
|
background-color: #252528; |
|
.renew { |
|
width: 100%; |
|
margin-bottom: 16px; |
|
font-size: 17px; |
|
} |
|
.entry { |
|
display: block; |
|
width: 100%; |
|
height: 34px; |
|
margin-bottom: 16px; |
|
font-size: 17px; |
|
color: #fff; |
|
text-align: center; |
|
line-height: 34px; |
|
background: url(../../../assets/img/entry.png) 0 0/100% 100% no-repeat; |
|
cursor: pointer; |
|
&:hover { |
|
opacity: 0.9; |
|
} |
|
} |
|
.types { |
|
display: flex; |
|
margin: 15px 0; |
|
.item { |
|
display: inline-flex; |
|
justify-content: center; |
|
align-items: center; |
|
width: 120px; |
|
height: 40px; |
|
color: #fff; |
|
background-color: #4e4e4e; |
|
border-radius: 4px; |
|
cursor: pointer; |
|
&:first-child { |
|
margin-right: 10px; |
|
} |
|
&:hover { |
|
background-color: #377be3; |
|
} |
|
&.active { |
|
background-color: $main-color; |
|
} |
|
} |
|
.icon { |
|
margin-right: 5px; |
|
font-size: 18px; |
|
} |
|
} |
|
} |
|
.list { |
|
height: calc(700px - 82px); |
|
overflow-y: auto; |
|
.title { |
|
margin-bottom: 11px; |
|
color: #fff; |
|
font-size: 16px; |
|
} |
|
.pro-title { |
|
margin-bottom: 5px; |
|
color: #fff; |
|
font-size: 12px; |
|
} |
|
/deep/.el-progress-bar { |
|
width: 92%; |
|
} |
|
/deep/.el-progress__text { |
|
color: #fff; |
|
} |
|
.desc-wrap { |
|
position: relative; |
|
.desc { |
|
font-size: 12px; |
|
color: rgba(0, 0, 0, 0.65); |
|
line-height: 22px; |
|
@include mul-ellipsis(2); |
|
&.active { |
|
display: block; |
|
overflow: visible; |
|
} |
|
} |
|
.arrow { |
|
position: absolute; |
|
bottom: 2px; |
|
right: 0; |
|
display: flex; |
|
justify-content: space-between; |
|
width: 46px; |
|
background-color: #fff; |
|
span { |
|
font-size: 14px; |
|
color: rgba(0, 0, 0, 0.65); |
|
} |
|
img { |
|
width: 16px; |
|
cursor: pointer; |
|
} |
|
&.active { |
|
span { |
|
opacity: 0; |
|
} |
|
img { |
|
transform: rotate(180deg); |
|
} |
|
} |
|
} |
|
} |
|
.chapters { |
|
margin-top: 16px; |
|
max-height: calc(100% - 53px); |
|
overflow: auto; |
|
} |
|
.chapter { |
|
margin-bottom: 20px; |
|
} |
|
.chapterName { |
|
color: #fff; |
|
font-size: 14px; |
|
} |
|
.section { |
|
padding: 5px 15px; |
|
margin-top: 12px; |
|
background-color: #121214; |
|
} |
|
.sectionName { |
|
position: relative; |
|
padding-right: 15px; |
|
margin: 12px 0; |
|
font-size: 12px; |
|
color: #999; |
|
cursor: pointer; |
|
@include ellipsis; |
|
img { |
|
margin-right: 8px; |
|
} |
|
.icon { |
|
position: absolute; |
|
right: 0; |
|
font-size: 14px; |
|
color: #00c935; |
|
} |
|
.circle { |
|
width: 14px; |
|
height: 14px; |
|
border-radius: 50%; |
|
border: 1px solid #ccc; |
|
} |
|
&.active { |
|
color: #fff; |
|
} |
|
} |
|
} |
|
.buy { |
|
text-align: center; |
|
.tips { |
|
margin-bottom: 10px; |
|
font-size: 14px; |
|
} |
|
img { |
|
width: 85%; |
|
} |
|
} |
|
.el-image-viewer__wrapper { |
|
transform: translateY(-10px); |
|
transition: transform 0.5s; |
|
|
|
&.active { |
|
transform: translateY(0); |
|
} |
|
} |
|
.el-image-viewer__close { |
|
z-index: 2000; |
|
top: 15px; |
|
right: 15px; |
|
&.doc-close { |
|
i { |
|
color: #000 !important; |
|
} |
|
} |
|
} |
|
.list::-webkit-scrollbar { |
|
width: 4px; |
|
} |
|
.list::-webkit-scrollbar-thumb { |
|
border-radius: 10px; |
|
background: rgba(0, 0, 0, 0.06); |
|
} |
|
|
|
.mask { |
|
z-index: 9; |
|
position: absolute; |
|
background-color: rgb(57, 58, 61); |
|
} |
|
.word-mask { |
|
z-index: 9; |
|
position: absolute; |
|
top: 0; |
|
right: 0; |
|
width: 100%; |
|
background-color: rgb(243, 242, 241); |
|
} |
|
.word-mask1 { |
|
z-index: 9; |
|
position: absolute; |
|
top: 0; |
|
right: 0; |
|
width: 100%; |
|
background-color: #185abd; |
|
} |
|
.word-mask2 { |
|
z-index: 9; |
|
position: absolute; |
|
background-color: transparent; |
|
} |
|
.excel-mask1 { |
|
z-index: 9; |
|
position: absolute; |
|
top: 0; |
|
left: 20%; |
|
width: 60%; |
|
background-color: #107c41; |
|
} |
|
/deep/.pdf-dia { |
|
border-radius: 0 !important; |
|
.el-dialog__header { |
|
display: none; |
|
} |
|
.el-dialog__body { |
|
padding: 0; |
|
} |
|
.el-dialog__headerbtn { |
|
top: 10px; |
|
.el-dialog__close { |
|
color: #fff; |
|
font-size: 16px; |
|
} |
|
} |
|
} |
|
.pdf { |
|
position: relative; |
|
.full { |
|
position: absolute; |
|
top: 7px; |
|
right: 10px; |
|
cursor: pointer; |
|
} |
|
.arrow { |
|
padding: 10px 0; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
font-size: 16px; |
|
color: #fff; |
|
background-color: #333; |
|
.turn { |
|
margin: 0 10px; |
|
font-size: 18px; |
|
cursor: pointer; |
|
} |
|
} |
|
.pdf-wrap { |
|
width: 80%; |
|
margin: 0 auto; |
|
} |
|
} |
|
/deep/.project-dia { |
|
.el-dialog__body { |
|
padding: 28px 32px; |
|
} |
|
} |
|
.projects { |
|
display: flex; |
|
flex-wrap: wrap; |
|
max-height: 400px; |
|
overflow: auto; |
|
li { |
|
display: inline-flex; |
|
align-items: center; |
|
width: 238px; |
|
padding: 16px; |
|
margin: 0 20px 20px 0; |
|
background-color: #f6f8fa; |
|
border-radius: 16px; |
|
cursor: pointer; |
|
&:hover { |
|
span { |
|
color: #007eff; |
|
} |
|
} |
|
&:nth-child(3n) { |
|
margin-right: 0; |
|
} |
|
&.active { |
|
background-color: #f2f7ff; |
|
span { |
|
color: #3988ff; |
|
} |
|
} |
|
} |
|
span { |
|
max-width: 140px; |
|
margin-left: 14px; |
|
font-size: 14px; |
|
color: #333; |
|
} |
|
} |
|
.tabs { |
|
display: flex; |
|
align-items: center; |
|
margin: 10px 0; |
|
.item { |
|
padding: 0 10px; |
|
margin-right: 16px; |
|
font-size: 16px; |
|
text-align: center; |
|
color: #333; |
|
line-height: 50px; |
|
border-bottom: 3px solid transparent; |
|
cursor: pointer; |
|
&.active { |
|
color: #007eff; |
|
border-color: #007eff; |
|
} |
|
} |
|
} |
|
.tab-content { |
|
width: calc(100% - 308px); |
|
} |
|
.rank { |
|
width: 296px; |
|
padding: 20px; |
|
margin-left: 12px; |
|
background-color: #fff; |
|
.title { |
|
margin-bottom: 10px; |
|
font-size: 15px; |
|
font-weight: 400; |
|
} |
|
.arches { |
|
max-height: 223px; |
|
padding-right: 10px; |
|
margin-top: 20px; |
|
overflow: auto; |
|
} |
|
.line { |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
margin-bottom: 10px; |
|
} |
|
.avatar { |
|
width: 35px; |
|
height: 35px; |
|
margin-right: 10px; |
|
} |
|
.point { |
|
font-weight: 600; |
|
color: #007eff; |
|
} |
|
.none { |
|
margin-top: 10px; |
|
text-align: center; |
|
font-size: 12px; |
|
color: #727272; |
|
} |
|
} |
|
@media (max-width: 1700px) { |
|
.wrap { |
|
padding: 12px 5% 20px; |
|
} |
|
} |
|
</style> |