Browse Source

我的考试

zjq 2 weeks ago
parent
commit
3200031abb

+ 40 - 4
src/api/examTrain/userExam.ts

@@ -4,6 +4,18 @@
  */
 import request from '@/utils/request';
 
+/**
+ * @description 开始考试
+ * @param {object} params
+ */
+export const startUserExam = (params?: object) => {
+    return request({
+        url: '/api/v1/UserExam/Start',
+        method: 'post',
+        data: params,
+    });
+};
+
 /**
  * @description 获取考试列表
  * @param {object} params
@@ -27,11 +39,35 @@ export const getExamQuestionGroup = (Id: any) => {
 };
 /**
  * @description 获取试题详情
- * @param {any} Id
+ * @param {any} QuestionId
+ * @param {any} ExamId
  */
-export const getExamQuestion = (Id: any) => {
+export const getExamQuestion = (QuestionId: any, ExamId: any) => {
     return request({
-        url: `/api/v1/UserExam/GetExamQuestion?QuestionId=${Id}`,
+        url: `/api/v1/UserExam/GetExamQuestion?QuestionId=${QuestionId}&ExamId=${ExamId}`,
         method: 'get',
     });
-};
+};
+/**
+ * @description 提交考试答案
+ * @param {object} params
+ */
+export const answerUserExam = (params?: object) => {
+    return request({
+        url: '/api/v1/UserExam/Exam',
+        method: 'post',
+        data: params,
+    });
+};
+
+/**
+ * @description 交卷
+ * @param {object} params
+ */
+export const submitUserExam = (params?: object) => {
+    return request({
+        url: '/api/v1/UserExam/Submit',
+        method: 'post',
+        data: params,
+    });
+};

+ 2 - 2
src/views/examTrain/exam/examManage/index.vue

@@ -88,11 +88,11 @@
                             <span v-else-if="row.examStatus == 2">考试结束</span>
                         </template>
                     </vxe-column>
-                    <vxe-column field="status" title="状态" width="160">
+                    <!-- <vxe-column field="status" title="状态" width="160">
                         <template #default="{ row }">
                             <el-switch v-model="row.status" />
                         </template>
-                    </vxe-column>
+                    </vxe-column> -->
                     <vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">
                         <template #default="{ row }">
                             <el-button link type="primary" @click="onEdit(row)" title="编辑" v-auth="'examManage:index:edit'">

+ 4 - 4
src/views/examTrain/exam/extractRule/index.vue

@@ -8,12 +8,12 @@
                 <el-form-item label="标题" prop="name">
                     <el-input v-model="state.queryParams.name" placeholder="标题" clearable @keyup.enter="handleQuery" class="keyword-input" />
                 </el-form-item>
-                <el-form-item label="状态" prop="status">
+                <!-- <el-form-item label="状态" prop="status">
                     <el-select v-model="state.queryParams.status" clearable placeholder="请选择" @change="handleQuery" :disabled="state.tableLoading">
                         <el-option key='0' label="停用" :value="0" />
                         <el-option key='1' label="启用" :value="1" />
                     </el-select>
-                </el-form-item>
+                </el-form-item> -->
                 <el-form-item>
                     <el-button type="primary" @click="handleQuery" :loading="state.tableLoading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
                     <el-button @click="resetQuery(ruleFormRef)" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置</el-button>
@@ -52,11 +52,11 @@
                 >
                     <vxe-column field="code" title="规则编号" width="250"></vxe-column>
                     <vxe-column field="name" title="规则标题" width="250"></vxe-column>
-                    <vxe-column field="status" title="状态" width="160">
+                    <!-- <vxe-column field="status" title="状态" width="160">
                         <template #default="{ row }">
                             <el-switch v-model="row.status" />
                         </template>
-                    </vxe-column>
+                    </vxe-column> -->
                     <vxe-column field="remark" title="备注" min-width="200"></vxe-column>
                     <vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">
                         <template #default="{ row }">

+ 1 - 1
src/views/examTrain/exam/testPaper/components/TestPaper-random.vue

@@ -69,7 +69,7 @@ import { examTagTreeList } from '@/api/examTrain/tag';
 import { getTagQuestionCount, getSecondsetQuestions } from '@/api/examTrain/testPaper';
 
 // 定义子组件向父组件传值/事件
-const emit = defineEmits(['updateList']);
+const emit = defineEmits(['choose']);
 
 // 定义变量内容
 const state = reactive<any>({

+ 4 - 4
src/views/examTrain/exam/testPaper/index.vue

@@ -54,11 +54,11 @@
                     <vxe-column field="name" title="试卷标题" width="200"></vxe-column>
                     <vxe-column field="examTypeDes" title="试卷类型" width="100"></vxe-column>
                     <vxe-column field="modeDes" title="组卷方式" width="100"></vxe-column>
-                    <vxe-column field="status" title="状态" width="160">
+                    <!-- <vxe-column field="status" title="状态" width="160">
                         <template #default="{ row }">
                             <el-switch v-model="row.status" />
                         </template>
-                    </vxe-column>
+                    </vxe-column> -->
                     <vxe-column field="remark" title="备注" min-width="150"></vxe-column>
                     <vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">
                         <template #default="{ row }">
@@ -89,12 +89,12 @@
                         <el-option key='1' label="自选试题" :value="1" />
                     </el-select>
                 </el-form-item>
-                <el-form-item label="状态" prop="status">
+                <!-- <el-form-item label="状态" prop="status">
                     <el-select v-model="state.queryParams.status" clearable placeholder="状态" @change="handleQuery" :disabled="state.tableLoading">
                         <el-option key='0' label="启用" :value="0" />
                         <el-option key='1' label="停用" :value="1" />
                     </el-select>
-                </el-form-item>
+                </el-form-item> -->
 			</el-form>
 			<template #footer>
 				<el-button type="primary" @click="handleQuery" :loading="state.tableLoading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>

+ 172 - 33
src/views/examTrain/exam/userExam/components/UserExam-exam.vue

@@ -1,26 +1,25 @@
 <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 }">
             <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 class="titleBox">
                     <span class="title">统一考试</span>
                 </div>
                 <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>
         </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">
                 <div class="examListContent" v-for="(item) in state.questionTypeData">
                     <p class="examType">{{item.questionTypeDesc}}</p>
                     <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>
                 </div>
             </el-col>
@@ -29,24 +28,28 @@
                     <p class="questionTitle">题干:{{ state.questionDetail.title }}({{ state.questionDetail.score }}分)</p>
                     <div class="questionAnswer">
                         <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>
                         </div>
                         <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>
                         </div>
                         <!-- <div v-else-if="state.questionDetail.questionType == 2"></div> -->
                         <div v-else-if="state.questionDetail.questionType == 3">
                             <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>
                         </div>
                         <div v-else-if="state.questionDetail.questionType == 4">
                             <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>
                         </div>
                     </div>
@@ -54,7 +57,7 @@
             </el-col>
         </el-row>
 		<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="onNextQuestion" :loading="state.loading">下一题</el-button>
 			</span>
@@ -67,7 +70,7 @@ import { reactive, ref } from 'vue';
 import { ElMessage, FormInstance } from 'element-plus';
 import { excludeSelfById } from '@/utils/tools';
 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']);
@@ -76,17 +79,72 @@ const emit = defineEmits(['updateList']);
 const state = reactive<any>({
 	dialogVisible: false, // 弹窗
 	loading: false, // 加载
+    userExamId: '', // 用户考试id
+    examId: '', // 考试id
+    examType: null, // 考试类型
     surplusTime: '00:00:00', // 剩余时间
 	questionTypeData: [] as any[], // 试卷题型数据
+    questionsData: [] as any[], // 试题集合
     selQuestionID: '', // 选择的问题id
     questionDetail: null, // 选择的问题明细
+    selRadioValue: '', // 单选题选择的值
+    selCheckboxValue: [] as any[], // 多选题选择的值
+    answerValue: '', // 填空问答填写的值
 });
 // 打开弹窗
 const ruleFormRef = ref<any>(); // 表单ref
 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 => {
             switch(item.questionType){
 				case 0: item.questionTypeDesc = '单选题'; break;
@@ -96,43 +154,124 @@ const openDialog = async (row: any) => {
 				case 4: item.questionTypeDesc = '问答题'; break;
 				default: break;
 			}
+            item.questions.forEach(it => {
+                it.sortIndex = sortIndexNum;
+                it.questionType = item.questionType;
+                state.questionsData.push(it)
+                sortIndexNum++ ;
+            });
         })
 		state.questionTypeData = result;
+        state.loading = false;
 	} catch (error) {
 		// 打印错误信息
 		console.error(error);
 	}
-};
-const close = () => {
-	ruleFormRef.value?.clearValidate();
-	ruleFormRef.value?.resetFields();
-};
+}
 // 关闭弹窗
 const closeDialog = () => {
 	state.dialogVisible = false;
+    state.questionTypeData = []; 
+    state.questionsData = []; 
+    state.selQuestionID = ''; 
+    state.questionDetail = null; 
+    state.selRadioValue = ''; 
+    state.selCheckboxValue = []; 
+    state.answerValue = '';
 };
 // 上一题下一题
 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 = () => {
-
+    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) => {
-    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 {
-		const { result } = await getExamQuestion(value);
+		const { result } = await getExamQuestion(state.selQuestionID, state.examId);
 		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) {
 		// 打印错误信息
 		console.error(error);
 	}
 }
-// 保存
+// 结束考试
 const onSubmit = async () => {
-	
+    state.loading = true;
+	submitUserExam({
+        isSubmit: 1
+    }).then(() => {
+        emit('updateList');
+        state.loading = false;
+        closeDialog(); // 关闭弹窗
+        ElMessage.success('操作成功');
+    }).catch(() => {
+        state.loading = false;
+    });
 };
 // 暴露变量
 defineExpose({
@@ -151,7 +290,7 @@ defineExpose({
     .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 .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 .questionTitle{margin-bottom: 10px;font-size: 16px;}
     .questionBox .questionAnswer{padding: 0 20px;}

+ 5 - 19
src/views/examTrain/exam/userExam/index.vue

@@ -19,20 +19,6 @@
                     <el-button @click="drawer = true" class="default-button"> <SvgIcon name="ele-Search" class="mr5" />更多查询</el-button>
                 </el-form-item>
             </el-form>
-            <vxe-toolbar
-                ref="toolbarRef"
-                :loading="state.tableLoading"
-                custom
-                :refresh="{
-                    queryMethod: handleQuery,
-                }"
-            >
-                <template #buttons>
-                    <el-button type="primary" @click="onExam({id: '08dd66b8-22b4-4dc3-83e5-85ca716c0957'})" :loading="state.tableLoading">
-                        <SvgIcon name="ele-Plus" class="mr10" />新增
-                    </el-button>
-                </template>
-            </vxe-toolbar>
             <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
                 <vxe-table
                     border
@@ -50,7 +36,7 @@
                     :custom-config="{ storage: true }"
                     showHeaderOverflow
                 >
-                    <vxe-column field="examName" title="考试标题" width="150"></vxe-column>
+                    <vxe-column field="examName" title="考试标题" min-width="150"></vxe-column>
                     <vxe-column field="examTypeDesc" title="考试类型" width="100"></vxe-column>
                     <vxe-column field="startTime" title="可考开始时间" width="160">
 						<template #default="{ row }">
@@ -79,14 +65,14 @@
                             <span v-else-if="!row.examStatus">不合格</span>
                         </template>
                     </vxe-column>
-                    <vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">
+                    <vxe-column title="操作" fixed="right" width="150" align="center" :show-overflow="false">
                         <template #default="{ row }">
-                            <el-button type="primary" @click="onExam(row)" title="考试" v-auth="'userExam:index:exam'">
+                            <el-button type="primary" @click="onExam(row)" size="small" title="考试" v-auth="'userExam:index:exam'">
                                 考试
                             </el-button>
-                            <el-button link type="primary" @click="onView(row)" title="查看" v-auth="'userExam:index:view'">
+                            <!-- <el-button link type="primary" @click="onView(row)" title="查看" v-auth="'userExam:index:view'">
                                 查看
-                            </el-button>
+                            </el-button> -->
                         </template>
                     </vxe-column>
                 </vxe-table>

+ 2 - 2
src/views/examTrain/questionBank/index.vue

@@ -82,11 +82,11 @@
                     <vxe-column field="tag" title="标签" width="150"></vxe-column>
                     <vxe-column field="difficultyLevelDesc" title="难度" width="100"></vxe-column>
                     <vxe-column field="title" title="题干" min-width="200"></vxe-column>
-                    <vxe-column field="status" title="状态" width="160">
+                    <!-- <vxe-column field="status" title="状态" width="160">
                         <template #default="{ row }">
                             <el-switch v-model="row.status" />
                         </template>
-                    </vxe-column>
+                    </vxe-column> -->
                     <vxe-column field="formalEnableDes" title="正式可用" width="150"></vxe-column>
                     <vxe-column field="simulateEnableDes" title="模拟可用" width="150"></vxe-column>
                     <vxe-column title="操作" fixed="right" width="120" align="center" :show-overflow="false">