|
@@ -1,6 +1,5 @@
|
|
|
import { ActionSheet, Button, Image, Popup, Swipe, SwipeItem } from 'vant';
|
|
|
import {
|
|
|
- computed,
|
|
|
defineComponent,
|
|
|
nextTick,
|
|
|
onMounted,
|
|
@@ -17,13 +16,12 @@ import AnswerList from '../model/answer-list';
|
|
|
import DragQuestion from '../model/drag-question';
|
|
|
import KeepLookQuestion from '../model/keep-look-question';
|
|
|
import PlayQuestion from '../model/play-question';
|
|
|
-import ErrorMode from '../model/error-mode';
|
|
|
import ResultFinish from '../model/result-finish';
|
|
|
import { eventUnit, QuestionType } from '../unit';
|
|
|
import request from '@/helpers/request';
|
|
|
-import { useRect } from '@vant/use';
|
|
|
+import { CurrentTime, useCountDown, useRect } from '@vant/use';
|
|
|
import MHeader from '@/components/m-header';
|
|
|
-import { useEventListener, useInterval, useWindowScroll } from '@vueuse/core';
|
|
|
+import { useEventListener, useWindowScroll } from '@vueuse/core';
|
|
|
|
|
|
export default defineComponent({
|
|
|
name: 'unit-detail',
|
|
@@ -35,16 +33,17 @@ export default defineComponent({
|
|
|
type: route.query.type, // 类型
|
|
|
knowledgePointIds: route.query.knowledgePointIds, // 智能组卷 多个编号
|
|
|
lessonCoursewareId: route.query.lessonCoursewareId, // 教材编号
|
|
|
+ studentUnitExaminationId: '', // 测验编号
|
|
|
background: 'transparent',
|
|
|
color: '#fff',
|
|
|
- visiableError: false,
|
|
|
visiableAnswer: false,
|
|
|
- id: route.query.id,
|
|
|
+ examDetail: {} as any,
|
|
|
currentIndex: 0,
|
|
|
+ time: 0,
|
|
|
questionList: [],
|
|
|
visiableInfo: {
|
|
|
show: false,
|
|
|
- operationType: 'RESULT' as 'RESULT' | 'BACK' | 'CONTINUE',
|
|
|
+ operationType: 'RESULT' as 'RESULT' | 'BACK' | 'CONTINUE' | 'TIME',
|
|
|
type: 'DEFAULT' as 'DEFAULT' | 'FAIL' | 'PASS' | 'GOOD' | 'COUNTDOWN',
|
|
|
content: '',
|
|
|
showCancelButton: false,
|
|
@@ -54,30 +53,57 @@ export default defineComponent({
|
|
|
},
|
|
|
nextStatus: false,
|
|
|
swipeHeight: 'auto' as any,
|
|
|
- answerAnalysis: '',
|
|
|
- questionTypeCode: '',
|
|
|
- overResult: {
|
|
|
- time: '00:00', // 时长
|
|
|
- questionLength: 0, // 答题数
|
|
|
- errorLength: 0, // 错题数
|
|
|
- rate: 0 // 正确率
|
|
|
- }
|
|
|
+ countDownOver: false // 是否已显示时间倒计时
|
|
|
});
|
|
|
|
|
|
// 计时
|
|
|
- const { counter, resume, pause } = useInterval(1000, { controls: true });
|
|
|
+ const countDownRef = useCountDown({
|
|
|
+ // 倒计时 60 秒
|
|
|
+ time: state.time,
|
|
|
+ onChange(current: CurrentTime) {
|
|
|
+ const diffTime = 5 * 60 * 1000;
|
|
|
+ if (diffTime >= current.total && !state.countDownOver) {
|
|
|
+ state.visiableInfo.show = true;
|
|
|
+ state.visiableInfo.title = '倒计时5分钟';
|
|
|
+ state.visiableInfo.showCancelButton = false;
|
|
|
+ state.visiableInfo.operationType = 'TIME';
|
|
|
+ state.visiableInfo.type = 'COUNTDOWN';
|
|
|
+ state.visiableInfo.confirmButtonText = '确认';
|
|
|
+ state.visiableInfo.content = `距离交卷时间还剩五分钟哦,请尽快答题~`;
|
|
|
+ state.countDownOver = true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onFinish: async () => {
|
|
|
+ eventUnit.emit('unitAudioStop');
|
|
|
+ await onResultPopup();
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
const getExamDetails = async () => {
|
|
|
try {
|
|
|
- const { data } = await request.post(
|
|
|
- '/edu-app/studentUnitExamination/mockExamination',
|
|
|
- {
|
|
|
- data: {
|
|
|
- lessonCoursewareId: state.lessonCoursewareId
|
|
|
+ let temp: any = {};
|
|
|
+ if (state.type === 'ai') {
|
|
|
+ const { data } = await request.post(
|
|
|
+ '/edu-app/studentUnitExamination/pointRandomSave',
|
|
|
+ {
|
|
|
+ data: {
|
|
|
+ knowledgePointIds: state.knowledgePointIds
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- );
|
|
|
- const temp = data || {};
|
|
|
+ );
|
|
|
+ temp = data || {};
|
|
|
+ } else {
|
|
|
+ const { data } = await request.post(
|
|
|
+ '/edu-app/studentUnitExamination/mockExamination',
|
|
|
+ {
|
|
|
+ data: {
|
|
|
+ lessonCoursewareId: state.lessonCoursewareId
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ temp = data || {};
|
|
|
+ }
|
|
|
+
|
|
|
temp.examinationQuestionAdds.forEach((item: any) => {
|
|
|
item.showAnalysis = false; // 默认不显示解析
|
|
|
item.analysis = {
|
|
@@ -88,44 +114,41 @@ export default defineComponent({
|
|
|
item.userAnswer = []; // 用户答题
|
|
|
});
|
|
|
state.questionList = temp.examinationQuestionAdds || [];
|
|
|
+ state.studentUnitExaminationId = temp.unitExaminationId;
|
|
|
+ state.examDetail = temp || {};
|
|
|
+ calcTime();
|
|
|
} catch {
|
|
|
//
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
+ * @description 计算考试时间剩余时间
|
|
|
+ */
|
|
|
+ const calcTime = async () => {
|
|
|
+ const examDetail = state.examDetail || {};
|
|
|
+ const timeMinutes = examDetail.timeMinutes || 0; // 测验时间
|
|
|
+ state.time = Math.ceil(timeMinutes * 60 * 1000);
|
|
|
+ setTimeout(() => {
|
|
|
+ countDownRef.reset(timeMinutes * 60 * 1000);
|
|
|
+ countDownRef.start();
|
|
|
+ }, 10);
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
* @description 下一题 | 测试完成
|
|
|
*/
|
|
|
const onNextQuestion = async () => {
|
|
|
try {
|
|
|
const questionList = state.questionList || [];
|
|
|
-
|
|
|
- let result: any = {};
|
|
|
+ const userAnswerList: any = []; // 所有题目的答案
|
|
|
questionList.forEach((question: any, index: number) => {
|
|
|
// 格式化所有题目的答案
|
|
|
- if (index === state.currentIndex) {
|
|
|
- result = {
|
|
|
+ if (question.userAnswer && question.userAnswer.length > 0) {
|
|
|
+ userAnswerList.push({
|
|
|
questionId: question.id,
|
|
|
- details: question.userAnswer || []
|
|
|
- };
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- const { data } = await request.post(
|
|
|
- '/edu-app/studentUnitExamination/submitAnswer',
|
|
|
- {
|
|
|
- hideLoading: true,
|
|
|
- data: result
|
|
|
- }
|
|
|
- );
|
|
|
- // 初始化是否显示解析
|
|
|
- questionList.forEach((question: any, index: number) => {
|
|
|
- // 格式化所有题目的答案
|
|
|
- if (index === state.currentIndex) {
|
|
|
- state.answerAnalysis = question.answerAnalysis;
|
|
|
- state.questionTypeCode = question.questionTypeCode;
|
|
|
- question.showAnalysis = true;
|
|
|
- question.analysis.userResult = data;
|
|
|
+ details: question.userAnswer
|
|
|
+ });
|
|
|
}
|
|
|
});
|
|
|
|
|
@@ -133,50 +156,32 @@ export default defineComponent({
|
|
|
if (state.questionList.length === state.currentIndex + 1) {
|
|
|
eventUnit.emit('unitAudioStop');
|
|
|
state.visiableInfo.show = true;
|
|
|
- state.visiableInfo.title = '练习完成';
|
|
|
+ state.visiableInfo.title = '测验完成';
|
|
|
state.visiableInfo.showCancelButton = true;
|
|
|
state.visiableInfo.operationType = 'CONTINUE';
|
|
|
+ state.visiableInfo.type = 'DEFAULT';
|
|
|
state.visiableInfo.cancelButtonText = '再等等';
|
|
|
state.visiableInfo.confirmButtonText = '确认完成';
|
|
|
- state.visiableInfo.content = `确认本次练习的题目都完成了吗?`;
|
|
|
-
|
|
|
+ state.visiableInfo.content = `确认本次测验的题目都完成了吗?`;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (data) {
|
|
|
- swipeRef.value?.next();
|
|
|
- } else {
|
|
|
- state.visiableError = true;
|
|
|
- }
|
|
|
+ state.nextStatus = true;
|
|
|
+ await request.post('/edu-app/studentUnitExamination/submitAnswer', {
|
|
|
+ hideLoading: true,
|
|
|
+ data: {
|
|
|
+ answers: userAnswerList,
|
|
|
+ studentUnitExaminationId: state.studentUnitExaminationId
|
|
|
+ }
|
|
|
+ });
|
|
|
+ swipeRef.value?.next();
|
|
|
+
|
|
|
+ state.nextStatus = false;
|
|
|
} catch {
|
|
|
//
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- //
|
|
|
- const getAnswerResult = computed(() => {
|
|
|
- const questionList = state.questionList || [];
|
|
|
- let count = 0;
|
|
|
- let passCount = 0;
|
|
|
- let noPassCount = 0;
|
|
|
- questionList.forEach((item: any) => {
|
|
|
- if (item.showAnalysis) {
|
|
|
- count += 1;
|
|
|
- if (item.analysis.userResult) {
|
|
|
- passCount += 1;
|
|
|
- } else {
|
|
|
- noPassCount += 1;
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return {
|
|
|
- count,
|
|
|
- passCount,
|
|
|
- noPassCount
|
|
|
- };
|
|
|
- });
|
|
|
-
|
|
|
/**
|
|
|
* @description 重置当前的题目高度
|
|
|
* @param {any} scroll 是否滚动到顶部
|
|
@@ -223,74 +228,99 @@ export default defineComponent({
|
|
|
router.back();
|
|
|
onAfter();
|
|
|
} else if (state.visiableInfo.operationType === 'BACK') {
|
|
|
- state.visiableInfo.show = false;
|
|
|
- onAfter();
|
|
|
+ onResultPopup();
|
|
|
} else if (state.visiableInfo.operationType === 'CONTINUE') {
|
|
|
onResultPopup();
|
|
|
+ } else if (state.visiableInfo.operationType === 'TIME') {
|
|
|
+ state.visiableInfo.show = false;
|
|
|
}
|
|
|
};
|
|
|
- const onCloseResult = async () => {
|
|
|
- if (state.visiableInfo.operationType === 'RESULT') {
|
|
|
- } else if (state.visiableInfo.operationType === 'BACK') {
|
|
|
- state.visiableInfo.show = false;
|
|
|
- window.history.pushState(null, '', document.URL);
|
|
|
- window.addEventListener('popstate', onBack, false);
|
|
|
+ const onCloseResult = async (status: boolean) => {
|
|
|
+ if (state.visiableInfo.operationType === 'BACK') {
|
|
|
+ if (status) {
|
|
|
+ state.visiableInfo.show = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ await request.get('/edu-app/studentUnitExamination/dropExamination', {
|
|
|
+ params: {
|
|
|
+ studentUnitExaminationId: state.studentUnitExaminationId
|
|
|
+ }
|
|
|
+ });
|
|
|
+ state.visiableInfo.show = false;
|
|
|
+ onAfter();
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
+ }
|
|
|
} else if (state.visiableInfo.operationType === 'CONTINUE') {
|
|
|
state.visiableInfo.show = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/** 结果页面弹窗 */
|
|
|
- const onResultPopup = () => {
|
|
|
- const answerResult = getAnswerResult.value;
|
|
|
- let rate = 0;
|
|
|
+ const onResultPopup = async () => {
|
|
|
+ try {
|
|
|
+ const questionList = state.questionList || [];
|
|
|
+ const userAnswerList: any = []; // 所有题目的答案
|
|
|
+ questionList.forEach((question: any) => {
|
|
|
+ // 格式化所有题目的答案
|
|
|
+ if (question.userAnswer && question.userAnswer.length > 0) {
|
|
|
+ userAnswerList.push({
|
|
|
+ questionId: question.id,
|
|
|
+ details: question.userAnswer
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const { data } = await request.post(
|
|
|
+ '/edu-app/studentUnitExamination/completionExamination',
|
|
|
+ {
|
|
|
+ hideLoading: false,
|
|
|
+ data: {
|
|
|
+ answers: userAnswerList,
|
|
|
+ studentUnitExaminationId: state.studentUnitExaminationId
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ // 60 及格
|
|
|
+ // 85 及以上优秀
|
|
|
+ state.visiableInfo.show = true;
|
|
|
+ state.visiableInfo.title = data.score + '分';
|
|
|
+ state.visiableInfo.showCancelButton = false;
|
|
|
+ state.visiableInfo.operationType = 'RESULT';
|
|
|
+ state.visiableInfo.confirmButtonText = '确认';
|
|
|
|
|
|
- if (answerResult.count > 0) {
|
|
|
- rate = Math.floor((answerResult.passCount / answerResult.count) * 100);
|
|
|
+ if (data.status === 'A_EXCELLENT') {
|
|
|
+ state.visiableInfo.type = 'GOOD';
|
|
|
+ state.visiableInfo.content = '<div>你很棒,题目掌握的非常不错,';
|
|
|
+ } else if (data.status === 'B_PASS') {
|
|
|
+ state.visiableInfo.type = 'PASS';
|
|
|
+ state.visiableInfo.content = '<div>还需要加油哦,';
|
|
|
+ } else {
|
|
|
+ state.visiableInfo.type = 'FAIL';
|
|
|
+ state.visiableInfo.content = '<div>别气馁,继续努力,';
|
|
|
+ }
|
|
|
+ state.visiableInfo.content += `您本次获得了<span class='${
|
|
|
+ styles.right
|
|
|
+ }'>${data.score}分</span>,正确率<span class='${styles.error}'>${
|
|
|
+ data.rightRate
|
|
|
+ }%</span>,实际用时<span class='${styles.minutes}'>${Math.ceil(
|
|
|
+ data.answerTime / 60
|
|
|
+ )}</span>分钟~</div>`;
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
}
|
|
|
-
|
|
|
- const times = counter.value;
|
|
|
- const minute =
|
|
|
- Math.floor(times / 60) >= 10
|
|
|
- ? Math.floor(times / 60)
|
|
|
- : '0' + Math.floor(times / 60);
|
|
|
- const seconds = times % 60 >= 10 ? times % 60 : '0' + (times % 60);
|
|
|
- state.overResult = {
|
|
|
- time: minute + ':' + seconds, // 时长
|
|
|
- questionLength: answerResult.count, // 答题数
|
|
|
- errorLength: answerResult.noPassCount, // 错题数
|
|
|
- rate // 正确率
|
|
|
- };
|
|
|
- // 重置计时
|
|
|
- pause();
|
|
|
- counter.value = 0;
|
|
|
-
|
|
|
- // 60 及格
|
|
|
- // 85 及以上优秀
|
|
|
- state.visiableInfo.show = true;
|
|
|
- state.visiableInfo.title = '已完成';
|
|
|
- state.visiableInfo.showCancelButton = false;
|
|
|
- state.visiableInfo.operationType = 'RESULT';
|
|
|
- state.visiableInfo.confirmButtonText = '确认';
|
|
|
- state.visiableInfo.content = `<div>您已完成本次测试,答对<span class='${
|
|
|
- styles.right
|
|
|
- }'>${answerResult.passCount}</span>,答错<span class='${styles.error}'>${
|
|
|
- answerResult.count - answerResult.passCount
|
|
|
- }</span>,正确率${rate}%~</div>`;
|
|
|
};
|
|
|
|
|
|
// 拦截
|
|
|
const onBack = () => {
|
|
|
- const answerResult = getAnswerResult.value;
|
|
|
state.visiableInfo.show = true;
|
|
|
- state.visiableInfo.title = '确认退出吗?';
|
|
|
+ state.visiableInfo.title = '确认要离开吗?';
|
|
|
state.visiableInfo.showCancelButton = true;
|
|
|
state.visiableInfo.operationType = 'BACK';
|
|
|
- state.visiableInfo.cancelButtonText = '取消';
|
|
|
+ state.visiableInfo.type = 'DEFAULT';
|
|
|
+ state.visiableInfo.cancelButtonText = '弃考';
|
|
|
state.visiableInfo.confirmButtonText = '确定';
|
|
|
- state.visiableInfo.content = `您已经完成${
|
|
|
- answerResult.passCount + answerResult.noPassCount
|
|
|
- }道题了,继续做题可以巩固所学知识哦~`;
|
|
|
+ state.visiableInfo.content = `还有题目未完成哦,是否要提前交卷?`;
|
|
|
eventUnit.emit('unitAudioStop');
|
|
|
};
|
|
|
|
|
@@ -314,8 +344,8 @@ export default defineComponent({
|
|
|
|
|
|
resizeSwipeItemHeight();
|
|
|
|
|
|
- // window.history.pushState(null, '', document.URL);
|
|
|
- // window.addEventListener('popstate', onBack, false);
|
|
|
+ window.history.pushState(null, '', document.URL);
|
|
|
+ window.addEventListener('popstate', onBack, false);
|
|
|
});
|
|
|
|
|
|
onUnmounted(() => {
|
|
@@ -365,8 +395,9 @@ export default defineComponent({
|
|
|
{state.questionList.length}
|
|
|
</div>
|
|
|
<div class={styles.questionType}>
|
|
|
- <i></i>
|
|
|
- <span>{item.knowledgePointName}</span>
|
|
|
+ {countDownRef.current.value.minutes +
|
|
|
+ countDownRef.current.value.hours * 60}
|
|
|
+ :{countDownRef.current.value.seconds}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
@@ -389,8 +420,9 @@ export default defineComponent({
|
|
|
{state.questionList.length}
|
|
|
</div>
|
|
|
<div class={styles.questionType}>
|
|
|
- <i></i>
|
|
|
- <span>{item.knowledgePointName}</span>
|
|
|
+ {countDownRef.current.value.minutes +
|
|
|
+ countDownRef.current.value.hours * 60}
|
|
|
+ :{countDownRef.current.value.seconds}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
@@ -420,8 +452,9 @@ export default defineComponent({
|
|
|
{state.questionList.length}
|
|
|
</div>
|
|
|
<div class={styles.questionType}>
|
|
|
- <i></i>
|
|
|
- <span>{item.knowledgePointName}</span>
|
|
|
+ {countDownRef.current.value.minutes +
|
|
|
+ countDownRef.current.value.hours * 60}
|
|
|
+ :{countDownRef.current.value.seconds}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
@@ -443,8 +476,9 @@ export default defineComponent({
|
|
|
{state.questionList.length}
|
|
|
</div>
|
|
|
<div class={styles.questionType}>
|
|
|
- <i></i>
|
|
|
- <span>{item.knowledgePointName}</span>
|
|
|
+ {countDownRef.current.value.minutes +
|
|
|
+ countDownRef.current.value.hours * 60}
|
|
|
+ :{countDownRef.current.value.seconds}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
@@ -456,7 +490,7 @@ export default defineComponent({
|
|
|
v-model:value={item.userAnswer}
|
|
|
data={item}
|
|
|
index={index + 1}
|
|
|
- unitId={state.id as any}
|
|
|
+ unitId={state.studentUnitExaminationId as any}
|
|
|
showAnalysis={item.showAnalysis}
|
|
|
analysis={item.analysis}>
|
|
|
{{
|
|
@@ -467,8 +501,9 @@ export default defineComponent({
|
|
|
{state.questionList.length}
|
|
|
</div>
|
|
|
<div class={styles.questionType}>
|
|
|
- <i></i>
|
|
|
- <span>{item.knowledgePointName}</span>
|
|
|
+ {countDownRef.current.value.minutes +
|
|
|
+ countDownRef.current.value.hours * 60}
|
|
|
+ :{countDownRef.current.value.seconds}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
@@ -520,7 +555,6 @@ export default defineComponent({
|
|
|
safeAreaInsetBottom>
|
|
|
<AnswerList
|
|
|
value={state.questionList}
|
|
|
- // lookType={'ANSWER'}
|
|
|
onSelect={(item: any) => {
|
|
|
// 跳转,并且跳过动画
|
|
|
swipeRef.value?.swipeTo(item, {
|
|
@@ -532,22 +566,6 @@ export default defineComponent({
|
|
|
</ActionSheet>
|
|
|
|
|
|
<Popup
|
|
|
- v-model:show={state.visiableError}
|
|
|
- style={{ width: '90%' }}
|
|
|
- round
|
|
|
- closeOnClickOverlay={false}>
|
|
|
- <ErrorMode
|
|
|
- onClose={() => (state.visiableError = false)}
|
|
|
- answerAnalysis={state.answerAnalysis}
|
|
|
- questionTypeCode={state.questionTypeCode}
|
|
|
- onConform={() => {
|
|
|
- swipeRef.value?.next();
|
|
|
- state.answerAnalysis = '';
|
|
|
- }}
|
|
|
- />
|
|
|
- </Popup>
|
|
|
-
|
|
|
- <Popup
|
|
|
v-model:show={state.visiableInfo.show}
|
|
|
closeOnClickOverlay={false}
|
|
|
style={{
|
|
@@ -563,6 +581,9 @@ export default defineComponent({
|
|
|
confirmButtonText={state.visiableInfo.confirmButtonText}
|
|
|
status={state.visiableInfo.type}
|
|
|
content={state.visiableInfo.content}
|
|
|
+ closeable={
|
|
|
+ state.visiableInfo.operationType === 'BACK' ? true : false
|
|
|
+ }
|
|
|
contentHtml
|
|
|
onConform={onConfirmResult}
|
|
|
onClose={onCloseResult}
|