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

merge...

......@@ -4,7 +4,8 @@ module.exports = {
apiBaseURL: 'https://learn-api.ezijing.com',
others: {
url: '/app/learn/course',
loginUrl: 'https://login.ezijing.com/seg/login/index'
loginUrl: 'https://login.ezijing.com/seg/login/index',
messageBaseURL: 'https://test-microservices-message-service-api.ezijing.com'
},
webpack: {
externals: {
......
......@@ -5,7 +5,8 @@ module.exports = {
// apiBaseURL: '/',
others: {
url: '/app/learn/course',
loginUrl: 'https://login.ezijing.com/seg/login/index'
loginUrl: 'https://login.ezijing.com/seg/login/index',
messageBaseURL: ''
},
webpack: {
externals: {
......
......@@ -5,7 +5,8 @@ module.exports = {
// apiBaseURL: '/',
others: {
url: '/app/learn/course',
loginUrl: 'https://login2.ezijing.com/seg/login/index'
loginUrl: 'https://login2.ezijing.com/seg/login/index',
messageBaseURL: 'https://test-microservices-message-service-api.ezijing.com'
},
isUploadStatic: false,
webpack: {
......
差异被折叠。
......@@ -36,6 +36,7 @@
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/runtime-corejs3": "^7.11.2",
"@vue/composition-api": "^1.0.0-rc.5",
"acorn": "^7.1.1",
"ali-oss": "^6.11.2",
"autoprefixer": "^9.8.6",
......@@ -72,17 +73,21 @@
"webpack-merge": "^4.2.2"
},
"dependencies": {
"@ckeditor/ckeditor5-build-classic": "^24.0.0",
"@ckeditor/ckeditor5-build-classic": "^27.0.0",
"@ckeditor/ckeditor5-build-decoupled-document": "^27.0.0",
"@ckeditor/ckeditor5-vue2": "^1.0.5",
"axios": "^0.21.0",
"core-js": "^3.8.1",
"@ezijing/web-message-sdk": "^0.2.1",
"axios": "^0.21.1",
"core-js": "^3.9.0",
"cross-env": "^7.0.3",
"element-ui": "^2.14.1",
"echarts": "^5.0.2",
"element-ui": "^2.15.1",
"js-cookie": "^2.2.1",
"lodash": "^4.17.20",
"lodash": "^4.17.21",
"qrcode.vue": "^1.7.0",
"qs": "^6.9.4",
"qs": "^6.9.6",
"vue": "^2.6.12",
"vue-echarts": "^6.0.0-rc.3",
"vue-i18n": "^8.22.2",
"vue-loader": "^15.9.5",
"vue-meta": "^2.4.0",
......
......@@ -74,6 +74,7 @@ export default class CourseAction extends BaseACTION {
: (_type === 2 ? _vIn.$t('action.courseAction.changeLearn')
: (_type === 3 ? _vIn.$t('action.courseAction.repeatLearn') : ''))
const json = {
course_check_type: cur.course_check_type,
headerInfo: {
isStart: !!cur.selected, // 是否为开始学习按钮 或者 选课按钮
id: cur.course_id,
......@@ -183,7 +184,7 @@ export default class CourseAction extends BaseACTION {
}
})
}
json.tabs1ChapterList.course.push({
cur.curriculum.curriculum_essay && json.tabs1ChapterList.course.push({
title: _vIn.$t('action.courseAction.courseWork'),
isUp: true,
chapters: [],
......@@ -287,6 +288,7 @@ export default class CourseAction extends BaseACTION {
})
}
const json = {
raw: cur,
score: cur.course_score,
duration: tool.convertTime.durationToTimeString(cur.course_duration || 0),
progress: cur.course_progress,
......
......@@ -9,26 +9,7 @@ import { Grade } from '@api'
export default class GradeAction extends BaseACTION {
/* 获取我的学分信息 */
getCredit () {
return Grade.getCredit().then(res => {
const _data = res
const json = {
total: _data.total_credits,
myTotal: _data.my_total_credits,
myTotalStr: (_data.total_credits && ((_data.my_total_credits * 1.0 / _data.total_credits * 100).toFixed(1) + '%')) || '0%',
must: _data.required_credits,
myMust: _data.my_required_credits,
myMustStr: (_data.required_credits && ((_data.my_required_credits * 1.0 / _data.required_credits * 100).toFixed(1) + '%')) || '0%',
unmust: _data.optional_credits,
myUnmust: _data.my_optional_credits,
myUnmustStr: (_data.optional_credits && ((_data.my_optional_credits * 1.0 / _data.optional_credits * 100).toFixed(1) + '%')) || '0%',
list: _data.lists
}
// callback(json) // 可以不使用callback 因为使用then
return json
})
}
getCredit () { return Grade.getCredit() }
/* 提交重修 */
rebuildSubmit (obj) { return Grade.submitRebuild(obj).then(res => 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}`)
}
/**
* 课程考核
* @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>
......@@ -13,8 +13,13 @@
<script>
import CKEditor from '@ckeditor/ckeditor5-vue2'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
import '@ckeditor/ckeditor5-build-classic/build/translations/zh-cn'
import DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document'
import '@ckeditor/ckeditor5-build-decoupled-document/build/translations/zh-cn'
// import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
// import '@ckeditor/ckeditor5-build-classic/build/translations/zh-cn'
import MyUploadAdapter from './MyUploadAdapter'
export default {
......@@ -27,14 +32,20 @@ export default {
},
data() {
return {
editor: ClassicEditor,
editor: DecoupledEditor,
editorConfig: {
language: this.$i18n.locale.toLocaleLowerCase()
language: this.$i18n.locale.toLocaleLowerCase(),
fontFamily: { options: ['default', '宋体', 'Times New Roman'] },
fontSize: { options: ['12px', '14px', '16px', '18px', '20px', '24px', '36px'] }
}
}
},
methods: {
onEditorReady(editor) {
editor.ui
.getEditableElement()
.parentElement.insertBefore(editor.ui.view.toolbar.element, editor.ui.getEditableElement())
const FileRepository = editor.plugins.get('FileRepository')
// 自定义上传图片插件
FileRepository.createUploadAdapter = loader => {
......@@ -43,6 +54,22 @@ export default {
},
onEditorInput(value) {
this.$emit('input', value)
this.dispatch('ElFormItem', 'el.form.change', value)
},
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root
var name = parent.$options.componentName
while (parent && (!name || name !== componentName)) {
parent = parent.$parent
if (parent) {
name = parent.$options.componentName
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params))
}
}
}
}
......@@ -53,6 +80,8 @@ export default {
min-height: 300px;
max-height: 500px;
word-break: break-word;
background: var(--ck-color-base-background);
border: 1px solid var(--ck-color-toolbar-border) !important;
}
.ck-sticky-panel__content {
position: unset !important;
......
......@@ -5,6 +5,7 @@
<div class="text-title" @click="setStatus">{{ $t('components.learnSysLayout.navigation.title') }}</div>
</div>
<div class="nav-right">
<div class="nav-message"></div>
<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>
......@@ -17,7 +18,7 @@
<script>
import LanguageSwitch from '@/components/languageSwitch/index.vue'
import cAction from '../../action'
// import Message from '@ezijing/web-message-sdk'
export default {
components: { LanguageSwitch },
data() {
......@@ -33,7 +34,8 @@ export default {
.catch(e => {
this.$message.error(e.message)
})
.finally(() => {})
// 新版通知
// Message({ container: '.nav-message', source: 'SOFIA_WEB', baseURL: webConf.others.messageBaseURL })
},
methods: {
goNotify() {
......
<template>
<div>
<div ref="box" :class="classes">
<slot></slot>
</div>
<div class="block-control" @click="toggle" v-if="hasMore">
<template v-if="isOpen"> <i class="el-icon-caret-top"></i><span>收起</span> </template>
<template v-else> <i class="el-icon-caret-bottom"></i><span>展开</span> </template>
</div>
</div>
</template>
<script>
export default {
data() {
return {
max: 300,
hasMore: false,
isOpen: false
}
},
computed: {
classes() {
return {
'has-more': this.hasMore && !this.isOpen
}
}
},
methods: {
init() {
const height = this.$refs.box.offsetHeight
this.hasMore = height > this.max
},
toggle() {
this.isOpen = !this.isOpen
}
},
mounted() {
this.init()
}
}
</script>
<style lang="scss">
.block-control {
height: 20px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
padding-top: 5px;
color: #d3dce6;
cursor: pointer;
border-top: 1px solid #eaeefb;
i {
font-size: 16px;
}
span {
display: none;
padding-left: 5px;
font-size: 12px;
}
&:hover {
color: #1f2f3d;
span {
display: block;
}
}
}
.has-more {
max-height: 300px;
overflow: hidden;
}
</style>
<template>
<editor v-bind="options" />
</template>
<script>
import Editor from '@tinymce/tinymce-vue'
export default {
components: {
editor: Editor
},
data() {
return {
options: {
init: {
min_height: 300,
max_height: 500,
menubar: false,
language: this.$i18n.locale.replace('-', '_'),
branding: false,
plugins: 'wordcount table autoresize charmap code fullscreen hr lists codesample',
toolbar:
'undo redo | removeformat formats | fontselect fontsizeselect lineheight bold italic underline strikethrough forecolor backcolor | link image | align indent outdent numlist bullist | charmap table blockquote hr codesample fullscreen',
fontsize_formats: '12px 14px 15px 16px 17px 18px 20px 24px',
lineheight_formats: '0.5 1 1.2 1.5 2'
}
}
}
}
}
</script>
......@@ -8,9 +8,7 @@ const httpRequest = new BaseAPI(webConf)
* @param {string} courseId 课程ID
*/
export function getCourse(semesterId, courseId) {
return httpRequest.get(
`/api/lms/v2/education/courses/${semesterId}/${courseId}`
)
return httpRequest.get(`/api/lms/v2/education/courses/${semesterId}/${courseId}`)
}
/**
......@@ -44,10 +42,7 @@ export function getChapterVideoAliyun(vid) {
* @param {Object} params
*/
export function getChapterVideoProgress(semesterId, resourseId, params) {
return httpRequest.get(
`/api/lms/v2/education/video/${semesterId}/${resourseId}/device`,
params
)
return httpRequest.get(`/api/lms/v2/education/video/${semesterId}/${resourseId}/device`, params)
}
/**
......@@ -65,9 +60,7 @@ export function updateChapterVideoProgress(params) {
* @param {string} resourseId 章节的资源ID
*/
export function getChapterHomework(semesterId, courseId, resourseId) {
return httpRequest.get(
`/api/lms/v2/education/homeworks/${semesterId}/${courseId}/${resourseId}`
)
return httpRequest.get(`/api/lms/v2/education/homeworks/${semesterId}/${courseId}/${resourseId}`)
}
/**
......@@ -77,9 +70,7 @@ export function getChapterHomework(semesterId, courseId, resourseId) {
* @param {string} chapterId 章节ID
*/
export function getChapterHomeworkDeadline(semesterId, courseId, chapterId) {
return httpRequest.get(
`/api/lms/v2/education/homeworks/${semesterId}/${courseId}/${chapterId}/deadline`
)
return httpRequest.get(`/api/lms/v2/education/homeworks/${semesterId}/${courseId}/${chapterId}/deadline`)
}
/**
......@@ -106,9 +97,7 @@ export function uploadFile(data) {
* @param {string} courseId 课程ID
*/
export function getCourseWork(semesterId, courseId) {
return httpRequest.get(
`/api/lms/v2/education/courses/${semesterId}/${courseId}/essay`
)
return httpRequest.get(`/api/lms/v2/education/courses/${semesterId}/${courseId}/essay`)
}
/**
......@@ -117,11 +106,9 @@ export function getCourseWork(semesterId, courseId) {
* @param {string} courseId 课程ID
*/
export function updateCourseWork(semesterId, courseId, data) {
return httpRequest.post(
`/api/lms/v2/education/courses/${semesterId}/${courseId}/essay`,
data,
{ headers: { 'Content-Type': 'multipart/form-data' } }
)
return httpRequest.post(`/api/lms/v2/education/courses/${semesterId}/${courseId}/essay`, data, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
/**
......@@ -130,9 +117,7 @@ export function updateCourseWork(semesterId, courseId, data) {
* @param {string} courseId 课程ID
*/
export function getCourseExam(semesterId, courseId) {
return httpRequest.get(
`/api/lms/v2/education/${semesterId}/${courseId}/examination`
)
return httpRequest.get(`/api/lms/v2/education/${semesterId}/${courseId}/examination`)
}
/**
......@@ -142,9 +127,7 @@ export function getCourseExam(semesterId, courseId) {
* @param {string} examId 试题ID
*/
export function getCourseExamStatus(semesterId, courseId, examId) {
return httpRequest.get(
`/api/lms/v2/education/${semesterId}/${courseId}/examination/${examId}/status`
)
return httpRequest.get(`/api/lms/v2/education/${semesterId}/${courseId}/examination/${examId}/status`)
}
/**
......@@ -154,11 +137,9 @@ export function getCourseExamStatus(semesterId, courseId, examId) {
* @param {string} examId 试题ID
*/
export function submitCourseExam(semesterId, courseId, examId, data) {
return httpRequest.post(
`/api/lms/v2/education/${semesterId}/${courseId}/examination/${examId}/sheet`,
data,
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
)
return httpRequest.post(`/api/lms/v2/education/${semesterId}/${courseId}/examination/${examId}/sheet`, data, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}
/**
......@@ -168,8 +149,32 @@ export function submitCourseExam(semesterId, courseId, examId, data) {
* @param {string} examId 试题ID
*/
export function getCourseExamResult(semesterId, courseId, examId, params) {
return httpRequest.get(
`/api/lms/v2/education/${semesterId}/${courseId}/examination/${examId}/sheet`,
params
)
return httpRequest.get(`/api/lms/v2/education/${semesterId}/${courseId}/examination/${examId}/sheet`, params)
}
/**
* 获取课后评论作业
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
* @param {string} chapterId 章节ID
*/
export function getChapterHomeworkOther(semesterId, courseId, chapterId, params) {
return httpRequest.get(`/api/lms/v2/education/homeworks/other/${semesterId}/${courseId}/${chapterId}`, params)
}
/**
* 获取课后课业回答
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
* @param {string} chapterId 章节ID
*/
export function getChapterHomeworkOtherAll(semesterId, courseId, chapterId, params) {
return httpRequest.get(`/api/lms/v2/education/homeworks/otherall/${semesterId}/${courseId}/${chapterId}`, params)
}
/**
* 提交作业互评
*/
export function addChapterHomeworkComment(data) {
return httpRequest.post('/api/lms/v2/education/homeworks/comment', data)
}
......@@ -60,15 +60,15 @@ export default {
if (data.type === 1) {
return
}
// zoom直播
if (data.type === 8) {
const live = data.live
// 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>
<div class="upload">
<el-upload action :disabled="disabled" :show-file-list="false" :http-request="httpRequest">
<el-upload action :disabled="disabled" :show-file-list="false" :http-request="httpRequest" v-bind="$attrs">
<slot></slot>
<el-button type="text" icon="el-icon-upload">{{ $t('viewerWork.clickUpload') }}</el-button>
<template v-slot:tip>
......
<template>
<div class="chatper-work-answer-item">
<div class="chapter-work-answer-item-hd">{{ nickname }}:</div>
<div class="chapter-work-answer-item-bd">
<div class="item" v-for="item in workList" :key="item.question_id">
<show-more>
<div v-html="decode(item.descreption)"></div>
<a :href="item.file_url" target="_blank" class="file" v-if="item.file_url">
<i class="el-icon-document"></i>
{{ $t('viewerWork.downloadName') }}
</a>
</show-more>
</div>
</div>
<slot />
</div>
</template>
<script>
import Base64 from 'Base64'
import showMore from '@/components/showMore'
export default {
props: {
index: { type: Number, default: 0 },
data: { type: Object, default: () => ({}) }
},
components: { showMore },
data() {
return {}
},
computed: {
// 作业列表
workList() {
return this.data.work_contents ? JSON.parse(this.data.work_contents) : []
},
nickname() {
const name = this.data.personal_name
return name ? name + this.$t('viewerWork.student') : this.$t('viewerWork.student') + (this.index + 1)
}
},
methods: {
decode(value) {
return value ? Base64.decode(value) : ''
}
}
}
</script>
<style lang="scss">
.chatper-work-answer-item {
// padding-bottom: 10px;
// border-bottom: 1px solid #c9c9c97a;
margin-bottom: 20px;
.item {
padding: 10px;
border-radius: 8px;
background-color: #fff;
}
.item + .item {
margin-top: 10px;
}
.file {
display: inline-block;
margin-bottom: 10px;
text-decoration: none;
color: #333;
&:hover {
color: #b49441;
}
}
}
.chapter-work-answer-item-hd {
margin-bottom: 10px;
}
.chapter-work-answer-item-bd {
img {
max-width: 100%;
}
}
</style>
<template>
<container :title="$t('viewerWork.submittedAssignments')">
<chapter-work-answer-item v-for="(item, index) in list" :data="item" :key="item.id" :index="index" />
<el-pagination
background
layout="prev, pager, next"
:current-page.sync="page.currentPage"
:page-size="page.limit"
:total="page.total"
:hide-on-single-page="true"
@current-change="handleCurrentChange"
style="margin: 0 auto; padding: 40px 0; text-align: center"
></el-pagination>
</container>
</template>
<script>
// api
import * as api from '../../api'
// componets
import Container from '../common/container.vue'
import chapterWorkAnswerItem from './chapterWorkAnswerItem.vue'
export default {
components: { Container, chapterWorkAnswerItem },
data() {
return {
list: [],
page: {
offset: 0,
total: 0,
currentPage: 1,
limit: 10
}
}
},
computed: {
// 学期ID
sid() {
return this.$route.params.sid
},
// 课程ID
cid() {
return this.$route.params.cid
},
// 当前页面的ID
pid() {
return this.$route.params.id
}
},
methods: {
getList() {
const params = { offset: this.page.offset, limit: this.page.limit }
api.getChapterHomeworkOtherAll(this.sid, this.cid, this.pid, params).then(response => {
this.list = response.data
this.page.total = parseInt(response.count)
})
},
handleCurrentChange() {
this.page.offset = (this.page.currentPage - 1) * this.page.limit
this.getList()
}
},
beforeMount() {
this.getList()
}
}
</script>
<style lang="scss" scoped>
.more {
text-align: center;
padding-top: 40px;
}
</style>
<template>
<container :title="$t('viewerWork.commentTitle')">
<template v-for="(item, index) in list">
<el-form
label-position="top"
:disabled="disabled"
:model="item"
:rules="rules"
:hide-required-asterisk="true"
:key="item.id"
ref="form"
>
<el-form-item prop="comment">
<chapter-work-comment-item
:data="item"
:index="index"
:endDate="endDate"
:disabled="disabled"
:key="item.id"
/> </el-form-item
></el-form>
</template>
<div class="button">
<el-button type="primary" :disabled="disabled" @click="handleSubmit">{{
$t('viewerWork.commentSubmitText')
}}</el-button>
<p v-if="status === 1">注:重新提交后不可修改,请检查好作业内容再做提交。</p>
<p v-else>{{ $t('viewerWork.commentSubmitTips', { date: endDate }) }}</p>
</div>
<div class="more">
<router-link :to="`${$route.path}/answers`" target="_blank">
<el-button round>{{ $t('viewerWork.answerMoreButtonText') }}</el-button>
</router-link>
</div>
</container>
</template>
<script>
// api
import * as api from '../../api'
// componets
import Container from '../common/container.vue'
import ChapterWorkCommentItem from './chapterWorkCommentItem.vue'
export default {
components: { Container, ChapterWorkCommentItem },
props: { endDate: { type: String }, status: { type: Number } },
data() {
return {
list: [],
rules: {
comment: [{ required: true, message: '请输入评价内容', trigger: 'change' }]
},
popoverVisible: false
}
},
computed: {
// 学期ID
sid() {
return this.$route.params.sid
},
// 课程ID
cid() {
return this.$route.params.cid
},
// 当前页面的ID
pid() {
return this.$route.params.id
},
isSubmitTime() {
// 大于开始时间,小于结束时间
const endTime = +new Date(this.endDate)
const currentTime = new Date().getTime()
return currentTime < endTime
},
// 是否禁用
disabled() {
// -1未处理 0已处理 1驳回
if (this.status === 1) {
return false
}
return !this.isSubmitTime
}
},
methods: {
getList() {
api.getChapterHomeworkOther(this.sid, this.cid, this.pid).then(response => {
this.list = response
})
},
// 提交校验
checkSubmit() {
let result = true
for (let i = 0; i < this.list.length; i++) {
this.$refs.form[i].validate(valid => {
if (!valid) {
result = valid
return result
}
})
}
return result
},
// 提交评价
handleSubmit() {
// 校验
if (!this.checkSubmit()) {
this.messageInstance && this.messageInstance.close()
this.messageInstance = this.$message.error(this.$t('viewerWork.examSubmitError'))
return
}
// 驳回提交
if (this.status === 1) {
this.$confirm('本次作业提交后不可修改,您确认提交吗?', '提示', {
confirmButtonText: '确定提交',
type: 'warning'
}).then(this.handleSubmitReqeuest)
} else {
// 正常提交
this.handleSubmitReqeuest()
}
},
handleSubmitReqeuest() {
const params = this.list.map(({ id, comment }) => {
return { id, comment }
})
api
.addChapterHomeworkComment(params)
.then(response => {
this.$message({ type: 'success', message: response.message })
this.getList()
this.$emit('update')
})
.catch(error => {
this.$message({ type: 'error', message: error.message })
})
}
},
beforeMount() {
this.getList()
}
}
</script>
<style lang="scss" scoped>
.more {
text-align: center;
padding-top: 40px;
}
.button {
display: flex;
align-items: center;
p {
padding-left: 20px;
}
}
::v-deep .el-form-item__content {
line-height: 24px;
}
</style>
<template>
<chapter-work-answer-item :data="data" :key="data.id" v-bind="$attrs">
<h3 class="comment-title">{{ $t('viewerWork.commentMyTitle') }}</h3>
<v-editor v-model="data.comment" :disabled="disabled"></v-editor>
</chapter-work-answer-item>
</template>
<script>
// componets
import ChapterWorkAnswerItem from './chapterWorkAnswerItem.vue'
import VEditor from '@/components/ckeditor'
export default {
props: { endDate: { type: String }, data: { type: Object, default: () => ({}) }, disabled: { type: Boolean } },
components: { ChapterWorkAnswerItem, VEditor },
data() {
return {}
}
}
</script>
<style lang="scss">
.comment-title {
margin: 10px 0;
font-size: 14px;
font-weight: normal;
// padding: 10px 0;
}
</style>
<template>
<div></div>
</template>
<script>
export default {
data() {
return {}
}
}
</script>
......@@ -221,6 +221,9 @@ p {
padding: 20px 0 30px;
font-size: 14px;
}
::v-deep .el-step__main {
overflow: hidden;
}
::v-deep .el-form-item__label {
font-weight: bold;
line-height: 24px;
......
......@@ -28,7 +28,7 @@
<!-- 简答题 -->
<template v-if="type === 3">
<v-editor v-model="currentValue.user_answer" :disabled="disabled"></v-editor>
<v-upload :disabled="disabled" v-model="currentValue.attachments"
<v-upload :disabled="disabled" v-model="currentValue.attachments" accept=".pdf" v-if="hasUpload"
>{{ $t('viewerWork.workUrl.label') }}</v-upload
>
</template>
......@@ -98,7 +98,8 @@ export default {
},
// 是否禁用,提交过的是禁用状态
disabled: { type: Boolean, default: false },
showResult: { type: Boolean, default: true }
showResult: { type: Boolean, default: true },
hasUpload: { type: Boolean, default: true }
},
data() {
return {
......
......@@ -104,17 +104,21 @@ export default {
// 章节列表
chapters() {
const chapters = this.detail.chapters || []
if (!chapters.length) {
return []
}
// if (!chapters.length) {
// return []
// }
const customeChapter = {
name: this.$t('viewerMain.workData'),
children: [
{ name: this.$t('viewerMain.courseWork'), id: 'course_work', type: 99 },
// { 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.detail.curriculum && this.detail.curriculum.curriculum_essay) {
customeChapter.children.unshift({ name: this.$t('viewerMain.courseWork'), id: 'course_work', type: 99 })
}
// 课程考试
if (this.detail.course_examination) {
customeChapter.children.push({
......@@ -235,13 +239,13 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
padding: 40px 0;
padding: 20px 0 40px;
// text-align: center;
}
.course-viewer-content-hd__title {
position: relative;
display: inline-block;
margin: 0 0 0 20px;
margin: 0;
padding: 0 0 5px;
font-size: 20px;
border-bottom: 3px solid #707070;
......
......@@ -41,7 +41,7 @@ export default {
anotherSubmitText: 'Take another test',
correctRate: 'Correct rate',
revisedButtonText: 'Corrected',
deadline: 'Please submit before the deadline {date}',
deadline: 'Deadline: {date} Modifications can be made any time before deadline.',
rejectTips: 'The assignment is rejected, click the "re-edit" button to re-edit the content and submit again',
rejectTime: 'Rejection time',
rejectContent: 'Rejection instructions',
......@@ -59,17 +59,25 @@ export default {
workStep3: 'Submit before the deadline',
workTheme: { label: 'Theme', placeholder: 'Theme', rule: 'Please enter Theme' },
workDescription: { label: 'Text', placeholder: 'Text', rule: 'Please enter the text' },
workUrl: { label: 'Please upload the corresponding file attachment', rule: 'Please upload attachment' },
workUrl: { label: 'Upload Attachment', rule: 'Please upload attachment' },
studentAnswer: 'Student answer',
correctAnswer: 'Correct answer',
analyze: 'Parsing',
viewResolution: 'View resolution',
singleChoice: 'Single Choice',
multipleChoice: 'Multiple Choice',
clickUpload: 'Click upload',
clickUpload: 'Click here to upload',
delete: 'Delete',
download: 'Download',
downloadName: 'Download attachment'
downloadName: 'Download attachment',
commentTips: 'Please continue to finish comment assignments after the deadline.',
commentTitle: 'Comment on Other Student Assignments',
commentMyTitle: 'My Comment',
commentSubmitText: 'Submit Comment ',
commentSubmitTips: 'Deadline: {date} Modifications can be made any time before deadline.',
answerMoreButtonText: 'Check other students assignments',
submittedAssignments: 'Submitted Assignments',
student: 'student'
},
viewerExam: {},
viewerRead: {
......
......@@ -34,14 +34,14 @@ export default {
examSubmitButtonTips: '提交之后就不能修改了哦',
examStartButtonText: '开始考试',
examEndButtonText: '考试结束',
examSubmitError: '还有题目未做,不能提交',
examSubmitError: '请检查输入内容是否正确',
examSubmitSuccess: '提交成功',
prevPapers: '上一套试卷',
nextPapers: '下一套试卷',
anotherSubmitText: '再考一次',
correctRate: '正确率',
revisedButtonText: '已批改',
deadline: '请于截止日期 {date} 前提交',
deadline: '提交作业截止时间为: {date} ,截止时间前均可修改。',
rejectTips: '作业被驳回,点击“重新编辑”按钮重新编辑内容再次提交',
rejectTime: '驳回时间',
rejectContent: '驳回说明',
......@@ -68,7 +68,15 @@ export default {
clickUpload: '点击上传',
delete: '删除',
download: '下载',
downloadName: '附件下载'
downloadName: '附件下载',
commentTips: '作业提交截止后,请继续完成评论作业。',
commentTitle: '评论同学作业',
commentSubmitTips: '提交评论截止时间为:{date},截止时间前均可修改。',
commentMyTitle: '我的评论',
commentSubmitText: '提交评价',
answerMoreButtonText: '去看其他同学作业',
submittedAssignments: '同学提交的作业',
student: '同学'
},
viewerRead: {
download: '下载',
......
......@@ -9,5 +9,9 @@ export default [
component: () => import('./components/layout.vue')
}
]
},
{
path: '/viewer/:sid/:cid/:id/answers',
component: () => import('./components/work/chapterWorkAnswerList.vue')
}
]
差异被折叠。
<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="detail">
<div class="detail-rich">
<div class="h1">一、最终成绩计算</div>
<div class="p">
<template v-for="(item, index) in checkList">
<span :key="index">{{ index ? '+' : '' }}{{ item.name }}得分*{{ item.percent }}%</span>
</template>
= 该门课程总得分,满分100分,低于{{ data.course_check_pass_score }}分为不及格,需重修此门课程。
</div>
<v-chart class="chart" :option="option" :autoresize="true" />
</div>
<div class="detail-rich">
<div class="h1">二、具体细则</div>
<template v-for="(item, index) in checkList">
<div :key="index">
<div class="h2">{{ `${item.name} 总分${item.score}分(占科目总成绩的${item.percent}%)` }}</div>
<template v-if="item.key === 'video'">
<div class="p">课程视频观看完成度:以后台数据统计为准,全部看完视频满分100分。</div>
<div class="em">
注:视频观看考核的是实际播放时长,不是进度条的显示状态,进度条满格并不一定表示观看完这个视频。以下方每日学习时长为准。
</div>
</template>
<template v-if="item.key === 'week_report'">
<div class="p">报告作业总分{{ item.score }}分,报告得分以老师批改的分数为准。</div>
<div class="p">课程反馈建设性问题老师可酌情加分,每周的课程提问加分以老师批改的分数为准。</div>
</template>
<div class="p" v-if="item.key === 'week_test'">
本课程所有测试的平均正确率*{{ item.score }}*占总成绩{{ item.percent }}%=此项得分
</div>
<div class="p" v-if="item.key === 'essay'">结业大作业满分为{{ item.score }}分,以老师批改的分数为准。</div>
<div class="p" v-if="item.key === 'attendance'">
互动课出勤满分{{ item.score }}分,以老师每次导出的参会记录进行打分。
</div>
<div class="p" v-if="item.key === 'question'">
课程提问满分{{ item.score }}分, 老师根据提问情况酌情给分。
</div>
</div>
</template>
</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: { 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 scoped>
.chart {
height: 300px;
}
</style>
<template>
<div class="detail">
<div class="detail-rich">
<div class="h1">一、最终成绩计算</div>
<div class="p">美方课程最终成绩请参考每学期初教务邮箱老师发到大家邮箱中的课程考核大纲进行计算。</div>
<v-chart class="chart" :option="option" :autoresize="true" />
</div>
<div class="detail-rich">
<div class="h1">二、具体细则</div>
<template v-for="(item, index) in checkList">
<div :key="index">
<div class="h2">{{ `${item.name} 总分${item.score}分(占科目总成绩的${item.percent}%)` }}</div>
<template v-if="item.key === 'video'">
<div class="p">
课程视频观看完成度:以后台数据统计为准,全部看完视频满分100分。(实际视频观看分数以课程考核大纲为准)
</div>
<div class="em">
注:视频观看考核的是实际播放时长,不是进度条的显示状态,进度条满格并不一定表示观看完这个视频。以下方每日学习时长为准。
</div>
</template>
<template v-if="item.key === 'week_report'">
<div class="p">报告作业总分{{ item.score }}分,报告得分以老师批改的分数为准。</div>
<div class="p">课程反馈建设性问题老师可酌情加分,每周的课程提问加分以老师批改的分数为准。</div>
</template>
<div class="p" v-if="item.key === 'week_test'">
本课程所有测试的平均正确率*{{ item.score }}*占总成绩{{ item.percent }}%=此项得分。
(实际章节测试占比以课程考核大纲为准)。
</div>
<div class="p" v-if="item.key === 'essay'">结业大作业满分为{{ item.score }}分,以老师批改的分数为准。</div>
<div class="p" v-if="item.key === 'attendance'">
互动课出勤满分{{ item.score }}分,以老师每次导出的参会记录进行打分。
</div>
<div class="p" v-if="item.key === 'question'">
课程提问满分{{ item.score }}分, 老师根据提问情况酌情给分。
</div>
</div>
</template>
</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: { 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 scoped>
.chart {
height: 300px;
}
</style>
<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 {
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;
}
}
}
}
.no-data {
text-align: center;
line-height: 2rem;
}
@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>
<div class="detail">
<div class="subtitle">
<span>{{ $t('pages.learn.courseDetail.Courseassessmentstandard') }}</span>
</div>
<div class="h1">一、最终成绩计算</div>
<div class="p">
课程表现得分*30%+每章试题得分*30%+结业大作业得分*40%=该门课程总得分,满分100分,低于80分为不及格,需重修此门课程。84分以上方可申请学位。
</div>
<img
class="b1"
src="https://zws-imgs-pub.oss-cn-beijing.aliyuncs.com/static/build/www/course-check.png"
mode="aspectFill"
/>
<div class="h1">二、具体细则</div>
<div class="h2">课程表现:总分100分(占科目总成绩的30%)</div>
<div class="p">1、每个视频观看完成度50分:以后台数据统计为准,全部看完视频满分50分,其它酌情给分。</div>
<div class="em">
注:视频观看考核的是实际播放时长,不是进度条的显示状态,进度条满格并不一定表示观看完这个视频。以下方每日学习时长为准。
</div>
<div class="p">
2、课程反馈建设性问题及课程完成情况50分:反馈问题以教务老师统计为准,课程完成情况以后台数据为准,是否按时完成视频观看、测试、作业提交,满分50分,酌情给分。
</div>
<div class="h2">每章试题:总分100分(占科目总成绩的30%)</div>
<div class="p">
本课程所有试题的平均正确率*100分*占总成绩30%=此项得分。(比如正确度为80%,则此项得分:80%*100*30%=24分)
</div>
<div class="h2">结业大作业:总分100分(占科目总成绩的40%)</div>
<div class="p">结业大作业满分为100分,以助教老师给分为准。</div>
</div>
<!-- <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%);
}
}
}
.course-assess .detail .h1 {
font-size: 0.16rem;
font-weight: 700;
color: #313131;
line-height: 0.38rem;
}
.course-assess .detail .h2 {
font-size: 0.14rem;
font-weight: 700;
color: #313131;
line-height: 0.3rem;
}
.course-assess .detail .p {
font-size: 0.14rem;
color: #313131;
line-height: 1.5;
text-align: justify;
}
.course-assess .detail .em {
font-size: 0.12rem;
color: #b49441;
line-height: 1.5;
text-align: justify;
}
</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>
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论