Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
L
learn-online-pc
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
learn-online-pc
Commits
01c48e8f
提交
01c48e8f
authored
5月 09, 2020
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: 修改课程考试页面
上级
523bbbb5
显示空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
1128 行增加
和
141 行删除
+1128
-141
PlayerAction.js
client/src/action/PlayerAction.js
+122
-0
base_api.js
client/src/api/base_api.js
+2
-2
player_api.js
client/src/api/player_api.js
+28
-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
main.js
client/src/main.js
+3
-0
exam.vue
client/src/pages/player/exam/exam.vue
+583
-139
没有找到文件。
client/src/action/PlayerAction.js
浏览文件 @
01c48e8f
...
@@ -284,4 +284,126 @@ export default class PlayerAction extends BaseACTION {
...
@@ -284,4 +284,126 @@ export default class PlayerAction extends BaseACTION {
getLiveList
()
{
return
Player
.
getLiveList
().
then
(
res
=>
res
)
}
getLiveList
()
{
return
Player
.
getLiveList
().
then
(
res
=>
res
)
}
/* 获取云课堂 url */
/* 获取云课堂 url */
getCloudUrl
()
{
return
Player
.
getCloudUrl
().
then
(
res
=>
res
)
}
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
浏览文件 @
01c48e8f
...
@@ -100,9 +100,9 @@ export default class API {
...
@@ -100,9 +100,9 @@ export default class API {
if
(
data
&&
data
.
code
!==
undefined
)
{
if
(
data
&&
data
.
code
!==
undefined
)
{
if
(
data
.
code
!==
0
)
{
if
(
data
.
code
!==
0
)
{
if
(
!
/account
\/
get-user-info/gi
.
test
(
res
.
config
.
url
))
{
if
(
!
/account
\/
get-user-info/gi
.
test
(
res
.
config
.
url
))
{
Message
({
type
:
'error'
,
message
:
data
.
msg
})
data
.
msg
&&
Message
({
type
:
'error'
,
message
:
data
.
msg
})
}
}
return
null
return
data
}
else
if
(
data
.
code
===
0
)
{
}
else
if
(
data
.
code
===
0
)
{
return
data
.
data
return
data
.
data
}
}
...
...
client/src/api/player_api.js
浏览文件 @
01c48e8f
...
@@ -101,4 +101,32 @@ export default class PlayerAPI extends BaseAPI {
...
@@ -101,4 +101,32 @@ export default class PlayerAPI extends BaseAPI {
* 跨域接口请求 - 直接获取云课堂设置
* 跨域接口请求 - 直接获取云课堂设置
*/
*/
getCloudUrl
=
(
obj
=
{})
=>
this
.
get
(
'https://node-server.ezijing.com/get/cloud-class'
,
obj
)
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/upload-form/elemInfo.md
0 → 100644
浏览文件 @
01c48e8f
## 组件简介
| 字段值 | 说明 | 字段属性 | 默认值 |
| ------- | ------------------------- | ------- | ----- |
|
`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
浏览文件 @
01c48e8f
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
浏览文件 @
01c48e8f
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
浏览文件 @
01c48e8f
<
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/main.js
浏览文件 @
01c48e8f
...
@@ -5,6 +5,7 @@ import VueI18n from 'vue-i18n' // 使用 国际化
...
@@ -5,6 +5,7 @@ import VueI18n from 'vue-i18n' // 使用 国际化
import
createI18n
from
'./assets/languages'
// 国际化定义
import
createI18n
from
'./assets/languages'
// 国际化定义
import
App
from
'./app.vue'
// 初始化 vue页面
import
App
from
'./app.vue'
// 初始化 vue页面
import
UploadForm
from
'./components/upload-form'
import
'./style.scss'
// 公共样式
import
'./style.scss'
// 公共样式
import
MetaInfo
from
'vue-meta-info'
import
MetaInfo
from
'vue-meta-info'
import
Element
from
'element-ui'
import
Element
from
'element-ui'
...
@@ -19,6 +20,8 @@ require('promise.prototype.finally').shim()
...
@@ -19,6 +20,8 @@ require('promise.prototype.finally').shim()
/* 兼容处理 end */
/* 兼容处理 end */
Vue
.
use
(
VueRouter
)
Vue
.
use
(
VueRouter
)
Vue
.
use
(
UploadForm
)
Vue
.
component
(
UploadForm
.
name
,
UploadForm
)
const
router
=
createRouter
()
const
router
=
createRouter
()
Vue
.
use
(
VueI18n
)
Vue
.
use
(
VueI18n
)
const
i18n
=
createI18n
()
const
i18n
=
createI18n
()
...
...
client/src/pages/player/exam/exam.vue
浏览文件 @
01c48e8f
<
template
>
<
template
>
<div
class=
"play-paper"
>
<div
class=
"play-paper"
>
<div
class=
"play-paper-body"
>
<div
class=
"play-paper-body"
>
<div
class=
"play-paper-title"
><div><h3>
{{
exam
.
title
}}
</h3></div></div>
<template
v-if=
"status.examinationStatus === '00'"
>
<div
class=
"no-exam"
>
暂无考试
</div>
</
template
>
<
template
v-else-if=
"status.examinationStatus === '90'"
>
<div
class=
"play-paper-title"
>
<div>
<h3>
{{
exam
.
title
}}
</h3>
</div>
</div>
<template
v-if=
"status.type === 2 && status.isPublished === 1"
>
<template
v-if=
"exam.score.total !== undefined"
>
<div
style=
"font-size: 18px;"
>
得分:单选:
{{
exam
.
score
.
radio
}}
分,多选:
{{
exam
.
score
.
checkbox
}}
分,简答:
{{
exam
.
score
.
shortAnswer
}}
分,总分:
{{
exam
.
score
.
total
}}
分
</div>
</
template
>
<
template
v-if=
"exam.shortAnswerList.length"
>
<template
v-for=
"(item, index) in exam.shortAnswerList"
>
<template
v-if=
"item.check_comment"
>
<div
style=
"font-size: 18px; margin-top: 10px;"
:key=
"index"
>
<div>
简答题
{{
index
+
1
}}
</div>
<div>
老师评语:
<div
style=
"display: inline-block"
v-html=
"item.check_comment || ''"
></div>
</div>
</div>
</
template
>
</template>
</template>
</template>
<
template
v-else-if=
"status.type === 0"
>
<div
class=
"no-exam"
>
考试结束 - 尚未进行考试
</div>
</
template
>
<
template
v-else
>
<div
class=
"no-exam"
>
考试结束 - 试卷批改中,请耐心等待
</div>
</
template
>
</template>
<
template
v-else
>
<div
class=
"play-paper-title"
>
<div>
<h3>
{{
exam
.
title
}}
</h3>
</div>
</div>
<template
v-if=
"status.isStart"
>
<div
class=
"play-paper-content play-chapter-exam"
>
<div
class=
"play-paper-content play-chapter-exam"
>
<template
v-if=
"exam.id"
>
<template
v-if=
"exam.id"
>
<div
class=
'exam'
>
<div
class=
"exam"
>
<div
style=
'text-align: center;'
>
<!--
<div
style=
'text-align: center;'
>
--
>
<div
class=
'topic'
>
<!--
<div
class=
'topic'
>
--
>
<!--
<div
class=
'tit'
>
{{
exam
.
title
}}
</div>
-->
<!--
<div
class=
'tit'
>
{{
exam
.
title
}}
</div>
-->
<template
v-if=
'exam.score.total !== undefined'
><div
class=
'cur'
>
单选:
{{
exam
.
score
.
radio
}}
分,多选:
{{
exam
.
score
.
checkbox
}}
分,简答:
{{
exam
.
score
.
shortAnswer
}}
分,总分:
{{
exam
.
score
.
total
}}
分
</div></
template
>
<template
v-if=
"exam.type === 2 && exam.isPublished === 1"
>
<template
v-if=
"exam.score.total !== undefined"
>
<div
style=
"font-size: 18px;"
>
得分:单选:
{{
exam
.
score
.
radio
}}
分,多选:
{{
exam
.
score
.
checkbox
}}
分,简答:
{{
exam
.
score
.
shortAnswer
}}
分,总分:
{{
exam
.
score
.
total
}}
分
</div>
</
template
>
<
template
v-if=
"exam.shortAnswerList.length"
>
<template
v-for=
"(item, index) in exam.shortAnswerList"
>
<template
v-if=
"item.check_comment"
>
<div
style=
"font-size: 18px; margin-top: 10px;"
:key=
"index"
>
<div>
简答题
{{
index
+
1
}}
</div>
<div>
老师评语:
<div
style=
"display: inline-block"
v-html=
"item.check_comment || ''"
></div>
</div>
</div>
</div>
</div>
</
template
>
</template>
</template>
</template>
<
template
v-else-if=
"exam.type === 1 || exam.type === 2"
>
<div
class=
"no-exam"
>
试卷批改中,请耐心等待
</div>
</
template
>
<!-- </div> -->
<!-- </div> -->
<
template
v-if=
"(exam.type !== 1 && exam.type !== 2)"
>
<div
style=
"text-align: center;"
>
考试截止时间为:
{{
status
.
terminateTime
}}
</div>
<!-- 单选题 -->
<!-- 单选题 -->
<
template
v-if=
'exam.radioList.length'
>
<template
v-if=
"exam.radioList.length"
>
<template
v-for=
'(item, index) in exam.radioList'
>
<template
v-for=
"(item, index) in exam.radioList"
>
<div
v-bind:key=
"item.id"
class=
'q-group'
:data-index=
'index'
>
<div
v-bind:key=
"item.id"
class=
"q-group"
:data-index=
"index"
>
<div
class=
'q-num'
>
{{
index
+
1
}}
.
</div><div
class=
'q-title'
v-html=
'item.content'
></div><div
class=
'q-type'
>
(单选题)
</div>
<div
class=
"q-num"
>
{{
index
+
1
}}
.
</div>
<el-radio-group
class=
'radio-group'
v-model=
"item.user_answer"
>
<div
class=
"q-title"
v-html=
"item.content"
></div>
<template
v-for=
'(item1, index1) in item.options'
>
<div
class=
"q-type"
>
(单选题)
</div>
<el-radio
v-bind:key=
"item1.id"
:label=
'item1.id'
:disabled=
'!!item.right_answer && !!exam.type'
:class=
'["radio", ((item.right_answer && !!exam.type) ? (item1.id === item.right_answer ? "success" : "error") : "")]'
>
{{
index1
|
getLetter
()
}}
.
{{
item1
.
option
}}
</el-radio>
<el-radio-group
class=
"radio-group"
v-model=
"item.user_answer"
>
<template
v-for=
"(item1, index1) in item.options"
>
<el-radio
v-bind:key=
"item1.id"
:label=
"item1.id"
:disabled=
"!!item.right_answer && !!exam.type"
:class=
"['radio', ((item.right_answer && !!exam.type) ? (item1.id === item.right_answer ? 'success' : 'error') : '')]"
>
{{
index1
|
getLetter
()
}}
.
{{
item1
.
option
}}
</el-radio>
</
template
>
</
template
>
</el-radio-group>
</el-radio-group>
<
template
v-if=
'item.right_answer && !!exam.type'
><div
class=
'result'
>
学生答案:
<div
:class=
'["stu", (item.right_answer === item.user_answer ? "success" : "error")]'
>
{{
item
.
user_answer
|
getRadioAnswer
(
item
.
options
)
}}
</div>
正确答案:
{{
item
.
right_answer
|
getRadioAnswer
(
item
.
options
)
}}
</div></
template
>
<
template
v-if=
"item.right_answer && !!exam.type"
>
<div
class=
"result"
>
学生答案:
<div
:class=
"['stu', (item.right_answer === item.user_answer ? 'success' : 'error')]"
>
{{
item
.
user_answer
|
getRadioAnswer
(
item
.
options
)
}}
</div>
正确答案:
{{
item
.
right_answer
|
getRadioAnswer
(
item
.
options
)
}}
</div>
</
template
>
</div>
</div>
</template>
</template>
</template>
</template>
<!-- 多选题 -->
<!-- 多选题 -->
<
template
v-if=
'exam.checkboxList.length'
>
<
template
v-if=
"exam.checkboxList.length"
>
<template
v-for=
'(item, index) in exam.checkboxList'
>
<template
v-for=
"(item, index) in exam.checkboxList"
>
<div
v-bind:key=
"item.id"
class=
'q-group'
:data-index=
'index'
>
<div
v-bind:key=
"item.id"
class=
"q-group"
:data-index=
"index"
>
<div
class=
'q-num'
>
{{
exam
.
radioList
.
length
+
index
+
1
}}
.
</div><div
class=
'q-title'
v-html=
'item.content'
></div><div
class=
'q-type'
>
(多选题)
</div>
<div
class=
"q-num"
>
{{
exam
.
radioList
.
length
+
index
+
1
}}
.
</div>
<el-checkbox-group
class=
'checkbox-group'
v-model=
"item.user_answer"
>
<div
class=
"q-title"
v-html=
"item.content"
></div>
<template
v-for=
'(item1, index1) in item.options'
>
<div
class=
"q-type"
>
(多选题)
</div>
<el-checkbox
v-bind:key=
"item1.id"
:label=
'item1.id'
:disabled=
'!!item.right_answer.length && !!exam.type'
:class=
'["checkbox", ((item.right_answer.length && !!exam.type) ? (isCheckboxChecked(item1.id, item.right_answer) ? "success" : "error") : "")]'
>
{{
index1
|
getLetter
()
}}
.
{{
item1
.
option
}}
</el-checkbox>
<el-checkbox-group
class=
"checkbox-group"
v-model=
"item.user_answer"
>
<template
v-for=
"(item1, index1) in item.options"
>
<el-checkbox
v-bind:key=
"item1.id"
:label=
"item1.id"
:disabled=
"!!item.right_answer.length && !!exam.type"
:class=
"['checkbox', ((item.right_answer.length && !!exam.type) ? (isCheckboxChecked(item1.id, item.right_answer) ? 'success' : 'error') : '')]"
>
{{
index1
|
getLetter
()
}}
.
{{
item1
.
option
}}
</el-checkbox>
</
template
>
</
template
>
</el-checkbox-group>
</el-checkbox-group>
<
template
v-if=
'item.right_answer.length && !!exam.type'
><div
class=
'result'
>
学生答案:
<div
:class=
'["stu", ((item.right_answer.length && isCheckboxRight(item.user_answer, item.right_answer)) ? "success" : "error")]'
>
{{
item
.
user_answer
|
getCheckboxAnswer
(
item
.
options
)
}}
</div>
正确答案:
{{
item
.
right_answer
|
getCheckboxAnswer
(
item
.
options
)
}}
</div></
template
>
<
template
v-if=
"item.right_answer.length && !!exam.type"
>
<div
class=
"result"
>
学生答案:
<div
:class=
"['stu', ((item.right_answer.length && isCheckboxRight(item.user_answer, item.right_answer)) ? 'success' : 'error')]"
>
{{
item
.
user_answer
|
getCheckboxAnswer
(
item
.
options
)
}}
</div>
正确答案:
{{
item
.
right_answer
|
getCheckboxAnswer
(
item
.
options
)
}}
</div>
</
template
>
</div>
</div>
</template>
</template>
</template>
</template>
<!-- 简答题 -->
<!-- 简答题 -->
<
template
v-if=
'exam.shortAnswerList.length'
>
<
template
v-if=
"exam.shortAnswerList.length"
>
<template
v-for=
'(item, index) in exam.shortAnswerList'
>
<template
v-for=
"(item, index) in exam.shortAnswerList"
>
<div
v-bind:key=
"index"
class=
'q-group'
>
<div
class=
"q-group"
:key=
"index"
>
<div
class=
'q-sa-title'
>
{{
exam
.
radioList
.
length
+
exam
.
checkboxList
.
length
+
index
+
1
}}
.
简答题
</div>
<div
class=
"q-sa-title"
>
{{
exam
.
radioList
.
length
+
exam
.
checkboxList
.
length
+
index
+
1
}}
.
简答题
</div>
<div
class=
"edit_html"
v-html=
"item.content || ''"
></div>
<div
class=
"edit_html"
v-html=
"item.content || ''"
></div>
<textarea
:id=
"('editor-exam' + index)"
v-model=
"item.user_answer"
></textarea>
<textarea
:id=
"('editor-exam' + index)"
v-model=
"item.user_answer"
></textarea>
<div
style=
"height: 10px;"
></div>
<div
style=
"height: 10px;"
></div>
<component
:is=
"item.upload.type"
v-bind:key=
"item.upload.model"
:item=
"item.upload"
:formData=
"item"
:isUpload=
"!exam.type"
></component>
<!-- 利用key值自动更新组件 -->
<component
:is=
"item.upload.type"
v-bind:key=
"item.upload.id + new Date().getTime()"
:item=
"item.upload"
:formData=
"item"
:isUpload=
"!exam.type"
></component>
</div>
</div>
</
template
>
</
template
>
</template>
</template>
<div
:class=
'["btn", (exam.type && "on")]'
@
click=
'submitExam'
:data-submit=
'!!exam.type'
@
mousedown=
'_SubmitMouseLeftDown()'
>
{{exam.type ? "已提交" : "提交"}}
</div>
<div
<div
class=
'care'
>
(注意:测试只有一次提交机会)
</div>
:class=
"['btn', (exam.type && 'on')]"
@
click=
"submitExam"
:data-submit=
"!!exam.type"
@
mousedown=
"_SubmitMouseLeftDown()"
>
{{exam.type ? "已提交" : "提交"}}
</div>
<div
class=
"care"
>
(注意:考试只有一次提交机会)
</div>
<!-- <div :class='["btn"]' @click='repeatExam($event, true)' v-if="exam.work_contents">重做</div> -->
<!-- <div :class='["btn"]' @click='repeatExam($event, true)' v-if="exam.work_contents">重做</div> -->
</template>
</div>
</div>
</template>
</template>
</div>
</div>
</template>
<
template
v-else
>
<div
class=
"exam"
>
<p>
考试须知:
</p>
<div
style=
"text-align: left;"
>
考试开始时间为:
{{
status
.
startTime
}}
</div>
<div
style=
"text-align: left;"
>
考试截止时间为:
{{
status
.
terminateTime
}}
</div>
<template
v-if=
"new Date(status.startTime).getTime() - new Date(status.serverTime).getTime()
<
=
0
"
>
<div
style=
"width: 25%;"
:class=
"['btn']"
@
click=
"beginExam(true)"
@
mousedown=
"beginExam(true)"
>
开始考试
</div>
</
template
>
</div>
</template>
</template>
</div>
</div>
</div>
</div>
</template>
</template>
<
script
>
<
script
>
...
@@ -68,21 +199,34 @@ import Base64 from 'Base64'
...
@@ -68,21 +199,34 @@ import Base64 from 'Base64'
import
CKEDITOR
from
'CKEDITOR'
import
CKEDITOR
from
'CKEDITOR'
var
getLetter
=
(
val
)
=>
{
var
getLetter
=
val
=>
{
switch
(
val
)
{
switch
(
val
)
{
case
0
:
return
'A'
case
0
:
case
1
:
return
'B'
return
'A'
case
2
:
return
'C'
case
1
:
case
3
:
return
'D'
return
'B'
case
4
:
return
'E'
case
2
:
case
5
:
return
'F'
return
'C'
case
6
:
return
'G'
case
3
:
case
7
:
return
'H'
return
'D'
case
8
:
return
'I'
case
4
:
case
9
:
return
'J'
return
'E'
case
10
:
return
'K'
case
5
:
case
11
:
return
'L'
return
'F'
case
12
:
return
'M'
case
6
:
return
'G'
case
7
:
return
'H'
case
8
:
return
'I'
case
9
:
return
'J'
case
10
:
return
'K'
case
11
:
return
'L'
case
12
:
return
'M'
}
}
}
}
...
@@ -115,13 +259,13 @@ export default {
...
@@ -115,13 +259,13 @@ export default {
return
str
.
substr
(
0
,
str
.
length
-
1
)
return
str
.
substr
(
0
,
str
.
length
-
1
)
}
}
},
},
data
()
{
data
()
{
return
{
return
{
_time
:
null
,
// 定时器,自动化提交
_time
:
null
,
// 定时器,自动化提交
exam
:
{
exam
:
{
// id: '1',
// id: '1',
// title: '标题',
// title: '标题',
// type: 0, // 0: 暂存,可以继续答题;1: 提交,不能再继续答题
// type: 0, // 0: 暂存,可以继续答题;1: 提交,不能再继续答题
(等待批改) 2: 已批改(这时候有分数)
// radioList: [
// radioList: [
// {
// {
// id: '6622309081933676544',
// id: '6622309081933676544',
...
@@ -242,31 +386,35 @@ export default {
...
@@ -242,31 +386,35 @@ export default {
// }
// }
// }
// }
// ]
// ]
}
}
},
},
mounted
()
{
status
:
{
this
.
loadAjax
()
isStart
:
false
,
startTime
:
''
,
if
(
this
.
_time
)
{
terminateTime
:
''
,
clearInterval
(
this
.
_time
)
serverTime
:
''
,
this
.
_time
=
null
examinationStatus
:
''
,
type
:
0
,
isPublished
:
0
}
}
this
.
_time
=
setInterval
(()
=>
{
if
(
!
this
.
exam
.
type
)
{
this
.
submitExam
({
submitType
:
true
})
// 暂存, submitType: true 暂存;其他或不填为提交
}
else
{
clearInterval
(
this
.
_time
)
this
.
_time
=
null
}
}
},
30000
)
},
},
destroyed
()
{
mounted
()
{
this
.
init
()
this
.
$emit
(
'changeSideBar'
,
''
)
setTimeout
(()
=>
{
if
(
window
.
document
.
getElementById
(
'switch-btn'
))
{
window
.
document
.
getElementById
(
'switch-btn'
).
style
.
display
=
'none'
}
},
500
)
},
destroyed
()
{
if
(
this
.
_time
)
{
if
(
this
.
_time
)
{
console
.
log
(
11
)
clearInterval
(
this
.
_time
)
clearInterval
(
this
.
_time
)
this
.
_time
=
null
this
.
_time
=
null
}
}
if
(
window
.
document
.
getElementById
(
'switch-btn'
))
{
window
.
document
.
getElementById
(
'switch-btn'
).
style
.
display
=
'block'
}
},
},
methods
:
{
methods
:
{
isCheckboxRight
:
(
val
,
arr
)
=>
{
isCheckboxRight
:
(
val
,
arr
)
=>
{
...
@@ -279,7 +427,10 @@ export default {
...
@@ -279,7 +427,10 @@ export default {
break
break
}
}
}
}
if
(
j
===
val
.
length
)
{
flag
=
false
;
break
}
if
(
j
===
val
.
length
)
{
flag
=
false
break
}
}
}
return
flag
return
flag
},
},
...
@@ -292,11 +443,15 @@ export default {
...
@@ -292,11 +443,15 @@ export default {
}
}
return
false
return
false
},
},
initckeditor
()
{
initckeditor
()
{
if
(
!
this
.
exam
.
shortAnswerList
)
{
return
}
if
(
!
this
.
exam
.
shortAnswerList
)
{
return
}
/* 删除所有 ckeditor 实例 */
/* 删除所有 ckeditor 实例 */
const
instances
=
CKEDITOR
.
instances
const
instances
=
CKEDITOR
.
instances
for
(
const
name
in
instances
)
{
instances
[
name
].
destroy
()
}
for
(
const
name
in
instances
)
{
instances
[
name
].
destroy
()
}
for
(
let
i
=
0
;
i
<
this
.
exam
.
shortAnswerList
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
this
.
exam
.
shortAnswerList
.
length
;
i
++
)
{
if
(
!
instances
[
'editor-exam'
+
i
])
{
if
(
!
instances
[
'editor-exam'
+
i
])
{
CKEDITOR
.
replace
(
'editor-exam'
+
i
,
{
CKEDITOR
.
replace
(
'editor-exam'
+
i
,
{
...
@@ -305,16 +460,51 @@ export default {
...
@@ -305,16 +460,51 @@ export default {
filebrowserImageUploadUrl
:
'/api/ckeditor/img/upload'
,
filebrowserImageUploadUrl
:
'/api/ckeditor/img/upload'
,
// resize_enabled: typeof this.props.resizable === 'boolean' ? this.props.resizable : true,
// resize_enabled: typeof this.props.resizable === 'boolean' ? this.props.resizable : true,
toolbar
:
[
toolbar
:
[
// { name: 'document', items: ['Source', '-', 'Save', 'NewPage', 'Preview'] },
// { name: 'document', items: [ 'Source', '-', 'Save', 'NewPage', 'Preview' ] },
{
name
:
'styles'
,
items
:
[
'Styles'
,
'Format'
,
'Font'
,
'FontSize'
]
},
{
name
:
'styles'
,
items
:
[
'Styles'
,
'Format'
,
'Font'
,
'FontSize'
]
},
{
name
:
'colors'
,
items
:
[
'TextColor'
,
'BGColor'
]
},
{
name
:
'colors'
,
items
:
[
'TextColor'
,
'BGColor'
]
},
{
name
:
'tools'
,
items
:
[
'Maximize'
,
'ShowBlocks'
]
},
{
name
:
'tools'
,
items
:
[
'Maximize'
,
'ShowBlocks'
]
},
// { name: 'clipboard', items: [
'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'
] },
// { name: 'clipboard', items: [
'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'
] },
{
name
:
'editing'
,
items
:
[
'Find'
,
'Replace'
]
},
{
name
:
'editing'
,
items
:
[
'Find'
,
'Replace'
]
},
// { name: 'forms', items: [
'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'
] },
// { 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
:
'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
:
'links'
,
items
:
[
'Link'
,
'Unlink'
,
'Anchor'
]
},
{
name
:
'insert'
,
items
:
[
'Image'
,
'Table'
,
'HorizontalRule'
]
}
{
name
:
'insert'
,
items
:
[
'Image'
,
'Table'
,
'HorizontalRule'
]
}
]
]
...
@@ -323,49 +513,169 @@ export default {
...
@@ -323,49 +513,169 @@ export default {
this
.
exam
.
shortAnswerList
[
i
].
ckeditor
=
instances
[
'editor-exam'
+
i
]
this
.
exam
.
shortAnswerList
[
i
].
ckeditor
=
instances
[
'editor-exam'
+
i
]
}
}
},
},
/**
init
()
{
* 生命周期函数--监听页面加载
this
.
loadExamStatus
()
*/
this
.
loadExamInfo
()
loadAjax
()
{
if
(
this
.
_time
)
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
clearInterval
(
this
.
_time
)
if
(
this
.
id
!==
'0'
)
{
this
.
_time
=
null
cAction
.
examAction
.
getExamAnswer
(
this
.
cid
,
this
.
sid
,
this
.
id
).
then
(
_data
=>
{
}
if
(
_data
.
code
===
8001
)
{
this
.
_time
=
setInterval
(()
=>
{
cAction
.
examAction
.
getExamInfo
(
this
.
cid
,
this
.
sid
).
then
(
_data
=>
{
this
.
loadExamStatus
()
this
.
exam
=
_data
if
(
!
this
.
exam
.
type
&&
this
.
status
.
isStart
)
{
this
.
exam
.
id
=
this
.
id
// console.log(11, '暂存')
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
this
.
submitExam
({
submitType
:
true
})
// 暂存, submitType: true 暂存;其他或不填为提交
/* 滚动到头部 */
document
.
querySelector
(
'.play-paper'
).
scrollTop
=
0
loading
.
close
()
this
.
initckeditor
()
})
return
}
}
/* 到时间 自动提交 */
if
(
!
this
.
exam
.
type
&&
this
.
status
.
isStart
&&
new
Date
(
this
.
status
.
terminateTime
).
getTime
()
-
new
Date
(
this
.
status
.
serverTime
).
getTime
()
<=
5000
)
{
this
.
submitExam
({
submitType
:
false
,
currentTarget
:
{
dataset
:
{}
}
})
}
},
3000
)
},
/* 定时调用 - 考试状态 */
loadExamStatus
()
{
cAction
.
Player
.
getExamStatus
(
this
.
cid
,
this
.
sid
,
this
.
id
)
.
then
(
_data
=>
{
if
(
_data
.
status
&&
_data
.
status
===
200
)
{
this
.
status
.
startTime
=
this
.
setTime
(
_data
.
start_time
)
this
.
status
.
terminateTime
=
_data
.
terminate_time
this
.
status
.
serverTime
=
this
.
setTime
(
_data
.
server_time
)
this
.
status
.
examinationStatus
=
_data
.
examination_status
}
else
{
this
.
$message
.
error
(
'数据异常,请联系管理员'
)
}
})
.
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
})
.
finally
(()
=>
{})
},
/* 时间格式化(解决safari时间兼容问题); */
setTime
(
time
)
{
return
time
.
replace
(
/-/g
,
'/'
)
},
/* 开始考试 */
beginExam
(
flag
)
{
this
.
status
.
isStart
=
true
this
.
loadAjax
(
flag
)
},
/* 加载题库基本数据 */
loadExamInfo
()
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
cAction
.
Player
.
getExamInfo
(
this
.
cid
,
this
.
sid
)
.
then
(
_data
=>
{
this
.
exam
=
_data
this
.
exam
=
_data
this
.
exam
.
id
=
this
.
id
this
.
exam
.
id
=
this
.
id
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
})
/* 滚动到头部 */
.
catch
(
e
=>
{
document
.
querySelector
(
'.play-paper'
).
scrollTop
=
0
this
.
$message
.
error
(
e
.
message
)
})
.
finally
(()
=>
{
loading
.
close
()
loading
.
close
()
this
.
initckeditor
()
/* 正在考试,考试结束 */
if
(
this
.
status
.
examinationStatus
===
'20'
||
this
.
status
.
examinationStatus
===
'90'
)
{
this
.
beginExam
()
}
})
})
},
/**
* 生命周期函数--监听页面加载
* @param flag 通过该字段 判别是点击进入考试 还是 自动调用
*/
loadAjax
(
flag
)
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
cAction
.
Player
.
getExamAnswer
(
this
.
cid
,
this
.
sid
,
this
.
id
)
.
then
(
_data
=>
{
if
(
_data
.
code
===
8001
)
{
console
.
log
(
'没有考试内容,认为是第一次答题,并且在答题期间,所以显示考试开始页面'
)
if
(
!
flag
)
{
this
.
status
.
isStart
=
false
}
}
else
{
}
else
{
cAction
.
examAction
.
getExamInfo
(
this
.
cid
,
this
.
sid
).
then
(
_data
=>
{
// this.exam.id = _data.id
this
.
exam
=
_data
this
.
exam
.
title
=
_data
.
title
this
.
exam
.
id
=
this
.
id
this
.
exam
.
type
=
_data
.
type
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
this
.
exam
.
score
=
_data
.
score
this
.
exam
.
submitted_time
=
_data
.
submitted_time
this
.
exam
.
isPublished
=
_data
.
isPublished
this
.
status
.
type
=
_data
.
type
this
.
status
.
isPublished
=
_data
.
isPublished
for
(
let
i
=
0
;
i
<
this
.
exam
.
radioList
.
length
;
i
++
)
{
for
(
let
j
=
0
;
j
<
_data
.
radioList
.
length
;
j
++
)
{
if
(
_data
.
radioList
[
j
].
id
===
this
.
exam
.
radioList
[
i
].
id
)
{
for
(
const
k
in
_data
.
radioList
[
j
])
{
this
.
exam
.
radioList
[
i
][
k
]
=
_data
.
radioList
[
j
][
k
]
}
}
}
}
for
(
let
i
=
0
;
i
<
this
.
exam
.
checkboxList
.
length
;
i
++
)
{
for
(
let
j
=
0
;
j
<
_data
.
checkboxList
.
length
;
j
++
)
{
if
(
_data
.
checkboxList
[
j
].
id
===
this
.
exam
.
checkboxList
[
i
].
id
)
{
for
(
const
k
in
_data
.
checkboxList
[
j
])
{
this
.
exam
.
checkboxList
[
i
][
k
]
=
_data
.
checkboxList
[
j
][
k
]
}
}
}
}
for
(
let
i
=
0
;
i
<
this
.
exam
.
shortAnswerList
.
length
;
i
++
)
{
for
(
let
j
=
0
;
j
<
_data
.
shortAnswerList
.
length
;
j
++
)
{
if
(
_data
.
shortAnswerList
[
j
].
id
===
this
.
exam
.
shortAnswerList
[
i
].
id
)
{
for
(
const
k
in
_data
.
shortAnswerList
[
j
])
{
this
.
exam
.
shortAnswerList
[
i
][
k
]
=
_data
.
shortAnswerList
[
j
][
k
]
}
}
}
}
}
})
.
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
})
.
finally
(()
=>
{
// console.log(this.exam.type, this.exam.isPublished)
loading
.
close
()
if
(
this
.
status
.
isStart
&&
this
.
exam
.
type
!==
1
&&
this
.
exam
.
type
!==
2
)
{
/* 滚动到头部 */
/* 滚动到头部 */
document
.
querySelector
(
'.play-paper'
).
scrollTop
=
0
document
.
querySelector
(
'.play-paper'
).
scrollTop
=
0
loading
.
close
()
this
.
initckeditor
()
this
.
initckeditor
()
})
}
}
})
},
},
/**
/**
* 提交试题
* 提交试题
*/
*/
submitExam
(
e
)
{
submitExam
(
e
)
{
if
(
!
e
.
submitType
&&
e
.
currentTarget
.
dataset
.
submit
)
{
if
(
!
e
.
submitType
&&
e
.
currentTarget
.
dataset
.
submit
)
{
this
.
$message
.
error
(
'已做过,不能再提交'
)
this
.
$message
.
error
(
'已做过,不能再提交'
)
return
return
...
@@ -405,28 +715,49 @@ export default {
...
@@ -405,28 +715,49 @@ export default {
}
}
body
.
answers
.
shortAnswerList
.
push
({
body
.
answers
.
shortAnswerList
.
push
({
id
:
tmp
.
id
,
id
:
tmp
.
id
,
user_answer
:
Base64
.
encode
(
tmp
.
user_answer
,
'utf-8'
),
user_answer
:
Base64
.
encode
(
tmp
.
user_answer
),
attachments
:
tmp
.
attachments
attachments
:
tmp
.
attachments
})
})
}
}
body
.
answers
=
JSON
.
stringify
(
body
.
answers
)
body
.
answers
=
JSON
.
stringify
(
body
.
answers
)
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
let
loading
=
null
cAction
.
examAction
.
submitExam
(
this
.
cid
,
this
.
sid
,
this
.
exam
.
id
,
body
).
then
(
_res
=>
{
if
(
!
e
.
submitType
)
{
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
}
cAction
.
Player
.
submitExam
(
this
.
cid
,
this
.
sid
,
this
.
exam
.
id
,
body
)
.
then
(
_res
=>
{
if
(
e
.
submitType
)
{
if
(
e
.
submitType
)
{
this
.
$message
.
success
(
'暂存成功'
)
// this.$message.success('暂存成功')
console
.
log
(
'暂存成功'
)
return
return
}
}
if
(
_res
.
code
===
200
)
{
if
(
_res
.
code
===
200
)
{
this
.
loadAjax
()
this
.
$message
.
success
(
'考试答卷提交成功'
)
// this.exam.type = 1
this
.
init
()
console
.
log
(
111
,
'this.loadAjax()'
)
}
else
{
}
else
{
this
.
$message
.
error
(
_res
.
data
.
error
)
this
.
$message
.
error
(
_res
.
data
.
error
)
}
}
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
loading
.
close
()
})
})
.
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
})
.
finally
(()
=>
{
if
(
!
e
.
submitType
)
{
loading
.
close
()
}
})
},
},
_SubmitMouseLeftDown
()
{
_SubmitMouseLeftDown
()
{
const
_fn1
=
this
.
repeatExam
.
bind
(
this
,
false
)
const
_fn1
=
this
.
repeatExam
.
bind
(
this
,
false
)
document
.
addEventListener
(
'keydown'
,
_fn1
,
false
)
document
.
addEventListener
(
'keydown'
,
_fn1
,
false
)
const
_fn3
=
function
()
{
const
_fn3
=
function
()
{
document
.
removeEventListener
(
'keydown'
,
_fn1
)
document
.
removeEventListener
(
'keydown'
,
_fn1
)
document
.
removeEventListener
(
'mouseup'
,
_fn3
)
document
.
removeEventListener
(
'mouseup'
,
_fn3
)
}
}
...
@@ -435,25 +766,38 @@ export default {
...
@@ -435,25 +766,38 @@ export default {
/**
/**
* 重做
* 重做
*/
*/
repeatExam
(
e
,
flag
)
{
repeatExam
(
e
,
flag
)
{
let
_flag
=
flag
let
_flag
=
flag
/* 字母 f */
/* 字母 f */
if
(
e
.
keyCode
===
70
)
{
if
(
e
.
keyCode
===
70
)
{
_flag
=
true
_flag
=
true
}
}
if
(
!
_flag
)
{
return
}
if
(
!
_flag
)
{
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
return
cAction
.
Player
.
getExamDetail
(
this
.
sid
,
this
.
cid
,
this
.
id
).
then
(
_data
=>
{
}
const
loading
=
this
.
$loading
({
lock
:
true
,
text
:
''
,
spinner
:
''
,
background
:
'rgba(255, 255, 255, 0.9)'
})
cAction
.
chapterAction
.
getExamDetail
(
this
.
sid
,
this
.
cid
,
this
.
id
)
.
then
(
_data
=>
{
this
.
exam
=
{}
this
.
exam
=
{}
}).
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
}).
finally
(()
=>
{
})
.
catch
(
e
=>
{
this
.
$message
.
error
(
e
.
message
)
})
.
finally
(()
=>
{
loading
.
close
()
loading
.
close
()
})
})
}
}
},
},
watch
:
{
watch
:
{
id
:
{
id
:
{
handler
()
{
handler
()
{
this
.
loadAjax
()
this
.
init
()
}
}
}
}
}
}
...
@@ -462,29 +806,124 @@ export default {
...
@@ -462,29 +806,124 @@ export default {
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.play
{
.play
{
.exam
{
padding
:
0
;
}
.exam
{
.exam
.topic
{
display
:
inline-block
;
margin-bottom
:
0
.1rem
;
}
padding
:
0
;
.exam
.topic
.tit
{
margin
:
0
auto
;
padding
:
0
0
.2rem
;
text-align
:
center
;
font-size
:
0
.24rem
;
color
:
#313131
;
background
:
#fff
;
box-sizing
:
border-box
;
-webkit-box-sizing
:
border-box
;
}
}
.exam
.topic
.cur
{
text-align
:
center
;
font-size
:
0
.18rem
;
color
:
#313131
;
line-height
:
0
.4rem
;
}
.exam
.topic
{
display
:
inline-block
;
margin-bottom
:
0
.1rem
;
}
.exam
.topic
.tit
{
margin
:
0
auto
;
padding
:
0
0
.2rem
;
text-align
:
center
;
font-size
:
0
.24rem
;
color
:
#313131
;
background
:
#fff
;
box-sizing
:
border-box
;
-webkit-box-sizing
:
border-box
;
}
.exam
.topic
.cur
{
text-align
:
center
;
font-size
:
0
.18rem
;
color
:
#313131
;
line-height
:
0
.4rem
;
}
/* 循环 所有选择题 */
/* 循环 所有选择题 */
.exam
.q-group
{
padding
:
0
.1rem
0
.1rem
;
border-bottom
:
1px
solid
#c9c9c9
7a
;
overflow
:
hidden
;
}
.exam
.q-group
{
.exam
.q-group
.q-num
{
float
:
left
;
margin-right
:
0
.1rem
;
font-size
:
0
.16rem
;
color
:
#676a6c
;
}
padding
:
0
.1rem
0
.1rem
;
.exam
.q-group
.q-title
{
float
:
left
;
width
:
90%
;
font-size
:
0
.16rem
;
color
:
#676a6c
;
text-align
:
justify
;
}
border-bottom
:
1px
solid
#c9c9c9
7a
;
.exam
.q-group
.q-type
{
float
:
right
;
font-size
:
0
.16rem
;
color
:
#676a6c
;
}
overflow
:
hidden
;
.exam
.q-group
.radio-group
{
float
:
left
;
margin-top
:
0
.1rem
;
width
:
100%
;
}
}
.exam
.q-group
.radio-group
.radio
{
display
:
block
;
font-size
:
0
.18rem
;
color
:
#3f3b3a
;
line-height
:
0
.3rem
;
margin-bottom
:
0
.1rem
;
}
.exam
.q-group
.q-num
{
.exam
.q-group
.checkbox-group
{
float
:
left
;
margin-top
:
0
.1rem
;
width
:
100%
;
}
float
:
left
;
.exam
.q-group
.checkbox-group
.checkbox
{
display
:
block
;
font-size
:
0
.18rem
;
color
:
#3f3b3a
;
line-height
:
0
.3rem
;
margin-bottom
:
0
.1rem
;
}
margin-right
:
0
.1rem
;
.exam
.q-group
.radio-group
.radio.error
,
.exam
.q-group
.checkbox-group
.checkbox.error
{
color
:
#d80000
;
}
font-size
:
0
.16rem
;
.exam
.q-group
.radio-group
.radio.success
,
.exam
.q-group
.checkbox-group
.checkbox.success
{
color
:
#090
;
}
color
:
#676a6c
;
.exam
.q-group
.result
{
float
:
right
;
font-size
:
0
.18rem
;
color
:
#3f3b3a
;
margin-right
:
0
;
}
}
.exam
.q-group
.result
.stu
{
display
:
inline-block
;
}
.exam
.q-group
.q-title
{
.exam
.q-group
.result
.stu.error
{
color
:
#d80000
;
}
float
:
left
;
.exam
.q-group
.result
.stu.success
{
color
:
#090
;
}
width
:
90%
;
.exam
.q-group
:last-child
{
border-bottom
:
none
;
}
font-size
:
0
.16rem
;
.exam
.btn
{
margin
:
0
.2rem
auto
;
width
:
60%
;
height
:
0
.5rem
;
line-height
:
0
.5rem
;
font-size
:
0
.16rem
;
text-align
:
center
;
font-weight
:
300
;
color
:
#fff
;
border-radius
:
0
.1rem
;
background
:
#b49441
;
cursor
:
pointer
;
}
color
:
#676a6c
;
.exam
.btn.on
{
opacity
:
0
.5
;
}
text-align
:
justify
;
.exam
.care
{
font-size
:
0
.16rem
;
color
:
#d80000
;
text-align
:
center
;
}
}
.exam
.q-group
.q-type
{
float
:
right
;
font-size
:
0
.16rem
;
color
:
#676a6c
;
}
.exam
.q-group
.radio-group
{
float
:
left
;
margin-top
:
0
.1rem
;
width
:
100%
;
}
.exam
.q-group
.radio-group
.radio
{
display
:
block
;
font-size
:
0
.18rem
;
color
:
#3f3b3a
;
line-height
:
0
.3rem
;
margin-bottom
:
0
.1rem
;
}
.exam
.q-group
.checkbox-group
{
float
:
left
;
margin-top
:
0
.1rem
;
width
:
100%
;
}
.exam
.q-group
.checkbox-group
.checkbox
{
display
:
block
;
font-size
:
0
.18rem
;
color
:
#3f3b3a
;
line-height
:
0
.3rem
;
margin-bottom
:
0
.1rem
;
}
.exam
.q-group
.radio-group
.radio.error
,
.exam
.q-group
.checkbox-group
.checkbox.error
{
color
:
#d80000
;
}
.exam
.q-group
.radio-group
.radio.success
,
.exam
.q-group
.checkbox-group
.checkbox.success
{
color
:
#090
;
}
.exam
.q-group
.result
{
float
:
right
;
font-size
:
0
.18rem
;
color
:
#3f3b3a
;
margin-right
:
0
;
}
.exam
.q-group
.result
.stu
{
display
:
inline-block
;
}
.exam
.q-group
.result
.stu.error
{
color
:
#d80000
;
}
.exam
.q-group
.result
.stu.success
{
color
:
#090
;
}
.exam
.q-group
:last-child
{
border-bottom
:
none
;
}
.exam
.btn
{
margin
:
0
.2rem
auto
;
width
:
60%
;
height
:
0
.5rem
;
line-height
:
0
.5rem
;
font-size
:
0
.16rem
;
text-align
:
center
;
font-weight
:
300
;
color
:
#fff
;
border-radius
:
0
.1rem
;
background
:
#b49441
;
cursor
:
pointer
;
}
.exam
.btn.on
{
opacity
:
0
.5
;
}
.exam
.care
{
font-size
:
0
.16rem
;
color
:
#d80000
;
text-align
:
center
;
}
.exam
.q-sa-title
{
.exam
.q-sa-title
{
float
:
left
;
float
:
left
;
...
@@ -494,4 +933,9 @@ export default {
...
@@ -494,4 +933,9 @@ export default {
text-align
:
justify
;
text-align
:
justify
;
}
}
}
}
.no-exam
{
font-size
:
0
.24rem
;
line-height
:
19
;
text-align
:
center
;
}
</
style
>
</
style
>
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论