浏览代码

更新添加知识库

lex 1 年之前
父节点
当前提交
a3ca311a47

+ 3 - 16
src/views/knowledge-library/examination-mode/index.module.less

@@ -28,22 +28,9 @@
     .questionType {
       display: flex;
       align-items: center;
-
-      span {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        max-width: 160px;
-      }
-
-      i {
-        margin-right: 6px;
-        display: inline-block;
-        width: 20px;
-        height: 20px;
-        background: url('../images/icon-book.png') no-repeat center;
-        background-size: contain;
-      }
+      font-weight: 600;
+      color: #FF5A56;
+      font-size: 14px;
     }
   }
 }

+ 179 - 158
src/views/knowledge-library/examination-mode/index.tsx

@@ -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}

二进制
src/views/knowledge-library/images/icon-bell.png


二进制
src/views/knowledge-library/images/icon-count-down.png


二进制
src/views/knowledge-library/images/icon-popup-close.png


二进制
src/views/knowledge-library/images/icon-question-nums.png


二进制
src/views/knowledge-library/images/icon-tag.png


二进制
src/views/knowledge-library/images/icon-timer.png


+ 11 - 0
src/views/knowledge-library/model/result-finish/index.module.less

@@ -77,6 +77,17 @@
   }
 }
 
+.finishClose {
+  display: inline-block;
+  width: 24px;
+  height: 24px;
+  position: absolute;
+  right: 1px;
+  top: 50px;
+  background: url('../../images/icon-popup-close.png') no-repeat center;
+  background-size: contain;
+}
+
 .finishBtnGroup {
   display: flex;
   align-items: center;

+ 11 - 0
src/views/knowledge-library/model/result-finish/index.tsx

@@ -47,9 +47,15 @@ export default defineComponent({
       type: String,
       default: ''
     },
+    /** 内容位置 */
     textAlign: {
       type: String as PropType<'flex-start' | 'center' | 'flex-end'>,
       default: 'flex-start'
+    },
+    /** 是否显示关闭 */
+    closeable: {
+      type: Boolean,
+      default: true
     }
   },
   emits: ['close', 'conform'],
@@ -64,6 +70,11 @@ export default defineComponent({
           props.status === 'COUNTDOWN' && styles.finishCountdown
         ]}>
         <div class={styles.finishContainer}>
+          {props.closeable && (
+            <i
+              class={styles.finishClose}
+              onClick={() => emit('close', true)}></i>
+          )}
           <div class={styles.finishContent}>
             <div class={styles.finishTitle}>
               <span>{props.title}</span>

+ 4 - 2
src/views/knowledge-library/wroing-book/ai-exam/index.tsx

@@ -24,11 +24,13 @@ export default defineComponent({
     const router = useRouter();
     const checkboxRefs = ref([] as any);
     const forms = reactive({
+      loading: false,
       list: [] as any,
       checked: [] as any
     });
 
     const getList = async () => {
+      forms.loading = true;
       try {
         const { data } = await request.post(
           '/edu-app/knowledgePoint/studentPage'
@@ -37,6 +39,7 @@ export default defineComponent({
       } catch {
         //
       }
+      forms.loading = false;
     };
 
     const checkboxToggle = (index: number) => {
@@ -45,7 +48,6 @@ export default defineComponent({
 
     const onSubmit = () => {
       try {
-        console.log(forms.checked);
         if (forms.checked.length <= 0) {
           showToast('请选择练习知识点');
           return;
@@ -125,7 +127,7 @@ export default defineComponent({
                 </Cell>
               ))}
             </CheckboxGroup>
-            {forms.list.length <= 0 && (
+            {forms.list.length <= 0 && !forms.loading && (
               <div class={styles.emptyContainer}>
                 <MEmpty />
               </div>