shmily1213 1 주 전
부모
커밋
6c9aada5db

+ 44 - 32
src/composables/useExam.ts

@@ -227,21 +227,17 @@ export const useExam = () => {
       return answer1.includes(answers[0]);
     } else if ([EnumQuestionType.MULTIPLE_CHOICE].includes(typeId)) {
       return answers.length === answer1.length && answers.every(item => answer1.includes(item));
-    } else if ([EnumQuestionType.SUBJECTIVE, EnumQuestionType.SHORT_ANSWER, EnumQuestionType.ESSAY].includes(typeId)) {
+    } else {
       // 主观题 A 对 B 错
       return answers.includes('A') && !answers.includes('B');
     }
-    if (!answer1) {
-      return false;
-    }
-    return false;
   };
   /// 题目是否未作答
   const isQuestionNotAnswer = (qs: Study.Question): boolean => {
     if (qs.subQuestions && qs.subQuestions.length > 0) {
       return qs.subQuestions.every(q => isQuestionNotAnswer(q));
     }
-    return !qs.answers || qs.answers.length === 0;
+    return !qs.answers || qs.answers.filter(item => !!item).length === 0;
   }
   /// 选项是否正确
   const isOptionCorrect = (question: Study.Question, option: Study.QuestionOption) => {
@@ -250,10 +246,9 @@ export const useExam = () => {
       return answer1?.includes(option.no);
     } else if ([EnumQuestionType.MULTIPLE_CHOICE].includes(typeId)) {
       return answer1?.includes(option.no);
-    } else if ([EnumQuestionType.SUBJECTIVE, EnumQuestionType.SHORT_ANSWER, EnumQuestionType.ESSAY].includes(typeId)) {
+    } else {
       return answers?.includes(option.no) && option.no === 'A';
     }
-    return false;
   }
   /// 选项是否选中
   const isOptionSelected = (question: Study.Question, option: Study.QuestionOption) => {
@@ -275,11 +270,20 @@ export const useExam = () => {
   const nextEnable = computed(() => {
     if (currentQuestion.value) {
       if (currentQuestion.value.isSubQuestion) {
+        console.log(5, subQuestionIndex.value < currentQuestion.value.subQuestions.length - 1)
         return subQuestionIndex.value < currentQuestion.value.subQuestions.length - 1;
       } else {
-        return currentIndex.value < questionList.value.length - 1;
+        if (currentQuestion.value.subQuestions && currentQuestion.value.subQuestions.length > 0) {
+          console.log(subQuestionIndex.value, currentQuestion.value.subQuestions.length - 1)
+          // 子题可以切换
+          return subQuestionIndex.value < currentQuestion.value.subQuestions.length - 1;
+        } else {
+          // 大题可以切换
+          return currentIndex.value < questionList.value.length - 1;
+        }
       }
     }
+    console.log(7, currentQuestion.value)
     return false;
   });
   // 下一题
@@ -310,31 +314,31 @@ export const useExam = () => {
       return;
     }
     if (currentIndex.value > 0) {
-      currentIndex.value--;
-      setTimeout(() => {
-        subQuestionIndex.value = 0;
-      }, 300);
+      // currentIndex.value--;
+      // setTimeout(() => {
+      //   subQuestionIndex.value = 0;
+      // }, 300);
+    }
+    if (currentQuestion.value.subQuestions && currentQuestion.value.subQuestions.length > 0) {
+      if (subQuestionIndex.value > 0) {
+        subQuestionIndex.value--;
+      } else {
+        currentIndex.value--;
+      }
+    } else {
+      if (currentIndex.value > 0) {
+        currentIndex.value--;
+        // 如果上一个题是子题,那么,默认选中最后一个子题
+        const prevQuestion = questionList.value[currentIndex.value - 1];
+        if (prevQuestion && prevQuestion.subQuestions && prevQuestion.subQuestions.length > 0) {
+          subQuestionIndex.value = prevQuestion.subQuestions.length - 1;
+        }
+      }
     }
-    // if (currentQuestion.value.subQuestions && currentQuestion.value.subQuestions.length > 0) {
-    //   if (subQuestionIndex.value > 0) {
-    //     subQuestionIndex.value--;
-    //   } else {
-    //     currentIndex.value--;
-    //   }
-    // } else {
-    //   if (currentIndex.value > 0) {
-    //     currentIndex.value--;
-    //     // 如果上一个题是子题,那么,默认选中最后一个子题
-    //     const prevQuestion = questionList.value[currentIndex.value - 1];
-    //     if (prevQuestion.subQuestions && prevQuestion.subQuestions.length > 0) {
-    //       subQuestionIndex.value = prevQuestion.subQuestions.length - 1;
-    //     }
-    //   }
-    // }
   }
   // 快速下一题
   const nextQuestionQuickly = () => {
-    if (!nextEnable.value) {
+    if (!nextEnable) {
       return;
     }
     swiperDuration.value = 0;
@@ -486,12 +490,16 @@ export const useExam = () => {
         answers: [],
         isMark: false,
         isNotKnow: false,
+        hasParsed: false,
+        showParse: false,
         subQuestions: item.subQuestions.map(subItem => {
           return {
             ...subItem,
             answers: [],
             isMark: false,
-            isNotKnow: false
+            isNotKnow: false,
+            hasParsed: false,
+            showParse: false
           }
         }),
         options: item.options.map(option => {
@@ -499,7 +507,9 @@ export const useExam = () => {
             ...option,
             isAnswer: false,
             isCorrect: false,
-            isSelected: false
+            isSelected: false,
+            isMissed: false,
+            isIncorrect: false
           }
         })
       }
@@ -525,6 +535,8 @@ export const useExam = () => {
   //   immediate: false
   // });
   watch([() => currentIndex.value, () => subQuestionIndex.value], (val) => {
+    console.log('currentIndex.value', currentIndex.value)
+    console.log('subQuestionIndex.value', subQuestionIndex.value)
     const qs = questionList.value[val[0]];
     virtualCurrentIndex.value = qs.index + qs.offset + val[1];
   }, {

+ 10 - 2
src/pagesStudy/pages/exam-start/components/exam-toolbar.vue

@@ -26,12 +26,12 @@
         <view class="w-48 h-48 flex items-center justify-center">
           <uv-icon name="info-circle" size="22" />
         </view>
-        <text class="mt-6 text-24 text-fore-subcontent">题纠错</text>
+        <text class="mt-6 text-24 text-fore-subcontent">题纠错</text>
       </view>
     </view>
   </ie-safe-toolbar>
   <exam-stats-card ref="examStatsCardRef" @submit="handleSubmit" />
-  <question-correct-popup ref="questionCorrectPopupRef" @close="startTiming" />
+  <question-correct-popup ref="questionCorrectPopupRef" @close="beforeClose" />
 </template>
 <script lang="ts" setup>
 import ExamStatsCard from './exam-stats-card.vue';
@@ -79,6 +79,14 @@ const handleFavorite = async () => {
     uni.$ie.showToast('取消收藏成功');
   }
 }
+const beforeClose = () => {
+  const { readonly } = examPageOptions;
+  // 避免在阅卷模式下点击纠错后开始计时
+  if (readonly) {
+    return;
+  }
+  startTiming();
+}
 const handleMark = () => {
   currentQuestion.value.isMark = !currentQuestion.value.isMark;
 };

+ 2 - 2
src/pagesStudy/pages/exam-start/components/question-correct-popup.vue

@@ -9,13 +9,13 @@
         <view class="theme-ie popup-content box-border bg-white">
           <view class="popup-header">
             <view class="popup-header-title">
-              <text>题纠错</text>
+              <text>题纠错</text>
             </view>
           </view>
           <view class="px-30">
             <view class="h-60 flex items-center justify-between border-0 border-b border-solid border-[#efefef]">
               <view class="flex items-center gap-x-10">
-                <text>题编号</text>
+                <text>题编号</text>
                 <ie-image src="/static/image/icon-lock.png" custom-class="w-26 h-26" mode="aspectFit" />
               </view>
               <text class="text-fore-light">{{ questionId }}</text>

+ 3 - 2
src/pagesStudy/pages/exam-start/components/question-item copy.vue

@@ -59,7 +59,7 @@
           <view class="h-40 w-1 bg-back"></view>
           <view v-if="!isOnlySubjective" class="flex-1">
             <view class="text-34 font-bold" :class="[question.isCorrect ? 'text-[#2CC6A0]' : 'text-[#FF5B5C]']">
-              {{ question.answers.join('') || (question.isNotKnow ? '不会' : '未答') }}
+              {{ question.answers.join('') || (question.isNotKnow ? '不会' : '未答') }}
             </view>
             <view class="mt-4 text-26">我的答案</view>
           </view>
@@ -127,7 +127,8 @@ const emit = defineEmits<{
   (e: 'changeQuestion', question: Study.Question): void;
 }>();
 const isOnlySubjective = computed(() => {
-  return [EnumQuestionType.SUBJECTIVE, EnumQuestionType.SHORT_ANSWER, EnumQuestionType.ESSAY].includes(props.question.typeId);
+  // 除了单选、判断、多选题,其他题型都是主观题
+  return ![EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT, EnumQuestionType.MULTIPLE_CHOICE].includes(props.question.typeId);
 });
 const getStyleClass = (option: Study.QuestionOption) => {
   if (!props.readonly) {

+ 26 - 3
src/pagesStudy/pages/exam-start/components/question-item.vue

@@ -5,12 +5,13 @@
     <question-result :question="question" />
     <question-parse :question="question" />
     <view v-if="question.subQuestions.length">
-      <scroll-view class="w-full h-fit sticky top-0 bg-white py-10 z-1" scroll-x>
+      <scroll-view class="w-full h-fit sticky top-0 bg-white py-10 z-1" scroll-x :scroll-into-view="scrollIntoView"
+        :scroll-left="scrollLeft">
         <view class="flex items-center px-20 gap-x-20">
-          <view class="px-40 py-8 rounded-full"
+          <view class="px-40 py-8 rounded-full" :id="`sub_question_${getNo(subIndex)}`"
             :class="[subIndex === subQuestionIndex ? 'bg-[#EBF9FF] text-primary font-bold' : 'bg-back']"
             v-for="(subQuestion, subIndex) in question.subQuestions" @click="setSubQuestionIndex(subIndex)">
-            {{ question.index + question.offset + subIndex + 1 }}
+            {{ getNo(subIndex) }}
           </view>
         </view>
       </scroll-view>
@@ -35,9 +36,31 @@ const examData = inject(EXAM_DATA) || {} as ReturnType<typeof useExam>;
 const { subQuestionIndex, nextQuestion, isAllDone, currentSubQuestion, setSubQuestionIndex } = examData;
 const examAutoSubmit = inject(EXAM_AUTO_SUBMIT);
 
+const scrollIntoView = ref('')
+const scrollLeft = ref(0);
+
+watch(subQuestionIndex, (val) => {
+  scrollIntoView.value = '';
+  nextTick(() => {
+    scrollIntoView.value = `sub_question_${val}`;
+    if (props.question.subQuestions && props.question.subQuestions.length > 0) {
+      if (props.question.subQuestions[0].subIndex === val) {
+        scrollLeft.value = -1;
+        setTimeout(() => {
+          scrollLeft.value = 0;
+        }, 0);
+      }
+    }
+  });
+}, {
+  immediate: false
+});
 
 const props = defineProps<{
   question: Study.Question;
 }>();
+const getNo = (subIndex: number) => {
+  return props.question.index + props.question.offset + subIndex + 1;
+}
 </script>
 <style lang="scss" scoped></style>

+ 27 - 10
src/pagesStudy/pages/exam-start/components/question-options.vue

@@ -28,7 +28,8 @@
       <view class="question-option-content text-fore-light">不会</view>
     </view>
     <!-- 用于多选题手动提交,背题模式才有 -->
-    <view class="mt-40" v-if="practiceSettings.reviewMode === EnumReviewMode.DURING_ANSWER && isMultipleChoice">
+    <view class="mt-40"
+      v-if="practiceSettings.reviewMode === EnumReviewMode.DURING_ANSWER && isMultipleChoice && !question.hasParsed">
       <ie-button type="primary" size="mini" :round="4" :shadow="false" custom-class="w-160"
         @click="handleSubmit">提交</ie-button>
     </view>
@@ -70,7 +71,8 @@ const isReadOnly = computed(() => {
   return false;
 });
 const isOnlySubjective = computed(() => {
-  return [EnumQuestionType.SUBJECTIVE, EnumQuestionType.SHORT_ANSWER, EnumQuestionType.ESSAY].includes(props.question.typeId);
+  // 除了单选、判断、多选题,其他题型都是主观题
+  return ![EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT, EnumQuestionType.MULTIPLE_CHOICE].includes(props.question.typeId);
 });
 const isMultipleChoice = computed(() => {
   return props.question.typeId === EnumQuestionType.MULTIPLE_CHOICE;
@@ -121,16 +123,22 @@ const handleSubmit = () => {
 
 const handleNotKnow = () => {
   props.question.answers = [];
-  props.question.isNotKnow = !props.question.isNotKnow;
+  props.question.isNotKnow = true;
   props.question.hasParsed = true;
   handleNext();
 }
 
 const handleNext = () => {
   // 如果是正常的练习,默认下一题,如果是背题模式,需要根据是否自动下一题来决定
+  console.log('handleNext')
   if (practiceSettings.value.reviewMode === EnumReviewMode.DURING_ANSWER) {
+    console.log(1)
     if (practiceSettings.value.autoNext) {
-      nextQuestion();
+      console.log(2)
+      if (props.question.typeId !== EnumQuestionType.MULTIPLE_CHOICE) {
+        console.log(3)
+        nextQuestion();
+      }
     }
   } else {
     nextQuestion();
@@ -140,7 +148,7 @@ const handleNext = () => {
   }
 }
 
-const handleSelect = (option: Study.QuestionOption) => {
+const handleSelect = async (option: Study.QuestionOption) => {
   if (isReadOnly.value) {
     return;
   }
@@ -150,10 +158,24 @@ const handleSelect = (option: Study.QuestionOption) => {
     EnumQuestionType.SUBJECTIVE,
     EnumQuestionType.SHORT_ANSWER,
     EnumQuestionType.ESSAY,
+    EnumQuestionType.FILL_IN_THE_BLANK,
     EnumQuestionType.ANALYSIS
   ].includes(props.question.typeId)) {
     props.question.answers = [option.no];
+    props.question.hasParsed = true;
+    await nextTick();
+    if (practiceSettings.value.reviewMode === EnumReviewMode.DURING_ANSWER) {
+      if (practiceSettings.value.autoNext) {
+        console.log('props.question.isCorrect', props.question.isCorrect)
+        if (props.question.isCorrect) {
+          handleNext();
+        }
+      }
+    } else {
+      handleNext();
+    }
   } else if (props.question.typeId === EnumQuestionType.MULTIPLE_CHOICE) {
+    // 多选题需要手动提交
     if (props.question.answers.includes(option.no)) {
       props.question.answers = props.question.answers.filter(item => item !== option.no);
     } else {
@@ -161,11 +183,6 @@ const handleSelect = (option: Study.QuestionOption) => {
     }
   }
   props.question.isNotKnow = false;
-  props.question.hasParsed = true;
-  // 多选题不自动切换下一题
-  if (props.question.typeId !== EnumQuestionType.MULTIPLE_CHOICE) {
-    handleNext();
-  }
 }
 
 const handleShowParse = () => {

+ 2 - 1
src/pagesStudy/pages/exam-start/components/question-parse.vue

@@ -38,7 +38,8 @@ const isReadOnly = computed(() => {
   return false;
 });
 const isOnlySubjective = computed(() => {
-  return [EnumQuestionType.SUBJECTIVE, EnumQuestionType.SHORT_ANSWER, EnumQuestionType.ESSAY].includes(props.question.typeId);
+  // 除了单选、判断、多选题,其他题型都是主观题
+  return ![EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT, EnumQuestionType.MULTIPLE_CHOICE].includes(props.question.typeId);
 });
 
 </script>

+ 5 - 2
src/pagesStudy/pages/exam-start/components/question-result.vue

@@ -3,6 +3,8 @@
     class="question-result mt-40 rounded-8 pt-60 pb-40 flex items-center text-center relative">
     <ie-image v-if="question.isCorrect" src="/pagesStudy/static/image/icon-answer-correct.png"
       class="absolute top-0 left-1/2 -translate-x-1/2 w-222 h-64" :class="{ 'top-30': isOnlySubjective }" />
+    <ie-image v-else-if="question.isNotAnswer" src="/pagesStudy/static/image/icon-answer-empty.png"
+      class="absolute top-0 left-1/2 -translate-x-1/2 w-240 h-104" mode="heightFix" :class="{ 'top-20': isOnlySubjective }" />
     <ie-image v-else src="/pagesStudy/static/image/icon-answer-incorrect.png"
       class="absolute top-0 left-1/2 -translate-x-1/2 w-240 h-64" :class="{ 'top-30': isOnlySubjective }" />
     <view v-if="!isOnlySubjective" class="flex-1">
@@ -12,7 +14,7 @@
     <view class="h-40 w-1 bg-back"></view>
     <view v-if="!isOnlySubjective" class="flex-1">
       <view class="text-34 font-bold" :class="[question.isCorrect ? 'text-[#2CC6A0]' : 'text-[#FF5B5C]']">
-        {{ question.answers.join('') || (question.isNotKnow ? '不会' : '未答') }}
+        {{ question.answers.join('') || (question.isNotKnow ? '不会' : '未答') }}
       </view>
       <view class="mt-4 text-26">我的答案</view>
     </view>
@@ -43,7 +45,8 @@ const isReadOnly = computed(() => {
   return false;
 });
 const isOnlySubjective = computed(() => {
-  return [EnumQuestionType.SUBJECTIVE, EnumQuestionType.SHORT_ANSWER, EnumQuestionType.ESSAY].includes(props.question.typeId);
+  // 除了单选、判断、多选题,其他题型都是主观题
+  return ![EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT, EnumQuestionType.MULTIPLE_CHOICE].includes(props.question.typeId);
 });
 </script>
 <style lang="scss" scoped>

+ 1 - 1
src/pagesStudy/pages/exam-start/exam-start.vue

@@ -63,7 +63,7 @@ const guideList = ref([
   {
     target: '#question-correct-btn',
     position: 'top',
-    msg: '[题纠错]\n点击题纠错,帮助我们改进题目'
+    msg: '[题纠错]\n点击题纠错,帮助我们改进题目'
   }
 ]);
 const guideIndex = ref(0);

+ 4 - 4
src/pagesStudy/pages/index/compoentns/index-banner.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="mx-30 mt-40">
-    <ie-image :is-oss="true" src="/banner/index-banner-4.png" :round="10" customClass="w-full h-178" mode="widthFix" />
-    <view class="mt-32 flex gap-x-30">
+    <!-- <ie-image :is-oss="true" src="/banner/index-banner-4.png" :round="10" customClass="w-full h-178" mode="widthFix" /> -->
+    <view class="flex gap-x-30">
       <view class="flex-1 rounded-12 bg-[#F0FFF2] py-40 pl-22 pr-8 flex items-center" @click="handleOpenPlan">
         <!-- /pagesStudy/pages/study-plan-edit/study-plan-edit -->
         <view class="flex-1">
@@ -16,10 +16,10 @@
       <view class="flex-1 rounded-12 bg-[#FFF6F0] py-40 pl-22 pr-8 flex items-center" @click="handleTest">
         <view class="flex-1">
           <view class="text-30 text-fore-title font-bold flex items-center">
-            <text class="mr-2">学情报告</text>
+            <text class="mr-2">教材同步练习</text>
             <uv-icon name="arrow-right" size="12" color="#808080" />
           </view>
-          <view class="mt-4 text-24 text-fore-tip">可视化学习成果</view>
+          <view class="mt-4 text-24 text-fore-tip">随课即时练习</view>
         </view>
         <ie-image :is-oss="true" src="/study-bg4.png" customClass="w-92 h-92" />
       </view>

+ 42 - 42
src/pagesStudy/pages/simulation-analysis/components/exam-stat.vue

@@ -38,7 +38,7 @@
       </view>
     </view>
     <view class="mt-30 grid grid-cols-5 gap-x-10 gap-y-30 place-items-center">
-      <view class="question-item" v-for="item in flatQuestionList" :key="item.id"
+      <view class="question-item" v-for="item in filteredQuestionList" :key="item.id"
         :class="[item.isNotAnswer ? 'is-unanswered' : (item.isCorrect ? 'is-correct' : 'is-incorrect')]"
         @click="handleDetail(item)">
         <view class="text-36 font-bold">{{ item.virtualIndex + 1 }}</view>
@@ -52,7 +52,7 @@ import { useTransferPage } from '@/hooks/useTransferPage';
 import { useExam } from '@/composables/useExam';
 import { Study } from '@/types';
 const { transferTo } = useTransferPage();
-const { isQuestionCorrect, questionList, setQuestionList, flatQuestionList } = useExam();
+const { setQuestionList, flatQuestionList } = useExam();
 const emit = defineEmits<{
   (e: 'detail', item: Study.Question): void;
 }>();
@@ -60,7 +60,7 @@ const props = defineProps<{
   data: Study.Examinee;
   showStats: boolean;
 }>();
-interface QuestionItem extends Study.ExamineeQuestion {
+interface QuestionItem extends Study.Question {
   originalIndex?: number;
   isRight?: boolean;
   isNotAnswer?: boolean;
@@ -75,45 +75,45 @@ const totalQuestions = computed(() => {
   return (flatQuestionList.value || []).length;
 });
 const filterTypes = ref<('correct' | 'incorrect' | 'unanswered')[]>(['correct', 'incorrect', 'unanswered']);
-// const questionList = computed(() => {
-//   const list: QuestionItem[] = [];
-//   let offset = 0;
-//   (props.data.questions || []).forEach((item, index) => {
-//     if (item.subQuestions && item.subQuestions.length > 0) {
-//       item.subQuestions.forEach((subItem, subIndex) => {
-//         offset++;
-//         list.push({
-//           ...subItem,
-//           originalIndex: index + subIndex + 1, // 保存原始序号
-//           isRight: isQuestionCorrect(subItem),
-//           isNotAnswer: !subItem.answers.filter(item => item !== ' ').length
-//         });
-//       });
-//     } else {
-//       list.push({
-//         ...item,
-//         originalIndex: index + offset + 1, // 保存原始序号
-//         isRight: isQuestionCorrect(item),
-//         isNotAnswer: !item.answers.filter(item => item !== ' ').length
-//       });
-//     }
-//   });
-//   return list.filter((item: QuestionItem) => {
-//     // 判断是否答对
-//     if (item.isRight && filterTypes.value.includes('correct')) {
-//       return true;
-//     }
-//     // 判断是否答错(答了但是错了)
-//     if (!item.isRight && !item.isNotAnswer && filterTypes.value.includes('incorrect')) {
-//       return true;
-//     }
-//     // 判断是否未答
-//     if (item.isNotAnswer && filterTypes.value.includes('unanswered')) {
-//       return true;
-//     }
-//     return false;
-//   });
-// });
+const filteredQuestionList = computed(() => {
+  const list: QuestionItem[] = [];
+  let offset = 0;
+  (flatQuestionList.value || []).forEach((item, index) => {
+    if (item.subQuestions && item.subQuestions.length > 0) {
+      item.subQuestions.forEach((subItem, subIndex) => {
+        offset++;
+        list.push({
+          ...subItem,
+          originalIndex: index + subIndex + 1, // 保存原始序号
+          // isRight: isQuestionCorrect(subItem),
+          // isNotAnswer: !subItem.answers.filter(item => item !== ' ').length
+        });
+      });
+    } else {
+      list.push({
+        ...item,
+        originalIndex: index + offset + 1, // 保存原始序号
+        // isRight: isQuestionCorrect(item),
+        // isNotAnswer: !item.answers.filter(item => item !== ' ').length
+      });
+    }
+  });
+  return list.filter((item: QuestionItem) => {
+    // 判断是否答对
+    if (item.isCorrect && filterTypes.value.includes('correct')) {
+      return true;
+    }
+    // 判断是否答错(答了但是错了)
+    if (!item.isCorrect && !item.isNotAnswer && filterTypes.value.includes('incorrect')) {
+      return true;
+    }
+    // 判断是否未答
+    if (item.isNotAnswer && filterTypes.value.includes('unanswered')) {
+      return true;
+    }
+    return false;
+  });
+});
 
 const handleFilter = (type: 'correct' | 'incorrect' | 'unanswered') => {
   if (filterTypes.value.includes(type)) {

+ 50 - 0
src/pagesStudy/pages/targeted-setting/components/directed-school-item.vue

@@ -0,0 +1,50 @@
+<template>
+  <view class="bg-white rounded-10 py-24 pl-30 pr-20 relative flex items-center gap-x-16 mb-20"
+    :class="{ 'border border-solid border-primary': active }">
+    <view class="flex-1 min-w-1 flex items-center gap-x-20">
+      <ie-image :src="data.universityLogo" custom-class="w-96 h-96" mode="aspectFill" />
+      <view class="flex-1 min-w-1">
+        <view class="text-28 text-fore-title font-bold">{{ data.universityName }}</view>
+        <view class="mt-8 w-fit text-20 text-primary border border-solid border-primary rounded-4 px-10 py-4">
+          {{ data.majorName }}
+        </view>
+        <view class="mt-8 text-20 text-fore-light">{{ data.majorAncestors }}</view>
+      </view>
+      <view class="w-fit flex flex-col items-end">
+        <view class="mb-30" @click="handleDelete">
+          <uv-icon name="trash" color="#bbb" size="24" />
+        </view>
+        <view v-if="active"
+          class="w-120 flex items-center justify-between gap-x-4 text-white bg-primary rounded-full px-8 py-4 box-border">
+          <ie-image src="/pagesStudy/static/image/icon-check-white.png" custom-class="w-24 h-24" />
+          <text class="text-24">学习中</text>
+        </view>
+        <view v-else
+          class="w-120 flex items-center justify-between gap-x-10 text-primary bg-[#EEF8FD] rounded-full px-8 py-4 border border-solid border-primary box-border"
+          @click="handleSetDirectedSchool">
+          <ie-image src="/pagesStudy/static/image/icon-switch.png" custom-class="w-28 h-28" mode="aspectFill" />
+          <text class="text-24">切换</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+<script lang="ts" setup>
+import { DirectedSchool } from '@/types/study';
+
+const props = defineProps<{
+  data: DirectedSchool;
+  active: boolean;
+}>();
+const emit = defineEmits<{
+  (e: 'choose', data: DirectedSchool): void;
+  (e: 'delete', data: DirectedSchool): void;
+}>();
+const handleDelete = () => {
+  emit('delete', props.data);
+}
+const handleSetDirectedSchool = () => {
+  emit('choose', props.data);
+}
+</script>
+<style lang="scss" scoped></style>

+ 115 - 0
src/pagesStudy/pages/targeted-setting/targeted-setting copy.vue

@@ -0,0 +1,115 @@
+<template>
+  <ie-page bg-color="#F6F8FA" :fix-height="true">
+    <ie-navbar title="定向刷题" />
+    <view class="p-32 bg-white flex items-center justify-between">
+      <view class="">
+        <text class="text-32 text-fore-title font-bold">添加定向院校</text>
+        <text class="ml-10 text-32 text-fore-title font-bold">({{ directedSchoolList.length }}/3)</text>
+      </view>
+    </view>
+    <view class="px-48 pt-52 pb-32 flex items-center justify-between">
+      <view class="text-32 text-fore-title font-bold">我的定向院校</view>
+    </view>
+    <view class="relative">
+      <view class="px-30">
+        <l-drag ref="dragRef" :list="sortList" :column="1" gridHeight="104px" :touchHandle="touchHandle" ghost handle
+          @change="changeSort">
+          <!-- // 每一项的插槽 grid 的 content 您传入的数据 -->
+          <template #grid="{ active, content, index }">
+            <!-- // grid.active 是否为当前拖拽项目 根据自己需要写样式 -->
+            <view class="bg-white rounded-10 py-28 pl-30 pr-20 relative flex items-center gap-x-16 mb-20"
+              :class="{ 'border border-solid border-primary': active }">
+              <view
+                class="w-44 text-64 text-primary font-bold absolute top-6 left-14 italic leading-[1] opacity-30 z-1">
+                {{ index + 1 }}
+              </view>
+              <view slot="handle" @touchstart="touchHandle = true" @touchend="touchHandle = false">
+                <ie-image src="/pagesStudy/static/image/icon-drag.png" custom-class="w-46 h-46" mode="aspectFill" />
+              </view>
+              <view class="flex-1 min-w-1 flex items-center gap-x-20">
+                <ie-image :src="content.universityLogo" custom-class="w-96 h-96" mode="aspectFill" />
+                <view class="flex-1 min-w-1">
+                  <view class="text-28 text-fore-title font-bold">{{ content.universityName }}</view>
+                  <view class="mt-8 w-fit text-20 text-primary border border-solid border-primary rounded-4 px-10 py-4">
+                    {{ content.majorName }}
+                  </view>
+                  <view class="flex items-center justify-between text-20">
+                    <view class="mt-8  text-fore-light">{{ content.majorAncestors }}</view>
+                    <view v-if="index === 0"
+                      class="flex items-center gap-x-4 text-white bg-gradient-to-r from-[#FED448] to-[#F9942F] rounded-full px-12 py-4">
+                      <ie-image src="/pagesStudy/static/image/icon-check-white.png" custom-class="w-24 h-24" />
+                      <text>定向学习中</text>
+                    </view>
+                    <view v-else class="flex items-center gap-x-4 text-22 text-primary px-12 py-4"
+                      @click="handleSetDirectedSchool(content, index)">
+                      <text>设置定向</text>
+                    </view>
+                  </view>
+                </view>
+              </view>
+            </view>
+          </template>
+          <template #ghots></template>
+        </l-drag>
+      </view>
+      <view v-if="sortList.length === 0" class="flex-1 flex flex-col items-center justify-center gap-y-50">
+        <ie-image src="/pagesStudy/static/image/icon-empty.png" custom-class="w-364 h-252 mx-auto" mode="aspectFill" />
+        <text class="text-30 text-fore-light text-center">目前暂无定向院校~</text>
+      </view>
+    </view>
+    <ie-safe-toolbar v-if="directedSchoolList.length < 3" :height="84" :shadow="false">
+      <view class="px-46 pt-24">
+        <ie-button type="primary" @click="handleAdd">添加</ie-button>
+      </view>
+    </ie-safe-toolbar>
+  </ie-page>
+</template>
+
+<script lang="ts" setup>
+import { useTransferPage } from '@/hooks/useTransferPage';
+import { DirectedSchool } from '@/types/study';
+import { useUserStore } from '@/store/userStore';
+
+const userStore = useUserStore();
+const { transferTo } = useTransferPage();
+const loading = ref(true);
+const sortList = ref<DirectedSchool[]>([]);
+const touchHandle = ref(false)
+const { directedSchoolList } = toRefs(userStore);
+const handleSetDirectedSchool = async (item: DirectedSchool, index: number) => {
+  // 将该院校设置为第一个
+  const list = [...directedSchoolList.value];
+  list.splice(index, 1);
+  list.unshift(item);
+  save(list);
+}
+const dragRef = ref();
+const handleAdd = () => {
+  transferTo('/pagesStudy/pages/targeted-add/targeted-add', {
+    data: {}
+  });
+}
+
+const changeSort = (e: any) => {
+  const list = e.map((item: any) => item.content);
+  save(list);
+}
+const save = async (list: DirectedSchool[]) => {
+  uni.$ie.showLoading();
+  await userStore.saveDirectedSchoolList(list);
+  await refresh();
+  uni.$ie.hideLoading();
+  uni.$ie.showSuccess('设置成功');
+  loading.value = false;
+}
+const refresh = async () => {
+  sortList.value = [...directedSchoolList.value];
+}
+onShow(() => {
+  nextTick(() => {
+    refresh();
+  });
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 40 - 59
src/pagesStudy/pages/targeted-setting/targeted-setting.vue

@@ -4,60 +4,31 @@
     <view class="p-32 bg-white flex items-center justify-between">
       <view class="">
         <text class="text-32 text-fore-title font-bold">添加定向院校</text>
-        <text class="ml-10 text-32 text-fore-light">({{ directedSchoolList.length }}/3)</text>
+        <text class="ml-10 text-32 text-fore-title font-bold">({{ directedSchoolList.length }}/3)</text>
       </view>
-      <view class="text-24 text-fore-light">添加后无法更改</view>
     </view>
-    <view class="px-48 pt-52 pb-32 flex items-center justify-between">
-      <view class="text-32 text-fore-title font-bold">我的定向院校</view>
+    <view class="px-48 pt-52 pb-20 flex items-center justify-between">
+      <view class="text-28 text-fore-title font-bold">当前学习目标</view>
     </view>
-    <view class="relative">
+    <template v-if="directedSchoolList.length > 0">
       <view class="px-30">
-        <l-drag ref="dragRef" :list="sortList" :column="1" gridHeight="104px" :touchHandle="touchHandle" ghost handle
-          @change="changeSort">
-          <!-- // 每一项的插槽 grid 的 content 您传入的数据 -->
-          <template #grid="{ active, content, index }">
-            <!-- // grid.active 是否为当前拖拽项目 根据自己需要写样式 -->
-            <view class="bg-white rounded-10 py-28 pl-30 pr-20 relative flex items-center gap-x-16 mb-20"
-              :class="{ 'border border-solid border-primary': active }">
-              <view
-                class="w-44 text-64 text-primary font-bold absolute top-6 left-14 italic leading-[1] opacity-30 z-1">
-                {{ index + 1 }}
-              </view>
-              <view slot="handle" @touchstart="touchHandle = true" @touchend="touchHandle = false">
-                <ie-image src="/pagesStudy/static/image/icon-drag.png" custom-class="w-46 h-46" mode="aspectFill" />
-              </view>
-              <view class="flex-1 min-w-1 flex items-center gap-x-20">
-                <ie-image :src="content.universityLogo" custom-class="w-96 h-96" mode="aspectFill" />
-                <view class="flex-1 min-w-1">
-                  <view class="text-28 text-fore-title font-bold">{{ content.universityName }}</view>
-                  <view class="mt-8 w-fit text-20 text-primary border border-solid border-primary rounded-4 px-10 py-4">
-                    {{ content.majorName }}
-                  </view>
-                  <view class="flex items-center justify-between text-20">
-                    <view class="mt-8  text-fore-light">{{ content.majorAncestors }}</view>
-                    <view v-if="index === 0"
-                      class="flex items-center gap-x-4 text-white bg-gradient-to-r from-[#FED448] to-[#F9942F] rounded-full px-12 py-4">
-                      <ie-image src="/pagesStudy/static/image/icon-check-white.png" custom-class="w-24 h-24" />
-                      <text>定向学习中</text>
-                    </view>
-                    <view v-else class="flex items-center gap-x-4 text-22 text-primary px-12 py-4"
-                      @click="handleSetDirectedSchool(content, index)">
-                      <text>设置定向</text>
-                    </view>
-                  </view>
-                </view>
-              </view>
-            </view>
-          </template>
-          <template #ghots></template>
-        </l-drag>
+        <directed-school-item :data="currentDirectedSchool" :active="true" @choose="handleChoose"
+          @delete="handleDelete" />
       </view>
-      <view v-if="sortList.length === 0" class="flex-1 flex flex-col items-center justify-center gap-y-50">
-        <ie-image src="/pagesStudy/static/image/icon-empty.png" custom-class="w-364 h-252 mx-auto" mode="aspectFill" />
-        <text class="text-30 text-fore-light text-center">目前暂无定向院校~</text>
+      <view v-if="directedSchoolList.length > 1" class="px-48 pt-40 pb-30">
+        <view class="text-28 text-fore-title font-bold">其他备选目标</view>
+        <view class="mt-4 text-24 text-fore-light">切换定向学习院校后将影响为你推荐的学习内容和题目!</view>
       </view>
+      <view class="px-30">
+        <directed-school-item v-for="(item, index) in otherDirectedSchoolList" :key="index" :data="item"
+          :active="false" @choose="handleChoose" @delete="handleDelete" />
+      </view>
+    </template>
+    <view v-if="sortList.length === 0" class="flex-1 flex flex-col items-center justify-center gap-y-50">
+      <ie-image src="/pagesStudy/static/image/icon-empty.png" custom-class="w-364 h-252 mx-auto" mode="aspectFill" />
+      <text class="text-30 text-fore-light text-center">目前暂无定向院校~</text>
     </view>
+
     <ie-safe-toolbar v-if="directedSchoolList.length < 3" :height="84" :shadow="false">
       <view class="px-46 pt-24">
         <ie-button type="primary" @click="handleAdd">添加</ie-button>
@@ -67,6 +38,7 @@
 </template>
 
 <script lang="ts" setup>
+import DirectedSchoolItem from './components/directed-school-item.vue';
 import { useTransferPage } from '@/hooks/useTransferPage';
 import { DirectedSchool } from '@/types/study';
 import { useUserStore } from '@/store/userStore';
@@ -75,26 +47,35 @@ const userStore = useUserStore();
 const { transferTo } = useTransferPage();
 const loading = ref(true);
 const sortList = ref<DirectedSchool[]>([]);
-const touchHandle = ref(false)
 const { directedSchoolList } = toRefs(userStore);
-const handleSetDirectedSchool = async (item: DirectedSchool, index: number) => {
-  // 将该院校设置为第一个
-  const list = [...directedSchoolList.value];
-  list.splice(index, 1);
-  list.unshift(item);
-  save(list);
-}
-const dragRef = ref();
+const currentDirectedSchool = computed(() => directedSchoolList.value[0]);
+const otherDirectedSchoolList = computed(() => directedSchoolList.value.slice(1));
+
 const handleAdd = () => {
   transferTo('/pagesStudy/pages/targeted-add/targeted-add', {
     data: {}
   });
 }
-
-const changeSort = (e: any) => {
-  const list = e.map((item: any) => item.content);
+const handleChoose = (data: DirectedSchool) => {
+  const otherList = directedSchoolList.value.filter(item => item.code !== data.code);
+  const list = [data, ...otherList];
   save(list);
 }
+const handleDelete = (data: DirectedSchool) => {
+  uni.$ie.showModal({
+    title: '提示',
+    content: '如删除该定向记录,关联的数据有变化,是否继续?',
+    showCancel: true,
+    confirmText: '确定',
+    cancelText: '取消'
+  }).then((confirm: boolean) => {
+    if (confirm) {
+      const list = [...directedSchoolList.value];
+      list.splice(list.indexOf(data), 1);
+      save(list);
+    }
+  });
+}
 const save = async (list: DirectedSchool[]) => {
   uni.$ie.showLoading();
   await userStore.saveDirectedSchoolList(list);

BIN
src/pagesStudy/static/image/icon-answer-empty.png


BIN
src/pagesStudy/static/image/icon-switch.png


+ 10 - 8
src/pagesSystem/pages/bind-profile/bind-profile.vue

@@ -160,16 +160,18 @@ const handleSchoolSelect = () => {
   // }
   transferTo('/pagesSystem/pages/school-select/school-select', {
     data: {
-      examType: form.value.examType || examTypeForm.value.examType,
+      examType: examTypeForm.value.examType,
     }
   }).then(res => {
-    const school = res as SchoolItem;
-    form.value.schoolId = school.id;
-    form.value.schoolName = school.name;
-    console.log(form.value)
-    form.value.classId = undefined;
-    classList.value = [];
-    handleGetClassList();
+    if (res) {
+      const school = res as SchoolItem;
+      form.value.schoolId = school.id;
+      form.value.schoolName = school.name;
+      console.log(form.value)
+      form.value.classId = undefined;
+      classList.value = [];
+      handleGetClassList();
+    }
   });
 }
 const handleGetClassList = () => {