|
@@ -1,34 +1,75 @@
|
|
|
<template>
|
|
|
- <el-dialog title="修改标签" v-model="state.dialogVisible" draggable append-to-body destroy-on-close @close="close" width="500px">
|
|
|
- <el-form :model="state.ruleForm" ref="ruleFormRef" label-width="80px" v-loading="state.loading">
|
|
|
- <el-row :gutter="10">
|
|
|
- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
- <el-form-item label="上级标签" prop="parentId" :rules="[{ required: false, message: '请选择上级标签', trigger: 'change' }]">
|
|
|
- <el-cascader
|
|
|
- :options="state.treeData"
|
|
|
- filterable
|
|
|
- :props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false, children: 'children' }"
|
|
|
- placeholder="请选择上级标签"
|
|
|
- clearable
|
|
|
- class="w100"
|
|
|
- v-model="state.ruleForm.parentId"
|
|
|
- ref="cascadeRef"
|
|
|
- @change="getExamTagList"
|
|
|
- >
|
|
|
- </el-cascader>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
- <el-form-item label="标签名称" prop="name" :rules="[{ required: true, message: '请填写标签名称', trigger: 'blur' }]">
|
|
|
- <el-input v-model="state.ruleForm.name" placeholder="请填写标签名称" clearable></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </el-form>
|
|
|
+ <el-dialog v-model="state.dialogVisible" draggable destroy-on-close :show-close="false" width="80%" :before-close="closeDialog">
|
|
|
+ <template #header="{ close, titleId, titleClass }">
|
|
|
+ <div class="topContent">
|
|
|
+ <div class="titleBox">
|
|
|
+ <span class="title">考试结果</span>
|
|
|
+ </div>
|
|
|
+ <div class="submitBox">
|
|
|
+ <el-button type="info" @click="closeDialog">关 闭</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <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" :class="{'select': questionsItem.id == state.selQuestionID, 'right': questionsItem.isRight, 'error': !questionsItem.isRight}" v-for="(questionsItem) in item.questions" @click="onSelQuestion(questionsItem.id)">{{questionsItem.sortIndex}}</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="18" :sm="18" :md="18" :lg="18" :xl="18">
|
|
|
+ <div class="questionBox" v-if="state.questionDetail">
|
|
|
+ <p class="questionTitle">题干:{{ state.questionDetail.title }}</p>
|
|
|
+ <div class="questionAnswer">
|
|
|
+ <div v-if="[0,2].includes(state.questionDetail.questionType)">
|
|
|
+ <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" disabled />
|
|
|
+ </el-form-item>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="state.questionDetail.questionType == 1">
|
|
|
+ <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" disabled />
|
|
|
+ </el-form-item>
|
|
|
+ </el-checkbox-group>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="state.questionDetail.questionType == 3">
|
|
|
+ <el-form-item label="答:">
|
|
|
+ <el-input type="textarea" v-model="state.answerValue" :rows="3" placeholder="请输入答案,如果有多个请依照顺序用英文 , 隔开" disabled></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="state.questionDetail.questionType == 4">
|
|
|
+ <el-form-item label="答:">
|
|
|
+ <el-input type="textarea" v-model="state.answerValue" :rows="3" placeholder="请输入答案" disabled></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="referenceAnswer" v-if="state.questionDetail">
|
|
|
+ <span>得分:{{state.questionDetail.realScore || 0}}</span>
|
|
|
+ </div>
|
|
|
+ <div class="referenceAnswer" v-if="state.questionDetail">
|
|
|
+ <span>参考答案:</span>
|
|
|
+ <p>{{state.questionDetail.correctAnswer || '略'}}</p>
|
|
|
+ </div>
|
|
|
+ <div class="referenceAnswer" v-if="state.questionDetail && state.questionDetail.questionKnowladgeDtos.length > 0">
|
|
|
+ <span>关联知识:</span>
|
|
|
+ <p v-for="item in state.questionDetail.questionKnowladgeDtos" @click="onKnowladgeTo(item)">{{item.title}}</p>
|
|
|
+ </div>
|
|
|
+ <div class="referenceAnswer" v-if="state.questionDetail && state.questionDetail.questionSourcewareDtos.length > 0">
|
|
|
+ <span>关联课件:</span>
|
|
|
+ <p v-for="item in state.questionDetail.questionSourcewareDtos" @click="onSourcewareTo(item)">{{item.name}}</p>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
<template #footer>
|
|
|
- <span class="dialog-footer">
|
|
|
- <el-button @click="closeDialog" class="default-button">取 消</el-button>
|
|
|
- <el-button type="primary" @click="onSubmit(ruleFormRef)" :loading="state.loading">确 定 </el-button>
|
|
|
+ <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>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
@@ -36,73 +77,183 @@
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { reactive, ref } from 'vue';
|
|
|
-import { ElMessage, FormInstance } from 'element-plus';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import { excludeSelfById } from '@/utils/tools';
|
|
|
import other from '@/utils/other';
|
|
|
-import { examTagDetail, updateExamTag } from '@/api/examTrain/tag';
|
|
|
+import { fileDownloadByUrl } from '@/api/public/file';
|
|
|
+import {getExamQuestionGroup, getViewExamQuestion} from '@/api/examTrain/userExam';
|
|
|
|
|
|
+const router = useRouter(); // 路由
|
|
|
// 定义子组件向父组件传值/事件
|
|
|
const emit = defineEmits(['updateList']);
|
|
|
|
|
|
// 定义变量内容
|
|
|
const state = reactive<any>({
|
|
|
dialogVisible: false, // 弹窗
|
|
|
- ruleForm: {
|
|
|
- name: '', // 类型名称
|
|
|
- parentId: '', // 上级类型
|
|
|
- },
|
|
|
- treeData: [], // 上级
|
|
|
loading: false, // 加载
|
|
|
+ id: '', // 用户考试id
|
|
|
+ examId: '', // 考试id
|
|
|
+ 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, treeData: any) => {
|
|
|
- try {
|
|
|
- state.dialogVisible = true;
|
|
|
- const { result } = await examTagDetail(row.id);
|
|
|
- state.ruleForm = result;
|
|
|
- state.treeData = other.deepClone(treeData);
|
|
|
- state.treeData = excludeSelfById(state.treeData, row.id);
|
|
|
+const openDialog = async (rows: any) => {
|
|
|
+ state.dialogVisible = true;
|
|
|
+ state.id = rows.id;
|
|
|
+ state.examId = rows.examId;
|
|
|
+ state.loading = true;
|
|
|
+ getQuestionsData();
|
|
|
+};
|
|
|
+const getQuestionsData = async () => {
|
|
|
+ try {
|
|
|
+ const { result } = await getExamQuestionGroup(state.examId);
|
|
|
+ let sortIndexNum = 1;
|
|
|
+ result.forEach(item => {
|
|
|
+ switch(item.questionType){
|
|
|
+ case 0: item.questionTypeDesc = '单选题'; break;
|
|
|
+ case 1: item.questionTypeDesc = '多选题'; break;
|
|
|
+ case 2: item.questionTypeDesc = '判断题'; break;
|
|
|
+ case 3: item.questionTypeDesc = '填空题'; break;
|
|
|
+ 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 cascadeRef = ref<RefType>();
|
|
|
-// 获取选择name值
|
|
|
-const getExamTagList = () => {
|
|
|
- let currentNode = cascadeRef.value.getCheckedNodes();
|
|
|
- state.ruleForm.parentName = currentNode[0]?.label ?? '';
|
|
|
-};
|
|
|
-// 保存
|
|
|
-const onSubmit = async (formEl: FormInstance | undefined) => {
|
|
|
- if (!formEl) return;
|
|
|
- await formEl.validate((valid: boolean) => {
|
|
|
- if (!valid) return;
|
|
|
- state.loading = true;
|
|
|
- updateExamTag(state.ruleForm)
|
|
|
- .then(() => {
|
|
|
- closeDialog(); // 关闭弹窗
|
|
|
- ElMessage.success('操作成功');
|
|
|
- state.loading = false;
|
|
|
- emit('updateList');
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- state.loading = false;
|
|
|
- });
|
|
|
+// 上一题下一题
|
|
|
+const onLastQuestion = () => {
|
|
|
+ let index:number = state.questionsData.findIndex(it => it.id == state.selQuestionID);
|
|
|
+ index = (index - 1) == -1 ? state.questionsData.length - 1 : 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 提交当前试题答案,再请求下一道题详情 value: 请求下一题详情id
|
|
|
+const onSelQuestion = async (value: any) => {
|
|
|
+ state.loading = true;
|
|
|
+ onGetQuestionDetail(value);
|
|
|
+}
|
|
|
+const onGetQuestionDetail = async (value) => {
|
|
|
+ // 初始化选择选择/填写的答案
|
|
|
+ state.selRadioValue = '';
|
|
|
+ state.selCheckboxValue = [];
|
|
|
+ // 请求下一道题
|
|
|
+ state.selQuestionID = value;
|
|
|
+ try {
|
|
|
+ const { result } = await getViewExamQuestion(state.id, state.selQuestionID);
|
|
|
+ state.questionDetail = result;
|
|
|
+ // let answerDesc = '';
|
|
|
+ // state.questionDetail.questionOptions.forEach(it => {it.isAnswer && (answerDesc += it.label)});
|
|
|
+ // state.questionDetail.answerDesc = answerDesc;
|
|
|
+ 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);})
|
|
|
+ state.selCheckboxValue = arr ? arr.map((x: any) => x.id) : [];
|
|
|
+ }else {
|
|
|
+ state.answerValue = state.questionDetail.answer || '';
|
|
|
+ }
|
|
|
+ state.loading = false;
|
|
|
+ } catch (error) {
|
|
|
+ // 打印错误信息
|
|
|
+ console.error(error);
|
|
|
+ }
|
|
|
+}
|
|
|
+// 跳转知识详情页面
|
|
|
+const onKnowladgeTo = (row: any) => {
|
|
|
+ router.push({
|
|
|
+ name: 'knowledgePreview',
|
|
|
+ params: {
|
|
|
+ id: row.knowladgeId,
|
|
|
+ isAddPv: 'isAddPv',
|
|
|
+ tagsViewName: row.title,
|
|
|
+ },
|
|
|
});
|
|
|
};
|
|
|
+// 课件预览下载
|
|
|
+const onSourcewareTo = (row: any) => {
|
|
|
+ ElMessageBox.confirm(`您确定要下载课件,是否继续?`, '提示', {
|
|
|
+ confirmButtonText: '确认',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ draggable: true,
|
|
|
+ cancelButtonClass: 'default-button',
|
|
|
+ autofocus: false,
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ fileDownloadByUrl({
|
|
|
+ Source: 'hotline',
|
|
|
+ Id: row.attachmentId,
|
|
|
+ }).then((res: any) => {
|
|
|
+ let blob: Blob = new Blob([res.data], { type: res.data.type }); // 创建blob 设置blob文件类型 data 设置为后端返回的文件(例如mp3,jpeg) type:这里设置后端返回的类型 为 mp3
|
|
|
+ let down: HTMLAnchorElement = document.createElement('a'); // 创建A标签
|
|
|
+ let href: string = window.URL.createObjectURL(blob); // 创建下载的链接
|
|
|
+ down.href = href; // 下载地址
|
|
|
+ down.download = row.name // 下载文件名
|
|
|
+ document.body.appendChild(down);
|
|
|
+ down.click(); // 模拟点击A标签
|
|
|
+ document.body.removeChild(down); // 下载完成移除元素
|
|
|
+ window.URL.revokeObjectURL(href); // 释放blob对象
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(() => {});
|
|
|
+}
|
|
|
// 暴露变量
|
|
|
defineExpose({
|
|
|
openDialog,
|
|
|
closeDialog,
|
|
|
});
|
|
|
</script>
|
|
|
+<style lang="scss">
|
|
|
+ .topContent{padding: 10px 30px;display: flex;justify-content: space-between;align-items: center;}
|
|
|
+ .topContent .surplusBox{text-align: center;}
|
|
|
+ .topContent .surplusBox .surplus{font-size: 18px;color: #000000;}
|
|
|
+ .topContent .titleBox{text-align: center;}
|
|
|
+ .topContent .titleBox .title{font-size: 20px;font-weight: bold;letter-spacing: 1px;color: #000000;}
|
|
|
+ .topContent .submitBox{text-align: right;vertical-align: top;}
|
|
|
+ .examListContent .examType{font-weight: bold;color: #000000;}
|
|
|
+ .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 .right::after{content: " ";width: 5px;height: 5px;position: absolute;top: 2px;right: 3px;background-color: #009688;border-radius: 50%;}
|
|
|
+ .examListContent .examList .error::after{content: " ";width: 5px;height: 5px;position: absolute;top: 2px;right: 3px;background-color: #FF5722;border-radius: 50%;}
|
|
|
+ .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;}
|
|
|
+ .referenceAnswer{background-color: #ebf9ff;margin: 15px 40px 15px 30px;padding: 10px;}
|
|
|
+ .referenceAnswer span{display: block;}
|
|
|
+ .referenceAnswer p{width: calc(100% - 80px);display: block;margin-top: 10px;color: #1890ff;cursor: pointer;}
|
|
|
+</style>
|