|
@@ -1,26 +1,25 @@
|
|
<template>
|
|
<template>
|
|
- <el-dialog v-model="state.dialogVisible" draggable append-to-body destroy-on-close :show-close="false" width="80%">
|
|
|
|
|
|
+ <el-dialog v-model="state.dialogVisible" draggable append-to-body destroy-on-close :show-close="false" width="80%" :before-close="closeDialog">
|
|
<template #header="{ close, titleId, titleClass }">
|
|
<template #header="{ close, titleId, titleClass }">
|
|
<div class="topContent">
|
|
<div class="topContent">
|
|
- <div class="surplusBox">
|
|
|
|
- <span>剩余时间:</span>
|
|
|
|
- <span class="surplus">{{ state.surplusTime }}</span>
|
|
|
|
|
|
+ <div class="surplusBox" v-if="state.examType == 0">
|
|
|
|
+ <el-countdown title="剩余时间:" :value="state.surplusTime" @finish="onSurplusTimeFinish" />
|
|
</div>
|
|
</div>
|
|
<div class="titleBox">
|
|
<div class="titleBox">
|
|
<span class="title">统一考试</span>
|
|
<span class="title">统一考试</span>
|
|
</div>
|
|
</div>
|
|
<div class="submitBox">
|
|
<div class="submitBox">
|
|
- <el-button type="info" @click="close">关 闭</el-button>
|
|
|
|
- <el-button type="primary" @click="onSubmit()" :loading="state.loading">交 卷</el-button>
|
|
|
|
|
|
+ <el-button type="info" @click="closeDialog">关 闭</el-button>
|
|
|
|
+ <el-button type="primary" @click="onSubmit" :loading="state.loading">交 卷</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
- <el-row :gutter="10">
|
|
|
|
|
|
+ <el-row :gutter="10" v-loading="state.loading">
|
|
<el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6">
|
|
<el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6">
|
|
<div class="examListContent" v-for="(item) in state.questionTypeData">
|
|
<div class="examListContent" v-for="(item) in state.questionTypeData">
|
|
<p class="examType">{{item.questionTypeDesc}}</p>
|
|
<p class="examType">{{item.questionTypeDesc}}</p>
|
|
<ul class="examList">
|
|
<ul class="examList">
|
|
- <li class="examListItem {{questionsItem.IsAnswer == '1' ? 'done' : ''}}" v-for="(questionsItem, questionsIndex) in item.questions" @click="onSelQuestion(questionsItem.id)">{{questionsIndex + 1}}</li>
|
|
|
|
|
|
+ <li class="examListItem" :class="questionsItem.id == state.selQuestionID ? 'select' : ''" v-for="(questionsItem) in item.questions" @click="onSelQuestion(questionsItem.id)">{{questionsItem.sortIndex}}</li>
|
|
</ul>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
</el-col>
|
|
@@ -29,24 +28,28 @@
|
|
<p class="questionTitle">题干:{{ state.questionDetail.title }}({{ state.questionDetail.score }}分)</p>
|
|
<p class="questionTitle">题干:{{ state.questionDetail.title }}({{ state.questionDetail.score }}分)</p>
|
|
<div class="questionAnswer">
|
|
<div class="questionAnswer">
|
|
<div v-if="[0,2].includes(state.questionDetail.questionType)">
|
|
<div v-if="[0,2].includes(state.questionDetail.questionType)">
|
|
- <el-radio-group>
|
|
|
|
- <el-radio :label="item.content" :value="item.id" v-for="item in state.questionDetail.questionOptions" />
|
|
|
|
|
|
+ <el-radio-group style="display: block;" v-model="state.selRadioValue">
|
|
|
|
+ <el-form-item :label="item.label + '. '" v-for="item in state.questionDetail.questionOptions" style="margin-bottom: 10px;">
|
|
|
|
+ <el-radio :label="item.content" :value="item.id" />
|
|
|
|
+ </el-form-item>
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
</div>
|
|
</div>
|
|
<div v-else-if="state.questionDetail.questionType == 1">
|
|
<div v-else-if="state.questionDetail.questionType == 1">
|
|
- <el-checkbox-group>
|
|
|
|
- <el-checkbox :label="item.content" :value="item.id" v-for="item in state.questionDetail.questionOptions" />
|
|
|
|
|
|
+ <el-checkbox-group v-model="state.selCheckboxValue">
|
|
|
|
+ <el-form-item :label="item.label + '. '" v-for="item in state.questionDetail.questionOptions" style="margin-bottom: 10px;">
|
|
|
|
+ <el-checkbox :label="item.content" :value="item.id" />
|
|
|
|
+ </el-form-item>
|
|
</el-checkbox-group>
|
|
</el-checkbox-group>
|
|
</div>
|
|
</div>
|
|
<!-- <div v-else-if="state.questionDetail.questionType == 2"></div> -->
|
|
<!-- <div v-else-if="state.questionDetail.questionType == 2"></div> -->
|
|
<div v-else-if="state.questionDetail.questionType == 3">
|
|
<div v-else-if="state.questionDetail.questionType == 3">
|
|
<el-form-item label="答:">
|
|
<el-form-item label="答:">
|
|
- <el-input type="textarea" :rows="3" placeholder="请输入答案,如果有多个请依照顺序用英文 , 隔开" clearable></el-input>
|
|
|
|
|
|
+ <el-input type="textarea" v-model="state.answerValue" :rows="3" placeholder="请输入答案,如果有多个请依照顺序用英文 , 隔开" clearable></el-input>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
</div>
|
|
</div>
|
|
<div v-else-if="state.questionDetail.questionType == 4">
|
|
<div v-else-if="state.questionDetail.questionType == 4">
|
|
<el-form-item label="答:">
|
|
<el-form-item label="答:">
|
|
- <el-input type="textarea" :rows="3" placeholder="请输入答案" clearable></el-input>
|
|
|
|
|
|
+ <el-input type="textarea" v-model="state.answerValue" :rows="3" placeholder="请输入答案" clearable></el-input>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -54,7 +57,7 @@
|
|
</el-col>
|
|
</el-col>
|
|
</el-row>
|
|
</el-row>
|
|
<template #footer>
|
|
<template #footer>
|
|
- <span class="dialog-footer">
|
|
|
|
|
|
+ <span class="dialog-footer" v-if="state.selQuestionID">
|
|
<el-button type="primary" @click="onLastQuestion" :loading="state.loading">上一题</el-button>
|
|
<el-button type="primary" @click="onLastQuestion" :loading="state.loading">上一题</el-button>
|
|
<el-button type="primary" @click="onNextQuestion" :loading="state.loading">下一题</el-button>
|
|
<el-button type="primary" @click="onNextQuestion" :loading="state.loading">下一题</el-button>
|
|
</span>
|
|
</span>
|
|
@@ -67,7 +70,7 @@ import { reactive, ref } from 'vue';
|
|
import { ElMessage, FormInstance } from 'element-plus';
|
|
import { ElMessage, FormInstance } from 'element-plus';
|
|
import { excludeSelfById } from '@/utils/tools';
|
|
import { excludeSelfById } from '@/utils/tools';
|
|
import other from '@/utils/other';
|
|
import other from '@/utils/other';
|
|
-import { getExamQuestionGroup, getExamQuestion } from '@/api/examTrain/userExam';
|
|
|
|
|
|
+import {startUserExam, getExamQuestionGroup, getExamQuestion, answerUserExam, submitUserExam } from '@/api/examTrain/userExam';
|
|
|
|
|
|
// 定义子组件向父组件传值/事件
|
|
// 定义子组件向父组件传值/事件
|
|
const emit = defineEmits(['updateList']);
|
|
const emit = defineEmits(['updateList']);
|
|
@@ -76,17 +79,72 @@ const emit = defineEmits(['updateList']);
|
|
const state = reactive<any>({
|
|
const state = reactive<any>({
|
|
dialogVisible: false, // 弹窗
|
|
dialogVisible: false, // 弹窗
|
|
loading: false, // 加载
|
|
loading: false, // 加载
|
|
|
|
+ userExamId: '', // 用户考试id
|
|
|
|
+ examId: '', // 考试id
|
|
|
|
+ examType: null, // 考试类型
|
|
surplusTime: '00:00:00', // 剩余时间
|
|
surplusTime: '00:00:00', // 剩余时间
|
|
questionTypeData: [] as any[], // 试卷题型数据
|
|
questionTypeData: [] as any[], // 试卷题型数据
|
|
|
|
+ questionsData: [] as any[], // 试题集合
|
|
selQuestionID: '', // 选择的问题id
|
|
selQuestionID: '', // 选择的问题id
|
|
questionDetail: null, // 选择的问题明细
|
|
questionDetail: null, // 选择的问题明细
|
|
|
|
+ selRadioValue: '', // 单选题选择的值
|
|
|
|
+ selCheckboxValue: [] as any[], // 多选题选择的值
|
|
|
|
+ answerValue: '', // 填空问答填写的值
|
|
});
|
|
});
|
|
// 打开弹窗
|
|
// 打开弹窗
|
|
const ruleFormRef = ref<any>(); // 表单ref
|
|
const ruleFormRef = ref<any>(); // 表单ref
|
|
const openDialog = async (row: any) => {
|
|
const openDialog = async (row: any) => {
|
|
- try {
|
|
|
|
- state.dialogVisible = true;
|
|
|
|
- const { result } = await getExamQuestionGroup(row.id);
|
|
|
|
|
|
+ state.dialogVisible = true;
|
|
|
|
+ state.userExamId = row.id;
|
|
|
|
+ state.examId = row.examId;
|
|
|
|
+ state.examType = row.examType;
|
|
|
|
+ state.loading = true;
|
|
|
|
+ startUserExam({
|
|
|
|
+ id: state.userExamId
|
|
|
|
+ }).then((res) => {
|
|
|
|
+ if (onStateControl(res.result)){
|
|
|
|
+ getQuestionsData();
|
|
|
|
+ }else {
|
|
|
|
+ closeDialog();
|
|
|
|
+ state.loading = false;
|
|
|
|
+ }
|
|
|
|
+ }).catch((error) => {
|
|
|
|
+ console.error(error);
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+const onStateControl = (obj) => {
|
|
|
|
+ console.log(obj);
|
|
|
|
+ if (!obj.isStart){
|
|
|
|
+ ElMessage.warning('考试尚未开始!')
|
|
|
|
+ return false;
|
|
|
|
+ }else if (obj.isCompleted){
|
|
|
|
+ ElMessage.warning('考试已经结束!')
|
|
|
|
+ return false;
|
|
|
|
+ }else if (!obj.isJoin){
|
|
|
|
+ ElMessage.warning('您不用参加此考试!')
|
|
|
|
+ return false;
|
|
|
|
+ }else{
|
|
|
|
+ if (state.examType == 0){
|
|
|
|
+ state.surplusTime = new Date(obj.startTime).getTime() + obj.timeSpan * 60 * 1000;
|
|
|
|
+ if (state.surplusTime <= new Date().getTime()){
|
|
|
|
+ ElMessage.warning('考试已经结束!')
|
|
|
|
+ return false;
|
|
|
|
+ }else {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }else {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+const onSurplusTimeFinish = () => {
|
|
|
|
+ ElMessage.warning('考试已经结束!')
|
|
|
|
+ closeDialog();
|
|
|
|
+}
|
|
|
|
+const getQuestionsData = async () => {
|
|
|
|
+ try {
|
|
|
|
+ const { result } = await getExamQuestionGroup(state.examId);
|
|
|
|
+ let sortIndexNum = 1;
|
|
result.forEach(item => {
|
|
result.forEach(item => {
|
|
switch(item.questionType){
|
|
switch(item.questionType){
|
|
case 0: item.questionTypeDesc = '单选题'; break;
|
|
case 0: item.questionTypeDesc = '单选题'; break;
|
|
@@ -96,43 +154,124 @@ const openDialog = async (row: any) => {
|
|
case 4: item.questionTypeDesc = '问答题'; break;
|
|
case 4: item.questionTypeDesc = '问答题'; break;
|
|
default: break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
+ item.questions.forEach(it => {
|
|
|
|
+ it.sortIndex = sortIndexNum;
|
|
|
|
+ it.questionType = item.questionType;
|
|
|
|
+ state.questionsData.push(it)
|
|
|
|
+ sortIndexNum++ ;
|
|
|
|
+ });
|
|
})
|
|
})
|
|
state.questionTypeData = result;
|
|
state.questionTypeData = result;
|
|
|
|
+ state.loading = false;
|
|
} catch (error) {
|
|
} catch (error) {
|
|
// 打印错误信息
|
|
// 打印错误信息
|
|
console.error(error);
|
|
console.error(error);
|
|
}
|
|
}
|
|
-};
|
|
|
|
-const close = () => {
|
|
|
|
- ruleFormRef.value?.clearValidate();
|
|
|
|
- ruleFormRef.value?.resetFields();
|
|
|
|
-};
|
|
|
|
|
|
+}
|
|
// 关闭弹窗
|
|
// 关闭弹窗
|
|
const closeDialog = () => {
|
|
const closeDialog = () => {
|
|
state.dialogVisible = false;
|
|
state.dialogVisible = false;
|
|
|
|
+ state.questionTypeData = [];
|
|
|
|
+ state.questionsData = [];
|
|
|
|
+ state.selQuestionID = '';
|
|
|
|
+ state.questionDetail = null;
|
|
|
|
+ state.selRadioValue = '';
|
|
|
|
+ state.selCheckboxValue = [];
|
|
|
|
+ state.answerValue = '';
|
|
};
|
|
};
|
|
// 上一题下一题
|
|
// 上一题下一题
|
|
const onLastQuestion = () => {
|
|
const onLastQuestion = () => {
|
|
-
|
|
|
|
|
|
+ let index:number = state.questionsData.findIndex(it => it.id == state.selQuestionID);
|
|
|
|
+ index = (index - 1) == -1 ? state.questionsData.length : index - 1;
|
|
|
|
+ onSelQuestion(state.questionsData[index].id);
|
|
}
|
|
}
|
|
const onNextQuestion = () => {
|
|
const onNextQuestion = () => {
|
|
-
|
|
|
|
|
|
+ let index:number = state.questionsData.findIndex(it => it.id == state.selQuestionID);
|
|
|
|
+ index = (index + 1) == state.questionsData.length ? 0 : index + 1;
|
|
|
|
+ onSelQuestion(state.questionsData[index].id);
|
|
}
|
|
}
|
|
-// 选择的问题id
|
|
|
|
|
|
+// 选择的问题id 提交当前试题答案,再请求下一道题详情 value: 请求下一题详情id
|
|
const onSelQuestion = async (value: any) => {
|
|
const onSelQuestion = async (value: any) => {
|
|
- console.log(value);
|
|
|
|
|
|
+ state.loading = true;
|
|
|
|
+ if (state.questionDetail){
|
|
|
|
+ // 保存当前题目选项
|
|
|
|
+ const selQuestionItem = state.questionsData.find(it => it.id == state.selQuestionID);
|
|
|
|
+ const submitObj = {
|
|
|
|
+ userExamId: state.userExamId,
|
|
|
|
+ questionId: selQuestionItem.id,
|
|
|
|
+ questionType: selQuestionItem.questionType,
|
|
|
|
+ userExamItemOptionDtos: [] as any[],
|
|
|
|
+ answer: ''
|
|
|
|
+ };
|
|
|
|
+ if ([0,2].includes(state.questionDetail.questionType)){
|
|
|
|
+ if (state.selRadioValue){
|
|
|
|
+ submitObj.userExamItemOptionDtos.push({questionOptionId: state.selRadioValue})
|
|
|
|
+ }
|
|
|
|
+ }else if (state.questionDetail.questionType == 1){
|
|
|
|
+ if (state.selCheckboxValue.length > 0){
|
|
|
|
+ state.selCheckboxValue.forEach(it => {
|
|
|
|
+ submitObj.userExamItemOptionDtos.push({questionOptionId: it})
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }else if ([3,4].includes(state.questionDetail.questionType)){
|
|
|
|
+ submitObj.answer = state.answerValue;
|
|
|
|
+ }
|
|
|
|
+ answerUserExam(submitObj)
|
|
|
|
+ .then((res) => {
|
|
|
|
+ if (onStateControl(res.result)){
|
|
|
|
+ onGetQuestionDetail(value);
|
|
|
|
+ }else {
|
|
|
|
+ closeDialog();
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ .catch(() => {
|
|
|
|
+ state.loading = false;
|
|
|
|
+ });
|
|
|
|
+ }else {
|
|
|
|
+ onGetQuestionDetail(value);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+const onGetQuestionDetail = async (value) => {
|
|
|
|
+ // 初始化选择选择/填写的答案
|
|
|
|
+ state.selRadioValue = '';
|
|
|
|
+ state.selCheckboxValue = [];
|
|
|
|
+ state.answerValue = '';
|
|
|
|
+ // 请求下一道题
|
|
|
|
+ state.selQuestionID = value;
|
|
try {
|
|
try {
|
|
- const { result } = await getExamQuestion(value);
|
|
|
|
|
|
+ const { result } = await getExamQuestion(state.selQuestionID, state.examId);
|
|
state.questionDetail = result;
|
|
state.questionDetail = result;
|
|
- console.log(state.questionDetail);
|
|
|
|
|
|
+ if ([0,2].includes(state.questionDetail.questionType)){
|
|
|
|
+ let obj = state.questionDetail.questionOptions.find((x: any) => x.isSelected === true);
|
|
|
|
+ state.selRadioValue = obj ? obj.id : '';
|
|
|
|
+ }else if (state.questionDetail.questionType == 1){
|
|
|
|
+ let arr = [] as any[];
|
|
|
|
+ state.questionDetail.questionOptions.forEach(it => {if (it.isSelected) arr.push(it);})
|
|
|
|
+ console.log(arr);
|
|
|
|
+ state.selCheckboxValue = arr ? arr.map((x: any) => x.id) : [];
|
|
|
|
+ console.log(state.selCheckboxValue);
|
|
|
|
+ }else {
|
|
|
|
+ state.answerValue = state.questionDetail.answer || '';
|
|
|
|
+ }
|
|
|
|
+ state.loading = false;
|
|
} catch (error) {
|
|
} catch (error) {
|
|
// 打印错误信息
|
|
// 打印错误信息
|
|
console.error(error);
|
|
console.error(error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-// 保存
|
|
|
|
|
|
+// 结束考试
|
|
const onSubmit = async () => {
|
|
const onSubmit = async () => {
|
|
-
|
|
|
|
|
|
+ state.loading = true;
|
|
|
|
+ submitUserExam({
|
|
|
|
+ isSubmit: 1
|
|
|
|
+ }).then(() => {
|
|
|
|
+ emit('updateList');
|
|
|
|
+ state.loading = false;
|
|
|
|
+ closeDialog(); // 关闭弹窗
|
|
|
|
+ ElMessage.success('操作成功');
|
|
|
|
+ }).catch(() => {
|
|
|
|
+ state.loading = false;
|
|
|
|
+ });
|
|
};
|
|
};
|
|
// 暴露变量
|
|
// 暴露变量
|
|
defineExpose({
|
|
defineExpose({
|
|
@@ -151,7 +290,7 @@ defineExpose({
|
|
.examListContent .examList{padding: 5px 0 10px;}
|
|
.examListContent .examList{padding: 5px 0 10px;}
|
|
.examListContent .examList .examListItem{display: inline-block;margin: 5px 10px 5px 0;text-align: center;width: 50px;height:35px;line-height: 35px;cursor: pointer;position: relative;border: #f2f2f2 1px solid;}
|
|
.examListContent .examList .examListItem{display: inline-block;margin: 5px 10px 5px 0;text-align: center;width: 50px;height:35px;line-height: 35px;cursor: pointer;position: relative;border: #f2f2f2 1px solid;}
|
|
.examListContent .examList .marked::after{content: "*";width: 5px;height: 5px;position: absolute;top: -3px;right: 4px;color: #FF5722;font-size: 25px;}
|
|
.examListContent .examList .marked::after{content: "*";width: 5px;height: 5px;position: absolute;top: -3px;right: 4px;color: #FF5722;font-size: 25px;}
|
|
- .examListContent .examList .done{background-color: #5FB878;color: #fff;}
|
|
|
|
|
|
+ .examListContent .examList .select{background-color: #1890ff;color: #fff;}
|
|
.questionBox{padding: 20px 10px 0;}
|
|
.questionBox{padding: 20px 10px 0;}
|
|
.questionBox .questionTitle{margin-bottom: 10px;font-size: 16px;}
|
|
.questionBox .questionTitle{margin-bottom: 10px;font-size: 16px;}
|
|
.questionBox .questionAnswer{padding: 0 20px;}
|
|
.questionBox .questionAnswer{padding: 0 20px;}
|