提交 adbc24de authored 作者: 王鹏飞's avatar 王鹏飞

对接直播中台;优化课程详情页;

上级 499d721a
......@@ -36,7 +36,7 @@ export default class API {
/* 具体执行请求失败后业务逻辑前,先执行该方法 */
const beforeFail = _config.beforeFail ? _config.beforeFail : this._reqFail
const headers = {
tenant: 'ciis',
tenant: 'sofia',
version: window.G.VERSION,
'Content-Type': 'application/x-www-form-urlencoded'
}
......
import BaseAPI from '@/api/base_api'
const httpRequest = new BaseAPI(webConf)
/**
* 获取课程详情
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
*/
export function getCourse(semesterId, courseId) {
return httpRequest.get(`/api/lms/v2/education/courses/${semesterId}/${courseId}`)
}
/**
* 课程考核
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
*/
export function getCourseAssess(semesterId, courseId) {
return httpRequest.get(`/api/lms/v2/analytics/courses/${semesterId}/${courseId}/evaluation`).then(res => {
// 视频数据组装
let videoList = Object.values(res.video_evaluation)
videoList = videoList.map(item => {
item.sections = Object.values(item.sections)
return item
})
res.video_evaluation = videoList
// 作业数据组装
let homeworkList = Object.values(res.homework_evaluation)
homeworkList = homeworkList.map(item => {
item.sections = Object.values(item.sections)
return item
})
res.homework_evaluation = homeworkList
return res
})
}
import BaseAPI from '@/api/base_api'
const httpRequest = new BaseAPI(webConf)
/**
* 获取课程详情
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
*/
export function getCourse(semesterId, courseId) {
return httpRequest.get(`/api/lms/v2/education/courses/${semesterId}/${courseId}`)
}
/**
* 获取讨论题目列表,“我提出的问题”和“我参与的问题”信息
* dataJson.limit - 获取数量
* dataJson.offset - 偏移量
* @param {[string]} path
* @param {[object]} dataJson
*/
export function getDiscussList(qid, params) {
return httpRequest.get(`/api/lms/v2/qa/questions/${qid}`, params)
}
/**
* 获取讨论题目列表,“课程的问题”信息
* dataJson.limit - 获取数量
* dataJson.offset - 偏移量
* dataJson.sort - 排序类型
* @param {[string]} cid
* @param {[string]} sid
* @param {[object]} dataJson
*/
export function getCourseDiscussList(sid, cid, params) {
return httpRequest.get(`/api/lms/v2/qa/questions/course/${sid}/${cid}`, { params })
}
/**
* 获取问题详情
* @param {[string]} qid
*/
export function getDiscussDetail(qid) {
return httpRequest.get(`/api/lms/v2/qa/questions/${qid}`, {})
}
/**
* 删除提问
* @param {[string]} qid
*/
export function deleteDiscuss(qid) {
return httpRequest.delete(`/api/lms/v2/qa/questions/${qid}`, {})
}
/**
* 提出问题
* @param {[object]} param
*/
export function publishQues(param) {
return httpRequest.post('/api/lms/v2/qa/questions', param, { headers: { 'Content-Type': 'application/json' } })
}
/**
* 回答问题
* @param {[object]} param
*/
export function answerQues(param) {
return httpRequest.post('/api/lms/v2/qa/answers', param, { headers: { 'Content-Type': 'application/json' } })
}
/**
* 删除回答
* @param {[string]} aid
*/
export function deleteAnswer(aid) {
return httpRequest.delete(`/api/lms/v2/qa/answers/${aid}`, {})
}
/**
* 回复评论
* @param {[object]} param
*/
export function callbackComment(param) {
return httpRequest.post('/api/lms/v2/qa/comments', param, { headers: { 'Content-Type': 'application/json' } })
}
/**
* 删除评论
* @param {[string]} cid
*/
export function deleteComment(cid) {
return httpRequest.delete(`/api/lms/v2/qa/comments/${cid}`)
}
/**
* 点赞
* @param {[object]} param
*/
export function like(param) {
return httpRequest.post('/api/lms/v2/qa/tags', param, { headers: { 'Content-Type': 'application/json' } })
}
/**
* 取消点赞
* @param {[string]} tagid
*/
export function unlike(tagid) {
return httpRequest.delete(`/api/lms/v2/qa/tags/${tagid}`, {})
}
<template>
<div class="app-container">
<div class="app-container-hd" v-if="title">
<div class="app-container-hd__title">{{ title }}</div>
<div class="app-container-hd__right">
<slot name="header-right"></slot>
</div>
</div>
<div class="app-container-bd">
<slot></slot>
</div>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
name: 'AppContainer',
props: { title: { type: String } }
}
</script>
<style lang="scss">
.app-container {
clear: both;
}
.app-container-hd {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
margin-bottom: 20px;
border-bottom: 1px solid #ccc;
}
.app-container-hd__title {
font-size: 16px;
line-height: 45px;
}
.app-container-bd {
margin: 0.3rem;
}
.app-container-ft {
background-color: #fff;
position: sticky;
bottom: 0;
padding-top: 30px;
border-top: 1px solid #ccc;
}
</style>
......@@ -6,10 +6,10 @@
</div>
<div class="nav-right">
<div class="nav-message"></div>
<!-- <div class="notify" @click="goNotify()">
<div class="notify" @click="goNotify()">
{{ $t('components.learnSysLayout.navigation.tip') }}
<div class="num" v-if="this.$store.getters.myMsg != 0">{{ this.$store.getters.myMsg }}</div>
</div> -->
</div>
<!-- <language-switch /> -->
</div>
</div>
......@@ -17,8 +17,8 @@
<script>
import LanguageSwitch from '@/components/languageSwitch/index.vue'
// import cAction from '../../action'
import Message from '@ezijing/web-message-sdk'
import cAction from '../../action'
// import Message from '@ezijing/web-message-sdk'
export default {
components: { LanguageSwitch },
data() {
......@@ -27,15 +27,15 @@ export default {
}
},
mounted() {
// cAction.Other.getNavMsg()
// .then(data => {
// this.$store.commit('myMsg', data.num)
// })
// .catch(e => {
// this.$message.error(e.message)
// })
cAction.Other.getNavMsg()
.then(data => {
this.$store.commit('myMsg', data.num)
})
.catch(e => {
this.$message.error(e.message)
})
// 新版通知
Message({ container: '.nav-message', source: 'SOFIA_WEB', baseURL: webConf.others.messageBaseURL })
// Message({ container: '.nav-message', source: 'SOFIA_WEB', baseURL: webConf.others.messageBaseURL })
},
methods: {
goNotify() {
......
......@@ -60,15 +60,15 @@ export default {
if (data.type === 1) {
return
}
// zoom直播
if (data.type === 8) {
// 5:CC直播; 6:腾讯会议; 8:Zoom
const live = data.live
if ([6, 8].includes(data.type) && live) {
const hasRecordUrl = live.enable_record && live.record_url
if ([3, 5].includes(live.live_status) && !hasRecordUrl) {
this.$message.error(this.$t('viewerMain.liveEnd'))
return
}
window.open(live.record_url || live.join_url)
window.open(live.record_url || live.join_url, '_blank')
return
}
// 课程大作业
......
<template>
<li class="course-list-item">
<div class="course-list-item-pic" @click="handleClick">
<img :src="data.src" v-if="data.src" />
<div class="no-img" v-else><i class="el-icon-self-13"></i></div>
</div>
<div class="course-list-item-main">
<div class="title" @click="handleClick">{{ data.title }}</div>
<div class="tags">
<template v-for="(item1, index) in data.arrTab">
<span v-bind:key="index">{{ item1 }}</span>
</template>
</div>
<div class="time">
{{ data.status }}<em>{{ data.time }}</em>
</div>
<div class="progress">
{{ $t('pages.learn.course.progress') }}
<el-progress :percentage="data.progress" color="#b49441"></el-progress>
</div>
</div>
<div class="course-list-item-aside">
<p>{{ data.myStatus }}</p>
<el-button type="primary" size="small" round @click="handleClick">
{{ $t('pages.learn.course.showCourse') }}
</el-button>
</div>
</li>
</template>
<script>
export default {
name: 'CourseListItem',
props: {
data: { type: Object, required: true },
showProgress: { type: Boolean, default: true }
},
data() {
return {}
},
methods: {
handleClick() {
this.$router.push({ path: `/app/learn/course-detail/${this.data.sid}/${this.data.id}` })
}
}
}
</script>
<style lang="scss">
.course-list-item {
display: flex;
padding: 0.2rem 0;
list-style: none;
}
.course-list-item + .course-list-item {
border-top: 1px solid #dcdcdc;
}
.course-list-item-pic {
position: relative;
width: 1.8rem;
height: 1rem;
cursor: pointer;
.no-img {
width: 100%;
height: 1rem;
text-align: center;
line-height: 1.1rem;
border: 1px solid #e2e2e2;
i {
font-size: 0.4rem;
color: #e2e2e2;
}
}
img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
}
.course-list-item-main {
flex: 1;
position: relative;
padding-left: 0.2rem;
.title {
width: 80%;
line-height: 1.5;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.tags {
margin: 0.05rem 0;
font-size: 0.14rem;
span {
padding: 0 0.1rem;
margin: 0;
border-right: 1px solid #313131;
&:first-child {
padding-left: 0;
}
&:last-child {
border: none;
}
}
}
.time {
font-size: 0.12rem;
em {
font-style: normal;
color: #898989;
}
}
.progress {
margin-top: 0.05rem;
font-size: 14px;
.el-progress {
width: 50%;
display: inline-block;
vertical-align: text-bottom;
}
}
}
.course-list-item-aside {
display: flex;
flex-direction: column;
justify-content: space-between;
p {
padding: 0 0.1rem;
font-size: 0.14rem;
text-align: right;
}
}
</style>
<template></template>
<script>
export default {}
</script>
<template>
<div>
<template v-if="newLiveMsg.live">
<div class="live-msg">
<div class="txt">{{ $t('pages.learn.course.title2') }}</div>
<div class="txt">
{{ $t('pages.learn.course.msg') }}{{ newLiveMsg.course_name }}{{ $t('pages.learn.course.msg1') }}
{{ newLiveMsg.live.start_time }}
{{ $t('pages.learn.course.msg2') }}
</div>
<el-button class="in-btn" type="primary" size="small" round @click="goLive">{{
$t('pages.learn.course.Enterlive')
}}</el-button>
</div>
</template>
<!-- 最新直播 -->
<course-latest-live />
<div class="con-title">{{ $t('pages.learn.course.title') }}</div>
<!-- 筛选 -->
<div class="con-box">
<template v-for="(item, index) in find">
<ul v-bind:key="index" class="tabs-list">
......@@ -37,76 +27,38 @@
</ul>
</template>
</div>
<!-- 排序 -->
<div class="switch-box">
<el-button type="info" size="medium" @click="studyEarlyUpdate" plain
>{{ $t('pages.learn.course.lastLearn')
}}<i :class="['el-icon-caret-' + (filter.studyEarly == 'down' ? 'bottom' : 'top'), 'icon']"></i
></el-button>
<el-button type="info" size="medium" @click="selectTimeUpdate" plain
>{{ $t('pages.learn.course.updateTime')
}}<i :class="['el-icon-caret-' + (filter.selectTime == 'down' ? 'bottom' : 'top'), 'icon']"></i
></el-button>
<!-- <el-button class="rbtn" type="primary" size="medium" icon="el-icon-self-cc-book icon" @click="goCourseAll">{{ $t('pages.learn.course.changeCourse') }}</el-button> -->
<el-button type="info" size="medium" plain @click="studyEarlyUpdate">
{{ $t('pages.learn.course.lastLearn') }}
<i :class="['el-icon-caret-' + (filter.studyEarly == 'down' ? 'bottom' : 'top'), 'icon']"></i>
</el-button>
<el-button type="info" size="medium" plain @click="selectTimeUpdate">
{{ $t('pages.learn.course.updateTime') }}
<i :class="['el-icon-caret-' + (filter.selectTime == 'down' ? 'bottom' : 'top'), 'icon']"></i>
</el-button>
<!--
<el-button class="rbtn" type="primary" size="medium" icon="el-icon-self-cc-book icon" @click="goCourseAll">
{{ $t('pages.learn.course.changeCourse') }}
</el-button>
-->
</div>
<!-- 课程列表 -->
<div class="con-box" v-loading="loading">
<ul class="course-list">
<template v-for="(item, index) in homeList">
<li v-bind:key="index" class="item">
<div class="left-pic">
<template v-if="item.src">
<img :src="item.src" alt="" @click="goCourseContent" :data-cid="item.id" :data-sid="item.sid" />
</template>
<template v-else>
<div class="no-img"><i class="el-icon-self-13"></i></div>
</template>
</div>
<div class="right-bd">
<div class="title" @click="goCourseContent" :data-cid="item.id" :data-sid="item.sid">
{{ item.title }}
</div>
<div class="tags">
<template v-for="(item1, index) in item.arrTab">
<span v-bind:key="index">{{ item1 }}</span>
</template>
</div>
<div class="time">
{{ item.status }}&emsp;&emsp;<em>{{ item.time }}</em>
</div>
<div class="progress">
{{ $t('pages.learn.course.progress') }}&emsp;<el-progress
:percentage="item.progress > 99.5 ? 100 : item.progress"
color="#b49441"
></el-progress>
</div>
<div class="right-sel">{{ item.myStatus }}</div>
<el-button
class="in-btn"
type="primary"
size="small"
round
@click="goCourseContent"
:data-cid="item.id"
:data-sid="item.sid"
>{{ $t('pages.learn.course.showCourse') }}</el-button
>
</div>
</li>
</template>
<template v-if="!homeList.length">
<div class="no-data">{{ $t('pages.learn.course.noCourseStr') }}</div>
</template>
<ul class="course-list" v-if="homeList.length">
<course-list-item v-for="(item, index) in homeList" :data="item" :key="index"></course-list-item>
</ul>
<div class="no-data" v-else>{{ $t('pages.learn.course.noCourseStr') }}</div>
</div>
</div>
</template>
<script>
import cAction from '@action'
import CourseLatestLive from './course/components/CourseLatestLive.vue'
import CourseListItem from './components/CourseListItem'
export default {
components: {},
components: { CourseLatestLive, CourseListItem },
data() {
return {
filter: {
......@@ -148,21 +100,10 @@ export default {
],
homeList: [], // 从后台请求
param: {},
timeInterval: null,
newLiveMsg: {},
loading: false
}
},
mounted() {
if (this.timeInterval) {
clearInterval(this.timeInterval)
this.timeInterval = null
}
// 获取最新直播
this.getLatestLive()
// 定时获取最新直播
this.timeInterval = setInterval(this.getLatestLive, 10000)
cAction.Course.getLearnFind()
.then(data => {
this.find[0].arrItem = data
......@@ -173,36 +114,17 @@ export default {
})
.finally(() => {})
},
destroyed() {
if (this.timeInterval) {
clearInterval(this.timeInterval)
this.timeInterval = null
}
},
methods: {
goCourseAll() {
this.$router.push({
path: '/app/learn/course-all'
})
},
goCourseContent(e) {
const cid = e.currentTarget.dataset.cid
const sid = e.currentTarget.dataset.sid
this.$router.push({
path: `/app/learn/course-detail/${sid}/${cid}`
})
},
getAjaxList(bool, str) {
this.loading = true
cAction.Course.getCourseList(bool, this.param)
.then(json => {
this.homeList = json
if (!json.length) {
this.$message(str)
}
})
.catch(e => {
this.$message.error(e.message)
})
.finally(() => {
this.loading = false
......@@ -254,57 +176,12 @@ export default {
}
this.getAjaxList(true, this.$t('pages.learn.course.goCourseAllStr'))
},
/* 直接进直播 */
goLive() {
const live = this.newLiveMsg.live
if (live.type === 8) {
window.open(live.record_url || live.join_url)
} else {
this.$router.push({
name: 'viewerCourseChapter',
params: {
sid: this.newLiveMsg.semester_id,
cid: this.newLiveMsg.course_id,
id: this.newLiveMsg.chapter_id
}
})
}
},
// 获取最新直播
getLatestLive() {
cAction.Player.getNewLiveMsg()
.then(json => {
if (json.status === 200) {
this.newLiveMsg = json.data
}
})
.catch(e => {
this.$message.error(e.message)
})
}
}
}
</script>
<style lang="scss" scoped>
.live-msg {
position: relative;
padding: 15px;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.1);
.txt {
padding-right: 100px;
}
.in-btn {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
}
}
/* 列表 筛选 */
ul.tabs-list {
float: left;
......@@ -372,127 +249,10 @@ ul.course-list {
padding: 0;
font-size: 0.18rem;
color: #313131;
.no-data {
}
.no-data {
text-align: center;
line-height: 2rem;
}
li.item {
padding: 0.1rem 0;
list-style: none;
border-bottom: 1px solid #dcdcdc;
overflow: hidden;
&:first-child {
padding-top: 0;
}
&:last-child {
margin-bottom: 0.2rem;
}
.left-pic {
position: relative;
float: left;
width: 1.8rem;
// min-height: 1rem;
overflow: hidden;
.no-img {
width: 100%;
height: 1rem;
text-align: center;
line-height: 1.1rem;
border: 1px solid #e2e2e2;
i {
font-size: 0.4rem;
color: #e2e2e2;
}
}
img {
// position: absolute;
// top: 50%;
// left: 50%;
display: block;
width: 100%;
// height: 100%;
// transform: translate(-50%, -50%);
cursor: pointer;
}
}
.right-bd {
position: relative;
display: block;
margin-left: 2rem;
.title {
width: 80%;
line-height: 1.5;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.tags {
margin: 0.05rem 0;
font-size: 0.14rem;
span {
padding: 0 0.1rem;
margin: 0;
border-right: 1px solid #313131;
&:first-child {
padding-left: 0;
}
&:last-child {
border: none;
}
}
}
.time {
font-size: 0.12rem;
em {
font-style: normal;
color: #898989;
}
}
.progress {
margin-top: 0.05rem;
font-size: 14px;
.el-progress {
width: 50%;
display: inline-block;
vertical-align: text-bottom;
}
}
.right-sel {
position: absolute;
right: 0;
top: 0;
font-size: 14px;
padding: 0 0.1rem;
}
.in-btn {
position: absolute;
right: 0;
bottom: 0;
}
}
}
}
@media (max-width: 767px) {
......
<!--课程考核-->
<template>
<div class="course-assess" v-loading="loading">
<h1 class="title">
{{ $t('pages.learn.courseDetail.Finalresult') }}
<template v-if="data.course_score"> {{ data.course_score }}{{ $t('pages.learn.courseDetail.point') }} </template>
<template v-else>{{ $t('pages.learn.courseDetail.no') }}</template>
</h1>
<course-assessment-standard :data="data" v-bind="$attrs" v-if="data.course_check" />
<course-assessment-progress :data="data" />
</div>
</template>
<script>
import * as api from '@/api/course'
import CourseAssessmentStandard from './CourseAssessmentStandard'
import CourseAssessmentProgress from './CourseAssessmentProgress'
export default {
components: { CourseAssessmentStandard, CourseAssessmentProgress },
data() {
return {
loading: false,
data: {}
}
},
computed: {
sid() {
return this.$route.params.sid
},
cid() {
return this.$route.params.cid
}
},
methods: {
getData() {
this.loading = true
api
.getCourseAssess(this.sid, this.cid)
.then(res => {
this.data = res
})
.finally(() => {
this.loading = false
})
}
},
beforeMount() {
this.getData()
}
}
</script>
<style lang="scss">
.course-assess {
padding: 20px;
background-color: #fff;
.title {
font-size: 0.2rem;
text-align: center;
}
.subtitle {
position: relative;
width: 300px;
margin: 40px auto 20px;
text-align: center;
span {
padding: 0 20px;
position: relative;
display: inline-block;
background-color: #fff;
}
&::before {
position: absolute;
left: 0;
top: 50%;
right: 0;
content: '';
height: 1px;
background-color: #000;
transform: translateY(50%);
}
}
}
</style>
<!--课程考核-学习进度及成绩-->
<template>
<div class="course-assess-progress">
<div class="subtitle">
<span>{{ $t('pages.learn.courseDetail.Learningprogressachievements') }}</span>
</div>
<!-- 视频 -->
<div class="item">
<div class="item-title">
{{ $t('pages.learn.courseDetail.title2') }}{{ formatDuration(data.course_duration)
}}{{ $t('pages.learn.courseDetail.Completionrate') }}{{ data.course_progress }}
</div>
<div class="table">
<div class="table-row table-row-th">
<div class="table-cell">{{ $t('pages.learn.courseDetail.chapter') }}</div>
<div class="table-cell w100">{{ $t('pages.learn.courseDetail.Lengthofstudy') }}</div>
<div class="table-cell w100">{{ $t('pages.learn.courseDetail.percent') }}</div>
</div>
<div class="table-row-group" v-for="(item, index) in videoList" :key="index">
<div class="table-row-group-th">{{ item.title }}</div>
<div class="table-row" v-for="(subItem, index) in item.sections" :key="index">
<div class="table-cell">{{ subItem.title }}</div>
<div class="table-cell w100">{{ formatDuration(subItem.duration) }}</div>
<div class="table-cell w100">{{ subItem.progress }}%</div>
</div>
</div>
<div class="empty-data" v-if="!videoList.length">{{ $t('pages.learn.courseDetail.Nodataavailable') }}</div>
</div>
</div>
<!-- 作业 -->
<div class="item">
<div class="item-title">
{{ $t('pages.learn.courseDetail.subjectivequestions') }}
</div>
<div class="table">
<div class="table-row table-row-th">
<div class="table-cell">{{ $t('pages.learn.courseDetail.chapter') }}</div>
<div class="table-cell w100">{{ $t('pages.learn.courseDetail.Submissiontime') }}</div>
<div class="table-cell w100">{{ $t('pages.learn.courseDetail.score') }}</div>
</div>
<div class="table-row-group" v-for="(item, index) in homeWorkList" :key="index">
<div class="table-row-group-th">{{ item.title }}</div>
<div class="table-row" v-for="(subItem, index) in item.sections" :key="index">
<div class="table-cell">{{ subItem.title }}</div>
<div class="table-cell w100">{{ subItem.created_time || '暂无提交' }}</div>
<div class="table-cell w100">{{ showCore(subItem.score) }}</div>
</div>
</div>
<div class="empty-data" v-if="!homeWorkList.length">{{ $t('pages.learn.courseDetail.Nodataavailable') }}</div>
</div>
</div>
<div class="item">
<div class="item-title">{{ $t('pages.learn.courseDetail.Bighomework') }}</div>
<div class="status-text">
{{ $t('pages.learn.courseDetail.Status') }}{{ essay.status || $t('action.courseAction.none') }}
</div>
<div class="status-text" v-if="essay.created_time">
{{ $t('pages.learn.courseDetail.Submissiontime') }}{{ essay.created_time }}
</div>
<div class="status-text">{{ $t('pages.learn.courseDetail.score2') }}{{ showCore(essay.score) }}</div>
</div>
</div>
</template>
<script>
export default {
props: { data: { type: Object, default: () => {} } },
computed: {
videoList() {
return this.data.video_evaluation || []
},
homeWorkList() {
return this.data.homework_evaluation || []
},
essay() {
return this.data.essay_evaluation || {}
}
},
methods: {
formatDuration(duration) {
const h = Math.floor(duration / 3600)
const m = Math.floor((duration - h * 3600) / 60)
const s = (duration - h * 3600 - m * 60) % 60
function tenify(a) {
return a >= 10 ? a : '0' + a
}
const to = { h: tenify(h), m: tenify(m), s: tenify(s) }
const format = 'h:m:s'
return format.replace(/h|m|s/g, k => to[k]).replace(/^00:/, '')
},
showCore(value) {
return value !== null ? value : this.$t('action.courseAction.none')
}
}
}
</script>
<style lang="scss">
.course-assess-progress {
.item {
padding: 0.1rem 0 0.2rem;
}
.item + .item {
border-top: 1px solid #c9c9c9;
}
.item-title {
padding: 0.1rem 0;
font-size: 0.16rem;
font-weight: 700;
color: #b49441;
}
.table {
margin: 10px 0;
}
.table-row {
display: flex;
}
.table-row-th {
font-weight: bold;
border-bottom: 1px solid #c9c9c9;
}
.table-row-group {
.table-row {
font-size: 12px;
&:hover {
background-color: #efefef;
}
}
}
.table-row-group-th {
font-weight: bold;
padding: 10px;
}
.table-cell {
padding: 5px 10px;
flex: 1;
&.w100 {
flex: 0 0 120px;
text-align: center;
}
}
.empty-data {
padding: 20px 0;
text-align: center;
}
.status-text {
padding-left: 0.1rem;
font-size: 0.14rem;
color: #000000;
line-height: 1.5;
}
}
</style>
<!--课程考核-->
<template>
<div class="course-assess-standard">
<div class="subtitle">
<span>{{ $t('pages.learn.courseDetail.Courseassessmentstandard') }}</span>
</div>
<div class="item">
<h2 class="item-title">一、最终成绩计算</h2>
<!-- 中方课程 -->
<p v-if="course.course_check_type === 1">
<template v-for="(item, index) in checkList">
<span :key="index">{{ index ? '+' : '' }}{{ item.name }}得分*{{ item.percent }}%</span>
</template>
= 该门课程总得分,满分100分,低于{{ data.course_check_pass_score }}分为不及格,需重修此门课程。
</p>
<!-- 美方课程 -->
<p v-else>美方课程最终成绩请参考每学期初教务邮箱老师发到大家邮箱中的课程考核大纲进行计算。</p>
<v-chart class="chart" :option="option" :autoresize="true" />
</div>
<div class="item">
<h2 class="item-title">二、具体细则</h2>
<div v-for="(item, index) in checkList" :key="index">
<div class="item-subtitle">{{ `${item.name} 总分${item.score}分(占科目总成绩的${item.percent}%)` }}</div>
<div v-html="item.content"></div>
</div>
</div>
</div>
</template>
<script>
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import VChart from 'vue-echarts'
use([CanvasRenderer, PieChart, TitleComponent, TooltipComponent, LegendComponent])
export default {
props: { course: { type: Object, default: () => {} }, data: { type: Object, default: () => {} } },
components: { VChart },
data() {
return {}
},
computed: {
checkList() {
return this.data.course_check.filter(item => parseInt(item.percent))
},
option() {
const data = this.checkList.map(item => {
return { name: item.name, value: item.percent }
})
return {
tooltip: { trigger: 'item', formatter: '{a} <br/>{b} 占比{c}%' },
series: [{ name: '课程考核', type: 'pie', data }]
}
}
}
}
</script>
<style lang="scss" scoped>
.chart {
height: 300px;
}
.course-assess-standard {
.item-title {
margin: 0;
font-size: 0.16rem;
font-weight: 700;
color: #313131;
line-height: 0.38rem;
}
.item-subtitle {
font-size: 0.14rem;
font-weight: 700;
color: #313131;
line-height: 0.3rem;
margin-top: 10px;
}
::v-deep p {
padding: 5px 0;
}
}
</style>
<template>
<div class="course-chapter">
<el-collapse v-model="activeNames">
<template v-for="item in chapters">
<el-collapse-item :title="item.name" :name="item.id" :key="item.id" v-if="item.children">
<ul>
<li v-for="subItem in item.children" :key="subItem.id" @click="handleClick(subItem)">
<div class="name">{{ subItem.name }}</div>
<div class="duration">
<!-- 视频 -->
<template v-if="subItem.video">{{ formatDuration(subItem.video.video_length) }}</template>
<!-- 直播 -->
<template v-if="subItem.live">
{{ subItem.live.start_time }}
{{ calcTimeText(subItem.live) }}
</template>
</div>
</li>
</ul>
</el-collapse-item>
<div class="el-collapse-item" :key="item.id" @click="handleClick(item)" v-else>
<div class="el-collapse-item__header">{{ item.name }}</div>
</div>
</template>
</el-collapse>
</div>
</template>
<script>
export default {
props: {
course: { type: Object, default: () => ({}) }
},
data() {
return {
activeNames: []
}
},
computed: {
sid() {
return this.$route.params.sid
},
cid() {
return this.$route.params.cid
},
// 章节列表
chapters() {
const chapters = this.course.chapters || []
const customeChapter = [
// { name: this.$t('viewerMain.courseWork'), id: 'course_work', type: 99 },
{ name: this.$t('viewerMain.courseData'), id: 'course_info', type: 100 },
{ name: this.$t('viewerMain.teachingEvaluation'), id: 'teach_evaluation', type: 102 }
]
// 课程大作业
if (this.course.curriculum && this.course.curriculum.curriculum_essay) {
customeChapter.unshift({ name: this.$t('viewerMain.courseWork'), id: 'course_work', type: 99 })
}
// 课程考试
if (this.course.course_examination) {
customeChapter.push({ name: this.$t('viewerMain.courseExam'), id: 'course_exam', type: 101 })
}
return chapters.concat(customeChapter)
}
},
methods: {
handleClick(data) {
// 直播
const live = data.live
if ([6, 8].includes(data.type) && live) {
const hasRecordUrl = live.enable_record && live.record_url
if ([3, 5].includes(live.live_status) && !hasRecordUrl) {
this.$message.error(this.$t('viewerMain.liveEnd'))
return
}
window.open(live.record_url || live.join_url, '_blank')
return
}
// 课程大作业
if (data.id === 'course_work' && !this.course.survey) {
this.$message(this.$t('viewerMain.teachingEvaluationTips'))
return
}
// 教学评估
if (data.id === 'teach_evaluation') {
const { sid, cid } = this.$route.params
this.$router.push({ name: 'survey', params: { sid, cid } })
return
}
this.$router.push({ name: 'viewerCourseChapter', params: { sid: this.sid, cid: this.cid, id: data.id } })
},
formatDuration(duration) {
const h = Math.floor(duration / 3600)
const m = Math.floor((duration - h * 3600) / 60)
const s = (duration - h * 3600 - m * 60) % 60
function tenify(a) {
return a >= 10 ? a : '0' + a
}
const to = { h: tenify(h), m: tenify(m), s: tenify(s) }
const format = 'h:m:s'
return format.replace(/h|m|s/g, k => to[k]).replace(/^00:/, '')
},
// 计算日期
calcTimeText(data) {
let { start_time: liveTime, live_status: liveStatus } = data
const map = {
1: this.$t('live.notStarted'),
2: this.$t('live.liveStreaming'),
3: this.$t('live.liveEnd'),
4: this.$t('live.start'),
5: this.$t('live.liveEnd')
}
let result = map[liveStatus] || liveTime
if (liveStatus === 1 && liveTime) {
liveTime = liveTime.replace(/-/g, '/')
const time = (new Date(liveTime).getTime() - new Date().getTime()) / 1000 || 0
if (time <= 5 * 60) {
result = this.$t('live.start')
} else if (time <= 1 * 60 * 60) {
result = this.$t('live.startInMinutes', {
minutes: parseInt(time / 60)
})
} else if (time <= 24 * 60 * 60) {
result = this.$t('live.startInHours', {
h: parseInt(time / (60 * 60)),
min: parseInt((time / 60) % 60)
})
} else {
result = this.$t('live.startInDay', {
day: parseInt(time / (24 * 60 * 60))
})
}
}
if (liveStatus === 3 && data.enable_record && data.record_url) {
result = this.$t('live.watchReplay')
}
return result
}
}
}
</script>
<style lang="scss" scoped>
.course-chapter {
padding: 0.2rem;
background-color: #fff;
}
.el-collapse {
border: 0;
}
::v-deep .el-collapse-item {
margin-bottom: 10px;
}
::v-deep .el-collapse-item__header {
font-size: 14px;
font-weight: 600;
color: #222;
background-color: #e5e5e5;
padding-left: 10px;
}
::v-deep .el-collapse-item__wrap {
border-bottom: 0;
}
::v-deep .el-collapse-item__content {
padding-bottom: 10px;
}
ul {
margin: 0;
padding: 0;
}
li {
display: flex;
padding: 10px;
cursor: pointer;
color: #666;
&:hover {
color: #c01540;
background-color: #f3f3f3;
}
.name {
flex: 1;
overflow: hidden;
}
.progress {
margin-left: 20px;
color: #999;
}
}
</style>
<!--课程讨论-->
<template>
<div>
<div class="publish" v-if="isPublicShow">
<div class="publish-header">
<el-button type="text" @click="gobackDiscuss">
{{ $t('pages.learn.courseDetail.Returntoquestionlist') }}
</el-button>
<div class="publish-title">
{{ $t('pages.learn.courseDetail.Releaseissues') }}
</div>
</div>
<el-form ref="setPublishform" :model="publish" :rules="publishRules" label-position="top">
<el-form-item :label="$t('pages.learn.courseDetail.title1')" prop="title">
<el-input
type="text"
:placeholder="$t('pages.learn.courseDetail.inputtitle1')"
v-model="publish.title"
></el-input>
</el-form-item>
<el-form-item :label="$t('pages.learn.courseDetail.Textcontent')" prop="contents">
<v-editor v-model="publish.contents"></v-editor>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmitPublish">{{
$t('pages.learn.courseDetail.Releaseissues')
}}</el-button>
</el-form-item>
</el-form>
</div>
<template v-else>
<div class="discuss-header">
<div class="discuss-btn" @click="isPublicShow = true">
<i class="el-icon-edit"></i>
{{ $t('pages.learn.courseDetail.Publishingissues') }}
</div>
<div class="aside">
<div class="discuss-btn" :class="{ 'is-active': sort === 'time' }" @click="handleSort('time')">
{{ $t('pages.learn.courseDetail.Sortbytime') }}
</div>
<div class="discuss-btn" :class="{ 'is-active': sort === 'tag' }" @click="handleSort('tag')">
{{ $t('pages.learn.courseDetail.Sortbyvote') }}
</div>
</div>
</div>
<div class="discuss-scroll">
<discuss :params="params"></discuss>
</div>
</template>
</div>
</template>
<script>
import * as api from '@/api/discuss'
import VEditor from '@/components/ckeditor'
export default {
components: { VEditor },
data() {
const { sid, cid } = this.$route.params
return {
sid,
cid,
isPublicShow: false,
publish: {
title: '',
contents: ''
},
publishRules: {
title: [{ required: true, message: this.$t('pages.learn.courseDetail.inputtitle1'), trigger: 'blur' }],
contents: [{ required: true, message: this.$t('pages.learn.courseDetail.inputTextcontent'), trigger: 'blur' }]
},
sort: '',
params: {
path: `/${sid}/${cid}`,
request: 'getCourseDiscussList',
dataJson: { limit: 10, offset: 0, sort: '' }
}
}
},
methods: {
/**
* 返回问题列表
*/
gobackDiscuss() {
this.isPublicShow = false
},
/**
* 问题发布 - 接口提交
*/
onSubmitPublish() {
this.$refs.setPublishform.validate(valid => {
if (valid) {
const params = Object.assign({}, this.publish, { course_id: this.cid, semester_id: this.sid })
api.publishQues(params).then(res => {
this.$message({ type: 'success', message: this.$t('pages.learn.courseDetail.publishSuccessTips') })
this.isPublicShow = false
this.$refs.setPublishform.resetFields()
})
}
})
},
/**
* 跳转到对应 问题详情页
*/
goDiscussDetail(e) {
const qid = e.currentTarget.dataset.id
this.$router.push({
path: `/app/learn/discuss-detail/${this.sid}/${this.cid}/${qid}`
})
},
/**
* 排序方式
*/
handleSort(sort) {
this.sort = this.sort === sort ? '' : sort
this.params = {
path: `/${this.sid}/${this.cid}`,
request: 'getCourseDiscussList',
dataJson: { limit: 10, offset: 0, sort: this.sort }
}
}
}
}
</script>
<style lang="scss">
/* 课程讨论 */
.discuss-header {
display: flex;
align-items: center;
justify-content: space-between;
.aside {
display: flex;
}
}
.discuss-btn {
padding: 0 0.3rem;
height: 0.42rem;
font-size: 0.2rem;
line-height: 0.42rem;
background: #fff;
border: 1rpx solid #dcdcdc;
cursor: pointer;
border-radius: 0.28rem;
white-space: nowrap;
&.is-active {
color: #fff;
background: #b49441;
}
}
.discuss-btn + .discuss-btn {
margin-left: 0.1rem;
}
// 发布
.publish {
padding: 0.3rem;
background-color: #f7f7f7;
}
.publish-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.publish-title {
font-size: 0.2rem;
}
</style>
<!--课程列表最新直播-->
<template>
<div class="course-latest-live" v-if="data.live">
<div class="txt">{{ $t('pages.learn.course.title2') }}</div>
<div class="txt">
{{ $t('pages.learn.course.msg') }}{{ data.course_name }}{{ $t('pages.learn.course.msg1') }}
{{ data.live.start_time }}
{{ $t('pages.learn.course.msg2') }}
</div>
<el-button class="in-btn" type="primary" size="small" round @click="handleClick">
{{ $t('pages.learn.course.Enterlive') }}
</el-button>
</div>
</template>
<script>
import cAction from '@action'
export default {
data() {
return {
data: {},
timer: null
}
},
methods: {
// 进入直播
handleClick() {
// 5:CC直播; 6:腾讯会议; 8:Zoom
const live = this.data.live
if (live.type === 5) {
this.$router.push({
name: 'viewerCourseChapter',
params: { sid: this.data.semester_id, cid: this.data.course_id, id: this.data.chapter_id }
})
} else {
window.open(live.record_url || live.join_url)
}
},
// 获取最新直播
getLatestLive() {
cAction.Player.getNewLiveMsg().then(res => {
this.data = res.data
})
},
// 定时刷新直播列表
setTimer() {
this.timer = setInterval(this.getLatestLive, 10000)
},
// 清除定时刷新
clearTimer() {
this.timer && setInterval(this.timer)
}
},
mounted() {
this.getLatestLive()
// 定时刷新直播列表
// this.setTimer()
},
destroyed() {
this.clearTimer()
}
}
</script>
<style lang="scss">
.course-latest-live {
position: relative;
padding: 40px;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.1);
margin: 0.3rem;
background-color: #fff;
.txt {
font-size: 16px;
font-weight: bold;
padding-right: 100px;
}
.in-btn {
position: absolute;
right: 40px;
top: 50%;
transform: translateY(-50%);
background-color: #d21f28;
border-color: #d21f28;
}
}
</style>
<template>
<li class="course-list-item">
<div class="course-list-item-pic" @click="handleClick">
<img :src="data.src" v-if="data.src" />
<div class="no-img" v-else><i class="el-icon-self-13"></i></div>
</div>
<div class="course-list-item-main">
<div class="title" @click="handleClick">{{ data.title }}</div>
<div class="tags">
<template v-for="(item1, index) in data.arrTab">
<span v-bind:key="index">{{ item1 }}</span>
</template>
</div>
<div class="time">
{{ data.status }}<em>{{ data.time }}</em>
</div>
<div class="progress">
{{ $t('pages.learn.course.progress') }}
<el-progress :percentage="data.progress" color="#b49441"></el-progress>
</div>
</div>
<div class="course-list-item-aside">
<p>{{ data.myStatus }}</p>
<el-button type="primary" size="small" round @click="handleClick">
{{ $t('pages.learn.course.showCourse') }}
</el-button>
</div>
</li>
</template>
<script>
export default {
name: 'CourseListItem',
props: {
data: { type: Object, required: true },
showProgress: { type: Boolean, default: true }
},
data() {
return {}
},
methods: {
handleClick() {
this.$router.push({ path: `/app/learn/course-detail/${this.data.sid}/${this.data.id}` })
}
}
}
</script>
<style lang="scss">
.course-list-item {
display: flex;
padding: 0.2rem 0;
list-style: none;
}
.course-list-item + .course-list-item {
border-top: 1px solid #dcdcdc;
}
.course-list-item-pic {
position: relative;
width: 1.8rem;
height: 1rem;
cursor: pointer;
.no-img {
width: 100%;
height: 1rem;
text-align: center;
line-height: 1.1rem;
border: 1px solid #e2e2e2;
i {
font-size: 0.4rem;
color: #e2e2e2;
}
}
img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
}
.course-list-item-main {
flex: 1;
position: relative;
padding-left: 0.2rem;
.title {
width: 80%;
line-height: 1.5;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.tags {
margin: 0.05rem 0;
font-size: 0.14rem;
span {
padding: 0 0.1rem;
margin: 0;
border-right: 1px solid #313131;
&:first-child {
padding-left: 0;
}
&:last-child {
border: none;
}
}
}
.time {
font-size: 0.12rem;
em {
font-style: normal;
color: #898989;
}
}
.progress {
margin-top: 0.05rem;
font-size: 14px;
.el-progress {
width: 50%;
display: inline-block;
vertical-align: text-bottom;
}
}
}
.course-list-item-aside {
display: flex;
flex-direction: column;
justify-content: space-between;
p {
padding: 0 0.1rem;
font-size: 0.14rem;
text-align: right;
}
}
</style>
<template>
<div class="course-live">
<template v-if="meetings.length">
<div class="course-live-item" v-for="item in meetings" :key="item.id" @click="handleClick(item)">
<div class="name"><i class="el-icon-video-play"></i>{{ item.topic }}</div>
<div class="aside">{{ item.start_time }} {{ calcTimeText(item) }}</div>
</div>
</template>
<div class="empty-data" v-else>暂无相关直播</div>
</div>
</template>
<script>
export default {
name: 'CourseLive',
props: { course: { type: Object, default: () => ({}) } },
data() {
return {}
},
computed: {
meetings() {
return this.course.meetings || []
}
},
methods: {
handleClick(live) {
// 直播
const hasRecordUrl = live.enable_record && live.record_url
if ([3, 5].includes(live.live_status) && !hasRecordUrl) {
this.$message.error(this.$t('viewerMain.liveEnd'))
return
}
window.open(live.record_url || live.join_url, '_blank')
},
// 计算日期
calcTimeText(data) {
let { start_time: liveTime, live_status: liveStatus } = data
const map = {
1: this.$t('live.notStarted'),
2: this.$t('live.liveStreaming'),
3: this.$t('live.liveEnd'),
4: this.$t('live.start'),
5: this.$t('live.liveEnd')
}
let result = map[liveStatus] || liveTime
if (liveStatus === 1 && liveTime) {
liveTime = liveTime.replace(/-/g, '/')
const time = (new Date(liveTime).getTime() - new Date().getTime()) / 1000 || 0
if (time <= 5 * 60) {
result = this.$t('live.start')
} else if (time <= 1 * 60 * 60) {
result = this.$t('live.startInMinutes', {
minutes: parseInt(time / 60)
})
} else if (time <= 24 * 60 * 60) {
result = this.$t('live.startInHours', {
h: parseInt(time / (60 * 60)),
min: parseInt((time / 60) % 60)
})
} else {
result = this.$t('live.startInDay', {
day: parseInt(time / (24 * 60 * 60))
})
}
}
if (liveStatus === 3 && data.enable_record && data.record_url) {
result = this.$t('live.watchReplay')
}
return result
}
}
}
</script>
<style lang="scss" scoped>
.course-live {
padding: 0.2rem;
background-color: #fff;
}
.course-live-item {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
padding: 5px;
i {
margin-right: 0.05rem;
font-size: 0.2rem;
vertical-align: middle;
}
.name {
line-height: 0.3rem;
}
.aside {
color: #666;
}
&:hover {
color: #c01540;
background-color: #f3f3f3;
}
}
.course-live-item + .course-live-item {
margin-top: 10px;
}
.empty-data {
font-size: 0.24rem;
color: #112c42;
line-height: 1rem;
text-align: center;
}
</style>
<template>
<div class="teacher">
<div class="teacher-item" v-for="item in data" :key="item.id">
<img :src="item.lecturer_avatar" class="teacher-item-pic" />
<div class="teacher-item-content">
<p class="t1">{{ item.lecturer_name }}</p>
<p class="t2">{{ item.lecturer_education }}</p>
<p class="t2">{{ item.lecturer_office }}</p>
<p class="t2">{{ item.lecturer_title }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
data: { type: Array, default: () => [] }
}
}
</script>
<style lang="scss" scoped>
.teacher-item {
display: flex;
margin-bottom: 10px;
}
.teacher-item-pic {
width: 90px;
height: 110px;
object-fit: cover;
margin-right: 10px;
}
.teacher-item-content {
flex: 1;
.t1 {
font-weight: bold;
}
.t2 {
font-size: 13px;
margin-top: 5px;
color: #707070;
}
}
</style>
<template>
<app-container :title="$t('pages.learn.courseDetail.title')" v-loading="loading">
<div class="course-top">
<div class="course-top-hd">
<div class="course-top-hd-left">
<div class="course-top__title">{{ curriculum.curriculum_name }}</div>
<div class="course-top__text" v-html="topText"></div>
<div class="course-top__progress">
{{ $t('pages.learn.courseDetail.Videoviewingprogress') }}
<el-progress :percentage="detail.video_progress" color="#b49441"></el-progress>
</div>
</div>
<div class="course-top-hd-right">
<el-button type="primary" size="small" @click="onChapterClick(latestVideo)" v-if="latestVideo">
{{ buttonText }}
</el-button>
</div>
</div>
<div class="course-top-bd">
<div class="course-top__pic"><img :src="curriculum.curriculum_picture" /></div>
<div class="course-top__content" v-html="curriculum.curriculum_represent"></div>
</div>
</div>
<div class="course-bottom">
<div class="course-bottom-left">
<el-tabs v-model="tabActive">
<el-tab-pane lazy :label="$t('pages.learn.courseDetail.Coursecontent')">
<course-chapter :course="detail"></course-chapter>
</el-tab-pane>
<el-tab-pane lazy :label="$t('pages.learn.courseDetail.Coursediscussion')">
<course-discuss :course="detail"></course-discuss>
</el-tab-pane>
<el-tab-pane lazy :label="$t('pages.learn.courseDetail.Courseassessment')">
<course-assessment :course="detail"></course-assessment>
</el-tab-pane>
<el-tab-pane lazy label="课程直播">
<course-live :course="detail"></course-live>
</el-tab-pane>
</el-tabs>
</div>
<div class="course-bottom-right">
<el-tabs>
<el-tab-pane label="课程讲师">
<course-teacher :data="detail.lecturers"></course-teacher>
</el-tab-pane>
</el-tabs>
</div>
</div>
</app-container>
</template>
<script>
import AppContainer from '@/components/AppContainer'
import CourseChapter from './components/CourseChapter'
import CourseDiscuss from './components/CourseDiscuss'
import CourseAssessment from './components/CourseAssessment'
import CourseLive from './components/CourseLive'
import CourseTeacher from './components/CourseTeacher'
import * as api from '@/api/course'
export default {
components: { AppContainer, CourseChapter, CourseDiscuss, CourseAssessment, CourseLive, CourseTeacher },
data() {
return {
tabActive: '0',
loading: false,
detail: {
chapters: []
}
}
},
computed: {
sid() {
return this.$route.params.sid
},
cid() {
return this.$route.params.cid
},
curriculum() {
return this.detail.curriculum || {}
},
buttonText() {
return this.detail.latest_play
? this.$t('pages.learn.courseDetail.Keeplearning')
: this.$t('pages.learn.courseDetail.Startlearning')
},
// 扁平化章节数据
flatChapters() {
return this.detail.chapters.reduce((result, item) => {
return result.concat(item.children)
}, [])
},
// 最新的视频
latestVideo() {
if (this.detail.latest_play && this.flatChapters.length) {
return this.flatChapters.find(item => item.resource_id === this.detail.latest_play)
} else {
return this.flatChapters.length ? this.flatChapters[0] : null
}
},
courseType() {
const map = {
1: this.$t('action.courseAction.mustLearn'),
2: this.$t('action.courseAction.changeLearn'),
3: this.$t('action.courseAction.repeatLearn')
}
return map[this.detail.course_type]
},
topText() {
if (this.loading) {
return ''
}
// 学分
let output = `${this.curriculum.curriculum_credit}${this.$t('action.courseAction.credit')}`
// 课程
output += ` | ${this.courseType} | ${this.detail.semester_name} `
// 时间
if (this.detail.begin_date) {
output += this.detail.begin_date + this.$t('action.courseAction.to') + this.detail.end_date + ' '
}
// 发布状态
output += this.curriculum.is_enabled
? this.$t('action.courseAction.publish')
: this.$t('action.courseAction.noPublish')
return output
}
},
methods: {
// 课程学习
getCourse() {
this.loading = true
api
.getCourse(this.sid, this.cid)
.then(response => {
this.detail = response
})
.finally(() => {
this.loading = false
})
},
onChapterClick(data) {
this.$router.push({ name: 'viewerCourseChapter', params: { cid: this.cid, id: data.id } })
}
},
beforeMount() {
this.getCourse()
}
}
</script>
<style lang="scss" scoped>
.main-container {
height: 100%;
padding: 30px;
background-color: #fff;
border-radius: 8px;
box-sizing: border-box;
}
.course-top {
padding-bottom: 20px;
}
.course-top-hd {
display: flex;
justify-content: space-between;
padding-bottom: 10px;
}
.course-top-hd-left {
flex: 1;
}
.course-top__title {
font-size: 18px;
font-weight: bold;
line-height: 1;
}
.course-top__text {
margin-top: 10px;
}
.course-top__progress {
margin-top: 10px;
display: flex;
align-items: center;
.el-progress {
width: 50%;
margin: 0 10px;
}
}
.course-top-bd {
display: flex;
}
.course-top__pic {
width: 160px;
height: 100px;
margin-right: 20px;
border-radius: 2px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.course-top__content {
flex: 1;
line-height: 24px;
overflow: hidden;
}
.course-bottom {
display: flex;
}
.course-bottom-left {
flex: 1;
overflow: hidden;
}
.course-bottom-right {
margin-left: 20px;
width: 300px;
}
</style>
<template>
<div>
<!-- 最新直播 -->
<course-latest-live />
<div class="con-title">{{ $t('pages.learn.course.title') }}</div>
<!-- 筛选 -->
<div class="con-box">
<template v-for="(item, index) in find">
<ul v-bind:key="index" class="tabs-list">
<template v-if="find[index].arrItem.length">
<li class="tabs-hd">{{ item.name }}</li>
<template v-for="(item1, index1) in item.arrItem">
<li v-bind:key="index1">
<div
:class="['tab', item.selectIndex == index1 ? 'on' : '']"
@click="selFindSelect"
:data-index="index1"
:data-i="index"
:data-key="item.key"
:data-val="item1.val"
>
{{ item1.name }}
</div>
</li>
</template>
</template>
</ul>
</template>
</div>
<!-- 排序 -->
<div class="switch-box">
<el-button type="info" size="medium" plain @click="studyEarlyUpdate">
{{ $t('pages.learn.course.lastLearn') }}
<i :class="['el-icon-caret-' + (filter.studyEarly == 'down' ? 'bottom' : 'top'), 'icon']"></i>
</el-button>
<el-button type="info" size="medium" plain @click="selectTimeUpdate">
{{ $t('pages.learn.course.updateTime') }}
<i :class="['el-icon-caret-' + (filter.selectTime == 'down' ? 'bottom' : 'top'), 'icon']"></i>
</el-button>
<!--
<el-button class="rbtn" type="primary" size="medium" icon="el-icon-self-cc-book icon" @click="goCourseAll">
{{ $t('pages.learn.course.changeCourse') }}
</el-button>
-->
</div>
<!-- 课程列表 -->
<div class="con-box" v-loading="loading">
<ul class="course-list" v-if="homeList.length">
<course-list-item v-for="(item, index) in homeList" :data="item" :key="index"></course-list-item>
</ul>
<div class="no-data" v-else>{{ $t('pages.learn.course.noCourseStr') }}</div>
</div>
</div>
</template>
<script>
import cAction from '@action'
import CourseLatestLive from './course/components/CourseLatestLive.vue'
import CourseListItem from './components/CourseListItem'
export default {
components: { CourseLatestLive, CourseListItem },
data() {
return {
filter: {
studyEarly: 'down',
selectTime: 'down'
},
find: [
{
name: this.$t('pages.learn.course.semeter'),
isShow: false,
selectIndex: 0,
key: 'semester_id',
arrItem: [] // 从后台请求
},
{
name: this.$t('pages.learn.course.courseType'),
isShow: false,
selectIndex: 0,
key: 'course_type',
arrItem: [
{
val: '-1',
name: this.$t('pages.learn.course.allLearn')
},
{
val: '1',
name: this.$t('pages.learn.course.mustLearn')
},
{
val: '2',
name: this.$t('pages.learn.course.changeLearn')
},
{
val: '3',
name: this.$t('pages.learn.course.repeatLearn')
}
]
}
],
homeList: [], // 从后台请求
param: {},
loading: false
}
},
mounted() {
cAction.Course.getLearnFind()
.then(data => {
this.find[0].arrItem = data
this.getAjaxList(true, this.$t('pages.learn.course.goCourseAllStr'))
})
.catch(e => {
this.$message.error(e.message)
})
.finally(() => {})
},
methods: {
goCourseAll() {
this.$router.push({
path: '/app/learn/course-all'
})
},
getAjaxList(bool, str) {
this.loading = true
cAction.Course.getCourseList(bool, this.param)
.then(json => {
this.homeList = json
})
.finally(() => {
this.loading = false
})
},
/**
* 分类选择 - 选中某一项
*/
selFindSelect(e) {
const _data = e.currentTarget.dataset
const index = _data.index
const json = this.find
const i = _data.i
json[i].selectIndex = index
json[i].isShow = false
if (_data.val === '-1') {
delete this.param[_data.key]
} else {
this.param[_data.key] = _data.val
}
/* 调用接口 */
this.getAjaxList(true, this.$t('pages.learn.course.noFitCourseStr'))
},
/**
* 筛选 - 最近学习顺序
*/
studyEarlyUpdate() {
const na = this.filter.studyEarly === 'up' ? 'down' : 'up'
this.filter.studyEarly = na
if (na === 'down') {
this.param.order_type = 'learn_time'
} else if (na === 'up') {
this.param.order_type = '-learn_time'
}
this.getAjaxList(true, this.$t('pages.learn.course.goCourseAllStr'))
},
/**
* 筛选 - 选修时间顺序
*/
selectTimeUpdate() {
const na = this.filter.selectTime === 'up' ? 'down' : 'up'
this.filter.selectTime = na
if (na === 'down') {
this.param.order_type = 'join_time'
} else if (na === 'up') {
this.param.order_type = '-join_time'
}
this.getAjaxList(true, this.$t('pages.learn.course.goCourseAllStr'))
}
}
}
</script>
<style lang="scss" scoped>
/* 列表 筛选 */
ul.tabs-list {
float: left;
width: 100%;
margin: 0 0 0.15rem 0;
padding: 0;
font-size: 0.16rem;
line-height: 1.5;
border-top: 1px solid #e8e8e8;
&:last-child {
margin-bottom: 0;
}
.tabs-hd {
display: inline-block;
color: #fff;
padding: 5px 0 9px;
margin-top: -5px;
width: 94px;
text-align: center;
background: url('~@/assets/images/type.png') no-repeat 0 0;
}
li {
float: left;
list-style: none;
padding: 0.1rem 0;
margin-right: 0.2rem;
.tab {
padding: 0 0.1rem;
cursor: pointer;
&.on {
background: #b49441;
color: #ffffff;
}
&:focus,
&:hover {
color: #b49441;
background: #eeeeee;
}
}
}
}
/* 筛选按钮 */
.switch-box {
margin: 0 0.3rem;
.icon {
margin-left: 0.1rem;
}
.rbtn {
float: right;
}
}
/* 课程列表 */
ul.course-list {
margin: 0;
padding: 0;
font-size: 0.18rem;
color: #313131;
}
.no-data {
text-align: center;
line-height: 2rem;
}
@media (max-width: 767px) {
.switch-box {
margin: 0;
}
}
</style>
......@@ -45,7 +45,7 @@ export default [
},
{
path: 'course-detail/:sid/:cid',
component: () => import('../pages/learn/courseDetail.vue'),
component: () => import('../pages/learn/course/detail.vue'),
props: true
},
{
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论