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

merge...

...@@ -4,7 +4,8 @@ module.exports = { ...@@ -4,7 +4,8 @@ module.exports = {
apiBaseURL: 'https://learn-api.ezijing.com', apiBaseURL: 'https://learn-api.ezijing.com',
others: { others: {
url: '/app/learn/course', url: '/app/learn/course',
loginUrl: 'https://login.ezijing.com/ciis/login/index' loginUrl: 'https://login.ezijing.com/ciis/login/index',
messageBaseURL: 'https://test-microservices-message-service-api.ezijing.com'
}, },
webpack: { webpack: {
externals: { externals: {
......
...@@ -4,7 +4,8 @@ module.exports = { ...@@ -4,7 +4,8 @@ module.exports = {
apiBaseURL: 'https://learn-api.ezijing.com', apiBaseURL: 'https://learn-api.ezijing.com',
others: { others: {
url: '/app/learn/course', url: '/app/learn/course',
loginUrl: 'https://login.ezijing.com/ciis/login/index' loginUrl: 'https://login.ezijing.com/ciis/login/index',
messageBaseURL: ''
}, },
webpack: { webpack: {
externals: { externals: {
......
...@@ -4,7 +4,8 @@ module.exports = { ...@@ -4,7 +4,8 @@ module.exports = {
apiBaseURL: 'https://learn-api2.ezijing.com', apiBaseURL: 'https://learn-api2.ezijing.com',
others: { others: {
url: '/app/learn/course', url: '/app/learn/course',
loginUrl: 'https://login2.ezijing.com/ciis/login/index' loginUrl: 'https://login2.ezijing.com/ciis/login/index',
messageBaseURL: 'https://test-microservices-message-service-api.ezijing.com'
}, },
isUploadStatic: false, isUploadStatic: false,
webpack: { webpack: {
......
...@@ -12,15 +12,18 @@ ...@@ -12,15 +12,18 @@
"@ckeditor/ckeditor5-build-classic": "^27.0.0", "@ckeditor/ckeditor5-build-classic": "^27.0.0",
"@ckeditor/ckeditor5-build-decoupled-document": "^27.0.0", "@ckeditor/ckeditor5-build-decoupled-document": "^27.0.0",
"@ckeditor/ckeditor5-vue2": "^1.0.5", "@ckeditor/ckeditor5-vue2": "^1.0.5",
"axios": "^0.21.0", "@ezijing/web-message-sdk": "^0.2.1",
"core-js": "^3.8.1", "axios": "^0.21.1",
"core-js": "^3.9.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"element-ui": "^2.14.1", "echarts": "^5.0.2",
"element-ui": "^2.15.1",
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"lodash": "^4.17.20", "lodash": "^4.17.21",
"qrcode.vue": "^1.7.0", "qrcode.vue": "^1.7.0",
"qs": "^6.9.4", "qs": "^6.9.6",
"vue": "^2.6.12", "vue": "^2.6.12",
"vue-echarts": "^6.0.0-rc.3",
"vue-i18n": "^8.22.2", "vue-i18n": "^8.22.2",
"vue-loader": "^15.9.5", "vue-loader": "^15.9.5",
"vue-meta": "^2.4.0", "vue-meta": "^2.4.0",
...@@ -36,6 +39,7 @@ ...@@ -36,6 +39,7 @@
"@babel/plugin-transform-runtime": "^7.11.5", "@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5", "@babel/preset-env": "^7.11.5",
"@babel/runtime-corejs3": "^7.11.2", "@babel/runtime-corejs3": "^7.11.2",
"@vue/composition-api": "^1.0.0-rc.5",
"acorn": "^7.1.1", "acorn": "^7.1.1",
"ali-oss": "^6.11.2", "ali-oss": "^6.11.2",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
...@@ -15296,7 +15300,8 @@ ...@@ -15296,7 +15300,8 @@
} }
}, },
"@ezijing/web-message-sdk": { "@ezijing/web-message-sdk": {
"version": "https://registry.npmjs.org/@ezijing/web-message-sdk/-/web-message-sdk-0.2.1.tgz", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@ezijing/web-message-sdk/-/web-message-sdk-0.2.1.tgz",
"integrity": "sha512-pkjX2jL3HLbpvwUo3cNWZnV7Sfl5EeH9O/vpL6PEUpV4f4m+fHVGoV0tyuPzrn8nuhEoMi0uOUCHkrsJqW6Ljw==", "integrity": "sha512-pkjX2jL3HLbpvwUo3cNWZnV7Sfl5EeH9O/vpL6PEUpV4f4m+fHVGoV0tyuPzrn8nuhEoMi0uOUCHkrsJqW6Ljw==",
"requires": { "requires": {
"axios": "^0.21.1", "axios": "^0.21.1",
...@@ -24582,7 +24587,8 @@ ...@@ -24582,7 +24587,8 @@
"requires": {} "requires": {}
}, },
"vue-echarts": { "vue-echarts": {
"version": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.0.0-rc.3.tgz", "version": "6.0.0-rc.3",
"resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.0.0-rc.3.tgz",
"integrity": "sha512-B7xfwpHaOeM9+cjallrK3AW893oaeqT9Sjx7tHgwoyGdfPTq6cwDc2Hcdmjgrs/KEp+ZZXMIajXky30Wu4/TDw==", "integrity": "sha512-B7xfwpHaOeM9+cjallrK3AW893oaeqT9Sjx7tHgwoyGdfPTq6cwDc2Hcdmjgrs/KEp+ZZXMIajXky30Wu4/TDw==",
"requires": { "requires": {
"resize-detector": "^0.3.0", "resize-detector": "^0.3.0",
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
"@babel/plugin-transform-runtime": "^7.11.5", "@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5", "@babel/preset-env": "^7.11.5",
"@babel/runtime-corejs3": "^7.11.2", "@babel/runtime-corejs3": "^7.11.2",
"@vue/composition-api": "^1.0.0-rc.5",
"acorn": "^7.1.1", "acorn": "^7.1.1",
"ali-oss": "^6.11.2", "ali-oss": "^6.11.2",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
...@@ -75,15 +76,18 @@ ...@@ -75,15 +76,18 @@
"@ckeditor/ckeditor5-build-classic": "^27.0.0", "@ckeditor/ckeditor5-build-classic": "^27.0.0",
"@ckeditor/ckeditor5-build-decoupled-document": "^27.0.0", "@ckeditor/ckeditor5-build-decoupled-document": "^27.0.0",
"@ckeditor/ckeditor5-vue2": "^1.0.5", "@ckeditor/ckeditor5-vue2": "^1.0.5",
"axios": "^0.21.0", "@ezijing/web-message-sdk": "^0.2.1",
"core-js": "^3.8.1", "axios": "^0.21.1",
"core-js": "^3.9.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"element-ui": "^2.14.1", "echarts": "^5.0.2",
"element-ui": "^2.15.1",
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"lodash": "^4.17.20", "lodash": "^4.17.21",
"qrcode.vue": "^1.7.0", "qrcode.vue": "^1.7.0",
"qs": "^6.9.4", "qs": "^6.9.6",
"vue": "^2.6.12", "vue": "^2.6.12",
"vue-echarts": "^6.0.0-rc.3",
"vue-i18n": "^8.22.2", "vue-i18n": "^8.22.2",
"vue-loader": "^15.9.5", "vue-loader": "^15.9.5",
"vue-meta": "^2.4.0", "vue-meta": "^2.4.0",
......
...@@ -74,6 +74,7 @@ export default class CourseAction extends BaseACTION { ...@@ -74,6 +74,7 @@ export default class CourseAction extends BaseACTION {
: (_type === 2 ? _vIn.$t('action.courseAction.changeLearn') : (_type === 2 ? _vIn.$t('action.courseAction.changeLearn')
: (_type === 3 ? _vIn.$t('action.courseAction.repeatLearn') : '')) : (_type === 3 ? _vIn.$t('action.courseAction.repeatLearn') : ''))
const json = { const json = {
course_check_type: cur.course_check_type,
headerInfo: { headerInfo: {
isStart: !!cur.selected, // 是否为开始学习按钮 或者 选课按钮 isStart: !!cur.selected, // 是否为开始学习按钮 或者 选课按钮
id: cur.course_id, id: cur.course_id,
...@@ -287,6 +288,7 @@ export default class CourseAction extends BaseACTION { ...@@ -287,6 +288,7 @@ export default class CourseAction extends BaseACTION {
}) })
} }
const json = { const json = {
raw: cur,
score: cur.course_score, score: cur.course_score,
duration: tool.convertTime.durationToTimeString(cur.course_duration || 0), duration: tool.convertTime.durationToTimeString(cur.course_duration || 0),
progress: cur.course_progress, progress: cur.course_progress,
......
...@@ -9,26 +9,7 @@ import { Grade } from '@api' ...@@ -9,26 +9,7 @@ import { Grade } from '@api'
export default class GradeAction extends BaseACTION { export default class GradeAction extends BaseACTION {
/* 获取我的学分信息 */ /* 获取我的学分信息 */
getCredit () { getCredit () { return Grade.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
})
}
/* 提交重修 */ /* 提交重修 */
rebuildSubmit (obj) { return Grade.submitRebuild(obj).then(res => res) } 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>
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
<!-- <a :href="documentUrl" target="_blank">{{ $t('components.learnSysLayout.navigation.handlbook') }}</a> --> <!-- <a :href="documentUrl" target="_blank">{{ $t('components.learnSysLayout.navigation.handlbook') }}</a> -->
<!-- <a href="https://library.ciis.edu" target="_blank">{{ $t('components.learnSysLayout.navigation.library') }}</a> --> <!-- <a href="https://library.ciis.edu" target="_blank">{{ $t('components.learnSysLayout.navigation.library') }}</a> -->
</div> </div>
<div class="nav-message"></div>
<div class="notify" @click="goNotify()"> <div class="notify" @click="goNotify()">
{{ $t('components.learnSysLayout.navigation.tip') }} {{ $t('components.learnSysLayout.navigation.tip') }}
<div class="num" v-if="this.$store.getters.myMsg != 0">{{ this.$store.getters.myMsg }}</div> <div class="num" v-if="this.$store.getters.myMsg != 0">{{ this.$store.getters.myMsg }}</div>
...@@ -21,7 +22,7 @@ ...@@ -21,7 +22,7 @@
<script> <script>
import LanguageSwitch from '@/components/languageSwitch/index.vue' import LanguageSwitch from '@/components/languageSwitch/index.vue'
import cAction from '../../action' import cAction from '../../action'
// import Message from '@ezijing/web-message-sdk'
export default { export default {
components: { LanguageSwitch }, components: { LanguageSwitch },
data() { data() {
...@@ -42,7 +43,8 @@ export default { ...@@ -42,7 +43,8 @@ export default {
.catch(e => { .catch(e => {
this.$message.error(e.message) this.$message.error(e.message)
}) })
.finally(() => {}) // 新版通知
// Message({ container: '.nav-message', source: 'SOFIA_WEB', baseURL: webConf.others.messageBaseURL })
}, },
methods: { methods: {
goNotify() { goNotify() {
......
...@@ -43,19 +43,25 @@ export default { ...@@ -43,19 +43,25 @@ export default {
computed: { computed: {
qrcodeValue() { qrcodeValue() {
return this.order.url return this.order.url
},
pid() {
return this.$route.params.id
},
notifyUrl() {
return `${webConf.apiBaseURL}/api/lms/v2/lobby/update-status-api/${this.pid}/sofia`
} }
}, },
methods: { methods: {
// 创建微信订单 // 创建微信订单
createWxPayOrder(productId, num = 1) { createWxPayOrder(productId, num = 1) {
return api.createWxpayOrder('ezijing', productId, { num }).then(response => { return api.createWxpayOrder('ezijing', productId, { num, notify_url: this.notifyUrl }).then(response => {
this.order = response this.order = response
return response return response
}) })
}, },
// 创建支付宝订单 // 创建支付宝订单
createAliPayOrder(productId, num = 1) { createAliPayOrder(productId, num = 1) {
return api.createAlipayOrder(productId, { type: 2, num }).then(response => { return api.createAlipayOrder(productId, { type: 2, num, notify_url: this.notifyUrl }).then(response => {
this.order = response this.order = response
return response return response
}) })
......
...@@ -2,7 +2,7 @@ import TableList from '@/components/comTable/tableList' ...@@ -2,7 +2,7 @@ import TableList from '@/components/comTable/tableList'
import * as api from '../api' import * as api from '../api'
export default { export default {
props: { affairId: { type: String, required: true } }, props: { affairId: { type: String, required: true }, hasAdd: { type: Boolean, default: true } },
components: { TableList }, components: { TableList },
data() { data() {
return { return {
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请新的活动</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请新的活动</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请索菲亚在读证明</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请索菲亚在读证明</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请紫荆在读证明</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请紫荆在读证明</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
</div> </div>
<Tap :tapParam="tapParam" @tapParam="tapIndexs"></Tap> <Tap :tapParam="tapParam" @tapParam="tapIndexs"></Tap>
<div class="con-box" v-if="affairId"> <div class="con-box" v-if="affairId">
<component :is="currentComponent" :affairId="affairId"></component> <component :is="currentComponent" :affairId="affairId" :hasAdd="hasAdd"></component>
</div> </div>
</div> </div>
</template> </template>
...@@ -64,6 +64,9 @@ export default { ...@@ -64,6 +64,9 @@ export default {
affairId() { affairId() {
return this.active ? this.active.id : '' return this.active ? this.active.id : ''
}, },
hasAdd() {
return this.active ? this.active.close !== '1' : false
},
currentComponent() { currentComponent() {
const components = { const components = {
symposium: 'ActivityList', symposium: 'ActivityList',
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请电子成绩单</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请电子成绩单</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
...@@ -111,9 +111,9 @@ export default { ...@@ -111,9 +111,9 @@ export default {
email: [{ required: true, message: '请输入邮寄地址', trigger: 'blur' }] email: [{ required: true, message: '请输入邮寄地址', trigger: 'blur' }]
}, },
productList: [ productList: [
{ label: '1份—¥126元', value: '1', product_id: '6747325185390542848' }, { label: '1份—¥127元', value: '1', product_id: '6747325185390542848' },
{ label: '2份—¥201元', value: '2', product_id: '6747325279879823360' }, { label: '2份—¥203元', value: '2', product_id: '6747325279879823360' },
{ label: '3份—¥276元', value: '3', product_id: '6747325351686307840' } { label: '3份—¥278元', value: '3', product_id: '6747325351686307840' }
], ],
payVisible: false payVisible: false
} }
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请纸质成绩单</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请纸质成绩单</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请重修</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请重修</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请新乐分享</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请新乐分享</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请复学</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请复学</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
<template> <template>
<div> <div>
<el-button type="primary" @click="handleAdd">申请休学</el-button> <el-button type="primary" @click="handleAdd" v-if="hasAdd">申请休学</el-button>
<div style="height: 20px"></div> <div style="height: 20px"></div>
<table-list :remote="remote" :columns="columns" ref="tableList"></table-list> <table-list :remote="remote" :columns="columns" ref="tableList"></table-list>
</div> </div>
......
...@@ -60,15 +60,15 @@ export default { ...@@ -60,15 +60,15 @@ export default {
if (data.type === 1) { if (data.type === 1) {
return return
} }
// zoom直播 // 5:CC直播; 6:腾讯会议; 8:Zoom
if (data.type === 8) { const live = data.live
const live = data.live if ([6, 8].includes(data.type) && live) {
const hasRecordUrl = live.enable_record && live.record_url const hasRecordUrl = live.enable_record && live.record_url
if ([3, 5].includes(live.live_status) && !hasRecordUrl) { if ([3, 5].includes(live.live_status) && !hasRecordUrl) {
this.$message.error(this.$t('viewerMain.liveEnd')) this.$message.error(this.$t('viewerMain.liveEnd'))
return return
} }
window.open(live.record_url || live.join_url) window.open(live.record_url || live.join_url, '_blank')
return return
} }
// 课程大作业 // 课程大作业
......
<template> <template>
<div> <div v-loading="loading">
<div class="con-title">{{ $t('pages.grade.credit.title') }}</div> <div class="con-title">{{ $t('pages.grade.credit.title') }}</div>
<div class="con-box"> <div class="con-box">
<div v-if="false">
<p>注:</p>
<p>1. 每一门课程达到77分及格通过,低于77分需要重修,重修费用按照每学分1000元进行缴纳。</p>
<p>2. 所有课程全部通过,且16门必修课平均分达到{{ detail.degree_score }}分,符合申请学位要求。</p>
</div>
<div class="total-core"> <div class="total-core">
<div class="title">{{ $t('pages.grade.credit.totalCredits') }}</div> <div class="title">{{ $t('pages.grade.credit.totalCredits') }}</div>
<div class="core">{{score.total}}</div> <div class="core">{{ detail.total_credits }}(分)</div>
<el-progress ref="credit_total" :text-inside="true" :stroke-width="30" :percentage="percent1" color="#df9d75">213123</el-progress> <el-progress
:text-inside="true"
:stroke-width="30"
:percentage="totalPercentage"
:format="progressFormat.bind(null, detail.my_total_credits)"
color="#df9d75"
></el-progress>
</div>
<div class="total-core" v-if="false">
<div class="title">综合必修平均分</div>
<div class="core">100(分)</div>
<div class="progress">
<div class="progress-baseline" :style="progressBasseLineStyle">
<span>{{ detail.degree_score }}(分)</span>
</div>
<el-progress
:text-inside="true"
:stroke-width="30"
:percentage="detail.average_score"
:format="progressFormat"
color="#8ca4cf"
>
</el-progress>
</div>
</div> </div>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12"> <el-col :span="12">
<div class="compulsory-core"> <div class="total-core">
<div class="title">{{ $t('pages.grade.credit.compCredits') }}</div> <div class="title">{{ $t('pages.grade.credit.compCredits') }}</div>
<div class="core">{{ score.must }}</div> <div class="core">{{ detail.required_credits }}(分)</div>
<el-progress <el-progress
ref="credit_must"
:text-inside="true" :text-inside="true"
:stroke-width="30" :stroke-width="30"
:percentage="percent2" :percentage="requiredPercentage"
color="#8ca4cf" :format="progressFormat.bind(null, detail.my_required_credits)"
></el-progress> color="#df9d75"
>234234234</el-progress
>
</div> </div>
</el-col> </el-col>
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12"> <el-col :span="12" v-if="false">
<div class="elective-core"> <div class="total-core">
<div class="title">{{ $t('pages.grade.credit.eleCredits') }}</div> <div class="title">{{ $t('pages.grade.credit.eleCredits') }}</div>
<div class="core">{{ score.unmust }}</div> <div class="core">{{ detail.optional_credits }}(分)</div>
<el-progress <el-progress
ref="credit_unmust"
:text-inside="true" :text-inside="true"
:stroke-width="30" :stroke-width="30"
:percentage="percent3" :percentage="optionalPercentage"
color="#66c6bd" :format="progressFormat.bind(null, detail.my_optional_credits)"
color="#df9d75"
></el-progress> ></el-progress>
</div> </div>
</el-col> --> </el-col>
</el-row> </el-row>
<div class="color-box"> <div class="color-box">
<div class="color" style="background: #8ca4cf;"></div><div class="txt">{{ $t('pages.grade.credit.compCredits') }}</div> <div class="color" style="background: #8ca4cf"></div>
<!-- <div class="color" style="background: #66c6bd;"></div><div class="txt">{{ $t('pages.grade.credit.eleCredits') }}</div> --> <div class="txt">{{ $t('pages.grade.credit.compCredits') }}</div>
<div class="color" style="background: #df9d75;"></div><div class="txt">{{ $t('pages.grade.credit.allCredits') }}</div> <!-- <div class="color" style="background: #66c6bd"></div>
<div class="txt">{{ $t('pages.grade.credit.eleCredits') }}</div> -->
<!-- <div class="color" style="background: #8ca4cf"></div>
<div class="txt">综合必修平均分</div> -->
<div class="color" style="background: #df9d75"></div>
<div class="txt">{{ $t('pages.grade.credit.allCredits') }}</div>
</div> </div>
<m-page <table-list v-bind="tableOptions"></table-list>
:tableHead="tableHead"
:tableData="tableData"
:params="params"
:objFn="objFn"
:tableConfig="tableConfig"
/>
<div style="height: 0.3rem"></div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import mPage from '@/components/comTable/mPage.vue' import mPage from '@/components/comTable/mPage.vue'
import cAction from '@action'
import TableList from '@/components/comTable/tableList'
export default { export default {
components: { mPage }, components: { mPage, TableList },
data() { data() {
return { return {
percent1: 0, loading: false,
percent2: 0, detail: {}
percent3: 0, }
score: { },
total: 0, computed: {
myTotal: 0, tableOptions() {
myTotalStr: '0%', return {
must: 0, columns: [
myMust: 0, { prop: 'semester_name', label: this.$t('pages.grade.credit.semester'), attrs: { width: '160px' } },
myMustStr: '0%', { prop: 'class_name', label: this.$t('pages.grade.credit.class'), attrs: { width: '120px' } },
unmust: 0, { prop: 'course_name', label: this.$t('pages.grade.credit.course') },
myUnmust: 0, { prop: 'course_credit', label: this.$t('pages.grade.credit.courseCredits'), attrs: { width: '100px' } },
myUnmustStr: '0%' {
}, prop: 'passed',
tableConfig: { border: '', size: '', selection: { has: false, sels: [], width: '50px', fix: 'left', align: '' } }, // 增加选择框,has设置为true label: this.$t('pages.grade.credit.status'),
tableHead: [ attrs: { width: '100px' },
{ prop: 'index', label: this.$t('pages.grade.credit.serialNumber'), width: '60px' }, computed: value => {
{ prop: 'semester_name', label: this.$t('pages.grade.credit.semester') }, const map = {
{ prop: 'class_name', label: this.$t('pages.grade.credit.class'), width: '200px' }, 0: this.$t('pages.grade.credit.failed'),
{ prop: 'course_name', label: this.$t('pages.grade.credit.course') }, 1: this.$t('pages.grade.credit.passed'),
{ prop: 'course_credit', label: this.$t('pages.grade.credit.courseCredits'), width: '100px' }, 2: this.$t('pages.grade.credit.unpublished')
{ prop: 'passed', label: this.$t('pages.grade.credit.status'), width: '100px' }, }
{ prop: 'credit', label: this.$t('pages.grade.credit.credit'), width: '100px' }, return map[value]
{ prop: 'score', label: this.$t('pages.grade.credit.grade'), width: '100px' }, }
// { prop: '', label: this.$t('pages.grade.credit')'', minWidth: '', fix: false, goObj: { routerName: '', params: {} } }, // 点击跳转页面并传参数 },
{ { prop: 'credit', label: this.$t('pages.grade.credit.credit'), attrs: { width: '100px' } },
prop: 'operate-x', { prop: 'score', label: this.$t('pages.grade.credit.grade'), attrs: { width: '100px' } },
label: this.$t('pages.grade.credit.operate'), {
width: '80px', label: this.$t('pages.grade.credit.operate'),
fix: false, attrs: { width: '100px' },
commandArr: [ buttons: [
{ command: 're-start', name: '重修' }, {
{ command: 're-start', name: '已申请' } label: '申请重修',
] isShow(row) {
} return row.passed === 0
], },
tableData: [], onClick: row => {
params: { keywords: '', curPage: 1, pageSize: 100, total: '' }, this.$router.push({ path: '/app/offices', query: { channel: 'retake' } })
objFn: { }
paramsFn: _this => { }
return {} ]
}, }
nameAPI: 'getCredit', ],
actionClass: 'Grade', data: this.detail.lists
// nameExcel: 'getStatSummaryExcel',
nameExcel: 'getExportExcel3_4_14',
callback: (_this, data) => {
this.score = data
this.percent1 = Math.floor((this.score.myTotal / this.score.total) * 100) || 0
this.percent2 = Math.floor((this.score.myMust / this.score.must) * 100) || 0
this.percent3 = Math.floor((this.score.myUnmust / (this.score.unmust ? this.score.unmust : 1)) * 100) || 0
// console.log(111, this.score, this.percent1, this.percent2, this.percent3)
this.$refs.credit_total.$el.children[0].children[0].children[0].children[0].innerHTML = this.score.myTotal + '(分)'
this.$refs.credit_must.$el.children[0].children[0].children[0].children[0].innerHTML = this.score.myMust + '(分)'
// this.$refs.credit_unmust.$el.children[0].children[0].children[0].children[0].innerHTML = this.score.myUnmust + '(分)'
_this.params.total = 100
data = data.list
data.forEach((elem, i) => {
elem.index = i + 1 + (_this.params.curPage - 1) * _this.params.pageSize
elem.passed =
elem.passed !== 0
? elem.passed === 2
? this.$t('pages.grade.credit.unpublished')
: this.$t('pages.grade.credit.passed')
: this.$t('pages.grade.credit.failed')
_this.tableData.push(elem)
})
}
} }
},
// 总计学分进度
totalPercentage() {
return (this.detail.my_total_credits / this.detail.total_credits) * 100 || 0
},
// 必修学分进度
requiredPercentage() {
return (this.detail.my_required_credits / this.detail.required_credits) * 100 || 0
},
// 选修学分进度
optionalPercentage() {
return (this.detail.my_optional_credits / this.detail.optional_credits) * 100 || 0
},
progressBasseLineStyle() {
return { left: `${this.detail.degree_score}%` }
} }
}, },
methods: {
getDetail() {
this.loading = true
cAction.Grade.getCredit()
.then(response => {
this.detail = response
})
.finally(() => {
this.loading = false
})
},
progressFormat(percentage) {
return `${percentage}(分)`
}
},
beforeMount() {
this.getDetail()
},
mounted() { mounted() {
this.VueEvent.$off('command-tablelist-x').$on('command-tablelist-x', data => { // this.VueEvent.$off('command-tablelist-x').$on('command-tablelist-x', data => {
if (data.command.command === 're-start') { // if (data.command.command === 're-start') {
const _data = data.tableData[data.index - 1] // const _data = data.tableData[data.index - 1]
window.localStorage.setItem('rebuild', JSON.stringify(_data)) // window.localStorage.setItem('rebuild', JSON.stringify(_data))
this.$router.push({ path: '/app/grade/rebuild' }) // this.$router.push({ path: '/app/grade/rebuild' })
} // }
}) // })
} }
} }
</script> </script>
...@@ -161,8 +197,10 @@ export default { ...@@ -161,8 +197,10 @@ export default {
} }
} }
.color-box { .color-box {
padding: 0.1rem 0 0.2rem 0; display: flex;
text-align: right; justify-content: flex-end;
align-items: center;
padding: 10px 0;
.color { .color {
display: inline-block; display: inline-block;
vertical-align: text-bottom; vertical-align: text-bottom;
...@@ -171,9 +209,37 @@ export default { ...@@ -171,9 +209,37 @@ export default {
} }
.txt { .txt {
display: inline-block; display: inline-block;
padding: 0 0.2rem 0 0.1rem; padding: 0 10px;
font-size: 14px; font-size: 14px;
line-height: 36px; line-height: 36px;
} }
} }
.progress {
position: relative;
.progress-baseline {
position: absolute;
bottom: 30px;
display: inline-block;
span {
font-size: 12px;
display: block;
transform: translateX(-50%);
}
&::after {
position: absolute;
bottom: -30px;
content: '';
display: block;
width: 4px;
height: 36px;
background-color: #f56c6c;
margin-left: -2px;
z-index: 1;
}
}
::v-deep .el-progress-bar__innerText {
position: relative;
z-index: 1;
}
}
</style> </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="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> <template>
<div> <div>
<template v-if="newLiveMsg.live"> <!-- 最新直播 -->
<div class="live-msg"> <course-latest-live />
<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>
<div class="con-title">{{ $t('pages.learn.course.title') }}</div> <div class="con-title">{{ $t('pages.learn.course.title') }}</div>
<!-- 筛选 -->
<div class="con-box"> <div class="con-box">
<template v-for="(item, index) in find"> <template v-for="(item, index) in find">
<ul v-bind:key="index" class="tabs-list"> <ul v-bind:key="index" class="tabs-list">
...@@ -37,76 +27,38 @@ ...@@ -37,76 +27,38 @@
</ul> </ul>
</template> </template>
</div> </div>
<!-- 排序 -->
<div class="switch-box"> <div class="switch-box">
<el-button type="info" size="medium" @click="studyEarlyUpdate" plain <el-button type="info" size="medium" plain @click="studyEarlyUpdate">
>{{ $t('pages.learn.course.lastLearn') {{ $t('pages.learn.course.lastLearn') }}
}}<i :class="['el-icon-caret-' + (filter.studyEarly == 'down' ? 'bottom' : 'top'), 'icon']"></i <i :class="['el-icon-caret-' + (filter.studyEarly == 'down' ? 'bottom' : 'top'), 'icon']"></i>
></el-button> </el-button>
<el-button type="info" size="medium" @click="selectTimeUpdate" plain <el-button type="info" size="medium" plain @click="selectTimeUpdate">
>{{ $t('pages.learn.course.updateTime') {{ $t('pages.learn.course.updateTime') }}
}}<i :class="['el-icon-caret-' + (filter.selectTime == 'down' ? 'bottom' : 'top'), 'icon']"></i <i :class="['el-icon-caret-' + (filter.selectTime == 'down' ? 'bottom' : 'top'), 'icon']"></i>
></el-button> </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 class="rbtn" type="primary" size="medium" icon="el-icon-self-cc-book icon" @click="goCourseAll">
{{ $t('pages.learn.course.changeCourse') }}
</el-button>
-->
</div> </div>
<!-- 课程列表 -->
<div class="con-box" v-loading="loading"> <div class="con-box" v-loading="loading">
<ul class="course-list"> <ul class="course-list" v-if="homeList.length">
<template v-for="(item, index) in homeList"> <course-list-item v-for="(item, index) in homeList" :data="item" :key="index"></course-list-item>
<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> </ul>
<div class="no-data" v-else>{{ $t('pages.learn.course.noCourseStr') }}</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import cAction from '@action' import cAction from '@action'
import CourseLatestLive from './course/components/CourseLatestLive.vue'
import CourseListItem from './components/CourseListItem'
export default { export default {
components: {}, components: { CourseLatestLive, CourseListItem },
data() { data() {
return { return {
filter: { filter: {
...@@ -148,21 +100,10 @@ export default { ...@@ -148,21 +100,10 @@ export default {
], ],
homeList: [], // 从后台请求 homeList: [], // 从后台请求
param: {}, param: {},
timeInterval: null,
newLiveMsg: {},
loading: false loading: false
} }
}, },
mounted() { mounted() {
if (this.timeInterval) {
clearInterval(this.timeInterval)
this.timeInterval = null
}
// 获取最新直播
this.getLatestLive()
// 定时获取最新直播
this.timeInterval = setInterval(this.getLatestLive, 10000)
cAction.Course.getLearnFind() cAction.Course.getLearnFind()
.then(data => { .then(data => {
this.find[0].arrItem = data this.find[0].arrItem = data
...@@ -173,36 +114,17 @@ export default { ...@@ -173,36 +114,17 @@ export default {
}) })
.finally(() => {}) .finally(() => {})
}, },
destroyed() {
if (this.timeInterval) {
clearInterval(this.timeInterval)
this.timeInterval = null
}
},
methods: { methods: {
goCourseAll() { goCourseAll() {
this.$router.push({ this.$router.push({
path: '/app/learn/course-all' 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) { getAjaxList(bool, str) {
this.loading = true this.loading = true
cAction.Course.getCourseList(bool, this.param) cAction.Course.getCourseList(bool, this.param)
.then(json => { .then(json => {
this.homeList = json this.homeList = json
if (!json.length) {
this.$message(str)
}
})
.catch(e => {
this.$message.error(e.message)
}) })
.finally(() => { .finally(() => {
this.loading = false this.loading = false
...@@ -254,57 +176,12 @@ export default { ...@@ -254,57 +176,12 @@ export default {
} }
this.getAjaxList(true, this.$t('pages.learn.course.goCourseAllStr')) 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> </script>
<style lang="scss" scoped> <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 { ul.tabs-list {
float: left; float: left;
...@@ -372,127 +249,10 @@ ul.course-list { ...@@ -372,127 +249,10 @@ ul.course-list {
padding: 0; padding: 0;
font-size: 0.18rem; font-size: 0.18rem;
color: #313131; color: #313131;
}
.no-data { .no-data {
text-align: center; text-align: center;
line-height: 2rem; 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) { @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>
...@@ -164,7 +164,7 @@ ...@@ -164,7 +164,7 @@
</div> </div>
</template> </template>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('pages.learn.courseDetail.Courseassessment')" name="3"> <el-tab-pane :label="$t('pages.learn.courseDetail.Courseassessment')" name="3" :lazy="true">
<div class="course-assess"> <div class="course-assess">
<div class="title"> <div class="title">
{{ $t('pages.learn.courseDetail.Finalresult') }} {{ $t('pages.learn.courseDetail.Finalresult') }}
...@@ -180,7 +180,9 @@ ...@@ -180,7 +180,9 @@
{{ $t('pages.learn.courseDetail.Courseassessmentstandard') }} {{ $t('pages.learn.courseDetail.Courseassessmentstandard') }}
</div> </div>
</div> </div>
<div class="detail"> <course-assessment-cn :data="courseAssessment" v-if="courseCheckType === 1"></course-assessment-cn>
<course-assessment-en :data="courseAssessment" v-else></course-assessment-en>
<!-- <div class="detail">
<div class="detail-rich" v-html="tabs[3].richText"></div> <div class="detail-rich" v-html="tabs[3].richText"></div>
</div> </div>
--> -->
...@@ -317,9 +319,10 @@ import cAction from '@action' ...@@ -317,9 +319,10 @@ import cAction from '@action'
import cTool from '@tool' import cTool from '@tool'
import VEditor from '@/components/ckeditor' import VEditor from '@/components/ckeditor'
import CourseAssessmentCn from './components/course_assessment_cn'
import CourseAssessmentEn from './components/course_assessment_en'
export default { export default {
components: { VEditor }, components: { VEditor, CourseAssessmentCn, CourseAssessmentEn },
props: { props: {
sid: { sid: {
type: String, type: String,
...@@ -447,7 +450,9 @@ export default { ...@@ -447,7 +450,9 @@ export default {
arrFn: [], arrFn: [],
cloudClassUrls: {}, cloudClassUrls: {},
timeHeart: null, timeHeart: null,
firstVideo: {} firstVideo: {},
courseCheckType: 2,
courseAssessment: {}
} }
}, },
mounted() { mounted() {
...@@ -473,6 +478,7 @@ export default { ...@@ -473,6 +478,7 @@ export default {
} }
cAction.Course.getCourseDetail(this.cid, this.sid) cAction.Course.getCourseDetail(this.cid, this.sid)
.then(json => { .then(json => {
this.courseCheckType = json.course_check_type
this.headerInfo = json.headerInfo this.headerInfo = json.headerInfo
this.tabs[0].content = json.tabs0Content this.tabs[0].content = json.tabs0Content
this.tabs[1].chapterList = json.tabs1ChapterList this.tabs[1].chapterList = json.tabs1ChapterList
...@@ -495,6 +501,7 @@ export default { ...@@ -495,6 +501,7 @@ export default {
cAction.Course.getCourseAssess(this.cid, this.sid) cAction.Course.getCourseAssess(this.cid, this.sid)
.then(json1 => { .then(json1 => {
this.courseAssessment = json1.raw
// const _courseArr = json.tabs1ChapterList.course // const _courseArr = json.tabs1ChapterList.course
// /* 进行一次 对照,将 视频 vid 赋值 */ // /* 进行一次 对照,将 视频 vid 赋值 */
// /* BUG: 如果有某一章 都没有视频时,对照失败,从没有的那一章开始,后面全部没有vid */ // /* BUG: 如果有某一章 都没有视频时,对照失败,从没有的那一章开始,后面全部没有vid */
......
...@@ -45,7 +45,7 @@ export default [ ...@@ -45,7 +45,7 @@ export default [
}, },
{ {
path: 'course-detail/:sid/:cid', path: 'course-detail/:sid/:cid',
component: () => import('../pages/learn/courseDetail.vue'), component: () => import('../pages/learn/course/detail.vue'),
props: true props: true
}, },
{ {
......
...@@ -73,6 +73,7 @@ p { ...@@ -73,6 +73,7 @@ p {
font-weight: 700; font-weight: 700;
color: #313131; color: #313131;
line-height: 0.3rem; line-height: 0.3rem;
margin-top: 10px;
} }
.course-assess .detail .p { .course-assess .detail .p {
font-size: 0.14rem; font-size: 0.14rem;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论