import { ActionSheet, Button, Image, Popup, Swipe, SwipeItem, showToast } from 'vant'; import { computed, defineComponent, nextTick, onMounted, onUnmounted, reactive, watch, ref } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import styles from './index.module.less'; import iconButtonList from '../images/icon-button-list.png'; import MSticky from '@/components/m-sticky'; import ChoiceQuestion from '../model/choice-question'; 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 MHeader from '@/components/m-header'; import { useEventListener, useInterval, useWindowScroll } from '@vueuse/core'; import { browser } from '@/helpers/utils'; export default defineComponent({ name: 'unit-detail', setup() { const route = useRoute(); const router = useRouter(); const swipeRef = ref(); const state = reactive({ background: 'transparent', // color: '#323333', visiableError: false, visiableAnswer: false, id: route.query.id, currentIndex: 0, questionList: [] as any, page: 1, rows: 50, total: 0, isFinish: false, // 是否完成加载 visiableInfo: { show: false, operationType: 'RESULT' as 'RESULT' | 'BACK' | 'CONTINUE' | 'GRASP', type: 'DEFAULT' as 'DEFAULT' | 'FAIL' | 'PASS' | 'GOOD' | 'COUNTDOWN', content: '', showCancelButton: false, confirmButtonText: '', cancelButtonText: '', title: '', graspItem: {} as any }, nextStatus: false, swipeHeight: 'auto' as any, answerAnalysis: '', questionTypeCode: '', overResult: { time: '00:00', // 时长 questionLength: 0, // 答题数 errorLength: 0, // 错题数 rate: 0 // 正确率 } }); // 计时 const { counter, resume, pause } = useInterval(1000, { controls: true }); const getExamDetails = async ( maxStudentExaminationErrorEditionId?: string ) => { try { const { data } = await request.post( '/edu-app/studentUnitExamination/errorEdition', { data: { page: state.page, rows: state.rows, maxStudentExaminationErrorEditionId } } ); const temp = data || {}; state.total = temp.total || 0; state.isFinish = temp.current < temp.pages ? false : true; temp.records.forEach((item: any) => { item.showAnalysis = false; // 默认不显示解析 item.grasp = false; // 是否掌握题目 item.analysis = { message: item.answerAnalysis, topic: true, // 是否显示结果 userResult: false // 用户答题对错 }; item.userAnswer = []; // 用户答题 }); state.questionList.push(...(temp.records || [])); } catch { // } }; // 监听索引 watch( () => state.currentIndex, () => { // 判断是否在倒数第三题,并且没有加载完 if ( state.currentIndex + 3 >= state.questionList.length && !state.isFinish ) { const lastQuestion = state.questionList[state.questionList.length - 1]; state.page = state.page + 1; getExamDetails(lastQuestion.id); } } ); /** 已掌握此题 */ const onGraspQuestion = async (item: any) => { // 判断是否掌握此题 if (item.grasp) return; state.visiableInfo.show = true; state.visiableInfo.title = '确定掌握此题?'; state.visiableInfo.showCancelButton = true; state.visiableInfo.operationType = 'GRASP'; state.visiableInfo.cancelButtonText = '取消'; state.visiableInfo.confirmButtonText = '确定'; state.visiableInfo.content = `你确定已掌握该题知识要点,此题将移除你的错题集。`; state.visiableInfo.graspItem = item; console.log(state.total, 'toa'); if (state.total <= 1) { onAfter(); } }; /** 已掌握此题确认 */ const onGraspQuestionConfirm = async () => { try { state.visiableInfo.show = false; await request.get('/edu-app/studentExaminationErrorEdition/del', { hideLoading: false, params: { questionId: state.visiableInfo.graspItem.id } }); state.visiableInfo.graspItem.grasp = true; eventUnit.emit('unitAudioStop'); // 只有一道题 if (state.total <= 1) { router.back(); router.back(); return; } // 后面还有题 if (state.total > state.currentIndex + 1) { const index = state.questionList.findIndex( (item: any) => item.studentExaminationErrorEditionId === state.visiableInfo.graspItem.studentExaminationErrorEditionId ); state.questionList.splice(index, 1); state.total -= 1; resizeSwipeItemHeight(); // swipeRef.value?.next(); return; } // 后面没有题 if (state.total == state.currentIndex + 1) { const index = state.questionList.findIndex( (item: any) => item.studentExaminationErrorEditionId === state.visiableInfo.graspItem.studentExaminationErrorEditionId ); state.questionList.splice(index, 1); state.total -= 1; state.currentIndex -= 1; resizeSwipeItemHeight(); // swipeRef.value?.prev(); return; } } catch { // } }; /** * @description 下一题 | 测试完成 */ const onNextQuestion = async () => { try { const questionList = state.questionList || []; const question: any = questionList[state.currentIndex]; if (question?.userAnswer?.length <= 0) { showToast('题目尚未做答'); state.nextStatus = false; return; } let result: any = {}; questionList.forEach((question: any, index: number) => { // 格式化所有题目的答案 if (index === state.currentIndex) { result = { questionId: question.id, details: question.userAnswer || [] }; } }); const { data } = await request.post( '/edu-app/studentUnitExamination/submitTrainingAnswer', { 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; } }); // 判断是否是最后一题 if (state.questionList.length === state.currentIndex + 1) { eventUnit.emit('unitAudioStop'); state.visiableInfo.show = true; state.visiableInfo.title = '练习完成'; state.visiableInfo.showCancelButton = true; state.visiableInfo.operationType = 'CONTINUE'; state.visiableInfo.cancelButtonText = '再等等'; state.visiableInfo.confirmButtonText = '确认完成'; state.visiableInfo.content = `确认本次练习的题目都完成了吗?`; onAfter(); return; } if (data) { swipeRef.value?.next(); } else { state.visiableError = true; } } 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 是否滚动到顶部 */ let size = 0; const resizeSwipeItemHeight = (scroll = true) => { nextTick(() => { scroll && window.scrollTo(0, 0); setTimeout(() => { const currentItemDom: any = document .querySelectorAll('.van-swipe-item') [state.currentIndex]?.querySelector('.swipe-item-question'); const allImg = currentItemDom?.querySelectorAll( '.answerTitleImg img' ); let status = true; // console.log(allImg) allImg?.forEach((img: any) => { if (!img.complete) { status = false; } }); // 判断图片是否加载完了 if (!status && size < 3) { setTimeout(() => { size += 1; resizeSwipeItemHeight(scroll); }, 300); } if (status) { size = 0; } const rect = useRect(currentItemDom); state.swipeHeight = rect.height; }, 100); }); }; const onConfirmResult = () => { if (state.visiableInfo.operationType === 'RESULT') { state.visiableInfo.show = false; router.back(); router.back(); } else if (state.visiableInfo.operationType === 'BACK') { state.visiableInfo.show = false; window.history.pushState(null, '', document.URL); window.addEventListener('popstate', onBack, false); } else if (state.visiableInfo.operationType === 'CONTINUE') { onResultPopup(); } else if (state.visiableInfo.operationType === 'GRASP') { onGraspQuestionConfirm(); } }; const onCloseResult = async () => { const operationType = state.visiableInfo.operationType; if (operationType === 'RESULT') { } else if (operationType === 'BACK') { state.visiableInfo.show = false; onAfter(); } else if (operationType === 'CONTINUE') { state.visiableInfo.show = false; } else if (operationType === 'GRASP') { state.visiableInfo.show = false; window.history.pushState(null, '', document.URL); window.addEventListener('popstate', onBack, false); } }; /** 结果页面弹窗 */ const onResultPopup = () => { const answerResult = getAnswerResult.value; let rate = 0; if (answerResult.count > 0) { rate = Math.floor((answerResult.passCount / answerResult.count) * 100); } 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 = `
您已完成本次测试,答对${answerResult.passCount},答错${ answerResult.count - answerResult.passCount },正确率${rate}%~
`; }; // 拦截 const onBack = () => { const answerResult = getAnswerResult.value; state.visiableInfo.show = true; state.visiableInfo.title = '确认退出吗?'; state.visiableInfo.showCancelButton = true; state.visiableInfo.operationType = 'BACK'; state.visiableInfo.cancelButtonText = '退出'; state.visiableInfo.confirmButtonText = '继续'; state.visiableInfo.content = `您已经完成${ answerResult.passCount + answerResult.noPassCount }道题了,继续做题可以巩固所学知识哦~`; eventUnit.emit('unitAudioStop'); }; const onAfter = () => { window.removeEventListener('popstate', onBack, false); router.back(); }; onMounted(async () => { useEventListener(document, 'scroll', () => { const { y } = useWindowScroll(); if (y.value > 52) { state.background = '#fff'; } else { state.background = 'transparent'; } }); await getExamDetails(); resizeSwipeItemHeight(); window.history.pushState(null, '', document.URL); window.addEventListener('popstate', onBack, false); }); onUnmounted(() => { // 关闭所有音频 eventUnit.emit('unitAudioStop'); }); return () => (
{ eventUnit.emit('unitAudioStop'); state.currentIndex = index; resizeSwipeItemHeight(); }}> {state.questionList.map((item: any, index: number) => (
{item.questionTypeCode === QuestionType.RADIO && ( {{ title: () => (

{item.knowledgePointName}

{state.currentIndex + 1}/{state.total}
) }}
)} {item.questionTypeCode === QuestionType.CHECKBOX && ( {{ title: () => (

{item.knowledgePointName}

{state.currentIndex + 1}/{state.total}
) }}
)} {item.questionTypeCode === QuestionType.SORT && ( { // 如果是空则滑动到顶部 const status = item.userAnswer && item.userAnswer.length > 0 ? false : true; resizeSwipeItemHeight(status); }} data={item} index={index + 1} showAnalysis={item.showAnalysis} analysis={item.analysis}> {{ title: () => (

{item.knowledgePointName}

{state.currentIndex + 1}/{state.total}
) }}
)} {item.questionTypeCode === QuestionType.LINK && ( {{ title: () => (

{item.knowledgePointName}

{state.currentIndex + 1}/{state.total}
) }}
)} {item.questionTypeCode === QuestionType.PLAY && ( {{ title: () => (
{state.currentIndex + 1}/{state.total}
{/*
{item.knowledgePointName}
*/}
) }}
)}
))}
(state.visiableAnswer = true)} />
{/* 题目集合 */} { // 跳转,并且跳过动画 swipeRef.value?.swipeTo(item, { immediate: true }); state.visiableAnswer = false; }} onLoadMore={() => { const lastQuestion = state.questionList[state.questionList.length - 1]; state.page = state.page + 1; getExamDetails(lastQuestion.id); }} /> (state.visiableError = false)} answerAnalysis={state.answerAnalysis} questionTypeCode={state.questionTypeCode} onConform={() => { swipeRef.value?.next(); setTimeout(() => { state.answerAnalysis = ''; }, 500); }} />
); } });