Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
L
learn-online-pc
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
learn-online-pc
Commits
f88708c9
提交
f88708c9
authored
5月 14, 2020
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
差异文件
merge...
上级
2023e8cc
bb3b7053
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
54 个修改的文件
包含
3153 行增加
和
202 行删除
+3153
-202
PlayerAction.js
client/src/action/PlayerAction.js
+122
-0
base_api.js
client/src/api/base_api.js
+1
-1
player_api.js
client/src/api/player_api.js
+28
-0
tableList.vue
client/src/components/comTable/tableList.vue
+4
-1
editor.vue
client/src/components/editor.vue
+135
-0
elemInfo.md
client/src/components/upload-form/elemInfo.md
+57
-0
index.js
client/src/components/upload-form/index.js
+8
-0
ajax.js
client/src/components/upload-form/src/ajax.js
+125
-0
uploadForm.vue
client/src/components/upload-form/src/uploadForm.vue
+200
-0
upload.vue
client/src/components/upload.vue
+81
-0
main.js
client/src/main.js
+3
-0
index.js
client/src/modules/discuss/api/index.js
+67
-28
en.json
client/src/modules/discuss/assets/languages/en.json
+12
-0
zh-CN.json
client/src/modules/discuss/assets/languages/zh-CN.json
+11
-0
answerItem.vue
client/src/modules/discuss/components/answerItem.vue
+68
-0
replyDetailItem.vue
client/src/modules/discuss/components/replyDetailItem.vue
+37
-0
replyItem.vue
client/src/modules/discuss/components/replyItem.vue
+39
-0
index.js
client/src/modules/discuss/index.js
+5
-2
discuss.vue
client/src/modules/discuss/src/discuss.vue
+13
-0
discussDetail.vue
client/src/modules/discuss/src/discussDetail.vue
+0
-0
api.js
client/src/modules/viewer/api.js
+137
-0
index.js
client/src/modules/viewer/api/index.js
+0
-35
chapter.vue
client/src/modules/viewer/components/aside/chapter.vue
+43
-13
index.vue
client/src/modules/viewer/components/aside/index.vue
+13
-14
lecture.vue
client/src/modules/viewer/components/aside/lecture.vue
+18
-11
container.vue
client/src/modules/viewer/components/common/container.vue
+24
-0
editor.vue
client/src/modules/viewer/components/common/editor.vue
+135
-0
fileList.vue
client/src/modules/viewer/components/common/fileList.vue
+70
-0
upload.vue
client/src/modules/viewer/components/common/upload.vue
+89
-0
layout.vue
client/src/modules/viewer/components/layout.vue
+59
-0
chapterLive.vue
client/src/modules/viewer/components/live/chapterLive.vue
+55
-0
chapterPlayer.vue
...nt/src/modules/viewer/components/player/chapterPlayer.vue
+77
-16
videoPlayer.vue
client/src/modules/viewer/components/player/videoPlayer.vue
+8
-6
chapterRead.vue
client/src/modules/viewer/components/read/chapterRead.vue
+44
-0
courseRead.vue
client/src/modules/viewer/components/read/courseRead.vue
+39
-0
fileList.vue
client/src/modules/viewer/components/read/fileList.vue
+63
-0
chapterExam.vue
client/src/modules/viewer/components/work/chapterExam.vue
+274
-0
chapterWork.vue
client/src/modules/viewer/components/work/chapterWork.vue
+260
-0
courseExam.vue
client/src/modules/viewer/components/work/courseExam.vue
+0
-0
courseWork.vue
client/src/modules/viewer/components/work/courseWork.vue
+235
-0
examItem.vue
client/src/modules/viewer/components/work/examItem.vue
+241
-0
index.vue
client/src/modules/viewer/components/work/index.vue
+47
-0
index.vue
client/src/modules/viewer/index.vue
+163
-63
routes.js
client/src/modules/viewer/routes.js
+13
-0
hall.vue
client/src/pages/affairsHall/hall.vue
+3
-2
course.vue
client/src/pages/learn/course.vue
+7
-7
courseDetail.vue
client/src/pages/learn/courseDetail.vue
+20
-3
discussDetail.vue
client/src/pages/learn/discussDetail.vue
+0
-0
list.vue
client/src/pages/mobileLive/list.vue
+0
-0
chapterRead.vue
client/src/pages/player/chapterRead/chapterRead.vue
+0
-0
chapterWork.vue
client/src/pages/player/chapterWork/chapterWork.vue
+0
-0
exam.vue
client/src/pages/player/exam/exam.vue
+0
-0
routes.js
client/src/router/routes.js
+0
-0
ProxyMonitor.js
server/server/controller/ProxyMonitor.js
+0
-0
没有找到文件。
client/src/action/PlayerAction.js
浏览文件 @
f88708c9
...
...
@@ -332,4 +332,126 @@ export default class PlayerAction extends BaseACTION {
getLiveList
()
{
return
Player
.
getLiveList
().
then
(
res
=>
res
)
}
/* 获取云课堂 url */
getCloudUrl
()
{
return
Player
.
getCloudUrl
().
then
(
res
=>
res
)
}
/* 获取考卷信息 */
getExamInfo
(
cid
,
sid
)
{
return
Player
.
getExamInfo
(
cid
,
sid
).
then
(
_res
=>
{
const
exam
=
{}
exam
.
id
=
_res
.
id
exam
.
title
=
_res
.
title
exam
.
score
=
{}
exam
.
radioList
=
_res
.
examination
.
radioList
for
(
let
i
=
0
;
i
<
exam
.
radioList
.
length
;
i
++
)
{
exam
.
radioList
[
i
].
user_answer
=
''
exam
.
radioList
[
i
].
right_answer
=
''
exam
.
radioList
[
i
].
get_score
=
-
1
}
exam
.
checkboxList
=
_res
.
examination
.
checkboxList
for
(
let
i
=
0
;
i
<
exam
.
checkboxList
.
length
;
i
++
)
{
exam
.
checkboxList
[
i
].
user_answer
=
[]
exam
.
checkboxList
[
i
].
right_answer
=
[]
exam
.
checkboxList
[
i
].
get_score
=
-
1
}
exam
.
shortAnswerList
=
_res
.
examination
.
shortAnswerList
for
(
let
i
=
0
;
i
<
exam
.
shortAnswerList
.
length
;
i
++
)
{
exam
.
shortAnswerList
[
i
].
user_answer
=
''
exam
.
shortAnswerList
[
i
].
get_score
=
-
1
exam
.
shortAnswerList
[
i
].
attachments
=
[]
exam
.
shortAnswerList
[
i
].
upload
=
{
type
:
'upload-form'
,
label
:
'附件上传:'
,
model
:
'attachments'
,
action
:
webConf
.
apiBaseURL
+
'/util/upload-file'
,
data
:
{
special
:
'exam'
},
attrs
:
{
multiple
:
true
,
headers
:
{
tenant
:
'sofia'
}
},
html
:
`
<div style="color: #72818c; font-size: 14px;">
<p style="margin: 0;">支持doc,docx,ppt,xls,txt,rar,zip,pdf,jpg,pic,png格式的文件,文件小于30M。</p>
</div>
`
}
}
return
exam
})
}
/* 获取考卷结果 */
getExamAnswer
(
cid
,
sid
,
eid
)
{
return
Player
.
getExamAnswer
(
cid
,
sid
,
eid
).
then
(
_res
=>
{
if
(
_res
.
code
)
{
return
_res
}
const
exam
=
{}
let
tmp
=
null
exam
.
id
=
_res
.
id
exam
.
title
=
_res
.
title
exam
.
type
=
_res
.
type
exam
.
score
=
_res
.
score
exam
.
isPublished
=
_res
.
is_published
||
''
exam
.
submitted_time
=
_res
.
submitted_time
exam
.
radioList
=
_res
.
sheet
.
radioList
for
(
let
i
=
0
;
i
<
exam
.
radioList
.
length
;
i
++
)
{
tmp
=
exam
.
radioList
[
i
]
if
(
!
tmp
.
user_answer
)
tmp
.
user_answer
=
''
if
(
!
tmp
.
right_answer
)
tmp
.
right_answer
=
''
if
(
!
tmp
.
get_score
)
tmp
.
get_score
=
-
1
}
exam
.
checkboxList
=
_res
.
sheet
.
checkboxList
for
(
let
i
=
0
;
i
<
exam
.
checkboxList
.
length
;
i
++
)
{
tmp
=
exam
.
checkboxList
[
i
]
if
(
!
tmp
.
user_answer
||
!
tmp
.
user_answer
.
length
)
tmp
.
user_answer
=
[]
if
(
!
tmp
.
right_answer
||
!
tmp
.
right_answer
.
length
)
tmp
.
right_answer
=
[]
if
(
!
tmp
.
get_score
)
tmp
.
get_score
=
-
1
}
exam
.
shortAnswerList
=
_res
.
sheet
.
shortAnswerList
for
(
let
i
=
0
;
i
<
exam
.
shortAnswerList
.
length
;
i
++
)
{
tmp
=
exam
.
shortAnswerList
[
i
]
tmp
.
user_answer
=
Base64
.
decode
(
tmp
.
user_answer
.
replace
(
/ /gi
,
'+'
))
if
(
!
tmp
.
attachments
||
!
tmp
.
attachments
.
length
)
tmp
.
attachments
=
[]
tmp
.
upload
=
{
type
:
'upload-form'
,
label
:
'附件上传:'
,
model
:
'attachments'
,
action
:
webConf
.
apiBaseURL
+
'/util/upload-file'
,
data
:
{
special
:
'exam'
},
attrs
:
{
multiple
:
true
,
headers
:
{
tenant
:
'sofia'
}
},
html
:
`
<div style="color: #72818c; font-size: 14px;">
<p style="margin: 0;">支持doc,docx,ppt,xls,txt,rar,zip,pdf,jpg,pic,png格式的文件,文件小于30M。</p>
</div>
`
}
}
return
exam
})
}
/* 获取考试状态 */
getExamStatus
(
cid
,
sid
,
eid
)
{
return
Player
.
getExamStatus
(
cid
,
sid
,
eid
).
then
(
_res
=>
{
/* 00: 考场未开放,不允许进入
10:考场开放,允许进入
20:开始答题
90:考试已结束 */
return
_res
})
}
/* 提交考卷 */
submitExam
(
cid
,
sid
,
eid
,
obj
)
{
return
Player
.
submitExam
(
cid
,
sid
,
eid
,
obj
).
then
(
_res
=>
{
return
_res
})
}
}
client/src/api/base_api.js
浏览文件 @
f88708c9
...
...
@@ -103,7 +103,7 @@ export default class API {
if
(
data
&&
data
.
code
!==
undefined
)
{
if
(
data
.
code
!==
0
)
{
if
(
!
/account
\/
get-user-info/gi
.
test
(
res
.
config
.
url
))
{
Message
({
type
:
'error'
,
message
:
data
.
msg
})
data
.
msg
&&
Message
({
type
:
'error'
,
message
:
data
.
msg
})
}
}
}
...
...
client/src/api/player_api.js
浏览文件 @
f88708c9
...
...
@@ -102,4 +102,32 @@ export default class PlayerAPI extends BaseAPI {
* 跨域接口请求 - 直接获取云课堂设置
*/
getCloudUrl
=
(
obj
=
{})
=>
this
.
get
(
'https://node-server.ezijing.com/get/cloud-class'
,
obj
)
/**
* 获取考卷信息
* @param {[string]} course_id -> cid
* @param {[string]} semester_id -> sid
*/
getExamInfo
=
(
cid
,
sid
)
=>
this
.
get
(
`/v2/education/
${
sid
}
/
${
cid
}
/examination`
,
{})
/**
* 获取考卷结果
* @param {[string]} course_id -> cid
* @param {[string]} semester_id -> sid
* @param {[string]} exam_id -> eid
*/
getExamAnswer
=
(
cid
,
sid
,
eid
)
=>
this
.
get
(
`/v2/education/
${
sid
}
/
${
cid
}
/examination/
${
eid
}
/sheet`
,
{})
/**
* 获取考试状态
* @param {[string]} course_id -> cid
* @param {[string]} semester_id -> sid
* @param {[string]} exam_id -> eid
*/
getExamStatus
=
(
cid
,
sid
,
eid
)
=>
this
.
get
(
`/v2/education/
${
sid
}
/
${
cid
}
/examination/
${
eid
}
/status`
,
{})
/**
* 提交考卷
* @param {[string]} course_id -> cid
* @param {[string]} semester_id -> sid
* @param {[string]} exam_id -> eid
* @param {[object]} obj -> 提交对象类
*/
submitExam
=
(
cid
,
sid
,
eid
,
obj
=
{})
=>
this
.
post
(
`/v2/education/
${
sid
}
/
${
cid
}
/examination/
${
eid
}
/sheet`
,
obj
,
{
headers
:
{
'Content-Type'
:
'application/x-www-form-urlencoded'
}
})
}
client/src/components/comTable/tableList.vue
浏览文件 @
f88708c9
...
...
@@ -165,7 +165,10 @@ export default {
this
.
fetchData
()
},
// 刷新
refersh
()
{
refersh
(
isForce
=
false
)
{
if
(
isForce
)
{
this
.
page
.
currentPage
=
1
}
this
.
fetchData
()
}
},
...
...
client/src/components/editor.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"editor"
>
<textarea
name=
"editor"
:id=
"textareaElementId"
:disabled=
"disabled"
></textarea>
</div>
</
template
>
<
script
>
import
{
uniqueId
}
from
'lodash'
export
default
{
name
:
'VEditor'
,
props
:
{
value
:
{
type
:
String
},
disabled
:
{
type
:
Boolean
,
default
:
false
}
},
data
()
{
return
{
textareaElementId
:
uniqueId
(
'editor_'
),
ckEditor
:
null
}
},
methods
:
{
createEditor
()
{
const
editor
=
(
this
.
ckEditor
=
CKEDITOR
.
replace
(
this
.
textareaElementId
,
{
height
:
400
,
uiColor
:
'#eeeeee'
,
filebrowserImageUploadUrl
:
'/api/ckeditor/img/upload'
,
// resize_enabled: typeof this.props.resizable === 'boolean' ? this.props.resizable : true,
toolbar
:
[
// { name: 'document', items: ['Source', '-', 'Save', 'NewPage', 'Preview'] },
{
name
:
'styles'
,
items
:
[
'Styles'
,
'Format'
,
'Font'
,
'FontSize'
]
},
{
name
:
'colors'
,
items
:
[
'TextColor'
,
'BGColor'
]
},
{
name
:
'tools'
,
items
:
[
'Maximize'
,
'ShowBlocks'
]
},
// { name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
{
name
:
'editing'
,
items
:
[
'Find'
,
'Replace'
]
},
// { name: 'forms', items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'] },
'/'
,
{
name
:
'basicstyles'
,
items
:
[
'Bold'
,
'Italic'
,
'Underline'
,
'Strike'
,
'Subscript'
,
'Superscript'
,
'-'
,
'RemoveFormat'
]
},
{
name
:
'paragraph'
,
items
:
[
'NumberedList'
,
'BulletedList'
,
'-'
,
'Outdent'
,
'Indent'
,
'-'
,
'Blockquote'
,
'CreateDiv'
,
'-'
,
'JustifyLeft'
,
'JustifyCenter'
,
'JustifyRight'
,
'JustifyBlock'
,
'-'
,
'BidiLtr'
,
'BidiRtl'
]
},
{
name
:
'links'
,
items
:
[
'Link'
,
'Unlink'
,
'Anchor'
]
},
{
name
:
'insert'
,
items
:
[
'Image'
,
'Table'
,
'HorizontalRule'
]
}
]
}))
editor
.
on
(
'instanceReady'
,
()
=>
{
const
data
=
this
.
value
editor
.
fire
(
'lockSnapshot'
)
editor
.
setData
(
data
,
{
callback
:
()
=>
{
this
.
bindEvent
()
const
newData
=
editor
.
getData
()
// Locking the snapshot prevents the 'change' event.
// Trigger it manually to update the bound data.
if
(
data
!==
newData
)
{
this
.
$once
(
'input'
,
()
=>
{
this
.
$emit
(
'ready'
,
editor
)
})
this
.
$emit
(
'input'
,
newData
)
}
else
{
this
.
$emit
(
'ready'
,
editor
)
}
editor
.
fire
(
'unlockSnapshot'
)
}
})
})
},
bindEvent
()
{
const
editor
=
this
.
ckEditor
editor
.
on
(
'change'
,
evt
=>
{
const
data
=
editor
.
getData
()
if
(
this
.
value
!==
data
)
{
this
.
$emit
(
'input'
,
data
,
evt
,
editor
)
}
})
editor
.
on
(
'focus'
,
evt
=>
{
this
.
$emit
(
'focus'
,
evt
,
editor
)
})
editor
.
on
(
'blur'
,
evt
=>
{
this
.
$emit
(
'blur'
,
evt
,
editor
)
})
}
},
mounted
()
{
this
.
createEditor
()
},
beforeDestroy
()
{
this
.
ckEditor
&&
this
.
ckEditor
.
destroy
()
this
.
ckEditor
=
null
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
*
{
margin
:
0
;
padding
:
0
;
}
</
style
>
client/src/components/upload-form/elemInfo.md
0 → 100644
浏览文件 @
f88708c9
## 组件简介
| 字段值 | 说明 | 字段属性 | 默认值 |
| ------- | ------------------------- | ------- | ----- |
|
`type`
| 类型:
`String`
; 说明:组件类型名 | 自定义字段 |
`upload-form`
|
|
`action`
| 类型:
`String`
; 说明:上传请求接口path | 自定义字段 |
`` |
| `deleteAction` | 类型:`String`; 说明:删除请求接口path | 自定义字段 | ``
|
|
`html`
| 类型:
`String`
; 说明:上传说明,支持html | 自定义字段 |
`` |
| `label` | 类型:`String`; 说明:组件左侧显示名称 | element-ui el-form-item对应字段 | `''` |
| `label-width` | 类型:`String`; 说明:组件左侧显示名称宽度(加单位),父级设置可以子级继承 | element-ui el-form-item对应字段 | `''` |
| `required` | 类型:`Boolean`; 说明:标识是否必填 | element-ui el-form-item对应字段 | `false` |
| `disabled` | 类型:`Boolean`; 说明:标识是否只读 | element-ui el-form-item对应字段 | `false` |
| `model` | 类型:`String`; 说明:表单提交name值和回显对照字段 | 自定义字段 | `''` |
| `placeholder` | 类型:`String`; 说明:组件input框中,默认提示文字 | element-ui el-input对应字段 | `''` |
| `attrs` | 类型:`Object`; 说明:定义标签上Data属性值 | element-ui对应字段 | `{}` |
| `rules` | 类型:`Array`; 说明:组件错误提示规则 | element-ui el-form-item对应字段 | `[]` |
### Demo Example:
``` js
return {
type: 'upload-form',
label: '姓名',
labeWidth: '160px',
required: true,
disabled: false,
model: 'uploadArrs',
action: '',
data: {
},
deleteAction: '',
deleteData: {
},
html: `
<div
style=
"color: #72818c; font-size: 14px;"
>
<p
style=
"margin: 0;"
>
申请者需要将有效身份证件原件扫描或者拍照后提交。
</p>
<p
style=
"margin: 0;"
>
请您提供有效身份证件的扫描件,身份证与台港澳居民大陆通行证应包括正反两面扫描件。
</p>
<p
style=
"margin: 0;"
>
只上传一个文件,多份文件需合并到一个文件后打印出来检查无误后再上传。
</p>
<p
style=
"margin: 0;"
>
上传文件仅限“jpg,jpeg,gif,png”格式,文件小于10MB。
</p>
</div>
`,
attrs: {
multiple: false,
limit: 1
},
rules: [
{
required: true,
message: '请上传',
trigger: 'blur'
}
]
}
`
``
*
其他属性
[
参考文档
](
[https://](https://element.eleme.cn/#/zh-CN/component/input
)
)
client/src/components/upload-form/index.js
0 → 100644
浏览文件 @
f88708c9
import
Upload
from
'./src/uploadForm.vue'
/* istanbul ignore next */
Upload
.
install
=
function
(
Vue
)
{
Vue
.
component
(
Upload
.
name
,
Upload
)
}
export
default
Upload
client/src/components/upload-form/src/ajax.js
0 → 100755
浏览文件 @
f88708c9
function
getError
(
action
,
option
,
xhr
)
{
let
msg
if
(
xhr
.
response
)
{
msg
=
`
${
xhr
.
response
.
error
||
xhr
.
response
}
`
}
else
if
(
xhr
.
responseText
)
{
msg
=
`
${
xhr
.
responseText
}
`
}
else
{
msg
=
`fail to post
${
action
}
${
xhr
.
status
}
`
}
const
err
=
new
Error
(
msg
)
err
.
status
=
xhr
.
status
err
.
method
=
'post'
err
.
url
=
action
return
err
}
function
getBody
(
xhr
)
{
const
text
=
xhr
.
responseText
||
xhr
.
response
if
(
!
text
)
{
return
text
}
try
{
return
JSON
.
parse
(
text
)
}
catch
(
e
)
{
return
text
}
}
export
function
deleteFile
(
option
)
{
if
(
typeof
XMLHttpRequest
===
'undefined'
)
{
return
}
// eslint-disable-next-line no-undef
const
xhr
=
new
XMLHttpRequest
()
let
action
=
option
.
action
xhr
.
onerror
=
function
error
(
e
)
{
option
.
onError
(
e
)
}
xhr
.
onload
=
function
onload
()
{
if
(
xhr
.
status
<
200
||
xhr
.
status
>=
300
)
{
return
option
.
onError
(
getError
(
action
,
option
,
xhr
))
}
option
.
onSuccess
(
getBody
(
xhr
))
}
xhr
.
open
(
'delete'
,
action
,
true
)
if
(
option
.
withCredentials
&&
'withCredentials'
in
xhr
)
{
xhr
.
withCredentials
=
true
}
const
headers
=
option
.
headers
||
{}
for
(
let
item
in
headers
)
{
if
(
headers
.
hasOwnProperty
(
item
)
&&
headers
[
item
]
!==
null
)
{
xhr
.
setRequestHeader
(
item
,
headers
[
item
])
}
}
xhr
.
send
()
return
xhr
}
export
default
function
upload
(
option
)
{
if
(
typeof
XMLHttpRequest
===
'undefined'
)
{
return
}
// eslint-disable-next-line no-undef
const
xhr
=
new
XMLHttpRequest
()
const
action
=
option
.
action
if
(
xhr
.
upload
)
{
xhr
.
upload
.
onprogress
=
function
progress
(
e
)
{
if
(
e
.
total
>
0
)
{
e
.
percent
=
e
.
loaded
/
e
.
total
*
100
}
option
.
onProgress
(
e
)
}
}
// eslint-disable-next-line no-undef
const
formData
=
new
FormData
()
if
(
option
.
data
)
{
Object
.
keys
(
option
.
data
).
forEach
(
key
=>
{
formData
.
append
(
key
,
option
.
data
[
key
])
})
}
formData
.
append
(
option
.
filename
,
option
.
file
,
option
.
file
.
name
)
xhr
.
onerror
=
function
error
(
e
)
{
option
.
onError
(
e
)
}
xhr
.
onload
=
function
onload
()
{
if
(
xhr
.
status
<
200
||
xhr
.
status
>=
300
)
{
return
option
.
onError
(
getError
(
action
,
option
,
xhr
))
}
option
.
onSuccess
(
getBody
(
xhr
))
}
xhr
.
open
(
'post'
,
action
,
true
)
if
(
option
.
withCredentials
&&
'withCredentials'
in
xhr
)
{
xhr
.
withCredentials
=
true
}
const
headers
=
option
.
headers
||
{}
for
(
let
item
in
headers
)
{
if
(
headers
.
hasOwnProperty
(
item
)
&&
headers
[
item
]
!==
null
)
{
xhr
.
setRequestHeader
(
item
,
headers
[
item
])
}
}
xhr
.
send
(
formData
)
return
xhr
}
client/src/components/upload-form/src/uploadForm.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"upload-form"
>
<div
class=
"u-babel"
>
{{
item
.
label
}}
</div>
<el-upload
style=
"display: inline-block;"
:action=
"item.action"
:data=
"item.data"
:before-upload=
"beforeUploadFile"
:on-success=
"onSuccessFile"
:with-credentials=
"true"
:show-file-list=
"false"
:disabled=
"(item.disabled || false) || !isUpload"
v-bind=
"item.attrs ||
{}"
>
<el-button
type=
"primary"
size=
"small"
:disabled=
"!isUpload"
>
点击上传
</el-button>
<template
v-if=
"formData[item.model] !== null && formData[item.model] !== '' && formData[item.model] !== undefined"
>
<div
class=
"self-icon el-icon-circle-check"
style=
"color: #237f00;"
></div>
</
template
>
<div
class=
"self-icon el-icon-circle-close"
style=
"color: #b01c40;"
></div>
</el-upload>
<div
style=
"overflow: hidden; padding: 10px 0 0 0;"
>
<
template
v-if=
"filesArr.length"
>
<!-- 遍历显示文件 -->
<template
v-for=
"(item, index) in filesArr"
>
<template
v-if=
"/(jpeg)|(jpg)|(png)|(gif)/gi.test(item.url)"
>
<div
v-bind:key=
"item.id"
class=
"show-file"
>
<template
v-if=
"!(item.disabled || false) && isUpload"
>
<div
class=
"close"
@
click=
"deleteFiles(index)"
>
X
</div>
</
template
>
<el-avatar
shape=
"square"
:size=
"100"
fit=
"contain"
:src=
"item.url"
></el-avatar>
<span
class=
"title"
>
{{ item.sso_file_name }}
</span>
<div
class=
"hover"
>
<a
target=
"_blank"
:href=
"item.url"
>
下载
</a>
</div>
</div>
</template>
<
template
v-else
>
<div
v-bind:key=
"item.id"
class=
"show-file"
>
<template
v-if=
"!(item.disabled || false) && isUpload"
>
<div
class=
"close"
@
click=
"deleteFiles(index)"
>
X
</div>
</
template
>
<el-avatar
shape=
"square"
:size=
"100"
fit=
"contain"
:src=
"item.url"
></el-avatar>
<span
class=
"title"
>
{{ item.sso_file_name }}
</span>
<div
class=
"hover"
>
<a
target=
"_blank"
:href=
"item.url"
>
下载
</a>
</div>
</div>
</template>
</template>
</template>
</div>
<div
class=
'info'
style=
"line-height: 1.5;"
v-html=
"item.html"
></div>
</div>
</template>
<
script
>
// import { deleteFile } from './ajax'
export
default
{
name
:
'UploadForm'
,
componentName
:
'UploadForm'
,
props
:
{
item
:
{
type
:
Object
,
default
()
{
return
{}
}
},
formData
:
{
type
:
Object
,
default
()
{
return
{}
}
},
isUpload
:
{
type
:
Boolean
,
default
()
{
return
true
}
}
},
data
()
{
const
tmpArr
=
this
.
formData
[
this
.
item
.
model
]
||
[]
this
.
formData
[
this
.
item
.
model
]
=
tmpArr
return
{
project_id
:
''
,
filesArr
:
tmpArr
}
},
methods
:
{
beforeUploadFile
(
file
)
{},
onSuccessFile
(
response
,
file
,
fileList
)
{
response
.
url
=
response
.
url
||
response
.
file
||
''
response
.
sso_file_name
=
file
.
name
this
.
filesArr
.
push
(
response
)
// this.$emit('onSubmit')
},
deleteFiles
(
index
)
{
this
.
filesArr
.
splice
(
index
,
1
)
// let temp = this.filesArr[index]
// deleteFile({
// action: this.item.deleteAction + '/' + temp.id + '?project_id=' + this.item.data.project_id,
// onError: () => {},
// onSuccess: (res) => {
// if (res.status === 200) {
// this.filesArr.splice(index, 1)
// }
// }
// })
}
},
watch
:
{
filesArr
:
{
immediate
:
true
,
deep
:
true
,
handler
(
value
)
{
if
(
this
.
formData
[
this
.
item
.
model
].
length
!==
value
.
length
)
{
this
.
formData
[
this
.
item
.
model
]
=
value
}
}
}
}
}
</
script
>
<
style
lang=
"scss"
>
.u-babel
{
display
:
inline-block
;
margin-right
:
10px
;
}
.self-icon
{
display
:
none
!
important
;
vertical-align
:
middle
;
margin-left
:
10px
;
font-size
:
21px
;
line-height
:
22px
;
}
.is-error
.self-icon.el-icon-circle-close
{
display
:
inline-block
!
important
;
}
.is-success
.self-icon.el-icon-circle-check
{
display
:
inline-block
!
important
;
}
.show-file
{
position
:
relative
;
float
:
left
;
margin-right
:
10px
;
.close
{
position
:
absolute
;
z-index
:
10
;
right
:
-10px
;
top
:
-10px
;
width
:
20px
;
height
:
20px
;
color
:
#fff
;
font-size
:
12px
;
line-height
:
20px
;
text-align
:
center
;
background
:
#efefef
;
border-radius
:
50%
;
cursor
:
pointer
;
}
.el-avatar
{
img
{
width
:
100%
;
}
}
.title
{
position
:
absolute
;
left
:
0
;
bottom
:
8px
;
width
:
100%
;
padding-left
:
5px
;
font-size
:
12px
;
line-height
:
20px
;
background
:
#efefef
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
word-break
:
break-all
;
box-sizing
:
border-box
;
}
.hover
{
display
:
none
;
position
:
absolute
;
z-index
:
9
;
top
:
0
;
left
:
0
;
height
:
100px
;
width
:
100%
;
background
:
rgba
(
0
,
0
,
0
,
0
.2
);
line-height
:
100px
;
text-align
:
center
;
a
{
color
:
#f1f1f1
;
}
}
&
:hover
{
.hover
{
display
:
block
;
}
}
}
</
style
>
client/src/components/upload.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"upload"
>
<el-upload
action
:show-file-list=
"false"
:http-request=
"httpRequest"
>
<slot></slot>
<el-button
type=
"text"
size=
"small"
icon=
"el-icon-upload"
>
点击上传
</el-button>
<template
v-slot:tip
>
<div
class=
"el-upload__tips"
>
<slot
name=
"tip"
></slot>
</div>
</
template
>
</el-upload>
<div
class=
"file-list"
v-if=
"value"
>
<div
class=
"file-list-item"
>
<a
:href=
"value"
target=
"_blank"
>
<i
class=
"el-icon-document"
></i>
{{ fileName }}
</a>
<a
:href=
"value"
:download=
"fileName"
target=
"_blank"
>
<el-tooltip
effect=
"dark"
content=
"下载"
>
<i
class=
"el-icon-download"
></i>
</el-tooltip>
</a>
</div>
</div>
</div>
</template>
<
script
>
import
cAction
from
'@action'
export
default
{
name
:
'VUpload'
,
props
:
{
value
:
{
type
:
String
}
},
data
()
{
return
{}
},
computed
:
{
fileName
()
{
return
this
.
value
?
this
.
value
.
split
(
'/'
).
pop
()
:
''
}
},
methods
:
{
httpRequest
(
xhr
)
{
cAction
.
Player
.
uploadFile
({
file
:
xhr
.
file
})
.
then
(
response
=>
{
if
(
response
.
success
)
{
this
.
$emit
(
'input'
,
response
.
url
)
}
})
.
catch
(
error
=>
{
console
.
log
(
error
)
})
},
handleRemove
()
{
this
.
$emit
(
'input'
,
''
)
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.file-list-item
{
display
:
flex
;
margin-bottom
:
10px
;
padding
:
0
10px
;
justify-content
:
space-between
;
line-height
:
30px
;
background-color
:
#fff
;
border-radius
:
4px
;
a
{
text-decoration
:
none
;
color
:
#333
;
&
:hover
{
color
:
#b49441
;
}
}
}
</
style
>
client/src/main.js
浏览文件 @
f88708c9
...
...
@@ -5,6 +5,7 @@ import VueI18n from 'vue-i18n' // 使用 国际化
import
createI18n
from
'./assets/languages'
// 国际化定义
import
App
from
'./app.vue'
// 初始化 vue页面
import
UploadForm
from
'./components/upload-form'
import
'./style.scss'
// 公共样式
import
MetaInfo
from
'vue-meta-info'
import
Element
from
'element-ui'
...
...
@@ -19,6 +20,8 @@ require('promise.prototype.finally').shim()
/* 兼容处理 end */
Vue
.
use
(
VueRouter
)
Vue
.
use
(
UploadForm
)
Vue
.
component
(
UploadForm
.
name
,
UploadForm
)
const
router
=
createRouter
()
Vue
.
use
(
VueI18n
)
const
i18n
=
createI18n
()
...
...
client/src/modules/discuss/api/index.js
浏览文件 @
f88708c9
...
...
@@ -2,55 +2,94 @@ import BaseAPI from '@/api/base_api'
const
httpRequest
=
new
BaseAPI
(
webConf
)
/**
* 获取课程详情
* @param {string} courseId 课程ID
* @param {string} semesterId 学期ID
* 获取课程讨论列表
*/
export
function
getCourse
(
courseId
,
semesterId
)
{
return
httpRequest
.
get
(
`/v2/education/courses/
${
courseId
}
/
${
semesterId
}
`
)
export
const
getDiscussList
=
(
param
)
=>
{
const
paramPath
=
param
.
path
||
''
return
httpRequest
.
get
(
`/v2/qa/questions
${
paramPath
}
`
,
param
.
dataJson
)
}
/**
* 获取我的课程讨论列表
*/
export
const
getCourseDiscussList
=
(
param
)
=>
{
const
paramPath
=
param
.
path
||
''
return
httpRequest
.
get
(
`/v2/qa/questions/course
${
paramPath
}
`
,
param
.
dataJson
)
}
/**
* 删除提问
*/
export
const
deleteDiscuss
=
(
qid
)
=>
{
return
httpRequest
.
delete
(
`/v2/qa/questions/
${
qid
}
`
)
}
/**
* 获取章节资源详情
* @param {string} vid 资源ID
* 获取问题详情
*/
export
function
getChapterVideo
(
vid
)
{
export
const
getDiscussDetail
=
(
qid
)
=>
{
return
httpRequest
.
get
(
`/v2/qa/questions/
${
qid
}
`
)
}
/**
* 删除评论
*/
export
const
deleteComment
=
(
cid
)
=>
{
return
httpRequest
.
delete
(
`/v2/qa/comments/
${
cid
}
`
)
}
/**
* 回复评论
*/
export
const
callbackComment
=
(
param
)
=>
{
return
httpRequest
.
post
(
'/v2/
education/video-streaming
'
,
{
vid
}
,
'/v2/
qa/comments
'
,
param
,
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
/**
* 获取章节资源详情
* @param {string} vid 章节的资源ID
* 回答问题
*/
export
function
getChapterVideoAliyun
(
vid
)
{
export
const
answerQues
=
(
param
)
=>
{
return
httpRequest
.
post
(
'/v2/
education/aliyun-video-streaming
'
,
{
vid
}
,
'/v2/
qa/answers
'
,
param
,
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
/**
*
获取课程讨论列表
*
删除回答
*/
export
const
getDiscussList
=
(
param
)
=>
{
const
paramPath
=
param
.
path
||
''
return
httpRequest
.
get
(
`/v2/qa/questions
${
paramPath
}
`
,
param
.
dataJson
export
const
deleteAnswer
=
(
aid
)
=>
{
return
httpRequest
.
delete
(
`/v2/qa/answers/
${
aid
}
`
)
}
/**
*
获取我的课程讨论列表
*
取消点赞
*/
export
const
getCourseDiscussList
=
(
param
)
=>
{
const
paramPath
=
param
.
path
||
''
return
httpRequest
.
get
(
`/v2/qa/questions/course
${
paramPath
}
`
,
param
.
dataJson
export
const
unlike
=
(
tagid
)
=>
{
return
httpRequest
.
delete
(
`/v2/qa/tags/
${
tagid
}
`
)
}
/**
* 点赞
*/
export
const
like
=
(
param
)
=>
{
return
httpRequest
.
post
(
'/v2/qa/tags'
,
param
,
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
client/src/modules/discuss/assets/languages/en.json
浏览文件 @
f88708c9
...
...
@@ -4,6 +4,17 @@
"answers"
:
"Answers"
,
"votes"
:
"Votes"
,
"noData"
:
"No discussion"
},
"DiscussDetail"
:
{
"title"
:
"Problem details"
,
"like"
:
"Like"
,
"discuss"
:
"Discuss"
,
"reply"
:
"Reply"
,
"delete"
:
"Delete"
,
"send"
:
"Send"
,
"noAnswer"
:
"No answer"
,
"deleteSuccess"
:
"Delete success"
,
"answering"
:
"Answer"
}
}
}
\ No newline at end of file
client/src/modules/discuss/assets/languages/zh-CN.json
浏览文件 @
f88708c9
...
...
@@ -4,6 +4,17 @@
"answers"
:
"回答"
,
"votes"
:
"投票"
,
"noData"
:
"暂无相关评论"
},
"DiscussDetail"
:
{
"title"
:
"问题详情"
,
"like"
:
"点赞"
,
"discuss"
:
"讨论"
,
"reply"
:
"回复"
,
"delete"
:
"删除"
,
"send"
:
"发送"
,
"noAnswer"
:
"暂无回答"
,
"deleteSuccess"
:
"删除成功"
,
"answering"
:
"回答问题"
}
}
}
client/src/modules/discuss/components/answerItem.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div>
<div
class=
"ask"
>
<div
class=
"user-1"
>
<img
class=
"img-1"
:src=
"avatar"
/>
<div
class=
"right-1"
>
<div
class=
"name-1"
>
{{
data
.
replier
.
nickname
}}
</div>
<div
class=
"time-1"
>
{{
data
.
created_time
}}
</div>
</div>
</div>
<div
class=
"text"
v-html=
"data.contents"
></div>
<div
class=
"user"
>
<template
v-if=
"data.mine"
>
<div
class=
"right-txt"
@
click=
"deleteAnswer(data.id)"
>
{{
$t
(
'pages.learn.discussDetail.delete'
)
}}
</div>
</
template
>
<div
class=
"right-txt"
@
click=
"$emit('reply', {answer_id: data.id})"
>
{{ $t('pages.learn.discussDetail.reply') }}
</div>
<div
class=
"right-txt"
@
click=
"commentVisible = !commentVisible"
>
{{ $t('pages.learn.discussDetail.discuss') }}({{data.comments.length}})
</div>
<div
class=
"right-txt"
@
click=
"$emit('btnlike', {tagId: data.tag ? data.tag.id : null, ansId: data.id})"
>
点赞({{data.tag_count}})
</div>
</div>
<
template
v-if=
"commentVisible"
>
<!-- 评论列表 -->
<template
v-for=
"item in data.comments"
>
<reply-item
v-on=
"$listeners"
:data=
"item"
:dataId=
"data.id"
:key=
"item.id"
></reply-item>
</
template
>
</template>
</div>
</div>
</template>
<
script
>
import
*
as
api
from
'../api/index.js'
import
replyItem
from
'./replyItem.vue'
export
default
{
components
:
{
replyItem
},
props
:
{
data
:
{
type
:
Object
,
default
:
()
=>
{}
}
},
data
()
{
console
.
log
(
this
.
data
)
return
{
commentVisible
:
false
}
},
computed
:
{
avatar
()
{
return
this
.
data
.
replier
.
avatar
||
'../assets/images/person-default.jpg'
}
},
methods
:
{
deleteAnswer
(
id
)
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
api
.
deleteAnswer
(
id
).
then
(
json
=>
{
this
.
$emit
(
'updateList'
)
this
.
$message
({
type
:
'success'
,
message
:
this
.
$t
(
'pages.learn.discussDetail.deleteSuccess'
)
})
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
loading
.
close
()
})
}
}
}
</
script
>
<
style
lang=
"sass"
scoped
>
</
style
>
client/src/modules/discuss/components/replyDetailItem.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"item-list"
>
<div
class=
"user"
>
<div
class=
"name"
>
{{
data
.
observer
.
nickname
}}
</div>
<div
class=
"time"
>
{{
data
.
created_time
}}
</div>
<template
v-if=
"data.mine"
>
<div
class=
"right-txt"
@
click=
"deleteComment(data.id)"
>
{{
$t
(
'pages.learn.discussDetail.delete'
)
}}
</div>
</
template
>
<div
class=
"right-txt"
@
click=
"$emit('reply', {to: data.observer.nickname, question_id: dataId})"
>
{{ $t('pages.learn.discussDetail.reply') }}
</div>
</div>
<div
class=
"text"
v-html=
"data.comments"
></div>
</div>
</template>
<
script
>
import
*
as
api
from
'../api/index.js'
export
default
{
props
:
{
data
:
{
type
:
Object
,
default
:
()
=>
{}
},
dataId
:
{
type
:
String
,
default
:
()
=>
{}
}
},
data
()
{
return
{}
},
methods
:
{
deleteComment
(
id
)
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
api
.
deleteComment
(
id
).
then
(
json
=>
{
this
.
$emit
(
'updateList'
)
this
.
$message
({
type
:
'success'
,
message
:
this
.
$t
(
'pages.learn.discussDetail.deleteSuccess'
)
})
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
loading
.
close
()
})
}
}
}
</
script
>
client/src/modules/discuss/components/replyItem.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"item-list"
>
<div
class=
"user"
>
<div
class=
"name"
>
{{
data
.
observer
.
nickname
}}
</div>
<div
class=
"time"
>
{{
data
.
created_time
}}
</div>
<template
v-if=
"data.mine"
>
<div
class=
"right-txt"
@
click=
"deleteComment(data.id)"
>
{{
$t
(
'pages.learn.discussDetail.delete'
)
}}
</div>
</
template
>
<div
class=
"right-txt"
@
click=
"$emit('reply', {answer_id: dataId, to: data.observer.nickname})"
>
{{ $t('pages.learn.discussDetail.reply') }}
</div>
</div>
<div
class=
"text"
v-html=
"data.comments"
></div>
</div>
</template>
<
script
>
import
*
as
api
from
'../api/index.js'
export
default
{
props
:
{
data
:
{
type
:
Object
,
default
:
()
=>
{}
},
dataId
:
{
type
:
String
,
default
:
()
=>
{}
}
},
data
()
{
return
{}
},
mounted
()
{
},
methods
:
{
deleteComment
(
id
)
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
api
.
deleteComment
(
id
).
then
(
json
=>
{
this
.
$emit
(
'updateList'
)
this
.
$message
({
type
:
'success'
,
message
:
this
.
$t
(
'pages.learn.discussDetail.deleteSuccess'
)
})
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
loading
.
close
()
})
}
}
}
</
script
>
client/src/modules/discuss/index.js
浏览文件 @
f88708c9
import
'./index.scss'
import
Discuss
from
'./src/discuss.vue'
import
DiscussDetail
from
'./src/discussDetail.vue'
const
components
=
[
Discuss
Discuss
,
DiscussDetail
]
const
install
=
function
(
Vue
,
opts
=
{})
{
...
...
@@ -26,5 +28,6 @@ if (typeof window !== 'undefined' && window.Vue) {
export
default
{
install
,
Discuss
Discuss
,
DiscussDetail
}
client/src/modules/discuss/src/discuss.vue
浏览文件 @
f88708c9
...
...
@@ -28,6 +28,19 @@
</template>
<
script
>
/**
* 调用:通过组件传值监听watch来请求渲染
*
params: {
path: '/my', 地址参数
request: 'getDiscussList', 请求方法
dataJson: { 参数
limit: 10,
offset: 0
}
}
*/
import
*
as
api
from
'../api/index'
export
default
{
...
...
client/src/modules/discuss/src/discussDetail.vue
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/modules/viewer/api.js
0 → 100644
浏览文件 @
f88708c9
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
(
`/v2/education/courses/
${
semesterId
}
/
${
courseId
}
`
)
}
/**
* 获取章节资源详情
* @param {string} vid 资源ID
*/
export
function
getChapterVideo
(
vid
)
{
return
httpRequest
.
post
(
'/v2/education/video-streaming'
,
{
vid
},
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
/**
* 获取章节资源详情
* @param {string} vid 章节的资源ID
*/
export
function
getChapterVideoAliyun
(
vid
)
{
return
httpRequest
.
post
(
'/v2/education/aliyun-video-streaming'
,
{
vid
},
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
/**
* 获取答题信息
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
* @param {string} resourseId 章节的资源ID
*/
export
function
getChapterExam
(
semesterId
,
courseId
,
resourseId
)
{
return
httpRequest
.
get
(
`/v2/education/homeworks/
${
semesterId
}
/
${
courseId
}
/
${
resourseId
}
`
)
}
/**
* 提交考试
*/
export
function
sbumitChapterExam
(
params
)
{
return
httpRequest
.
post
(
'/v2/education/homeworks'
,
params
,
{
headers
:
{
'Content-Type'
:
'application/json'
}
})
}
/**
* 上传文件
*/
export
function
uploadFile
(
data
)
{
return
httpRequest
.
post
(
'/util/upload-file'
,
data
,
{
headers
:
{
'Content-Type'
:
'multipart/form-data'
}
})
}
/**
* 获取课程大作业详情
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
*/
export
function
getCourseWork
(
semesterId
,
courseId
)
{
return
httpRequest
.
get
(
`/v2/education/courses/
${
semesterId
}
/
${
courseId
}
/essay`
)
}
/**
* 提交课程大作业
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
*/
export
function
updateCourseWork
(
semesterId
,
courseId
,
data
)
{
return
httpRequest
.
post
(
`/v2/education/courses/
${
semesterId
}
/
${
courseId
}
/essay`
,
data
,
{
headers
:
{
'Content-Type'
:
'multipart/form-data'
}
}
)
}
/**
* 获取课程考试试题
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
*/
export
function
getCourseExam
(
semesterId
,
courseId
)
{
return
httpRequest
.
get
(
`/v2/education/
${
semesterId
}
/
${
courseId
}
/examination`
)
}
/**
* 获取课程考试状态
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
* @param {string} examId 试题ID
*/
export
function
getCourseExamStatus
(
semesterId
,
courseId
,
examId
)
{
return
httpRequest
.
get
(
`/v2/education/
${
semesterId
}
/
${
courseId
}
/examination/
${
examId
}
/status`
)
}
/**
* 提交课程考试
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
* @param {string} examId 试题ID
*/
export
function
submitCourseExam
(
semesterId
,
courseId
,
examId
,
data
)
{
return
httpRequest
.
post
(
`/v2/education/
${
semesterId
}
/
${
courseId
}
/examination/
${
examId
}
/sheet`
,
data
,
{
headers
:
{
'Content-Type'
:
'application/x-www-form-urlencoded'
}
}
)
}
/**
* 获取课程考试结果
* @param {string} semesterId 学期ID
* @param {string} courseId 课程ID
* @param {string} examId 试题ID
*/
export
function
getCourseExamResult
(
semesterId
,
courseId
,
examId
)
{
return
httpRequest
.
get
(
`/v2/education/
${
semesterId
}
/
${
courseId
}
/examination/
${
examId
}
/sheet`
)
}
client/src/modules/viewer/api/index.js
deleted
100644 → 0
浏览文件 @
2023e8cc
import
BaseAPI
from
'@/api/base_api'
const
httpRequest
=
new
BaseAPI
(
webConf
)
/**
* 获取课程详情
* @param {string} courseId 课程ID
* @param {string} semesterId 学期ID
*/
export
function
getCourse
(
courseId
,
semesterId
)
{
return
httpRequest
.
get
(
`/v2/education/courses/
${
courseId
}
/
${
semesterId
}
`
)
}
/**
* 获取章节资源详情
* @param {string} vid 资源ID
*/
export
function
getChapterVideo
(
vid
)
{
return
httpRequest
.
post
(
'/v2/education/video-streaming'
,
{
vid
},
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
/**
* 获取章节资源详情
* @param {string} vid 章节的资源ID
*/
export
function
getChapterVideoAliyun
(
vid
)
{
return
httpRequest
.
post
(
'/v2/education/aliyun-video-streaming'
,
{
vid
},
{
headers
:
{
'Content-Type'
:
'application/json'
}
}
)
}
client/src/modules/viewer/components/aside/
asideC
hapter.vue
→
client/src/modules/viewer/components/aside/
c
hapter.vue
浏览文件 @
f88708c9
<
template
>
<ul
class=
"chapter-list"
>
<li
class=
"chapter-item"
v-for=
"item in
list
"
:key=
"item.id"
>
<li
class=
"chapter-item"
v-for=
"item in
data
"
:key=
"item.id"
>
<h4>
{{
item
.
name
}}
</h4>
<ul
class=
"knot-list"
>
<li
v-for=
"subItem in item.children"
:key=
"subItem.id"
@
click=
"onClick(subItem)"
>
<span
class=
"knot-name"
>
{{
subItem
.
name
|
showName
(
subItem
.
type
)
}}
</span>
<ul
class=
"chapter-item-list"
>
<li
v-for=
"subItem in item.children"
:key=
"subItem.id"
@
click=
"onClick(subItem)"
:class=
"
{'is-active': subItem.id === (active ? active.id : '')}"
>
<span
class=
"chapter-item-list__name"
>
{{
subItem
.
name
|
showName
(
subItem
.
type
)
}}
</span>
<i
class=
"el-icon"
:class=
"genIconClass(subItem.type)"
></i>
</li>
</ul>
</li>
...
...
@@ -14,10 +20,12 @@
<
script
>
export
default
{
props
:
{
data
:
{
type
:
Array
,
data
:
{
type
:
Array
,
default
:
()
=>
[]
},
// 当前选中的章节
active
:
{
type
:
Object
,
default
()
{
return
[]
return
{}
}
}
},
...
...
@@ -46,8 +54,29 @@ export default {
}
},
methods
:
{
genIconClass
(
type
)
{
const
map
=
{
2
:
'el-icon-self-iconset0481'
,
3
:
'el-icon-edit-outline'
,
4
:
'el-icon-self-cc-book'
}
return
map
[
type
]
||
'el-icon-self-cc-book'
},
onClick
(
data
)
{
console
.
log
(
data
)
// 课程大作业
// if (data.id === 'course_work') {
// this.$router.push({ name: 'viewerCourseWork' })
// return
// }
// 课程资料
// if (data.id === 'course_info') {
// this.$router.push({ name: 'viewerCourseFile' })
// return
// }
this
.
$router
.
push
({
name
:
'viewerCourseChapter'
,
params
:
{
id
:
data
.
id
}
})
}
}
}
...
...
@@ -62,23 +91,23 @@ export default {
overflow
:
hidden
;
.chapter-item
{
h4
{
padding
:
10px
3
2px
;
padding
:
10px
2
2px
;
margin
:
0
;
font-size
:
15px
;
color
:
#b0b0b0
;
background-color
:
#2f2f2f
;
}
/* 节列表样式 */
.
knot
-list
{
.
chapter-item
-list
{
margin
:
0
;
padding
:
0
;
line-height
:
1
.6
;
overflow
:
hidden
;
li
{
position
:
relative
;
&
.
on
{
&
.
is-active
{
background
:
#3c3c3c
;
a
{
.chapter-item-list__name
{
color
:
#b49441
;
}
}
...
...
@@ -110,7 +139,7 @@ export default {
background
:
#616161
;
}
}
.
knot-
name
{
.
chapter-item-list__
name
{
display
:
block
;
padding
:
15px
35px
15px
40px
;
font-size
:
14px
;
...
...
@@ -126,6 +155,7 @@ export default {
right
:
10px
;
top
:
50%
;
transform
:
translateY
(
-50%
);
color
:
#a0a0a0
;
}
}
}
...
...
client/src/modules/viewer/components/aside/
aside
.vue
→
client/src/modules/viewer/components/aside/
index
.vue
浏览文件 @
f88708c9
...
...
@@ -3,12 +3,12 @@
<el-tabs
v-model=
"activeName"
>
<el-tab-pane
label=
"章节"
name=
"0"
>
<div
class=
"tab-pane"
>
<aside-chapter
:data=
"chapters"
></aside-chapter>
<aside-chapter
:data=
"chapters"
:active=
"active"
></aside-chapter>
</div>
</el-tab-pane>
<!--
<el-tab-pane
label=
"讲义"
name=
"1"
>
<div
class=
"tab-pane"
>
<aside-lecture
:data=
"ppts"
></aside-lecture>
<aside-lecture
:data=
"ppts"
:pptIndex=
"pptIndex"
v-on=
"$listeners"
></aside-lecture>
</div>
</el-tab-pane>
-->
</el-tabs>
...
...
@@ -16,25 +16,24 @@
</
template
>
<
script
>
import
AsideChapter
from
'./
asideC
hapter.vue'
import
AsideLecture
from
'./
asideL
ecture.vue'
import
AsideChapter
from
'./
c
hapter.vue'
import
AsideLecture
from
'./
l
ecture.vue'
export
default
{
props
:
{
// 章节
chapters
:
{
type
:
Array
,
default
()
{
return
[]
}
},
chapters
:
{
type
:
Array
,
default
:
()
=>
[]
},
// 讲义
ppts
:
{
type
:
Array
,
ppts
:
{
type
:
Array
,
default
:
()
=>
[]
},
// 当前选中的章节
active
:
{
type
:
Object
,
default
()
{
return
[]
return
{}
}
}
},
// 当前选择的PPT
pptIndex
:
{
type
:
Number
,
default
:
0
}
},
components
:
{
AsideChapter
,
AsideLecture
},
data
()
{
...
...
client/src/modules/viewer/components/aside/
asideL
ecture.vue
→
client/src/modules/viewer/components/aside/
l
ecture.vue
浏览文件 @
f88708c9
<
template
>
<ul
class=
"lecture-list"
>
<li
v-for=
"item in data"
:key=
"item.id"
@
click=
"onClick(item)"
>
<li
v-for=
"(item, index) in data"
:key=
"item.id"
@
click=
"onClick(index)"
:class=
"
{'is-active': index === activeIndex}"
>
<img
:src=
"item.ppt_url"
/>
</li>
</ul>
...
...
@@ -9,23 +14,25 @@
<
script
>
export
default
{
props
:
{
data
:
{
type
:
Array
,
default
()
{
return
[]
}
}
// 当前选择的PPT
pptIndex
:
{
type
:
Number
,
default
:
0
},
data
:
{
type
:
Array
,
default
:
()
=>
[]
}
},
data
()
{
return
{
activeIndex
:
0
activeIndex
:
this
.
pptIndex
}
},
watch
:
{
pptIndex
(
index
)
{
this
.
activeIndex
=
index
}
},
methods
:
{
// 点击PPT
onClick
(
data
)
{
this
.
activeIndex
=
data
.
id
this
.
$emit
(
'c
lickPPT'
,
data
)
onClick
(
index
)
{
this
.
activeIndex
=
index
this
.
$emit
(
'c
hange-ppt'
,
index
)
}
}
}
...
...
client/src/modules/viewer/components/common/container.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"course-viewer-content"
>
<div
class=
"course-viewer-content-hd"
>
<slot
name=
"header"
>
<h3
class=
"course-viewer-content-hd__title"
>
<slot
name=
"title"
>
{{
title
}}
</slot>
</h3>
<div
class=
"course-viewer-content-hd__aside"
>
<slot
name=
"header-aside"
></slot>
</div>
</slot>
</div>
<div
class=
"course-viewer-content-bd"
>
<slot></slot>
</div>
</div>
</
template
>
<
script
>
export
default
{
name
:
'Continaer'
,
props
:
{
title
:
String
}
}
</
script
>
client/src/modules/viewer/components/common/editor.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"editor"
>
<textarea
name=
"editor"
:id=
"textareaElementId"
:disabled=
"disabled"
></textarea>
</div>
</
template
>
<
script
>
import
{
uniqueId
}
from
'lodash'
export
default
{
name
:
'VEditor'
,
props
:
{
value
:
{
type
:
String
},
disabled
:
{
type
:
Boolean
,
default
:
false
}
},
data
()
{
return
{
textareaElementId
:
uniqueId
(
'editor_'
),
ckEditor
:
null
}
},
methods
:
{
createEditor
()
{
const
editor
=
(
this
.
ckEditor
=
CKEDITOR
.
replace
(
this
.
textareaElementId
,
{
height
:
400
,
uiColor
:
'#eeeeee'
,
filebrowserImageUploadUrl
:
'/api/ckeditor/img/upload'
,
// resize_enabled: typeof this.props.resizable === 'boolean' ? this.props.resizable : true,
toolbar
:
[
// { name: 'document', items: ['Source', '-', 'Save', 'NewPage', 'Preview'] },
{
name
:
'styles'
,
items
:
[
'Styles'
,
'Format'
,
'Font'
,
'FontSize'
]
},
{
name
:
'colors'
,
items
:
[
'TextColor'
,
'BGColor'
]
},
{
name
:
'tools'
,
items
:
[
'Maximize'
,
'ShowBlocks'
]
},
// { name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
{
name
:
'editing'
,
items
:
[
'Find'
,
'Replace'
]
},
// { name: 'forms', items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'] },
'/'
,
{
name
:
'basicstyles'
,
items
:
[
'Bold'
,
'Italic'
,
'Underline'
,
'Strike'
,
'Subscript'
,
'Superscript'
,
'-'
,
'RemoveFormat'
]
},
{
name
:
'paragraph'
,
items
:
[
'NumberedList'
,
'BulletedList'
,
'-'
,
'Outdent'
,
'Indent'
,
'-'
,
'Blockquote'
,
'CreateDiv'
,
'-'
,
'JustifyLeft'
,
'JustifyCenter'
,
'JustifyRight'
,
'JustifyBlock'
,
'-'
,
'BidiLtr'
,
'BidiRtl'
]
},
{
name
:
'links'
,
items
:
[
'Link'
,
'Unlink'
,
'Anchor'
]
},
{
name
:
'insert'
,
items
:
[
'Image'
,
'Table'
,
'HorizontalRule'
]
}
]
}))
editor
.
on
(
'instanceReady'
,
()
=>
{
const
data
=
this
.
value
editor
.
fire
(
'lockSnapshot'
)
editor
.
setData
(
data
,
{
callback
:
()
=>
{
this
.
bindEvent
()
const
newData
=
editor
.
getData
()
// Locking the snapshot prevents the 'change' event.
// Trigger it manually to update the bound data.
if
(
data
!==
newData
)
{
this
.
$once
(
'input'
,
()
=>
{
this
.
$emit
(
'ready'
,
editor
)
})
this
.
$emit
(
'input'
,
newData
)
}
else
{
this
.
$emit
(
'ready'
,
editor
)
}
editor
.
fire
(
'unlockSnapshot'
)
}
})
})
},
bindEvent
()
{
const
editor
=
this
.
ckEditor
editor
.
on
(
'change'
,
evt
=>
{
const
data
=
editor
.
getData
()
if
(
this
.
value
!==
data
)
{
this
.
$emit
(
'input'
,
data
,
evt
,
editor
)
}
})
editor
.
on
(
'focus'
,
evt
=>
{
this
.
$emit
(
'focus'
,
evt
,
editor
)
})
editor
.
on
(
'blur'
,
evt
=>
{
this
.
$emit
(
'blur'
,
evt
,
editor
)
})
}
},
mounted
()
{
this
.
createEditor
()
},
beforeDestroy
()
{
this
.
ckEditor
&&
this
.
ckEditor
.
destroy
()
this
.
ckEditor
=
null
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
*
{
margin
:
0
;
padding
:
0
;
}
</
style
>
client/src/modules/viewer/components/common/fileList.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div>
<ul
class=
"file-list"
v-if=
"files.length"
>
<li
class=
"file-list-item"
v-for=
"file in files"
:key=
"file.id"
>
<a
:href=
"file.file_url"
target=
"_blank"
>
<i
class=
"el-icon-document"
></i>
<div
v-html=
"file.file_name"
></div>
</a>
<span
v-if=
"file.file_size"
>
{{
file
.
file_size
}}
</span>
<a
:href=
"file.file_url"
:download=
"file.file_name"
target=
"_blank"
>
<el-tooltip
effect=
"dark"
content=
"下载"
>
<i
class=
"el-icon-download"
></i>
</el-tooltip>
</a>
</li>
</ul>
<div
class=
"empty"
v-else
>
<slot
name=
"empty"
>
暂无课程资料
</slot>
</div>
</div>
</
template
>
<
script
>
export
default
{
name
:
'FilePanel'
,
props
:
{
// 标题
title
:
{
type
:
String
,
default
:
'课程资料'
},
// 文件列表
files
:
{
type
:
Array
,
default
:
()
=>
[]
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.file-list
{
padding
:
0
;
}
.file-list-item
{
display
:
flex
;
font-size
:
16px
;
padding
:
20px
30px
;
margin-bottom
:
10px
;
background-color
:
#fff
;
list-style
:
none
;
border-radius
:
32px
;
justify-content
:
space-between
;
a
{
display
:
flex
;
align-items
:
center
;
text-decoration
:
none
;
color
:
#333
;
white-space
:
nowrap
;
&
:hover
{
color
:
#b49441
;
}
::v-deep
*
{
margin
:
0
;
padding
:
0
;
}
}
}
.empty
{
font-size
:
18px
;
line-height
:
80px
;
background-color
:
#fff
;
text-align
:
center
;
border-radius
:
40px
;
}
</
style
>
client/src/modules/viewer/components/common/upload.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"upload"
>
<el-upload
action
:show-file-list=
"false"
:http-request=
"httpRequest"
>
<slot></slot>
<el-button
type=
"text"
icon=
"el-icon-upload"
>
点击上传
</el-button>
<template
v-slot:tip
>
<div
class=
"el-upload__tips"
>
<slot
name=
"tip"
></slot>
</div>
</
template
>
</el-upload>
<div
class=
"file-list"
v-if=
"fileList.length"
>
<div
class=
"file-list-item"
v-for=
"(fileUrl, index) in fileList"
:key=
"index"
>
<a
:href=
"fileUrl"
target=
"_blank"
>
<i
class=
"el-icon-document"
></i>
{{ fileUrl | fileName }}
</a>
<a
:href=
"fileUrl"
:download=
"fileUrl | fileName"
target=
"_blank"
>
<el-tooltip
effect=
"dark"
content=
"下载"
>
<i
class=
"el-icon-download"
></i>
</el-tooltip>
</a>
</div>
</div>
</div>
</template>
<
script
>
import
*
as
api
from
'../../api'
export
default
{
name
:
'VUpload'
,
props
:
{
value
:
{
type
:
[
String
,
Array
]
}
},
data
()
{
return
{
fileList
:
[]
}
},
watch
:
{
value
(
value
)
{
if
(
value
)
{
this
.
fileList
=
Array
.
isArray
(
value
)
?
value
:
[
value
]
}
}
},
filters
:
{
fileName
(
value
)
{
return
value
?
value
.
split
(
'/'
).
pop
()
:
''
}
},
methods
:
{
httpRequest
(
xhr
)
{
api
.
uploadFile
({
file
:
xhr
.
file
}).
then
(
response
=>
{
if
(
response
.
success
)
{
if
(
Array
.
isArray
(
this
.
value
))
{
this
.
fileList
.
push
(
response
.
url
)
this
.
$emit
(
'input'
,
this
.
fileList
)
}
else
{
this
.
fileList
=
[
response
.
url
]
this
.
$emit
(
'input'
,
response
.
url
)
}
}
})
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.file-list-item
{
display
:
flex
;
margin-bottom
:
10px
;
padding
:
0
10px
;
justify-content
:
space-between
;
line-height
:
30px
;
background-color
:
#fff
;
border-radius
:
4px
;
a
{
text-decoration
:
none
;
color
:
#333
;
&
:hover
{
color
:
#b49441
;
}
}
}
</
style
>
client/src/modules/viewer/components/layout.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<component
:is=
"currentCompoent"
:chapter=
"chapter"
v-bind=
"$attrs"
v-on=
"$listeners"
v-if=
"chapter"
:key=
"pid"
/>
</
template
>
<
script
>
// components
import
ChapterPlayer
from
'./player/ChapterPlayer.vue'
// 章节视频
import
ChapterWork
from
'./work/index.vue'
// 章节作业
import
ChapterRead
from
'./read/chapterRead.vue'
// 章节资料
import
ChapterLive
from
'./live/chapterLive.vue'
// 章节直播
import
CourseWork
from
'./work/courseWork.vue'
// 课程大作业
import
CourseRead
from
'./read/courseRead.vue'
// 课程资料
import
CourseExam
from
'./work/courseExam.vue'
// 课程考试
export
default
{
name
:
'ViewerLayout'
,
components
:
{
ChapterPlayer
,
ChapterWork
,
ChapterRead
,
ChapterLive
,
CourseWork
,
CourseRead
,
CourseExam
},
props
:
{
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
computed
:
{
currentCompoent
()
{
const
componentNames
=
{
2
:
'ChapterPlayer'
,
// 视频
3
:
'ChapterWork'
,
// 作业
4
:
'ChapterRead'
,
// 资料
5
:
'ChapterLive'
,
// 直播
99
:
'CourseWork'
,
// 课程大作业
100
:
'CourseRead'
,
// 课程资料
101
:
'CourseExam'
// 课程考试
}
return
this
.
chapter
?
componentNames
[
this
.
chapter
.
type
]
||
''
:
''
},
pid
()
{
return
this
.
$route
.
params
.
id
}
}
}
</
script
>
client/src/modules/viewer/components/live/chapterLive.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
style=
"width:100%;height:100%;"
>
<iframe
:src=
"iframeUrl"
frameborder=
"0"
width=
"100%"
height=
"100%"
allow=
"autoplay;geolocation;microphone;camera;midi;encrypted-media;"
></iframe>
</div>
</
template
>
<
script
>
// 章节视频
export
default
{
name
:
'ChapterLive'
,
props
:
{
// 当前选中的
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 课程详情接口返回的数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
computed
:
{
user
()
{
return
window
.
G
.
UserInfo
?
window
.
G
.
UserInfo
.
student_info
:
{}
},
nickName
()
{
return
this
.
user
.
personal_name
||
'匿名'
},
iframeUrl
()
{
const
live
=
this
.
chapter
.
live
const
liveStatus
=
live
.
live_status
live
.
viewer_name
=
live
.
viewer_name
||
this
.
nickName
if
(
liveStatus
===
103
&&
live
.
enable_record
===
1
)
{
// enable_record 0:不启用回放 1:开启回放
// 查看回放
return
`https://view.csslcloud.net/api/view/callback?recordid=
${
live
.
record_id
}
&roomid=
${
live
.
room_id
}
&userid=
${
live
.
user_id
}
&autoLogin=true&viewername=
${
live
.
viewer_name
}
&viewertoken=
${
live
.
viewer_token
}
`
}
else
{
// 直播
return
`https://view.csslcloud.net/api/view/index?roomid=
${
live
.
room_id
}
&userid=
${
live
.
user_id
}
&autoLogin=true&viewername=
${
live
.
viewer_name
}
&viewertoken=
${
live
.
viewer_token
}
`
}
}
}
}
</
script
>
client/src/modules/viewer/components/player/
p
layer.vue
→
client/src/modules/viewer/components/player/
chapterP
layer.vue
浏览文件 @
f88708c9
<
template
>
<div
class=
"player"
>
<div
class=
"player"
v-if=
"chatperResources"
>
<div
class=
"player-main"
>
<div
class=
"player-column"
v-show=
"videoVisible"
>
<!-- 视频 -->
<video-player
:video=
"video"
></video-player>
<video-player
:isSkip=
"isSkip"
:video=
"chatperResources.video"
@
timeupdate=
"onTimeupdate"
ref=
"videoPlayer"
></video-player>
</div>
<div
class=
"player-column"
v-if=
"pptVisible"
>
<!-- ppt -->
<ppt-player
:ppts=
"ppts"
@
close=
"pptVisible = false"
@
fullscreen=
"onPPTFullscreen"
></ppt-player>
<ppt-player
:ppts=
"chatperResources.ppts"
@
close=
"pptVisible = false"
@
fullscreen=
"onPPTFullscreen"
></ppt-player>
</div>
</div>
<div
class=
"player-footer"
>
<em
class=
"player-button player-button-download"
v-if=
"pdf"
>
<a
:href=
"
pdf"
target=
"_blank"
>
下载PPT
</a>
<em
class=
"player-button player-button-download"
v-if=
"
chapter.
pdf"
>
<a
:href=
"
chapter.pdf"
download
target=
"_blank"
>
下载PPT
</a>
</em>
<em
:class=
"pptClass"
@
click=
"togglePPTVisible"
v-if=
"ppts.length"
>
同步显示PPT
</em>
<em
:class=
"pptClass"
@
click=
"togglePPTVisible"
v-if=
"
chatperResources.
ppts.length"
>
同步显示PPT
</em>
<em
:class=
"skipClass"
@
click=
"toggleSkip"
>
始终跳过片头
</em>
</div>
</div>
</
template
>
<
script
>
// api
import
*
as
api
from
'../../api'
// components
import
videoPlayer
from
'./videoPlayer.vue'
import
pptPlayer
from
'./pptPlayer.vue'
export
default
{
name
:
'Player'
,
name
:
'
Chapter
Player'
,
components
:
{
videoPlayer
,
pptPlayer
},
props
:
{
video
:
{
type
:
Object
},
pdf
:
{
type
:
String
},
ppts
:
{
type
:
Array
,
default
()
{
return
[]
}
}
// 当前章节
chapter
:
{
type
:
Object
},
// PPT当前选中的索引
pptIndex
:
{
type
:
Number
,
default
:
0
}
},
data
()
{
return
{
videoVisible
:
true
,
pptVisible
:
false
,
isSkip
:
false
isSkip
:
false
,
chatperResources
:
null
}
},
watch
:
{
pptIndex
(
index
)
{
this
.
updateVideoCurrentTime
(
index
)
}
},
computed
:
{
// 视频资源ID
resourceId
()
{
return
this
.
chapter
.
resource_id
},
/**
* 视频提供者
* @return 1是CC加密; 2是非加密; 3是阿里云
*/
videoProvider
()
{
const
video
=
this
.
chapter
.
video
||
{}
return
video
.
video_provider
||
3
},
pptClass
()
{
return
{
'player-button'
:
true
,
...
...
@@ -72,7 +99,40 @@ export default {
// PPT全屏
onPPTFullscreen
(
value
)
{
this
.
videoVisible
=
!
value
},
// 当前播放时间更新
onTimeupdate
(
time
)
{
const
ppts
=
this
.
chatperResources
.
ppts
||
[]
let
index
=
this
.
chatperResources
.
ppts
.
findIndex
(
item
=>
item
.
ppt_point
>
time
)
index
=
index
!==
-
1
?
index
-
1
:
ppts
.
length
-
1
this
.
$emit
(
'change-ppt'
,
index
)
},
// 更新视频当前播放时间
updateVideoCurrentTime
()
{
const
player
=
this
.
$refs
.
videoPlayer
.
player
const
ppt
=
this
.
chatperResources
.
ppts
[
this
.
pptIndex
]
ppt
&&
player
.
seek
(
ppt
.
ppt_point
)
// 增加2秒
},
// 获取章节视频详情
getChapterVideo
()
{
// 视频播放类型 1是CC加密; 2是非加密; 3是阿里云
if
(
this
.
videoProvider
===
3
)
{
api
.
getChapterVideoAliyun
(
this
.
resourceId
).
then
(
response
=>
{
this
.
chatperResources
=
response
Array
.
isArray
(
response
.
ppts
)
&&
this
.
$emit
(
'pptupdate'
,
response
.
ppts
)
})
}
else
{
api
.
getChapterVideo
(
this
.
resourceId
).
then
(
response
=>
{
this
.
chatperResources
=
response
Array
.
isArray
(
response
.
ppts
)
&&
this
.
$emit
(
'pptupdate'
,
response
.
ppts
)
})
}
}
},
beforeMount
()
{
this
.
getChapterVideo
()
}
}
</
script
>
...
...
@@ -83,6 +143,7 @@ export default {
flex-direction
:
column
;
width
:
100%
;
height
:
100%
;
background-color
:
#3f3f3f
;
}
.player-main
{
display
:
flex
;
...
...
client/src/modules/viewer/components/player/videoPlayer.vue
浏览文件 @
f88708c9
...
...
@@ -5,12 +5,13 @@
<
script
>
export
default
{
name
:
'VideoPlayer'
,
props
:
{
video
:
Object
},
props
:
{
isSkip
:
Boolean
,
video
:
Object
},
data
()
{
return
{
player
:
null
}
},
methods
:
{
createPlayer
()
{
const
_this
=
this
const
{
FD
,
LD
,
SD
}
=
this
.
video
this
.
player
=
new
Aliplayer
(
{
...
...
@@ -29,16 +30,17 @@ export default {
]
},
function
(
player
)
{
console
.
log
(
'The player is created'
)
/* Register the sourceloaded of the player, query the resolution of the video, invoke the resolution component, and call the setCurrentQuality method to set the resolution. */
player
.
on
(
'sourceloaded'
,
function
(
params
)
{
var
paramData
=
params
.
paramData
var
desc
=
paramData
.
desc
var
definition
=
paramData
.
definition
const
paramData
=
params
.
paramData
const
desc
=
paramData
.
desc
const
definition
=
paramData
.
definition
player
.
getComponent
(
'QualityComponent'
)
.
setCurrentQuality
(
desc
,
definition
)
})
player
.
on
(
'timeupdate'
,
function
(
event
)
{
_this
.
$emit
(
'timeupdate'
,
player
.
getCurrentTime
())
})
}
)
}
...
...
client/src/modules/viewer/components/read/chapterRead.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<container
:title=
"chapter.name"
>
<file-list
:files=
"files"
></file-list>
</container>
</
template
>
<
script
>
// components
import
Container
from
'../common/container.vue'
import
FileList
from
'../common/fileList.vue'
// 章节阅读资料
export
default
{
name
:
'ChapterRead'
,
components
:
{
Container
,
FileList
},
props
:
{
// 当前选中的
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 课程详情接口返回的数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
computed
:
{
files
()
{
const
reading
=
this
.
chapter
.
reading
const
file
=
{
file_name
:
reading
.
reading_content
,
file_url
:
reading
.
reading_attachment
}
return
[
file
]
}
}
}
</
script
>
client/src/modules/viewer/components/read/courseRead.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<container
:title=
"chapter.name"
>
<file-list
:files=
"files"
></file-list>
</container>
</
template
>
<
script
>
// components
import
Container
from
'../common/container.vue'
import
FileList
from
'../common/fileList.vue'
// 课程阅读资料
export
default
{
name
:
'CourseRead'
,
components
:
{
Container
,
FileList
},
props
:
{
// 当前选中的
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 课程详情接口返回的数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
computed
:
{
files
()
{
return
this
.
data
.
files
||
[]
}
}
}
</
script
>
client/src/modules/viewer/components/read/fileList.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div>
<ul
class=
"file-list"
v-if=
"files.length"
>
<li
class=
"file-list-item"
v-for=
"file in files"
:key=
"file.id"
>
<a
:href=
"file.file_url"
target=
"_blank"
>
<i
class=
"el-icon-document"
></i>
{{
file
.
file_name
}}
</a>
<span
v-if=
"file.file_size"
>
{{
file
.
file_size
}}
</span>
<a
:href=
"file.file_url"
:download=
"file.file_name"
target=
"_blank"
>
<el-tooltip
effect=
"dark"
content=
"下载"
>
<i
class=
"el-icon-download"
></i>
</el-tooltip>
</a>
</li>
</ul>
<div
class=
"empty"
v-else
>
<slot
name=
"empty"
>
暂无课程资料
</slot>
</div>
</div>
</
template
>
<
script
>
export
default
{
name
:
'FilePanel'
,
props
:
{
// 标题
title
:
{
type
:
String
,
default
:
'课程资料'
},
// 文件列表
files
:
{
type
:
Array
,
default
:
()
=>
[]
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.file-list
{
padding
:
0
;
}
.file-list-item
{
display
:
flex
;
font-size
:
16px
;
padding
:
20px
30px
;
margin-bottom
:
10px
;
background-color
:
#fff
;
list-style
:
none
;
border-radius
:
32px
;
justify-content
:
space-between
;
a
{
text-decoration
:
none
;
color
:
#333
;
&
:hover
{
color
:
#b49441
;
}
}
}
.empty
{
font-size
:
18px
;
line-height
:
80px
;
background-color
:
#fff
;
text-align
:
center
;
border-radius
:
40px
;
}
</
style
>
client/src/modules/viewer/components/work/chapterExam.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<container
:title=
"chapter.name"
v-loading=
"loading"
>
<template
v-slot:header-aside
v-if=
"isSubmited"
>
正确率:
{{
detail
.
score
}}
%
</
template
>
<div
class=
"exam"
>
<div
class=
"exam-form"
>
<el-form
:disabled=
"isSubmited"
>
<exam-item
v-for=
"(item, index) in unorderedQuestions"
:index=
"index"
:type=
"item.question_type"
:data=
"item"
:value=
"item.formModel"
:disabled=
"isSubmited"
:key=
"item.id"
></exam-item>
<div
class=
"exam-buttons"
>
<el-tooltip
effect=
"dark"
content=
"提交之后就不能修改了哦"
placement=
"right"
>
<el-button
type=
"primary"
@
click=
"onSubmit"
>
{{submitText}}
</el-button>
</el-tooltip>
</div>
</el-form>
</div>
</div>
</container>
</template>
<
script
>
// libs
import
{
shuffle
}
from
'lodash'
// components
import
Container
from
'../common/container.vue'
import
ExamItem
from
'./examItem.vue'
// api
import
*
as
api
from
'../../api'
// 章节测试题
export
default
{
name
:
'ChapterExam'
,
components
:
{
Container
,
ExamItem
},
props
:
{
// 当前选中的章节
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
data
()
{
return
{
loading
:
false
,
detail
:
null
,
questions
:
[],
// 问题列表
startTime
:
new
Date
().
getTime
(),
// 进入时间
messageInstance
:
null
}
},
watch
:
{
chapter
:
{
immediate
:
true
,
handler
(
data
)
{
this
.
questions
=
data
.
homework
?
this
.
genQuenstions
(
data
.
homework
.
questions
)
:
[]
}
}
},
computed
:
{
// 学期ID
sid
()
{
return
this
.
$route
.
params
.
sid
},
// 课程ID
cid
()
{
return
this
.
$route
.
params
.
cid
},
// 当前页面的ID
pid
()
{
return
this
.
$route
.
params
.
id
},
// 资源ID
resourceId
()
{
return
this
.
chapter
.
resource_id
},
// 打乱顺序的问题列表
unorderedQuestions
()
{
const
ids
=
this
.
questions
.
map
(
item
=>
item
.
id
)
const
sortIds
=
shuffle
(
ids
)
return
sortIds
.
map
(
id
=>
this
.
questions
.
find
(
item
=>
item
.
id
===
id
))
},
// 是否提交
isSubmited
()
{
return
this
.
detail
?
!!
this
.
detail
.
work_contents
:
false
},
// 提交按钮文本
submitText
()
{
return
this
.
isSubmited
?
'已提交'
:
'提交'
}
},
methods
:
{
// 获取测试答题详情
getDetail
()
{
this
.
loading
=
true
api
.
getChapterExam
(
this
.
sid
,
this
.
cid
,
this
.
resourceId
)
.
then
(
response
=>
{
this
.
detail
=
Array
.
isArray
(
response
)
?
null
:
response
if
(
this
.
detail
)
{
const
parseAnswers
=
JSON
.
parse
(
this
.
detail
.
work_contents
)
// 设置答案
this
.
questions
=
this
.
questions
.
map
(
item
=>
{
const
found
=
parseAnswers
.
find
(
answer
=>
answer
.
question_id
===
item
.
id
)
if
(
found
)
{
const
selectedIds
=
found
.
options
.
reduce
((
result
,
item
)
=>
{
item
.
selected
&&
result
.
push
(
item
.
id
)
return
result
},
[])
item
.
user_answer
=
item
.
question_type
===
2
?
selectedIds
:
selectedIds
[
0
]
}
return
item
})
this
.
questions
=
this
.
genQuenstions
(
this
.
questions
)
}
})
.
finally
(()
=>
{
this
.
loading
=
false
})
},
// 组装问题数据
genQuenstions
(
list
)
{
if
(
!
list
)
{
return
[]
}
return
list
.
map
(
item
=>
{
let
temp
=
null
if
(
item
.
question_type
===
1
)
{
// 单选
temp
=
{
formModel
:
{
id
:
item
.
id
,
user_answer
:
item
.
user_answer
||
''
}
}
}
else
if
(
item
.
question_type
===
2
)
{
// 多选
temp
=
{
formModel
:
{
id
:
item
.
id
,
user_answer
:
item
.
user_answer
||
[]
}
}
}
else
if
(
item
.
question_type
===
3
)
{
// 简答
temp
=
{
formModel
:
{
id
:
item
.
id
,
user_answer
:
item
.
user_answer
?
Base64
.
decode
(
item
.
user_answer
)
:
''
,
attachments
:
item
.
attachments
||
''
}
}
}
return
Object
.
assign
(
{},
item
,
{
content
:
item
.
question_content
,
options
:
item
.
question_options
?
JSON
.
parse
(
item
.
question_options
)
:
[]
},
temp
)
})
},
// 提交校验
checkSubmit
()
{
const
quenstions
=
this
.
questions
for
(
let
i
=
0
;
i
<
quenstions
.
length
;
i
++
)
{
const
value
=
quenstions
[
i
].
formModel
.
user_answer
if
(
Array
.
isArray
(
value
)
?
!
value
.
length
:
!
value
)
{
return
false
}
}
return
true
},
// 提交
onSubmit
()
{
// 校验
if
(
!
this
.
checkSubmit
())
{
this
.
messageInstance
&&
this
.
messageInstance
.
close
()
this
.
messageInstance
=
this
.
$message
.
error
(
'还有题目未做,不能提交'
)
return
}
// 计算答题时间
const
duration
=
Math
.
floor
(
(
new
Date
().
getTime
()
-
this
.
startTime
)
/
1000
)
// 答案数据
const
data
=
this
.
handleSubmitData
()
// 计算分数
const
score
=
data
.
reduce
((
result
,
item
)
=>
{
item
.
is_correct
&&
result
++
return
result
},
0
)
const
total
=
this
.
questions
.
length
const
params
=
{
semester_id
:
this
.
sid
,
course_id
:
this
.
cid
,
chapter_id
:
this
.
pid
,
work_id
:
this
.
resourceId
,
work_contents
:
JSON
.
stringify
(
data
),
duration
,
score
:
((
score
/
total
)
*
100
).
toFixed
(
1
)
}
// 请求接口
this
.
handleSubmitRequest
(
params
)
},
// 提交的答案数据
handleSubmitData
()
{
const
result
=
this
.
questions
.
map
(
item
=>
{
// 设置提交选中状态
let
isCorrect
=
true
const
options
=
item
.
options
.
map
(
option
=>
{
// 选择的项
const
answers
=
item
.
formModel
.
user_answer
// 是否选中该项
const
selected
=
Array
.
isArray
(
answers
)
?
answers
.
includes
(
option
.
id
)
:
option
.
id
===
answers
// 是否选择正确
if
(
option
.
checked
!==
selected
&&
isCorrect
)
{
isCorrect
=
false
}
return
{
id
:
option
.
id
,
checked
:
option
.
checked
,
option
:
option
.
option
,
selected
}
})
return
{
question_id
:
item
.
id
,
is_correct
:
isCorrect
?
1
:
0
,
options
}
})
return
result
},
// 请求提交接口
handleSubmitRequest
(
params
)
{
api
.
sbumitChapterExam
(
params
).
then
(
response
=>
{
if
(
response
.
status
)
{
this
.
getDetail
()
}
else
{
this
.
$message
.
error
(
response
.
data
.
error
)
}
})
}
},
beforeMount
()
{
this
.
getDetail
()
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.exam-buttons
{
padding
:
40px
0
;
text-align
:
center
;
.el-button
{
width
:
240px
;
margin
:
40px
auto
;
}
}
</
style
>
client/src/modules/viewer/components/work/chapterWork.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<container
:title=
"chapter.name"
v-loading=
"loading"
>
<div
class=
"exam-form"
>
<el-form
:disabled=
"isRevised"
>
<exam-item
v-for=
"(item, index) in questions"
:index=
"index"
:type=
"item.question_type"
:data=
"item"
:value=
"item.formModel"
:disabled=
"isRevised"
:key=
"item.id"
></exam-item>
</el-form>
</div>
<div
class=
"work-bottom"
v-if=
"detail"
>
<div
class=
"info"
>
<template
v-if=
"isRevised"
>
<div
class=
"paper-check"
>
<p>
批改时间:
{{
detail
.
check_date
}}
</p>
<div
class=
"paper-check-item"
>
<b>
评分:
</b>
{{
detail
.
score
}}
</div>
<div
class=
"paper-check-item"
>
<b>
评语:
</b>
<div
class=
"edit_html"
v-html=
"detail.check_comments"
></div>
</div>
</div>
</
template
>
<
template
v-else-if=
"detail.created_time"
>
<p
class=
"help"
>
已于
{{
detail
.
created_time
}}
提交,等待老师批改中。
</p>
<template
v-if=
"detail.updated_time && detail.updated_time !== detail.created_time"
>
<p
class=
"help"
>
最近提交时间:
{{
detail
.
updated_time
}}
</p>
</
template
>
</template>
</div>
</div>
<div
class=
"buttons"
>
<el-tooltip
content=
"在获老师批改之前,可以多次提交,将以最后一次提交为准"
placement=
"right"
>
<el-button
type=
"primary"
@
click=
"onSubmit"
:disabled=
"isRevised"
>
{{submitText}}
</el-button>
</el-tooltip>
</div>
</container>
</template>
<
script
>
import
Base64
from
'Base64'
// componets
import
Container
from
'../common/container.vue'
import
ExamItem
from
'./examItem.vue'
// api
import
*
as
api
from
'../../api'
// 章节作业
export
default
{
name
:
'ChapterWork'
,
components
:
{
Container
,
ExamItem
},
props
:
{
// 当前选中的
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 课程详情接口返回的数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
data
()
{
return
{
loading
:
false
,
detail
:
null
,
questions
:
[],
// 问题列表
startTime
:
new
Date
().
getTime
(),
// 进入时间
messageInstance
:
null
}
},
watch
:
{
chapter
:
{
immediate
:
true
,
handler
(
data
)
{
this
.
questions
=
data
.
homework
?
this
.
genQuenstions
(
data
.
homework
.
questions
)
:
[]
}
}
},
computed
:
{
// 学期ID
sid
()
{
return
this
.
$route
.
params
.
sid
},
// 课程ID
cid
()
{
return
this
.
$route
.
params
.
cid
},
// 当前页面的ID
pid
()
{
return
this
.
$route
.
params
.
id
},
// 资源ID
resourceId
()
{
return
this
.
chapter
.
resource_id
},
// 是否批改
isRevised
()
{
return
this
.
detail
?
!!
this
.
detail
.
check_date
:
false
},
// 提交按钮文本
submitText
()
{
return
this
.
isRevised
?
'已批改'
:
'提交'
}
},
methods
:
{
// 获取详情
getDetail
()
{
this
.
loading
=
true
api
.
getChapterExam
(
this
.
sid
,
this
.
cid
,
this
.
resourceId
)
.
then
(
response
=>
{
this
.
detail
=
Array
.
isArray
(
response
)
?
null
:
response
if
(
this
.
detail
)
{
const
parseAnswers
=
JSON
.
parse
(
this
.
detail
.
work_contents
)
// 设置答案
this
.
questions
=
this
.
questions
.
map
(
item
=>
{
const
found
=
parseAnswers
.
find
(
answer
=>
answer
.
question_id
===
item
.
id
)
if
(
found
)
{
item
.
user_answer
=
found
.
descreption
item
.
attachments
=
found
.
file_url
}
return
item
})
this
.
questions
=
this
.
genQuenstions
(
this
.
questions
)
}
})
.
finally
(()
=>
{
this
.
loading
=
false
})
},
// 组装问题数据
genQuenstions
(
list
)
{
if
(
!
list
)
{
return
[]
}
return
list
.
map
(
item
=>
{
let
temp
=
null
if
(
item
.
question_type
===
1
)
{
// 单选
temp
=
{
formModel
:
{
id
:
item
.
id
,
user_answer
:
item
.
user_answer
||
''
}
}
}
else
if
(
item
.
question_type
===
2
)
{
// 多选
temp
=
{
formModel
:
{
id
:
item
.
id
,
user_answer
:
item
.
user_answer
||
[]
}
}
}
else
if
(
item
.
question_type
===
3
)
{
// 简答
temp
=
{
formModel
:
{
id
:
item
.
id
,
user_answer
:
item
.
user_answer
?
Base64
.
decode
(
item
.
user_answer
)
:
''
,
attachments
:
item
.
attachments
||
''
}
}
}
return
Object
.
assign
(
{},
item
,
{
content
:
item
.
question_content
,
options
:
item
.
question_options
?
JSON
.
parse
(
item
.
question_options
)
:
[]
},
temp
)
})
},
// 提交校验
checkSubmit
()
{
const
quenstions
=
this
.
questions
for
(
let
i
=
0
;
i
<
quenstions
.
length
;
i
++
)
{
const
value
=
quenstions
[
i
].
formModel
.
user_answer
if
(
Array
.
isArray
(
value
)
?
!
value
.
length
:
!
value
)
{
return
false
}
}
return
true
},
// 提交
onSubmit
()
{
// 校验
if
(
!
this
.
checkSubmit
())
{
this
.
messageInstance
&&
this
.
messageInstance
.
close
()
this
.
messageInstance
=
this
.
$message
.
error
(
'还有题目未做,不能提交'
)
return
}
// 计算答题时间
const
duration
=
Math
.
floor
(
(
new
Date
().
getTime
()
-
this
.
startTime
)
/
1000
)
// 提交的答案数据
const
answers
=
this
.
questions
.
map
(
item
=>
{
if
(
item
.
question_type
===
3
)
{
item
.
formModel
.
user_answer
=
Base64
.
encode
(
item
.
formModel
.
user_answer
)
}
return
{
question_id
:
item
.
id
,
descreption
:
item
.
formModel
.
user_answer
,
file_url
:
item
.
formModel
.
attachments
,
is_encoded
:
1
}
})
// 提交参数
const
params
=
{
semester_id
:
this
.
sid
,
course_id
:
this
.
cid
,
chapter_id
:
this
.
pid
,
work_id
:
this
.
resourceId
,
work_contents
:
JSON
.
stringify
(
answers
),
duration
}
// 请求接口
this
.
handleSubmitRequest
(
params
)
},
// 请求提交接口
handleSubmitRequest
(
params
)
{
api
.
sbumitChapterExam
(
params
)
.
then
(
response
=>
{
if
(
response
.
status
)
{
this
.
$message
.
success
(
'提交成功,等待批改'
)
this
.
getDetail
()
}
else
{
this
.
$message
.
error
(
response
.
data
.
error
)
}
})
.
catch
(
error
=>
{
this
.
$message
.
error
(
error
.
message
)
})
}
},
beforeMount
()
{
this
.
getDetail
()
}
}
</
script
>
client/src/modules/viewer/components/work/courseExam.vue
0 → 100644
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/modules/viewer/components/work/courseWork.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<container
:title=
"chapter.name"
v-loading=
"loading"
>
<el-steps
direction=
"vertical"
v-if=
"data.curriculum"
>
<el-step
title=
"阅读大作业要求"
status=
"process"
>
<template
v-slot:description
>
<div
v-html=
"data.curriculum.curriculum_essay"
></div>
<p>
截止日期:
{{
data
.
essay_date
}}
</p>
</
template
>
</el-step>
<el-step
title=
"填写作业主题、正文,上传附件(点击“提交”保存)"
status=
"process"
>
<
template
v-slot:description
>
<el-form
:model=
"ruleForm"
:rules=
"rules"
:hide-required-asterisk=
"true"
:disabled=
"isRevised"
label-position=
"top"
ref=
"ruleForm"
>
<el-form-item
label=
"主题"
prop=
"essay_name"
>
<el-input
v-model=
"ruleForm.essay_name"
placeholder=
"主题"
></el-input>
</el-form-item>
<el-form-item
label=
"正文"
prop=
"essay_description"
>
<!-- 编辑器 -->
<v-editor
:disabled=
"isRevised"
v-model=
"ruleForm.essay_description"
></v-editor>
</el-form-item>
<el-form-item
prop=
"url"
>
<!-- 文件上传 -->
<v-upload
v-model=
"ruleForm.url"
>
请上传对应的文件附件:
<!--
<template
v-slot:tip
>
只支持docx格式的文件,文件小于10M
</
template
>
-->
</v-upload>
</el-form-item>
</el-form>
</template>
</el-step>
<el-step
title=
"截止日期前提交"
status=
"process"
>
<
template
v-slot:description
>
<div
class=
"work-bottom"
v-if=
"detail"
>
<div
class=
"info"
>
<template
v-if=
"isRevised"
>
<div
class=
"paper-check"
>
<p>
批改时间:
{{
detail
.
check_date
}}
</p>
<div
class=
"paper-check-item"
>
<b>
评分:
</b>
{{
detail
.
score
}}
</div>
<div
class=
"paper-check-item"
>
<b>
评语:
</b>
<div
class=
"edit_html"
v-html=
"detail.check_comments"
></div>
</div>
</div>
</
template
>
<
template
v-else-if=
"detail.created_time"
>
<p
class=
"help"
>
已于
{{
detail
.
created_time
}}
提交,等待老师批改中。
</p>
<template
v-if=
"detail.updated_time && detail.updated_time !== detail.created_time"
>
<p
class=
"help"
>
最近提交时间:
{{
detail
.
updated_time
}}
</p>
</
template
>
</template>
</div>
</div>
<div
class=
"buttons"
>
<el-tooltip
content=
"在获老师批改之前,可以多次提交,将以最后一次提交为准"
placement=
"right"
>
<el-button
type=
"primary"
@
click=
"onSubmit"
:disabled=
"isRevised"
>
{{submitText}}
</el-button>
</el-tooltip>
</div>
</template>
</el-step>
</el-steps>
</container>
</template>
<
script
>
// componets
import
Container
from
'../common/container.vue'
import
VEditor
from
'../common/editor.vue'
import
VUpload
from
'../common/upload.vue'
// api
import
*
as
api
from
'../../api'
// 课程大作业
export
default
{
name
:
'CourseWork'
,
components
:
{
Container
,
VEditor
,
VUpload
},
props
:
{
// 当前选中的
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 课程详情接口返回的数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
data
()
{
return
{
ruleForm
:
{
essay_name
:
''
,
essay_description
:
''
,
url
:
''
},
rules
:
{
essay_name
:
[
{
required
:
true
,
message
:
'请输入主题'
,
trigger
:
'blur'
},
{
max
:
5
,
message
:
'最多输入 50 个字符'
,
trigger
:
'blur'
}
],
essay_description
:
[
{
required
:
true
,
message
:
'请输入正文'
,
trigger
:
'blur'
}
],
url
:
[{
required
:
true
,
message
:
'请上传附件'
,
trigger
:
'change'
}]
},
detail
:
null
,
loading
:
false
,
messageInstance
:
null
}
},
computed
:
{
// 学期ID
sid
()
{
return
this
.
$route
.
params
.
sid
},
// 课程ID
cid
()
{
return
this
.
$route
.
params
.
cid
},
// 是否批改
isRevised
()
{
return
this
.
detail
?
!!
this
.
detail
.
check_date
:
false
},
// 提交按钮文本
submitText
()
{
return
this
.
isRevised
?
'已批改'
:
'提交'
}
},
methods
:
{
// 获取大作业详情
getDetail
()
{
this
.
loading
=
true
api
.
getCourseWork
(
this
.
sid
,
this
.
cid
)
.
then
(
response
=>
{
this
.
detail
=
Array
.
isArray
(
response
)
?
null
:
response
if
(
this
.
detail
)
{
this
.
ruleForm
.
essay_name
=
this
.
detail
.
essay_name
this
.
ruleForm
.
essay_description
=
this
.
detail
.
essay_description
this
.
ruleForm
.
url
=
this
.
detail
.
file_url
}
})
.
finally
(()
=>
{
this
.
loading
=
false
})
},
// 提交
onSubmit
()
{
this
.
$refs
.
ruleForm
.
validate
()
.
then
(
response
=>
{
const
params
=
Object
.
assign
(
this
.
ruleForm
,
{
semester_id
:
this
.
sid
,
course_id
:
this
.
cid
})
this
.
handleSubmitRequest
(
params
)
})
.
catch
(()
=>
{
this
.
messageInstance
&&
this
.
messageInstance
.
close
()
this
.
messageInstance
=
this
.
$message
.
error
(
'还有题目未做,不能提交'
)
})
},
// 请求提交接口
handleSubmitRequest
(
params
)
{
api
.
updateCourseWork
(
this
.
sid
,
this
.
cid
,
params
)
.
then
(
response
=>
{
if
(
response
.
status
)
{
this
.
$message
.
success
(
'提交成功,等待批改'
)
this
.
getDetail
()
}
else
{
this
.
$message
.
error
(
response
.
data
.
error
)
}
})
.
catch
(
error
=>
{
this
.
$message
.
error
(
error
.
message
)
})
}
},
beforeMount
()
{
this
.
getDetail
()
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
p
{
margin
:
0
;
}
::v-deep
.el-step__title
{
border-bottom
:
1px
dashed
#cecece
;
}
::v-deep
.el-step__description
{
padding
:
20px
0
30px
;
font-size
:
14px
;
}
::v-deep
.el-form-item__label
{
font-weight
:
bold
;
line-height
:
24px
;
padding
:
0
0
5px
;
}
.work-bottom
{
.info
{
color
:
#999
;
line-height
:
28px
;
}
}
.buttons
{
padding
:
20px
0
;
::v-deep
.el-button
{
width
:
120px
;
}
}
.paper-check
{
padding
:
10px
;
color
:
#000
;
border
:
1px
solid
#dedede
;
}
.paper-check-item
{
display
:
flex
;
}
</
style
>
client/src/modules/viewer/components/work/examItem.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<div
class=
"q-item"
>
<div
class=
"q-item-hd"
>
<div
class=
"q-item-num"
>
{{
index
+
1
}}
.
</div>
<div
class=
"q-item-title"
v-html=
"data.content"
>
{{
data
.
title
}}
</div>
<div
class=
"q-item-aside"
v-if=
"typeText"
>
(
{{
typeText
}}
)
</div>
</div>
<div
class=
"q-item-bd"
>
<!-- 单选 -->
<el-radio-group
v-model=
"currentValue.user_answer"
v-if=
"type === 1"
>
<div
class=
"q-option-item"
v-for=
"item in currentOptions"
:key=
"item.id"
>
<el-radio
:class=
"genClass(item)"
:label=
"item.id"
>
{{
item
.
abc_option
}}
</el-radio>
</div>
</el-radio-group>
<!-- 多选 -->
<el-checkbox-group
v-model=
"currentValue.user_answer"
v-if=
"type === 2"
>
<div
class=
"q-option-item"
v-for=
"item in currentOptions"
:key=
"item.id"
>
<el-checkbox
:class=
"genClass(item)"
:label=
"item.id"
>
{{
item
.
abc_option
}}
</el-checkbox>
</div>
</el-checkbox-group>
<!-- 简答题 -->
<template
v-if=
"type === 3"
>
<v-editor
v-model=
"currentValue.user_answer"
:disabled=
"disabled"
></v-editor>
<v-upload
v-model=
"currentValue.attachments"
>
请上传对应的文件附件:
</v-upload>
</
template
>
</div>
<div
class=
"q-item-ft"
v-if=
"disabled"
>
<
template
v-if=
"type === 3"
>
<p>
<span>
老师评语:
</span>
<span>
{{
data
.
check_comment
}}
</span>
</p>
</
template
>
<
template
v-else
>
<p>
<span>
学生答案:
</span>
<span
:class=
"isCorrect ? 'is-success' : 'is-error'"
>
{{
submitAnswerText
}}
</span>
</p>
<p>
<span>
正确答案:
</span>
<span>
{{
correctAnswerText
}}
</span>
</p>
</
template
>
</div>
</div>
</template>
<
script
>
// components
import
VEditor
from
'../common/editor.vue'
import
VUpload
from
'../common/upload.vue'
export
default
{
name
:
'ExamItem'
,
components
:
{
VEditor
,
VUpload
},
props
:
{
// 索引
index
:
{
type
:
Number
},
// 问题类型
type
:
{
type
:
Number
},
// 单条数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 提交的答案
value
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 是否禁用,提交过的是禁用状态
disabled
:
{
type
:
Boolean
,
default
:
false
}
},
data
()
{
return
{
currentValue
:
{}
}
},
watch
:
{
value
:
{
immediate
:
true
,
handler
(
value
)
{
this
.
currentValue
=
value
}
}
},
computed
:
{
// 26个英文字母
A_Z
()
{
const
result
=
[]
for
(
let
i
=
0
;
i
<
26
;
i
++
)
{
result
.
push
(
String
.
fromCharCode
(
65
+
i
))
}
return
result
},
// 选项类型
typeText
()
{
const
map
=
{
1
:
'单选题'
,
2
:
'多选题'
}
return
map
[
this
.
type
]
},
// 处理后的options数据
currentOptions
()
{
if
(
!
this
.
data
.
options
)
{
return
[]
}
return
this
.
data
.
options
.
map
((
item
,
index
)
=>
{
// 英文字母 + 名称
item
.
abc
=
this
.
A_Z
[
index
]
item
.
abc_option
=
`
${
this
.
A_Z
[
index
]}
.
${
item
.
option
}
`
// 提交时的选中状态
const
value
=
this
.
value
.
user_answer
||
''
item
.
selected
=
Array
.
isArray
(
value
)
?
value
.
includes
(
item
.
id
)
:
value
===
item
.
id
return
item
})
},
// 正确答案显示的英文字母
correctAnswerText
()
{
const
result
=
this
.
currentOptions
.
reduce
((
result
,
item
)
=>
{
item
.
checked
&&
result
.
push
(
item
.
abc
)
return
result
},
[])
return
result
.
join
(
'、'
)
},
// 提交答案显示的英文字母
submitAnswerText
()
{
const
result
=
this
.
currentOptions
.
reduce
((
result
,
item
)
=>
{
item
.
selected
&&
result
.
push
(
item
.
abc
)
return
result
},
[])
return
result
.
join
(
'、'
)
},
// 是否回答正确
isCorrect
()
{
const
options
=
this
.
currentOptions
for
(
let
i
=
0
;
i
<
options
.
length
;
i
++
)
{
if
(
options
[
i
].
checked
!==
!!
options
[
i
].
selected
)
{
return
false
}
}
return
true
}
},
methods
:
{
// 生成class
genClass
(
item
)
{
if
(
!
this
.
disabled
)
{
return
null
}
return
{
'is-error'
:
!
this
.
isCorrect
&&
item
.
selected
,
'is-success'
:
this
.
isCorrect
&&
item
.
selected
}
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.q-item
{
font-size
:
16px
;
padding
:
10px
0
;
border-bottom
:
1px
solid
#c9c9c9
7a
;
.upload
{
font-size
:
14px
;
}
}
.q-item-hd
{
display
:
flex
;
padding
:
10px
0
20px
;
::v-deep
p
{
margin
:
0
;
padding
:
0
;
}
::v-deep
ul
{
margin
:
0
;
padding
:
0
;
list-style
:
none
;
}
}
.q-item-num
{
width
:
20px
;
text-align
:
center
;
}
.q-item-title
{
flex
:
1
;
padding
:
0
10px
;
}
.q-item-aside
{
padding-left
:
20px
;
// align-self: flex-end;
}
.q-option-item
{
padding-left
:
30px
;
margin-bottom
:
14px
;
}
.is-success
{
color
:
#090
;
}
.is-error
{
color
:
#d80000
;
}
::v-deep
.el-radio
{
&
.is-disabled
.el-radio__label
{
color
:
#3c3c3c
;
}
&
.is-error
.el-radio__label
{
color
:
#d80000
;
}
&
.is-success
.el-radio__label
{
color
:
#090
;
}
}
::v-deep
.el-checkbox
{
&
.is-disabled
.el-checkbox__label
{
color
:
#3c3c3c
;
}
&
.is-error
.el-checkbox__label
{
color
:
#d80000
;
}
&
.is-success
.el-checkbox__label
{
color
:
#090
;
}
}
.q-item-ft
{
display
:
flex
;
justify-content
:
flex-end
;
padding
:
10px
0
;
p
{
font-size
:
14px
;
margin
:
0
;
padding-left
:
20px
;
}
}
</
style
>
client/src/modules/viewer/components/work/index.vue
0 → 100644
浏览文件 @
f88708c9
<
template
>
<component
:is=
"currentCompoent"
:chapter=
"chapter"
:data=
"data"
v-bind=
"$attrs"
v-on=
"$listeners"
v-if=
"chapter"
/>
</
template
>
<
script
>
// componets
import
ChapterWork
from
'./chapterWork.vue'
import
ChapterExam
from
'./chapterExam.vue'
export
default
{
name
:
'ViewerWork'
,
components
:
{
ChapterWork
,
ChapterExam
},
props
:
{
// 当前选中的
chapter
:
{
type
:
Object
,
default
()
{
return
{}
}
},
// 课程详情接口返回的数据
data
:
{
type
:
Object
,
default
()
{
return
{}
}
}
},
computed
:
{
currentCompoent
()
{
const
componentNames
=
{
1
:
'ChapterExam'
,
// 考试
2
:
'ChapterWork'
// 作业
}
const
homework
=
this
.
chapter
.
homework
return
homework
?
componentNames
[
homework
.
work_type
]
:
''
}
}
}
</
script
>
client/src/modules/viewer/index.vue
浏览文件 @
f88708c9
<
template
>
<div
class=
"course-viewer"
>
<div
class=
"course-viewer-main"
>
<!-- 顶部区域 -->
<div
class=
"course-viewer-hd"
>
<router-link
to=
"/mobile/help/student
"
>
<div
class=
"course-viewer-
main-
hd"
>
<router-link
:to=
"`/app/learn/course-detail/$
{sid}/${cid}`
">
<i
class=
"el-icon-arrow-left"
></i>
</router-link>
<h1
class=
"course-viewer-hd__title"
>
{{
detail
.
course_name
}}
</h1>
<router-link
to=
"/app/account/feedbackCreate"
target=
"_blank"
>
<el-tooltip
effect=
"light"
content=
"意见反馈"
>
<i
class=
"el-icon-self-fankuiyijian"
></i>
</el-tooltip>
</router-link>
<router-link
to=
"/mobile/help/student"
target=
"_blank"
>
<el-tooltip
effect=
"light"
content=
"帮助"
>
<i
class=
"el-icon-self-icon-test"
></i>
</el-tooltip>
</router-link>
<h1
class=
"course-viewer-main-hd__title"
>
{{
detail
.
course_name
}}
</h1>
<!-- 直播的时候显示帮助按钮 -->
<template
v-if=
"isLive"
>
<router-link
to=
"/app/account/feedbackCreate"
target=
"_blank"
>
<el-tooltip
effect=
"light"
content=
"意见反馈"
>
<i
class=
"el-icon-self-fankuiyijian"
></i>
</el-tooltip>
</router-link>
<router-link
to=
"/mobile/help/student"
target=
"_blank"
>
<el-tooltip
effect=
"light"
content=
"帮助"
>
<i
class=
"el-icon-self-icon-test"
></i>
</el-tooltip>
</router-link>
</
template
>
</div>
<!-- 主体区域 -->
<div
class=
"course-viewer-bd"
>
<player
:video=
"chatperResources.video"
pdf=
"https://img1.ezijing.com/ppts/6437335122927681536/PPT_3.2%20%E6%A1%88%E4%BE%8B%E7%A0%94%E7%A9%B6%E6%96%B9%E6%B3%95%EF%BC%88%E4%B8%80%EF%BC%89.pdf"
:ppts=
"chatperResources.ppts"
v-if=
"chatperResources.video"
<div
class=
"course-viewer-main-bd"
>
<router-view
:data=
"detail"
:chapter=
"activeChapter"
:pptIndex=
"pptIndex"
:key=
"pid"
@
pptupdate=
"handlePPTupdate"
@
change-ppt=
"handleChangePPT"
/>
</div>
</div>
<!-- 侧边栏 -->
<v-aside
:chapters=
"detail.chapters"
:ppts=
"chatperResources.ppts"
></v-aside>
<v-aside
:chapters=
"chapters"
:active=
"activeChapter"
:ppts=
"ppts"
:pptIndex=
"pptIndex"
@
change-ppt=
"handleChangePPT"
v-if=
"detail.chapters"
v-show=
"!isLive && !isCourseExam"
></v-aside>
</div>
</template>
<
script
>
// api
import
*
as
api
from
'./api
/index
'
import
*
as
api
from
'./api'
// components
import
VAside
from
'./components/aside/
aside
.vue'
import
Player
from
'./components/player/player.vue'
import
VAside
from
'./components/aside/
index
.vue'
export
default
{
name
:
'CourseViewer'
,
components
:
{
VAside
,
Player
},
components
:
{
VAside
},
data
()
{
return
{
detail
:
{},
chatperResources
:
{}
ppts
:
[],
pptIndex
:
0
}
},
computed
:
{
// 当前章节
watch
:
{
activeChapter
()
{
return
{
resource_id
:
'6414747439944695808'
this
.
ppts
=
[]
this
.
pptIndex
=
0
}
},
computed
:
{
// 学期ID
sid
()
{
return
this
.
$route
.
params
.
sid
},
// 课程ID
cid
()
{
return
this
.
$route
.
params
.
cid
},
// 当前页面的ID
pid
()
{
return
this
.
$route
.
params
.
id
},
// 章节列表
chapters
()
{
const
chapters
=
this
.
detail
.
chapters
||
[]
if
(
!
chapters
.
length
)
{
return
[]
}
const
customeChapter
=
{
name
:
'大作业及资料'
,
children
:
[
{
name
:
'课程大作业'
,
id
:
'course_work'
,
type
:
99
},
{
name
:
'课程资料'
,
id
:
'course_info'
,
type
:
100
},
{
name
:
'教学评估'
,
id
:
'teach_evaluation'
,
type
:
102
},
{
name
:
'课程考试'
,
id
:
'course_exam'
,
type
:
101
}
]
}
chapters
.
push
(
customeChapter
)
return
chapters
},
// 当前选中的章节
activeChapter
()
{
const
id
=
this
.
pid
const
list
=
this
.
chapters
return
this
.
findChapter
(
id
,
list
)
},
//
视频资源ID
resourceId
()
{
return
this
.
activeChapter
.
resource_id
//
直播
isLive
()
{
return
this
.
activeChapter
?
this
.
activeChapter
.
type
===
5
:
false
},
/**
* 视频提供者
* @return 1是CC加密; 2是非加密; 3是阿里云
*/
videoProvider
()
{
const
video
=
this
.
activeChapter
.
video
||
{}
return
video
.
video_provider
||
3
// 课程考试
isCourseExam
()
{
return
this
.
activeChapter
?
this
.
activeChapter
.
type
===
101
:
false
}
},
methods
:
{
// 查找当前章节
findChapter
(
id
,
list
)
{
for
(
const
item
of
list
)
{
if
(
item
.
id
===
id
)
{
return
item
}
if
(
item
.
children
&&
item
.
children
.
length
)
{
const
found
=
this
.
findChapter
(
id
,
item
.
children
)
if
(
found
)
{
return
found
}
}
}
return
null
},
// 获取课程详情
getCourse
()
{
api
.
getCourse
(
'6437296642994470912'
,
'6437335122927681536'
)
.
then
(
response
=>
{
this
.
detail
=
response
})
api
.
getCourse
(
this
.
sid
,
this
.
cid
).
then
(
response
=>
{
this
.
detail
=
response
})
},
// 获取章节视频详情
getChapterVideo
()
{
// 视频播放类型 1是CC加密; 2是非加密; 3是阿里云
if
(
this
.
videoProvider
===
3
)
{
api
.
getChapterVideoAliyun
(
this
.
resourceId
).
then
(
response
=>
{
this
.
chatperResources
=
response
})
}
else
{
api
.
getChapterVideo
(
this
.
resourceId
).
then
(
response
=>
{
this
.
chatperResources
=
response
})
}
// PPT列表更新
handlePPTupdate
(
list
)
{
this
.
ppts
=
list
},
// 右侧菜单选中的PPT修改
handleChangePPT
(
index
)
{
this
.
pptIndex
=
index
}
},
beforeMount
()
{
this
.
getCourse
()
this
.
getChapterVideo
()
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
>
.course-viewer
{
display
:
flex
;
background-color
:
#3f3f3f
;
height
:
100vh
;
overflow
:
hidden
;
}
.course-viewer-main
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
}
.course-viewer-hd
{
.course-viewer-
main-
hd
{
display
:
flex
;
align-items
:
center
;
background-color
:
#3f3f3f
;
height
:
56px
;
a
{
color
:
#fff
;
...
...
@@ -121,13 +175,59 @@ export default {
font-size
:
24px
;
}
}
.course-viewer-hd__title
{
.course-viewer-
main-
hd__title
{
flex
:
1
;
font-size
:
1
.5em
;
text-align
:
center
;
color
:
#a0a0a0
;
}
.course-viewer-bd
{
.course-viewer-
main-
bd
{
flex
:
1
;
height
:
calc
(
100vh
-
56px
);
overflow-y
:
auto
;
}
.course-viewer-content
{
// min-height: 50%;
max-width
:
900px
;
padding
:
40px
120px
80px
;
margin
:
40px
auto
;
background-color
:
#f2f2f2
;
box-shadow
:
0
0
2px
rgba
(
0
,
0
,
0
,
0
.05
);
}
.course-viewer-content-hd
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
40px
0
;
// text-align: center;
}
.course-viewer-content-hd__title
{
position
:
relative
;
display
:
inline-block
;
margin
:
0
0
0
20px
;
padding
:
0
0
5px
;
font-size
:
20px
;
border-bottom
:
3px
solid
#707070
;
&
:
:
before
{
content
:
'·'
;
position
:
absolute
;
left
:
-30px
;
top
:
50%
;
font-size
:
30px
;
transform
:
translateY
(
-50%
);
}
&
:
:
after
{
content
:
''
;
position
:
absolute
;
left
:
0
;
bottom
:
-8px
;
width
:
100%
;
height
:
1px
;
background-color
:
#707070
;
}
}
.course-viewer-content-hd__aside
{
font-size
:
18px
;
// border-bottom: 3px solid #707070;
}
</
style
>
client/src/modules/viewer/routes.js
0 → 100644
浏览文件 @
f88708c9
export
default
[
{
path
:
'/viewer/:sid/:cid'
,
component
:
()
=>
import
(
'./index.vue'
),
children
:
[
{
name
:
'viewerCourseChapter'
,
path
:
':id'
,
component
:
()
=>
import
(
'./components/layout.vue'
)
}
]
}
]
client/src/pages/affairsHall/hall.vue
浏览文件 @
f88708c9
...
...
@@ -21,7 +21,7 @@
@
click=
"golearningAdd('/app/affairs-hall/again-add/-1')"
>
申请重修
</el-button>
<div
style=
"width: 100%; height: 0.2rem;"
></div>
<table-list
:key=
"affairId"
v-bind=
"tableOption"
v-if=
"affairId"
></table-list>
<table-list
:key=
"affairId"
v-bind=
"tableOption"
v-if=
"affairId"
ref=
"tableList"
></table-list>
</div>
<el-dialog
title=
"提示"
:visible
.
sync=
"dialogVisible"
width=
"30%"
>
<span>
确定删除?
</span>
...
...
@@ -249,7 +249,8 @@ export default {
.
then
(
data
=>
{
if
(
data
.
success
)
{
this
.
$message
({
type
:
'success'
,
message
:
'删除成功'
})
this
.
$router
.
go
(
0
)
// 刷新列表,强刷新,返回第一页
this
.
$refs
.
tableList
&&
this
.
$refs
.
tableList
.
refersh
(
true
)
}
})
.
catch
(
e
=>
{
...
...
client/src/pages/learn/course.vue
浏览文件 @
f88708c9
...
...
@@ -115,13 +115,13 @@ export default {
clearInterval
(
this
.
timeInterval
)
this
.
timeInterval
=
null
}
//
this.timeInterval = setInterval(() => {
// cAction.chapterAction
.getNewLiveMsg().then(json => {
//
if (json.status === 200) {
//
this.newLiveMsg = json.data
//
}
//
}).catch(e => { this.$message.error(e.message) }).finally(() => { })
//
}, 3000)
this
.
timeInterval
=
setInterval
(()
=>
{
cAction
.
Player
.
getNewLiveMsg
().
then
(
json
=>
{
if
(
json
.
status
===
200
)
{
this
.
newLiveMsg
=
json
.
data
}
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
})
},
3000
)
// const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
// cAction.Course.getLearnFind().then(data => {
...
...
client/src/pages/learn/courseDetail.vue
浏览文件 @
f88708c9
...
...
@@ -8,7 +8,7 @@
<el-button
class=
"rbtn"
type=
"primary"
size=
"mini"
@
click=
'startLearn'
:data-cid=
'cid'
:data-sid=
'sid'
:data-type=
'tabs[1].chapterList.currentVideoProvider'
:data-vid=
'tabs[1].chapterList.currentChapterId'
>
继续学习
</el-button>
</
template
>
<
template
v-else-if=
'headerInfo.isStart'
>
<el-button
class=
"rbtn"
type=
"primary"
size=
"mini"
@
click=
'startLearn'
:data-cid=
'cid'
:data-sid=
'sid'
:data-type=
'
tabs[1].chapterList.course[0].chapters[0].video_provider'
:data-vid=
'tabs[1].chapterList.course[0].chapters[0]
.vid'
>
开始学习
</el-button>
<el-button
class=
"rbtn"
type=
"primary"
size=
"mini"
@
click=
'startLearn'
:data-cid=
'cid'
:data-sid=
'sid'
:data-type=
'
firstVideo.video_provider'
:data-vid=
'firstVideo
.vid'
>
开始学习
</el-button>
</
template
>
<
template
v-else
>
<el-button
class=
"rbtn"
type=
"primary"
size=
"mini"
@
click=
'wantThisCourse'
>
选课
</el-button>
...
...
@@ -420,7 +420,8 @@ export default {
isOpenNewTabFlag
:
false
,
arrFn
:
[],
cloudClassUrls
:
{},
timeHeart
:
null
timeHeart
:
null
,
firstVideo
:
{}
}
},
mounted
()
{
...
...
@@ -444,6 +445,23 @@ export default {
this
.
tabs
[
0
].
content
=
json
.
tabs0Content
this
.
tabs
[
1
].
chapterList
=
json
.
tabs1ChapterList
json
.
tabs3richTest
&&
(
this
.
tabs
[
3
].
richText
=
json
.
tabs3richTest
)
// 设置开始学习的视频
const
courseList
=
json
.
tabs1ChapterList
.
course
for
(
let
i
=
0
;
i
<
courseList
.
length
;
i
++
)
{
const
children
=
courseList
[
i
].
chapters
||
[]
if
(
this
.
firstVideo
&&
this
.
firstVideo
.
vid
)
{
break
}
for
(
let
k
=
0
;
k
<
children
.
length
;
k
++
)
{
const
item
=
children
[
k
]
if
(
item
.
vid
&&
item
.
video_provider
)
{
this
.
firstVideo
=
item
break
}
}
}
cAction
.
Course
.
getCourseAssess
(
this
.
cid
,
this
.
sid
).
then
(
json1
=>
{
const
_courseArr
=
json
.
tabs1ChapterList
.
course
/* 进行一次 对照,将 视频 vid 赋值 */
...
...
@@ -461,7 +479,6 @@ export default {
}
}
}
this
.
tabs
[
3
].
assess
=
json1
loading
.
close
()
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
})
...
...
client/src/pages/learn/discussDetail.vue
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/pages/mobileLive/list.vue
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/pages/player/chapterRead/chapterRead.vue
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/pages/player/chapterWork/chapterWork.vue
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/pages/player/exam/exam.vue
浏览文件 @
f88708c9
差异被折叠。
点击展开。
client/src/router/routes.js
浏览文件 @
f88708c9
差异被折叠。
点击展开。
server/server/controller/ProxyMonitor.js
0 → 100644
浏览文件 @
f88708c9
差异被折叠。
点击展开。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论