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

update viewer module

上级 c54eb22e
...@@ -90,13 +90,23 @@ export function updateCourseWork(semesterId, courseId, data) { ...@@ -90,13 +90,23 @@ export function updateCourseWork(semesterId, courseId, data) {
} }
/** /**
* 获取课程考试详情 * 获取课程考试试题
* @param {string} semesterId 学期ID * @param {string} semesterId 学期ID
* @param {string} courseId 课程ID * @param {string} courseId 课程ID
*/ */
export function getCourseExam(semesterId, courseId) { 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( return httpRequest.get(
`/v2/education/${semesterId}/${courseId}/examination` `/v2/education/${semesterId}/${courseId}/examination/${examId}/status`
) )
} }
...@@ -104,11 +114,24 @@ export function getCourseExam(semesterId, courseId) { ...@@ -104,11 +114,24 @@ export function getCourseExam(semesterId, courseId) {
* 提交课程考试 * 提交课程考试
* @param {string} semesterId 学期ID * @param {string} semesterId 学期ID
* @param {string} courseId 课程ID * @param {string} courseId 课程ID
* @param {string} examId 试题ID
*/ */
export function updateCourseExam(semesterId, courseId, data) { export function submitCourseExam(semesterId, courseId, examId, data) {
return httpRequest.post( return httpRequest.post(
`/v2/education/${semesterId}/${courseId}/essay`, `/v2/education/${semesterId}/${courseId}/examination/${examId}/sheet`,
data, data,
{ headers: { 'Content-Type': 'multipart/form-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`
) )
} }
...@@ -22,7 +22,12 @@ export default { ...@@ -22,7 +22,12 @@ export default {
props: { props: {
data: { type: Array, default: () => [] }, data: { type: Array, default: () => [] },
// 当前选中的章节 // 当前选中的章节
active: { type: Object, default: () => {} } active: {
type: Object,
default() {
return {}
}
}
}, },
data() { data() {
return { return {
......
...@@ -26,7 +26,12 @@ export default { ...@@ -26,7 +26,12 @@ export default {
// 讲义 // 讲义
ppts: { type: Array, default: () => [] }, ppts: { type: Array, default: () => [] },
// 当前选中的章节 // 当前选中的章节
active: { type: Object, default: () => {} }, active: {
type: Object,
default() {
return {}
}
},
// 当前选择的PPT // 当前选择的PPT
pptIndex: { type: Number, default: 0 } pptIndex: { type: Number, default: 0 }
}, },
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<li class="file-list-item" v-for="file in files" :key="file.id"> <li class="file-list-item" v-for="file in files" :key="file.id">
<a :href="file.file_url" target="_blank"> <a :href="file.file_url" target="_blank">
<i class="el-icon-document"></i> <i class="el-icon-document"></i>
{{ file.file_name }} <div v-html="file.file_name"></div>
</a> </a>
<span v-if="file.file_size">{{ file.file_size }}</span> <span v-if="file.file_size">{{ file.file_size }}</span>
<a :href="file.file_url" :download="file.file_name" target="_blank"> <a :href="file.file_url" :download="file.file_name" target="_blank">
...@@ -46,11 +46,18 @@ export default { ...@@ -46,11 +46,18 @@ export default {
border-radius: 32px; border-radius: 32px;
justify-content: space-between; justify-content: space-between;
a { a {
display: flex;
align-items: center;
text-decoration: none; text-decoration: none;
color: #333; color: #333;
white-space: nowrap;
&:hover { &:hover {
color: #b49441; color: #b49441;
} }
::v-deep * {
margin: 0;
padding: 0;
}
} }
} }
.empty { .empty {
......
<template> <template>
<div class="upload"> <div class="upload">
<el-upload action :show-file-list="false" :http-request="httpRequest"> <el-upload action :show-file-list="false" :http-request="httpRequest">
<el-button size="small" icon="el-icon-upload">点击上传</el-button> <slot></slot>
<el-button type="text" icon="el-icon-upload">点击上传</el-button>
<template v-slot:tip> <template v-slot:tip>
<div class="el-upload__tips"> <div class="el-upload__tips">
<slot name="tip"></slot> <slot name="tip"></slot>
</div> </div>
</template> </template>
</el-upload> </el-upload>
<div class="file-list" v-if="value"> <div class="file-list" v-if="fileList.length">
<div class="file-list-item"> <div class="file-list-item" v-for="(fileUrl, index) in fileList" :key="index">
<a :href="value" target="_blank"> <a :href="fileUrl" target="_blank">
<i class="el-icon-document"></i> <i class="el-icon-document"></i>
{{ fileName }} {{ fileUrl | fileName }}
</a> </a>
<a :href="value" :download="fileName" target="_blank"> <a :href="fileUrl" :download="fileUrl | fileName" target="_blank">
<el-tooltip effect="dark" content="下载"> <el-tooltip effect="dark" content="下载">
<i class="el-icon-download"></i> <i class="el-icon-download"></i>
</el-tooltip> </el-tooltip>
...@@ -25,36 +26,43 @@ ...@@ -25,36 +26,43 @@
</template> </template>
<script> <script>
import * as api from '../api/index' import * as api from '../../api'
export default { export default {
name: 'VUpload', name: 'VUpload',
props: { props: {
value: { type: String } value: { type: [String, Array] }
}, },
data() { data() {
return {} return {
fileList: []
}
},
watch: {
value(value) {
if (value) {
this.fileList = Array.isArray(value) ? value : [value]
}
}
}, },
computed: { filters: {
fileName() { fileName(value) {
return this.value ? this.value.split('/').pop() : '' return value ? value.split('/').pop() : ''
} }
}, },
methods: { methods: {
httpRequest(xhr) { httpRequest(xhr) {
api api.uploadFile({ file: xhr.file }).then(response => {
.uploadFile({ file: xhr.file })
.then(response => {
if (response.success) { 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) this.$emit('input', response.url)
} }
}
}) })
.catch(error => {
console.log(error)
})
},
handleRemove() {
this.$emit('input', '')
} }
} }
} }
...@@ -66,6 +74,7 @@ export default { ...@@ -66,6 +74,7 @@ export default {
margin-bottom: 10px; margin-bottom: 10px;
padding: 0 10px; padding: 0 10px;
justify-content: space-between; justify-content: space-between;
line-height: 30px;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
......
...@@ -11,12 +11,13 @@ ...@@ -11,12 +11,13 @@
<script> <script>
// components // components
import ChapterPlayer from '../src/player/ChapterPlayer.vue' // 章节视频 import ChapterPlayer from './player/ChapterPlayer.vue' // 章节视频
import ChapterWork from '../src/work/index.vue' // 章节作业 import ChapterWork from './work/index.vue' // 章节作业
import ChapterRead from '../src/read/chapterRead.vue' // 章节资料 import ChapterRead from './read/chapterRead.vue' // 章节资料
import CourseWork from '../src/work/courseWork.vue' // 课程大作业 import ChapterLive from './live/chapterLive.vue' // 章节直播
import CourseRead from '../src/read/courseRead.vue' // 课程资料 import CourseWork from './work/courseWork.vue' // 课程大作业
import CourseExam from '../src/work/courseExam.vue' // 课程资料 import CourseRead from './read/courseRead.vue' // 课程资料
import CourseExam from './work/courseExam.vue' // 课程考试
export default { export default {
name: 'ViewerLayout', name: 'ViewerLayout',
...@@ -24,12 +25,18 @@ export default { ...@@ -24,12 +25,18 @@ export default {
ChapterPlayer, ChapterPlayer,
ChapterWork, ChapterWork,
ChapterRead, ChapterRead,
ChapterLive,
CourseWork, CourseWork,
CourseRead, CourseRead,
CourseExam CourseExam
}, },
props: { props: {
chapter: { type: Object, default: () => {} } chapter: {
type: Object,
default() {
return {}
}
}
}, },
computed: { computed: {
currentCompoent() { currentCompoent() {
......
<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>
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
<script> <script>
// api // api
import * as api from '../../api/index' import * as api from '../../api'
// components // components
import videoPlayer from './videoPlayer.vue' import videoPlayer from './videoPlayer.vue'
import pptPlayer from './pptPlayer.vue' import pptPlayer from './pptPlayer.vue'
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
<script> <script>
// components // components
import Container from '../../components/container.vue' import Container from '../common/container.vue'
import FileList from '../../components/fileList.vue' import FileList from '../common/fileList.vue'
// 章节阅读资料 // 章节阅读资料
export default { export default {
...@@ -16,9 +16,19 @@ export default { ...@@ -16,9 +16,19 @@ export default {
components: { Container, FileList }, components: { Container, FileList },
props: { props: {
// 当前选中的 // 当前选中的
chapter: { type: Object, default: () => {} }, chapter: {
type: Object,
default() {
return {}
}
},
// 课程详情接口返回的数据 // 课程详情接口返回的数据
data: { type: Object, default: () => {} } data: {
type: Object,
default() {
return {}
}
}
}, },
computed: { computed: {
files() { files() {
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
<script> <script>
// components // components
import Container from '../../components/container.vue' import Container from '../common/container.vue'
import FileList from '../../components/fileList.vue' import FileList from '../common/fileList.vue'
// 课程阅读资料 // 课程阅读资料
export default { export default {
...@@ -16,9 +16,19 @@ export default { ...@@ -16,9 +16,19 @@ export default {
components: { Container, FileList }, components: { Container, FileList },
props: { props: {
// 当前选中的 // 当前选中的
chapter: { type: Object, default: () => {} }, chapter: {
type: Object,
default() {
return {}
}
},
// 课程详情接口返回的数据 // 课程详情接口返回的数据
data: { type: Object, default: () => {} } data: {
type: Object,
default() {
return {}
}
}
}, },
computed: { computed: {
files() { files() {
......
<template> <template>
<container :title="chapter.name" v-loading="loading"> <container :title="chapter.name" v-loading="loading">
<template v-slot:header-aside v-if="isSubmited">正确率:{{detail.score}}%</template>
<div class="exam"> <div class="exam">
<div class="exam-form"> <div class="exam-form">
<el-form :disabled="isSubmited" size="medium"> <el-form :disabled="isSubmited">
<chapter-exam-item <exam-item
v-for="(item, index) in unorderedQuestions" v-for="(item, index) in unorderedQuestions"
:disabled="isSubmited"
:data="item"
:value="findAnswerById(item.id)"
:index="index" :index="index"
:type="item.question_type"
:data="item"
:value="item.formModel"
:disabled="isSubmited"
:key="item.id" :key="item.id"
@change="onChange(item.id, ...arguments)" ></exam-item>
></chapter-exam-item>
<div class="exam-buttons"> <div class="exam-buttons">
<el-tooltip effect="dark" content="提交之后就不能修改了哦" placement="right"> <el-tooltip effect="dark" content="提交之后就不能修改了哦" placement="right">
<el-button type="primary" @click="onSubmit">{{submitText}}</el-button> <el-button type="primary" @click="onSubmit">{{submitText}}</el-button>
...@@ -27,36 +28,51 @@ ...@@ -27,36 +28,51 @@
// libs // libs
import { shuffle } from 'lodash' import { shuffle } from 'lodash'
// components // components
import Container from '../../components/container.vue' import Container from '../common/container.vue'
import ChapterExamItem from './chapterExamItem.vue' import ExamItem from './examItem.vue'
// api // api
import * as api from '../../api/index' import * as api from '../../api'
// 章节测试题 // 章节测试题
export default { export default {
name: 'ChapterExam', name: 'ChapterExam',
components: { Container, ChapterExamItem }, components: { Container, ExamItem },
props: { props: {
// 当前选中的章节 // 当前选中的章节
chapter: { type: Object, default: () => {} } chapter: {
type: Object,
default() {
return {}
}
}
}, },
data() { data() {
return { return {
loading: false, loading: false,
detail: null, detail: null,
values: [], // 提交的答案 questions: [], // 问题列表
startTime: new Date().getTime(), // 进入时间 startTime: new Date().getTime(), // 进入时间
messageInstance: null messageInstance: null
} }
}, },
watch: {
chapter: {
immediate: true,
handler(data) {
this.questions = data.homework
? this.genQuenstions(data.homework.questions)
: []
}
}
},
computed: { computed: {
// 学期ID // 学期ID
sid() { sid() {
return '6552021107166150656' return this.$route.params.sid
}, },
// 课程ID // 课程ID
cid() { cid() {
return '6568035374902280192' return this.$route.params.cid
}, },
// 当前页面的ID // 当前页面的ID
pid() { pid() {
...@@ -66,33 +82,12 @@ export default { ...@@ -66,33 +82,12 @@ export default {
resourceId() { resourceId() {
return this.chapter.resource_id return this.chapter.resource_id
}, },
// 问题列表
questions() {
const homework = this.chapter.homework
return homework ? homework.questions : []
},
// 打乱顺序的问题列表 // 打乱顺序的问题列表
unorderedQuestions() { unorderedQuestions() {
const ids = this.questions.map(item => item.id) const ids = this.questions.map(item => item.id)
const sortIds = shuffle(ids) const sortIds = shuffle(ids)
return sortIds.map(id => this.questions.find(item => item.id === id)) return sortIds.map(id => this.questions.find(item => item.id === id))
}, },
/**
* 解析用户提交的数据
* @return [{{ question_id: 'xxx', value: ['xxx', 'xxx'] }}]
*/
answers() {
const answers = this.isSubmited
? JSON.parse(this.detail.work_contents)
: []
return answers.map(item => {
const ids = item.options.reduce((result, subitem) => {
subitem.selected && result.push(subitem.id)
return result
}, [])
return { question_id: item.question_id, value: ids }
})
},
// 是否提交 // 是否提交
isSubmited() { isSubmited() {
return this.detail ? !!this.detail.work_contents : false return this.detail ? !!this.detail.work_contents : false
...@@ -110,33 +105,78 @@ export default { ...@@ -110,33 +105,78 @@ export default {
.getChapterExam(this.sid, this.cid, this.resourceId) .getChapterExam(this.sid, this.cid, this.resourceId)
.then(response => { .then(response => {
this.detail = Array.isArray(response) ? null : 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(() => { .finally(() => {
this.loading = false this.loading = false
}) })
}, },
// 通过问题ID查找答案 // 组装问题数据
findAnswerById(id) { genQuenstions(list) {
const found = this.answers.find(item => item.question_id === id) if (!list) {
return found ? found.value : [] return []
}, }
onChange(qid, value) { return list.map(item => {
const index = this.values.findIndex(item => item.question_id === qid) let temp = null
if (index === -1) { if (item.question_type === 1) {
this.values.push({ question_id: qid, value }) // 单选
} else { temp = {
this.values.splice(index, 1, { question_id: qid, value }) 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() { checkSubmit() {
if (this.values.length !== this.questions.length) { const quenstions = this.questions
return false for (let i = 0; i < quenstions.length; i++) {
} const value = quenstions[i].formModel.user_answer
const values = this.values if (Array.isArray(value) ? !value.length : !value) {
for (let i = 0; i < values.length; i++) {
const options = values[i].value
if (!options.length) {
return false return false
} }
} }
...@@ -144,6 +184,7 @@ export default { ...@@ -144,6 +184,7 @@ export default {
}, },
// 提交 // 提交
onSubmit() { onSubmit() {
// 校验
if (!this.checkSubmit()) { if (!this.checkSubmit()) {
this.messageInstance && this.messageInstance.close() this.messageInstance && this.messageInstance.close()
this.messageInstance = this.$message.error('还有题目未做,不能提交') this.messageInstance = this.$message.error('还有题目未做,不能提交')
...@@ -176,21 +217,25 @@ export default { ...@@ -176,21 +217,25 @@ export default {
// 提交的答案数据 // 提交的答案数据
handleSubmitData() { handleSubmitData() {
const result = this.questions.map(item => { const result = this.questions.map(item => {
// 查找提交的option id
const found = this.values.find(
subitem => subitem.question_id === item.id
)
const ids = found ? found.value : []
// 解析
const parseOptions = JSON.parse(item.question_options)
// 设置提交选中状态 // 设置提交选中状态
let isCorrect = true let isCorrect = true
const options = parseOptions.map(option => { const options = item.options.map(option => {
option.selected = ids.includes(option.id) ? 1 : 0 // 选择的项
if (option.checked !== !!option.selected && isCorrect) { 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 isCorrect = false
} }
return option return {
id: option.id,
checked: option.checked,
option: option.option,
selected
}
}) })
return { return {
question_id: item.id, question_id: item.id,
......
<template> <template>
<container :title="chapter.name" v-loading="loading"> <container :title="chapter.name" v-loading="loading">
<chapter-work-item <div class="exam-form">
<el-form :disabled="isRevised">
<exam-item
v-for="(item, index) in questions" v-for="(item, index) in questions"
:disabled="isSubmited"
:data="item"
:index="index" :index="index"
:type="item.question_type"
:data="item"
:value="item.formModel"
:disabled="isRevised"
:key="item.id" :key="item.id"
@change="onChange(item.id, ...arguments)" ></exam-item>
></chapter-work-item> </el-form>
</div>
<div class="work-bottom" v-if="detail"> <div class="work-bottom" v-if="detail">
<div class="info"> <div class="info">
<template v-if="isRevised"> <template v-if="isRevised">
...@@ -26,7 +31,7 @@ ...@@ -26,7 +31,7 @@
</template> </template>
<template v-else-if="detail.created_time"> <template v-else-if="detail.created_time">
<p class="help">已于 {{detail.created_time}} 提交,等待老师批改中。</p> <p class="help">已于 {{detail.created_time}} 提交,等待老师批改中。</p>
<template v-if="detail.updated_time !== detail.created_time"> <template v-if="detail.updated_time && detail.updated_time !== detail.created_time">
<p class="help">最近提交时间: {{detail.updated_time}}</p> <p class="help">最近提交时间: {{detail.updated_time}}</p>
</template> </template>
</template> </template>
...@@ -41,52 +46,61 @@ ...@@ -41,52 +46,61 @@
</template> </template>
<script> <script>
import Base64 from 'Base64'
// componets // componets
import Container from '../../components/container.vue' import Container from '../common/container.vue'
import ChapterWorkItem from './chapterWorkItem.vue' import ExamItem from './examItem.vue'
// api // api
import * as api from '../../api/index' import * as api from '../../api'
// 章节作业 // 章节作业
export default { export default {
name: 'ChapterWork', name: 'ChapterWork',
components: { Container, ChapterWorkItem }, components: { Container, ExamItem },
props: { props: {
// 当前选中的 // 当前选中的
chapter: { type: Object, default: () => {} }, chapter: {
type: Object,
default() {
return {}
}
},
// 课程详情接口返回的数据 // 课程详情接口返回的数据
data: { type: Object, default: () => {} } data: {
type: Object,
default() {
return {}
}
}
}, },
data() { data() {
return { 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, loading: false,
detail: null,
questions: [], // 问题列表
startTime: new Date().getTime(), // 进入时间
messageInstance: null messageInstance: null
} }
}, },
watch: {
chapter: {
immediate: true,
handler(data) {
this.questions = data.homework
? this.genQuenstions(data.homework.questions)
: []
}
}
},
computed: { computed: {
// 学期ID // 学期ID
sid() { sid() {
return '6552021107166150656' return this.$route.params.sid
}, },
// 课程ID // 课程ID
cid() { cid() {
return '6568035374902280192' return this.$route.params.cid
}, },
// 当前页面的ID // 当前页面的ID
pid() { pid() {
...@@ -96,11 +110,6 @@ export default { ...@@ -96,11 +110,6 @@ export default {
resourceId() { resourceId() {
return this.chapter.resource_id return this.chapter.resource_id
}, },
// 问题列表
questions() {
const homework = this.chapter.homework
return homework ? homework.questions : []
},
// 是否批改 // 是否批改
isRevised() { isRevised() {
return this.detail ? !!this.detail.check_date : false return this.detail ? !!this.detail.check_date : false
...@@ -119,30 +128,113 @@ export default { ...@@ -119,30 +128,113 @@ export default {
.then(response => { .then(response => {
this.detail = Array.isArray(response) ? null : response this.detail = Array.isArray(response) ? null : response
if (this.detail) { if (this.detail) {
this.ruleForm.essay_name = this.detail.essay_name const parseAnswers = JSON.parse(this.detail.work_contents)
this.ruleForm.essay_description = this.detail.essay_description // 设置答案
this.ruleForm.url = this.detail.file_url 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(() => { .finally(() => {
this.loading = false 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() { onSubmit() {
this.$refs.ruleForm // 校验
.validate() if (!this.checkSubmit()) {
.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.messageInstance.close()
this.messageInstance = this.$message.error('还有题目未做,不能提交') 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) { handleSubmitRequest(params) {
......
...@@ -25,9 +25,10 @@ ...@@ -25,9 +25,10 @@
<!-- 编辑器 --> <!-- 编辑器 -->
<v-editor :disabled="isRevised" v-model="ruleForm.essay_description"></v-editor> <v-editor :disabled="isRevised" v-model="ruleForm.essay_description"></v-editor>
</el-form-item> </el-form-item>
<el-form-item label="附件" prop="url"> <el-form-item prop="url">
<!-- 文件上传 --> <!-- 文件上传 -->
<v-upload v-model="ruleForm.url"> <v-upload v-model="ruleForm.url">
请上传对应的文件附件:
<!-- <template v-slot:tip>只支持docx格式的文件,文件小于10M</template> --> <!-- <template v-slot:tip>只支持docx格式的文件,文件小于10M</template> -->
</v-upload> </v-upload>
</el-form-item> </el-form-item>
...@@ -53,7 +54,7 @@ ...@@ -53,7 +54,7 @@
</template> </template>
<template v-else-if="detail.created_time"> <template v-else-if="detail.created_time">
<p class="help">已于 {{detail.created_time}} 提交,等待老师批改中。</p> <p class="help">已于 {{detail.created_time}} 提交,等待老师批改中。</p>
<template v-if="detail.updated_time !== detail.created_time"> <template v-if="detail.updated_time && detail.updated_time !== detail.created_time">
<p class="help">最近提交时间: {{detail.updated_time}}</p> <p class="help">最近提交时间: {{detail.updated_time}}</p>
</template> </template>
</template> </template>
...@@ -72,11 +73,11 @@ ...@@ -72,11 +73,11 @@
<script> <script>
// componets // componets
import Container from '../../components/container.vue' import Container from '../common/container.vue'
import VEditor from '../../components/editor.vue' import VEditor from '../common/editor.vue'
import VUpload from '../../components/upload.vue' import VUpload from '../common/upload.vue'
// api // api
import * as api from '../../api/index' import * as api from '../../api'
// 课程大作业 // 课程大作业
export default { export default {
...@@ -84,9 +85,19 @@ export default { ...@@ -84,9 +85,19 @@ export default {
components: { Container, VEditor, VUpload }, components: { Container, VEditor, VUpload },
props: { props: {
// 当前选中的 // 当前选中的
chapter: { type: Object, default: () => {} }, chapter: {
type: Object,
default() {
return {}
}
},
// 课程详情接口返回的数据 // 课程详情接口返回的数据
data: { type: Object, default: () => {} } data: {
type: Object,
default() {
return {}
}
}
}, },
data() { data() {
return { return {
...@@ -113,11 +124,11 @@ export default { ...@@ -113,11 +124,11 @@ export default {
computed: { computed: {
// 学期ID // 学期ID
sid() { sid() {
return '6552021107166150656' return this.$route.params.sid
}, },
// 课程ID // 课程ID
cid() { cid() {
return '6568035374902280192' return this.$route.params.cid
}, },
// 是否批改 // 是否批改
isRevised() { isRevised() {
......
<template> <template>
<div class="q-item"> <div class="q-item">
<div class="q-item-hd"> <div class="q-item-hd">
<div class="q-item-num">{{currentIndex}}.</div> <div class="q-item-num">{{index + 1}}.</div>
<div class="q-item-title" v-html="data.question_content"></div> <div class="q-item-title" v-html="data.content">{{data.title}}</div>
<div class="q-item-aside" v-if="currentTypeText">({{currentTypeText}})</div> <div class="q-item-aside" v-if="typeText">({{typeText}})</div>
</div> </div>
<div class="q-item-bd"> <div class="q-item-bd">
<!-- 单选 --> <!-- 单选 -->
<el-radio-group v-model="radioValue" @change="onRadioChange" v-if="currentType === 1"> <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"> <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> <el-radio :class="genClass(item)" :label="item.id">{{item.abc_option}}</el-radio>
</div> </div>
</el-radio-group> </el-radio-group>
<!-- 多选 --> <!-- 多选 -->
<el-checkbox-group <el-checkbox-group v-model="currentValue.user_answer" v-if="type === 2">
v-model="checkboxValue"
@change="onCheckboxChange"
v-if="currentType === 2"
>
<div class="q-option-item" v-for="item in currentOptions" :key="item.id"> <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> <el-checkbox :class="genClass(item)" :label="item.id">{{item.abc_option}}</el-checkbox>
</div> </div>
</el-checkbox-group> </el-checkbox-group>
<!-- 简答题 --> <!-- 简答题 -->
<v-editor></v-editor> <template v-if="type === 3">
<v-upload></v-upload> <v-editor v-model="currentValue.user_answer" :disabled="disabled"></v-editor>
<v-upload v-model="currentValue.attachments">请上传对应的文件附件:</v-upload>
</template>
</div> </div>
<div class="q-item-ft" v-if="disabled"> <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> <p>
<span>学生答案:</span> <span>学生答案:</span>
<span :class="isCorrect ? 'is-success' : 'is-error'">{{submitAnswerText}}</span> <span :class="isCorrect ? 'is-success' : 'is-error'">{{submitAnswerText}}</span>
...@@ -35,41 +40,51 @@ ...@@ -35,41 +40,51 @@
<span>正确答案:</span> <span>正确答案:</span>
<span>{{correctAnswerText}}</span> <span>{{correctAnswerText}}</span>
</p> </p>
</template>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import VEditor from '../../components/editor.vue' // components
import VUpload from '../../components/upload.vue' import VEditor from '../common/editor.vue'
import VUpload from '../common/upload.vue'
export default { export default {
name: 'ChapterExamItem', name: 'ExamItem',
components: { VEditor, VUpload }, components: { VEditor, VUpload },
props: { props: {
// 索引 // 索引
index: { type: Number }, index: { type: Number },
// 问题类型
type: { type: Number },
// 单条数据 // 单条数据
data: { type: Object, default: () => {} }, data: {
type: Object,
default() {
return {}
}
},
// 提交的答案 // 提交的答案
value: { type: Array, default: () => [] }, value: {
type: Object,
default() {
return {}
}
},
// 是否禁用,提交过的是禁用状态 // 是否禁用,提交过的是禁用状态
disabled: { type: Boolean, default: false } disabled: { type: Boolean, default: false }
}, },
data() { data() {
return { return {
radioValue: '', currentValue: {}
checkboxValue: []
} }
}, },
watch: { watch: {
value: { value: {
immediate: true, immediate: true,
handler(value) { handler(value) {
if (this.currentType === 1) { this.currentValue = value
this.radioValue = value[0] || ''
} else {
this.checkboxValue = value
}
} }
} }
}, },
...@@ -82,33 +97,25 @@ export default { ...@@ -82,33 +97,25 @@ export default {
} }
return result return result
}, },
// 序号
currentIndex() {
return this.index + 1
},
// 当前类型
currentType() {
return this.data.question_type
},
// 选项类型 // 选项类型
currentTypeText() { typeText() {
const map = { 1: '单选题', 2: '多选题' } const map = { 1: '单选题', 2: '多选题' }
return map[this.currentType] return map[this.type]
},
// 接口返回的options数据
options() {
return this.data.question_options
? JSON.parse(this.data.question_options)
: []
}, },
// 处理后的options数据 // 处理后的options数据
currentOptions() { currentOptions() {
return this.options.map((item, index) => { if (!this.data.options) {
return []
}
return this.data.options.map((item, index) => {
// 英文字母 + 名称 // 英文字母 + 名称
item.abc = this.A_Z[index] item.abc = this.A_Z[index]
item.abc_option = `${this.A_Z[index]}. ${item.option}` item.abc_option = `${this.A_Z[index]}. ${item.option}`
// 提交时的选中状态 // 提交时的选中状态
item.selected = this.value.includes(item.id) const value = this.value.user_answer || ''
item.selected = Array.isArray(value)
? value.includes(item.id)
: value === item.id
return item return item
}) })
}, },
...@@ -146,17 +153,9 @@ export default { ...@@ -146,17 +153,9 @@ export default {
return null return null
} }
return { return {
'is-error': item.selected !== item.checked, 'is-error': !this.isCorrect && item.selected,
'is-success': item.checked 'is-success': this.isCorrect && item.selected
} }
},
// 单选
onRadioChange(value) {
this.$emit('change', [value])
},
// 多选
onCheckboxChange(value) {
this.$emit('change', value)
} }
} }
} }
...@@ -167,6 +166,9 @@ export default { ...@@ -167,6 +166,9 @@ export default {
font-size: 16px; font-size: 16px;
padding: 10px 0; padding: 10px 0;
border-bottom: 1px solid #c9c9c97a; border-bottom: 1px solid #c9c9c97a;
.upload {
font-size: 14px;
}
} }
.q-item-hd { .q-item-hd {
display: flex; display: flex;
...@@ -176,6 +178,11 @@ export default { ...@@ -176,6 +178,11 @@ export default {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
::v-deep ul {
margin: 0;
padding: 0;
list-style: none;
}
} }
.q-item-num { .q-item-num {
width: 20px; width: 20px;
......
...@@ -19,9 +19,19 @@ export default { ...@@ -19,9 +19,19 @@ export default {
components: { ChapterWork, ChapterExam }, components: { ChapterWork, ChapterExam },
props: { props: {
// 当前选中的 // 当前选中的
chapter: { type: Object, default: () => {} }, chapter: {
type: Object,
default() {
return {}
}
},
// 课程详情接口返回的数据 // 课程详情接口返回的数据
data: { type: Object, default: () => {} } data: {
type: Object,
default() {
return {}
}
}
}, },
computed: { computed: {
currentCompoent() { currentCompoent() {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="course-viewer-main"> <div class="course-viewer-main">
<!-- 顶部区域 --> <!-- 顶部区域 -->
<div class="course-viewer-main-hd"> <div class="course-viewer-main-hd">
<router-link to="/mobile/help/student"> <router-link :to="`/app/learn/course-detail/${sid}/${cid}`">
<i class="el-icon-arrow-left"></i> <i class="el-icon-arrow-left"></i>
</router-link> </router-link>
<h1 class="course-viewer-main-hd__title">{{ detail.course_name }}</h1> <h1 class="course-viewer-main-hd__title">{{ detail.course_name }}</h1>
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
<script> <script>
// api // api
import * as api from './api/index' import * as api from './api'
// components // components
import VAside from './components/aside/index.vue' import VAside from './components/aside/index.vue'
...@@ -71,11 +71,11 @@ export default { ...@@ -71,11 +71,11 @@ export default {
computed: { computed: {
// 学期ID // 学期ID
sid() { sid() {
return '6552021107166150656' return this.$route.params.sid
}, },
// 课程ID // 课程ID
cid() { cid() {
return '6568035374902280192' return this.$route.params.cid
}, },
// 当前页面的ID // 当前页面的ID
pid() { pid() {
...@@ -84,8 +84,10 @@ export default { ...@@ -84,8 +84,10 @@ export default {
// 章节列表 // 章节列表
chapters() { chapters() {
const chapters = this.detail.chapters || [] const chapters = this.detail.chapters || []
return chapters.concat([ if (!chapters.length) {
{ return []
}
const customeChapter = {
name: '大作业及资料', name: '大作业及资料',
children: [ children: [
{ name: '课程大作业', id: 'course_work', type: 99 }, { name: '课程大作业', id: 'course_work', type: 99 },
...@@ -94,7 +96,8 @@ export default { ...@@ -94,7 +96,8 @@ export default {
{ name: '课程考试', id: 'course_exam', type: 101 } { name: '课程考试', id: 'course_exam', type: 101 }
] ]
} }
]) chapters.push(customeChapter)
return chapters
}, },
// 当前选中的章节 // 当前选中的章节
activeChapter() { activeChapter() {
......
export default [ export default [
{ {
path: '/viewer', path: '/viewer/:sid/:cid',
component: () => import('./index.vue'), component: () => import('./index.vue'),
children: [ children: [
{ {
......
<template></template>
<script>
// 章节视频
export default {
name: 'ChapterPlayer'
}
</script>
<template>
<div class="q-item">
<div class="q-item-hd">
<div class="q-item-num">{{currentIndex}}.</div>
<div class="q-item-title" v-html="data.question_content"></div>
<div class="q-item-aside">({{currentTypeText}})</div>
</div>
<div class="q-item-bd">
<!-- 单选 -->
<el-radio-group v-model="radioValue" @change="onRadioChange" v-if="currentType === 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="checkboxValue"
@change="onCheckboxChange"
v-if="currentType === 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>
</div>
<div class="q-item-ft" v-if="disabled">
<p>
<span>学生答案:</span>
<span :class="isCorrect ? 'is-success' : 'is-error'">{{submitAnswerText}}</span>
</p>
<p>
<span>正确答案:</span>
<span>{{correctAnswerText}}</span>
</p>
</div>
</div>
</template>
<script>
export default {
name: 'ChapterExamItem',
props: {
// 索引
index: { type: Number },
// 单条数据
data: { type: Object, default: () => {} },
// 提交的答案
value: { type: Array, default: () => [] },
// 是否禁用,提交过的是禁用状态
disabled: { type: Boolean, default: false }
},
data() {
return {
radioValue: '',
checkboxValue: []
}
},
watch: {
value: {
immediate: true,
handler(value) {
if (this.currentType === 1) {
this.radioValue = value[0] || ''
} else {
this.checkboxValue = value
}
}
}
},
computed: {
// 26个英文字母
A_Z() {
const result = []
for (let i = 0; i < 26; i++) {
result.push(String.fromCharCode(65 + i))
}
return result
},
// 序号
currentIndex() {
return this.index + 1
},
// 当前类型
currentType() {
return this.data.question_type
},
// 选项类型
currentTypeText() {
const map = { 1: '单选题', 2: '多选题' }
return map[this.currentType]
},
// 接口返回的options数据
options() {
return this.data.question_options
? JSON.parse(this.data.question_options)
: []
},
// 处理后的options数据
currentOptions() {
return this.options.map((item, index) => {
// 英文字母 + 名称
item.abc = this.A_Z[index]
item.abc_option = `${this.A_Z[index]}. ${item.option}`
// 提交时的选中状态
item.selected = this.value.includes(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': item.selected !== item.checked,
'is-success': item.checked
}
},
// 单选
onRadioChange(value) {
this.$emit('change', [value])
},
// 多选
onCheckboxChange(value) {
this.$emit('change', value)
}
}
}
</script>
<style lang="scss" scoped>
.q-item {
font-size: 16px;
padding: 10px 0;
border-bottom: 1px solid #c9c9c97a;
}
.q-item-hd {
display: flex;
padding: 10px 0 20px;
::v-deep p {
margin: 0;
padding: 0;
}
}
.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>
<template>
<container :title="detail.title" v-loading="loading">
<div class="exam">
<div class="exam-form">
<el-form :disabled="isSubmited" size="medium">
<course-exam-item
v-for="(item, index) in questions"
:index="index"
:type="item.type"
:data="item"
:key="item.id"
></course-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>
// components
import Container from '../../components/container.vue'
import CourseExamItem from './courseExamItem.vue'
// api
import * as api from '../../api/index'
// 章节测试题
export default {
name: 'CourseExam',
components: { Container, CourseExamItem },
props: {
// 当前选中的章节
chapter: { type: Object, default: () => {} }
},
data() {
return {
loading: false,
detail: {},
// questions: [],
values: [], // 提交的答案
startTime: new Date().getTime(), // 进入时间
messageInstance: null
}
},
computed: {
// 学期ID
sid() {
return '6552021107166150656'
},
// 课程ID
cid() {
return '6568035374902280192'
},
// 当前页面的ID
pid() {
return this.$route.params.id
},
// 问题列表
questions() {
if (!this.detail.examination) {
return []
}
const addKey = function(array, key, value) {
return array.map(item => {
item[key] = value
return item
})
}
let { radioList, checkboxList, shortAnswerList } = this.detail.examination
// 单选
radioList = addKey(radioList, 'type', 1)
// 多选
checkboxList = addKey(checkboxList, 'type', 2)
// 问答
shortAnswerList = addKey(shortAnswerList, 'type', 3)
return [...radioList, ...checkboxList, ...shortAnswerList]
},
// 是否提交
isSubmited() {
return this.detail ? !!this.detail.work_contents : false
},
// 提交按钮文本
submitText() {
return this.isSubmited ? '已提交' : '提交'
}
},
methods: {
// 获取测试答题详情
getDetail() {
this.loading = true
api
.getCourseExam(this.sid, this.cid)
.then(response => {
this.detail = Array.isArray(response) ? null : response
})
.finally(() => {
this.loading = false
})
},
// 通过问题ID查找答案
findAnswerById(id) {
const found = this.answers.find(item => item.question_id === id)
return found ? found.value : []
},
onChange(qid, value) {
const index = this.values.findIndex(item => item.question_id === qid)
if (index === -1) {
this.values.push({ question_id: qid, value })
} else {
this.values.splice(index, 1, { question_id: qid, value })
}
},
// 提交校验
checkSubmit() {
if (this.values.length !== this.questions.length) {
return false
}
const values = this.values
for (let i = 0; i < values.length; i++) {
const options = values[i].value
if (!options.length) {
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 => {
// 查找提交的option id
const found = this.values.find(
subitem => subitem.question_id === item.id
)
const ids = found ? found.value : []
// 解析
const parseOptions = JSON.parse(item.question_options)
// 设置提交选中状态
let isCorrect = true
const options = parseOptions.map(option => {
option.selected = ids.includes(option.id) ? 1 : 0
if (option.checked !== !!option.selected && isCorrect) {
isCorrect = false
}
return option
})
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>
<template>
<div class="q-item">
<div class="q-item-hd">
<div class="q-item-num">{{currentIndex}}.</div>
<div class="q-item-title" v-html="data.content">{{data.title}}</div>
<div class="q-item-aside" v-if="currentTypeText">({{currentTypeText}})</div>
</div>
<div class="q-item-bd">
<!-- 单选 -->
<el-radio-group v-model="radioValue" @change="onRadioChange" v-if="currentType === 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="checkboxValue"
@change="onCheckboxChange"
v-if="currentType === 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="currentType === 3">
<v-editor></v-editor>
<v-upload></v-upload>
</template>
</div>
</div>
</template>
<script>
// components
import VEditor from '../../components/editor.vue'
import VUpload from '../../components/upload.vue'
export default {
name: 'CourseExamItem',
components: { VEditor, VUpload },
props: {
// 索引
index: { type: Number },
// 问题类型
type: { type: Number },
// 单条数据
data: { type: Object, default: () => {} },
// 提交的答案
value: { type: Array, default: () => [] },
// 是否禁用,提交过的是禁用状态
disabled: { type: Boolean, default: false }
},
data() {
return {
radioValue: '',
checkboxValue: []
}
},
watch: {
value: {
immediate: true,
handler(value) {
if (this.currentType === 1) {
this.radioValue = value[0] || ''
} else {
this.checkboxValue = value
}
}
}
},
computed: {
// 26个英文字母
A_Z() {
const result = []
for (let i = 0; i < 26; i++) {
result.push(String.fromCharCode(65 + i))
}
return result
},
// 序号
currentIndex() {
return this.index + 1
},
// 当前类型
currentType() {
return this.type || this.data.question_type
},
// 选项类型
currentTypeText() {
const map = { 1: '单选题', 2: '多选题' }
return map[this.currentType]
},
// 处理后的options数据
currentOptions() {
return this.data.options.map((item, index) => {
// 英文字母 + 名称
item.abc = this.A_Z[index]
item.abc_option = `${this.A_Z[index]}. ${item.option}`
// 提交时的选中状态
item.selected = this.value.includes(item.id)
return item
})
}
},
methods: {
// 生成class
genClass(item) {
if (!this.disabled) {
return null
}
return {
'is-error': item.selected !== item.checked,
'is-success': item.checked
}
},
// 单选
onRadioChange(value) {
this.$emit('change', [value])
},
// 多选
onCheckboxChange(value) {
this.$emit('change', value)
}
}
}
</script>
<style lang="scss" scoped>
.q-item {
font-size: 16px;
padding: 10px 0;
border-bottom: 1px solid #c9c9c97a;
}
.q-item-hd {
display: flex;
padding: 10px 0 20px;
::v-deep p {
margin: 0;
padding: 0;
}
}
.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>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论