|
@@ -1,31 +1,546 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
- <van-nav-bar title="答题" left-arrow @click-left="onClickLeft" />
|
|
|
+ <div v-if="isInited" class="page-exam-question-box">
|
|
|
+ <van-nav-bar title="答题" left-arrow @click-left="handleBackFun" />
|
|
|
+ <!-- 题目卡片 -->
|
|
|
+ <div class="exam-question-div">
|
|
|
+ <div class="exam-question-card">
|
|
|
+ <!-- 标题、分数、题页 -->
|
|
|
+ <div class="exam-question-head">
|
|
|
+ <div class="exam-question-head-left">
|
|
|
+ <div class="exam-question-head-left-icon"></div>
|
|
|
+ <div class="exam-question-head-left-txt">
|
|
|
+ {{ examQuestionList[answerIndex].typeTxt || "undefind" }}
|
|
|
+ </div>
|
|
|
+ <span class="exam-question-head-left-txt"
|
|
|
+ >{{ examQuestionList[answerIndex].grade || "undefind" }} 分</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="exam-question-head-right">
|
|
|
+ <span class="exam-question-head-right-now">{{
|
|
|
+ answerIndex + 1
|
|
|
+ }}</span>
|
|
|
+ <span>/</span>
|
|
|
+ <span>{{ examQuestionList.length }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 描述 -->
|
|
|
+ <div class="exam-question-describe">
|
|
|
+ {{ examQuestionList[answerIndex].content }}
|
|
|
+ </div>
|
|
|
+ <!-- 答题列表 -->
|
|
|
+ <!-- 单选题、多选题的选项区域 -->
|
|
|
+ <div
|
|
|
+ v-if="
|
|
|
+ examQuestionList[answerIndex].type === questionType.singleChoice ||
|
|
|
+ examQuestionList[answerIndex].type === questionType.multipleChoice
|
|
|
+ "
|
|
|
+ class="exam-question-options"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in examQuestionList[answerIndex].answers"
|
|
|
+ :key="index"
|
|
|
+ :class="{
|
|
|
+ 'exam-question-options-item': true,
|
|
|
+ 'exam-question-options-item-checked': answerValue.includes(item),
|
|
|
+ 'exam-question-options-false':
|
|
|
+ answerValue.includes(item) && answerStatus === 2
|
|
|
+ }"
|
|
|
+ @click="handleExamQuestionOptionsItemFun(item)"
|
|
|
+ >
|
|
|
+ {{ formatQuestionIndex(index) }}. {{ item }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 填空题的答题区域 -->
|
|
|
+ <div
|
|
|
+ v-if="examQuestionList[answerIndex].type === questionType.gapFilling"
|
|
|
+ class="exam-question-gapFilling"
|
|
|
+ :class="{ 'exam-question-gapFilling-false': answerStatus === 2 }"
|
|
|
+ >
|
|
|
+ <textarea
|
|
|
+ ref="questionInputRef"
|
|
|
+ v-model="inputValue"
|
|
|
+ :disabled="[1, 2].includes(answerStatus)"
|
|
|
+ maxlength="200"
|
|
|
+ rows="5"
|
|
|
+ @change="handleExamQuestionOptionsItemFun(inputValue)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="exam-question-button-box">
|
|
|
+ <van-button
|
|
|
+ v-show="answerStatus === 0"
|
|
|
+ class="exam-question-button"
|
|
|
+ type="primary"
|
|
|
+ color="#FE6347"
|
|
|
+ @click="handleSureFun"
|
|
|
+ >确定</van-button
|
|
|
+ >
|
|
|
+ <van-button
|
|
|
+ v-show="answerStatus === 2"
|
|
|
+ class="exam-question-button"
|
|
|
+ type="primary"
|
|
|
+ color="#FE6347"
|
|
|
+ @click="handleNextFun"
|
|
|
+ >下一题</van-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 题目卡片 -->
|
|
|
+ <div v-show="answerStatus === 2" class="exam-question-div">
|
|
|
+ <div class="exam-question-card">
|
|
|
+ <div class="exam-question-result">
|
|
|
+ 正确答案:{{
|
|
|
+ formatQuestionFinalAnswerIndex(
|
|
|
+ examQuestionList[answerIndex].finalAnswer
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </div>
|
|
|
+ <div class="exam-question-analysis">
|
|
|
+ 答案解析:{{ this.examQuestionList[this.answerIndex].answerAnalysis }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import { Toast } from "vant";
|
|
|
import { mapState } from "vuex";
|
|
|
export default {
|
|
|
- name: "page-answer-recruit",
|
|
|
+ name: "page-exam-item-doing",
|
|
|
components: {},
|
|
|
data() {
|
|
|
- return {};
|
|
|
+ return {
|
|
|
+ userInfo: {
|
|
|
+ id: 0,
|
|
|
+ username: "string",
|
|
|
+ firstName: "string",
|
|
|
+ lastName: "string",
|
|
|
+ email: "string",
|
|
|
+ encodedPassword: "string",
|
|
|
+ userStatus: "suspended"
|
|
|
+ },
|
|
|
+ questionType: {
|
|
|
+ singleChoice: "DanXuan", // 单选题
|
|
|
+ multipleChoice: "DuoXuan", // 多选题
|
|
|
+ gapFilling: "TianKong" // 填空题
|
|
|
+ }, // 试题类型
|
|
|
+ examQuestionList: [], // 试题列表
|
|
|
+ answerIndex: null, // 当前试题的下标索引
|
|
|
+ answerValue: [], // 当前试题的所答
|
|
|
+ inputValue: "", // 填空题时输入框的值
|
|
|
+ isInited: false, // 是否已初始化完毕
|
|
|
+ answerTime: {
|
|
|
+ startTime: 0,
|
|
|
+ endTime: 0
|
|
|
+ }, // 答题的开始、结束时间
|
|
|
+ answerStatus: 0 // 当前答题状态: 0未作答 1已做答且正确 2已做答但错误
|
|
|
+ };
|
|
|
},
|
|
|
created() {
|
|
|
- console.log("竞赛答题的类型ID", this.answerRecruitId);
|
|
|
+ this.initDataFun(); // 初始化数据信息
|
|
|
},
|
|
|
+ watch: {},
|
|
|
computed: {
|
|
|
...mapState({
|
|
|
answerRecruitId: state => state.answer.answerRecruitId
|
|
|
})
|
|
|
},
|
|
|
methods: {
|
|
|
- onClickLeft() {
|
|
|
+ // 初始化数据信息
|
|
|
+ initDataFun() {
|
|
|
+ this.examStartFun(); // 方法:开始考试
|
|
|
+ },
|
|
|
+ // 方法:开始考试
|
|
|
+ examStartFun() {
|
|
|
+ if (!this.answerRecruitId) {
|
|
|
+ Toast("数据出错,请重新从上一级页面进入");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$store.commit("toggleLoading", true);
|
|
|
+ this.$_http
|
|
|
+ .post(
|
|
|
+ this.$pathParams(this.$_API.POST_JTXT_GET_EXAMS_START, {
|
|
|
+ examId: this.answerRecruitId
|
|
|
+ }),
|
|
|
+ this.userInfo
|
|
|
+ )
|
|
|
+ .then(res => {
|
|
|
+ if (res.data) {
|
|
|
+ let httpResultData = [];
|
|
|
+ res.data.forEach((item, index) => {
|
|
|
+ this.getExamQuestionsListFun(
|
|
|
+ item,
|
|
|
+ httpResultData,
|
|
|
+ index === res.data.length - 1
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.$store.commit("toggleLoading", false);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 查询:试题信息
|
|
|
+ async getExamQuestionsListFun(questionId, httpResultData, isLast) {
|
|
|
+ this.$_http
|
|
|
+ .get(
|
|
|
+ this.$pathParams(this.$_API.GET_JTXT_GET_EXAMS_ONE_QUESTIONS_LIST, {
|
|
|
+ questionId: questionId
|
|
|
+ })
|
|
|
+ )
|
|
|
+ .then(res => {
|
|
|
+ let resData = { ...res.data };
|
|
|
+ httpResultData.push(this.setPersonDataFun(resData));
|
|
|
+ if (isLast) {
|
|
|
+ // 临时解决多个异步请求进行的问题
|
|
|
+ setTimeout(() => {
|
|
|
+ this.examQuestionList = httpResultData;
|
|
|
+ this.handleExamQuestionItemFun(this.examQuestionList[0], 0); // 设置第一题开始
|
|
|
+ let curTime = new Date();
|
|
|
+ this.answerTime.startTime = curTime; // 赋值开始时间
|
|
|
+ this.isInited = true;
|
|
|
+ this.$store.commit("toggleLoading", false);
|
|
|
+ }, 100);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.$store.commit("toggleLoading", false);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 方法:过滤试题的类型,添加用户作答的字段
|
|
|
+ setPersonDataFun(httpResultDataItme) {
|
|
|
+ httpResultDataItme.typeTxt = this.formatQuestionType(
|
|
|
+ httpResultDataItme.type
|
|
|
+ );
|
|
|
+ httpResultDataItme.userAnswer = [];
|
|
|
+ return httpResultDataItme;
|
|
|
+ },
|
|
|
+ // 方法:过滤试题的类型
|
|
|
+ formatQuestionType(type) {
|
|
|
+ let typeTxt = "";
|
|
|
+ switch (type) {
|
|
|
+ case this.questionType.singleChoice:
|
|
|
+ typeTxt = "单选题";
|
|
|
+ break;
|
|
|
+ case this.questionType.multipleChoice:
|
|
|
+ typeTxt = "多选题";
|
|
|
+ break;
|
|
|
+ case this.questionType.gapFilling:
|
|
|
+ typeTxt = "填空题";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return typeTxt;
|
|
|
+ },
|
|
|
+ // 方法:过滤选项的编号
|
|
|
+ formatQuestionIndex(index) {
|
|
|
+ return String.fromCharCode(index + 65);
|
|
|
+ },
|
|
|
+ // 方法:过滤正确答案的选项的编号
|
|
|
+ formatQuestionFinalAnswerIndex(finalAnswer) {
|
|
|
+ let txt = "";
|
|
|
+ switch (this.examQuestionList[this.answerIndex].type) {
|
|
|
+ case this.questionType.singleChoice:
|
|
|
+ case this.questionType.multipleChoice:
|
|
|
+ finalAnswer.forEach((item, index) => {
|
|
|
+ this.examQuestionList[this.answerIndex].answers.forEach(
|
|
|
+ (it, ind) => {
|
|
|
+ if (item === it) {
|
|
|
+ txt = txt + this.formatQuestionIndex(ind);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case this.questionType.gapFilling:
|
|
|
+ finalAnswer.forEach((item, index) => {
|
|
|
+ txt = txt + `${index > 0 ? "、" : ""}` + item;
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return txt;
|
|
|
+ },
|
|
|
+ // 操作:点击了某个题序
|
|
|
+ handleExamQuestionItemFun(item, index) {
|
|
|
+ if (this.answerIndex === index || !this.examQuestionList.length) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.answerStatus = 0;
|
|
|
+ this.answerIndex = index;
|
|
|
+ switch (item.type) {
|
|
|
+ case this.questionType.singleChoice:
|
|
|
+ case this.questionType.multipleChoice:
|
|
|
+ this.answerValue = [...new Set(item.userAnswer)];
|
|
|
+ break;
|
|
|
+ case this.questionType.gapFilling:
|
|
|
+ this.answerValue = [...new Set(item.userAnswer)];
|
|
|
+ this.inputValue = this.answerValue[0] || "";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 操作:作答了某个选项
|
|
|
+ handleExamQuestionOptionsItemFun(value) {
|
|
|
+ // 答错了的不允许修改
|
|
|
+ if (this.answerStatus === 2) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let answerQuestionType = this.examQuestionList[this.answerIndex].type;
|
|
|
+ switch (answerQuestionType) {
|
|
|
+ // 单选题
|
|
|
+ case this.questionType.singleChoice:
|
|
|
+ this.answerValue = [value];
|
|
|
+ break;
|
|
|
+ // 多选题
|
|
|
+ case this.questionType.multipleChoice:
|
|
|
+ // 遍历所答的数组,看是否有重复的
|
|
|
+ let repetitiveIndex = null;
|
|
|
+ for (let i = 0; i < this.answerValue.length; i++) {
|
|
|
+ let it = this.answerValue[i];
|
|
|
+ if (value === it) {
|
|
|
+ repetitiveIndex = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果有重复的就移除
|
|
|
+ if (repetitiveIndex !== null) {
|
|
|
+ this.answerValue.splice(repetitiveIndex, 1);
|
|
|
+ } else {
|
|
|
+ this.answerValue.push(value);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ // 填空题
|
|
|
+ case this.questionType.gapFilling:
|
|
|
+ this.$refs.questionInputRef.blur(); // 清除input的焦点
|
|
|
+ this.answerValue = [value];
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 操作:确定
|
|
|
+ handleSureFun() {
|
|
|
+ this.examQuestionList[this.answerIndex].userAnswer = this.answerValue;
|
|
|
+ this.answerStatus = this.getAnswerItemResultFun(
|
|
|
+ this.examQuestionList[this.answerIndex]
|
|
|
+ )
|
|
|
+ ? 1
|
|
|
+ : 2;
|
|
|
+ if (this.answerStatus === 1) {
|
|
|
+ this.handleNextFun();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 方法:判断当前题目是否正确
|
|
|
+ getAnswerItemResultFun(item) {
|
|
|
+ let isSure = false;
|
|
|
+ let sureNum = 0;
|
|
|
+ item.finalAnswer.forEach(it => {
|
|
|
+ item.userAnswer.forEach(answerItem => {
|
|
|
+ if (it === answerItem) {
|
|
|
+ sureNum++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ if (sureNum === item.finalAnswer.length) {
|
|
|
+ isSure = true;
|
|
|
+ }
|
|
|
+ return isSure;
|
|
|
+ },
|
|
|
+ // 操作:下一题
|
|
|
+ handleNextFun() {
|
|
|
+ let nextIndex = this.answerIndex + 1;
|
|
|
+ if (nextIndex >= this.examQuestionList.length) {
|
|
|
+ this.examsEndFun(); // 结束考试
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.handleExamQuestionItemFun(
|
|
|
+ this.examQuestionList[nextIndex],
|
|
|
+ nextIndex
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 方法:结束考试
|
|
|
+ examsEndFun() {
|
|
|
+ let curTime = new Date();
|
|
|
+ this.answerTime.endTime = curTime; // 赋值开始时间
|
|
|
+ this.$store.commit("toggleLoading", true);
|
|
|
+ let grades = this.getUserExamAllPointsFun(); // 方法:计算成绩
|
|
|
+ let params = {
|
|
|
+ user: this.userInfo,
|
|
|
+ exam: {
|
|
|
+ id: 0,
|
|
|
+ name: "",
|
|
|
+ description: "",
|
|
|
+ creator: this.userInfo,
|
|
|
+ duration: "",
|
|
|
+ deadline: "",
|
|
|
+ startTime: ""
|
|
|
+ },
|
|
|
+ points: grades, // 成绩:积分
|
|
|
+ startTime: "", // this.answerTime.startTime,
|
|
|
+ endTime: "" // this.answerTime.endTime
|
|
|
+ };
|
|
|
+ this.$_http
|
|
|
+ .post(
|
|
|
+ this.$pathParams(this.$_API.POST_JTXT_GET_EXAMS_END, {
|
|
|
+ examId: this.answerRecruitId
|
|
|
+ }),
|
|
|
+ params
|
|
|
+ )
|
|
|
+ .then(res => {
|
|
|
+ this.$store.commit("toggleLoading", false);
|
|
|
+ this.$router.replace({
|
|
|
+ name: "answerRecruitResult",
|
|
|
+ params: {
|
|
|
+ allQuestionsNum: this.examQuestionList.length,
|
|
|
+ grades: grades
|
|
|
+ }
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.$store.commit("toggleLoading", false);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 方法:计算成绩
|
|
|
+ getUserExamAllPointsFun() {
|
|
|
+ let grades = 0;
|
|
|
+ this.examQuestionList.forEach((item, index) => {
|
|
|
+ let sureNum = 0;
|
|
|
+ item.finalAnswer.forEach(it => {
|
|
|
+ item.userAnswer.forEach(answerItem => {
|
|
|
+ if (it === answerItem) {
|
|
|
+ sureNum++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ if (sureNum === item.finalAnswer.length) {
|
|
|
+ grades++; // 默认每题一分
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return grades;
|
|
|
+ },
|
|
|
+ // 操作:返回
|
|
|
+ handleBackFun() {
|
|
|
this.$router.back();
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped></style>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.page-exam-question-box {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow-y: auto;
|
|
|
+ overflow-x: hidden;
|
|
|
+ font-size: 0.6rem;
|
|
|
+ .exam-question-div {
|
|
|
+ padding: 0.5rem 0.5rem;
|
|
|
+ .exam-question-card {
|
|
|
+ padding: 0.5rem 0.5rem;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.16);
|
|
|
+ .exam-question-head {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ border-bottom: 1px solid #fe6347;
|
|
|
+ padding-bottom: 0.25rem;
|
|
|
+ height: 1.25rem;
|
|
|
+ font-size: 0.65rem;
|
|
|
+ .exam-question-head-left {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ .exam-question-head-left-icon {
|
|
|
+ width: 0.25rem;
|
|
|
+ height: 1rem;
|
|
|
+ background-color: #fe6347;
|
|
|
+ }
|
|
|
+ .exam-question-head-left-txt {
|
|
|
+ margin-left: 0.25rem;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .exam-question-head-right {
|
|
|
+ span {
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ .exam-question-head-right-now {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #000;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .exam-question-describe {
|
|
|
+ padding: 0.5rem 0;
|
|
|
+ font-size: 0.65rem;
|
|
|
+ }
|
|
|
+ .exam-question-options {
|
|
|
+ .exam-question-options-item {
|
|
|
+ width: 100%;
|
|
|
+ padding: 0.5rem 0.25rem;
|
|
|
+ background-color: #f3f3f3;
|
|
|
+ margin-bottom: 0.5rem;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ }
|
|
|
+ .exam-question-options-item-checked {
|
|
|
+ border-color: #fe6347;
|
|
|
+ color: #fe6347;
|
|
|
+ }
|
|
|
+ .exam-question-options-false {
|
|
|
+ border-color: red !important;
|
|
|
+ background-color: #fedada !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .exam-question-gapFilling {
|
|
|
+ padding-top: 0.5rem;
|
|
|
+ textarea {
|
|
|
+ width: 100%;
|
|
|
+ padding: 0.25rem 0.25rem;
|
|
|
+ background-color: #f3f3f3;
|
|
|
+ margin-bottom: 0.5rem;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ &:active,
|
|
|
+ &:focus {
|
|
|
+ border-color: #fe6347;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .exam-question-gapFilling-false {
|
|
|
+ textarea {
|
|
|
+ border-color: red !important;
|
|
|
+ background-color: #fedada !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .exam-question-button-box {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ .exam-question-button {
|
|
|
+ width: 6rem;
|
|
|
+ height: auto;
|
|
|
+ padding: 0.5rem 0.5rem;
|
|
|
+ font-size: 0.65rem;
|
|
|
+ margin-top: 0.75rem;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .exam-question-result {
|
|
|
+ border-bottom: 1px solid #fe6347;
|
|
|
+ padding-bottom: 0.25rem;
|
|
|
+ font-size: 0.6rem;
|
|
|
+ }
|
|
|
+ .exam-question-analysis {
|
|
|
+ padding-top: 0.25rem;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|