浏览代码

分析页面对接

shmily1213 3 周之前
父节点
当前提交
0bf2dad067

+ 23 - 1
package.json

@@ -6,9 +6,31 @@
     "dev:h5": "uni",
     "dev:mp-weixin": "uni -p mp-weixin",
     "build:custom": "uni build -p",
-    "build:h5": "uni build",
+    "build": "uni build",
     "build:mp-weixin": "uni build -p mp-weixin"
   },
+  "uni-app": {
+    "scripts": {
+      "dev": {
+        "title": "测试环境",
+        "browser": "",
+        "env": {
+          "UNI_PLATFORM": "h5",
+          "IE_ENV": "development"
+        },
+        "define": {}
+      },
+      "pro": {
+        "title": "正式环境",
+        "browser": "",
+        "env": {
+          "UNI_PLATFORM": "h5",
+          "IE_ENV": "production"
+        },
+        "define": {}
+      }
+    }
+  },
   "dependencies": {
     "@dcloudio/uni-app": "3.0.0-4070620250821001",
     "@dcloudio/uni-app-harmony": "3.0.0-4070620250821001",

+ 1 - 1
src/api/flyio.ts

@@ -3,7 +3,7 @@ import Fly from "flyio/dist/npm/wx"
 import config from "@/config";
 import { useUserStore } from '@/store/userStore';
 const { serverBaseUrl } = config;
-
+console.log(serverBaseUrl)
 const requestConfig = {
   baseURL: serverBaseUrl,
   timeout: 10000,

+ 1 - 1
src/api/modules/study.ts

@@ -103,7 +103,7 @@ export function commitExamineePaper(params: ExamPaperSubmit) {
  * 获取试卷结果
  */
 export function getExamineeResult(examineeId: number) {
-  return flyio.get('/front/exam/loadExaminee', { examineeId }) as Promise<ApiResponse<any>>;
+  return flyio.get('/front/exam/loadExaminee', { examineeId }) as Promise<ApiResponse<Examinee>>;
 }
 
 

+ 12 - 1
src/components/mx-question-content/mx-question-content.vue

@@ -8,7 +8,7 @@
                                :class="groupClass" @change="emits('update:modelValue', $event)">
                         <component :is="optComponent" v-for="op in optionsWithCode" :name="op.code"
                                    :class="calcOptionClass(op)">
-                            <view v-html="op.option" :class="textClass"/>
+                            <view v-html="formatOption(op.option)" :class="textClass"/>
                         </component>
                     </component>
                 </slot>
@@ -163,6 +163,17 @@ const tryRandomAnswer = () => {
     }
 }
 
+const formatOption = (option) => {
+  // 替换 option 中可能存在的A.这样的序号
+    return option.replace(/A\./g, '')
+    .replace(/B\./g, '')
+    .replace(/C\./g, '')
+    .replace(/D\./g, '')
+    .replace(/E\./g, '')
+    .replace(/F\./g, '')
+    .replace(/G\./g, '')
+}
+
 defineExpose({tryRandomAnswer})
 </script>
 

+ 4 - 4
src/components/mx-question/components/mx-question-parse.vue

@@ -35,10 +35,10 @@ const {updateMathJax} = useMathJaxService()
 const {disabled: disabledMathJax} = useInjectMathJaxSwitch()
 
 const sysAnswer = computed(() => {
-    if (array(props.question.answers) && !empty(props.question.answers)) {
-        return props.question.answers[0]
-    }
-    return ''
+    // if (array(props.question.answers) && !empty(props.question.answers)) {
+    //     return props.question.answers[0]
+    // }
+    return props.question.answer1 || ''
 })
 
 onMounted(() => updateMathJaxIfNeeded())

+ 13 - 14
src/config.ts

@@ -8,18 +8,17 @@ const config = {
   responseErrorCatch: true,
 };
 
-if (process.env.NODE_ENV === "development") {
-  // config.serverBaseUrl = "https://ieh5.jinlizhiyuan.com/prod-api";
-  // config.serverBaseUrl = "https://www.dz1kt.com/admin/prod-api";
-  config.serverBaseUrl = "https://dz.shineking.top/prod-api";
-  config.paySiteUrl = 'https://www.dz1kt.com/h5';
-  config.responseErrorCatch = true; // API 返回错误时,是否控制台打印
-} else {
-  // config.serverBaseUrl = "https://ieh5.jinlizhiyuan.com/prod-api";
-  config.serverBaseUrl = "https://www.dz1kt.com/admin/prod-api";
-  // config.serverBaseUrl = "https://dz.shineking.top/prod-api";
-  config.paySiteUrl = 'https://www.dz1kt.com/h5';
-  config.responseErrorCatch = true;
+export const env = {
+  development: {
+    serverBaseUrl: 'https://dz.shineking.top/prod-api',
+    paySiteUrl: 'https://www.dz1kt.com/h5'
+  },
+  production: {
+    serverBaseUrl: 'https://www.dz1kt.com/admin/prod-api',
+    paySiteUrl: 'https://www.dz1kt.com/h5'
+  }
 }
-
-export default config;
+export default {
+  ...config,
+  ...env[process.env.IE_ENV as keyof typeof env]
+};

+ 5 - 3
src/manifest.json

@@ -1,6 +1,6 @@
 {
     "name" : "单招一卡通",
-    "appid" : "__UNI__6934FFB",
+    "appid" : "__UNI__37DEDFB",
     "description" : "单招一卡通,升学一路通!",
     "versionName" : "1.0.0",
     "versionCode" : "100",
@@ -54,8 +54,10 @@
         "singlePage" : {
             "navigationBarFit" : "float"
         },
-        "optimization":{"subPackages":true},
-        "mergeVirtualHostAttributes": true
+        "optimization" : {
+            "subPackages" : true
+        },
+        "mergeVirtualHostAttributes" : true
     },
     "mp-alipay" : {
         "usingComponents" : true

+ 1 - 1
src/pagesOther/pages/topic-center/wrong-book/components/wrong-book-item.vue

@@ -1,7 +1,7 @@
 <template>
     <view class="bg-white mx-card">
         <view class="p-30">
-            <mx-question-content v-model="question.answer" :question="question" :sys-answer="question.answer"
+            <mx-question-content v-model="question.answer" :question="question" :sys-answer="question.answer1"
                                  disabled readonly/>
             <mx-question-parse :question="question" class="mt-40"/>
         </view>

+ 1 - 1
src/pagesStudy/pages/index/compoentns/index-exam-record.vue

@@ -21,7 +21,7 @@ const loadData = async () => {
   const { data } = await getSimulatedRecord();
   list.value = data || [];
 }
-onLoad(() => {
+onShow(() => {
   loadData();
 });
 </script>

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

@@ -3,20 +3,20 @@
     <view class="text-30 text-fore-title font-bold">答题情况</view>
     <view class="mt-20 flex items-center">
       <view class="flex-1 text-center">
-        <view class="text-40 text-fore-title font-bold">100</view>
-        <view class="mt-5 text-28 text-fore-light">题量</view>
+        <view class="text-40 text-fore-title font-bold">{{ questionList.length }}</view>
+        <view class="mt-5 text-28 text-fore-light">题量</view>
       </view>
       <view class="flex-1 text-center">
-        <view class="text-40 text-fore-title font-bold">68</view>
-        <view class="mt-5 text-28 text-fore-light">答对</view>
+        <view class="text-40 text-fore-title font-bold">{{ paperInfo.score }}</view>
+        <view class="mt-5 text-28 text-fore-light">总分</view>
       </view>
       <view class="flex-1 text-center">
-        <view class="text-40 text-fore-title font-bold">68</view>
-        <view class="mt-5 text-28 text-fore-light">答对</view>
+        <view class="text-40 text-fore-title font-bold">{{ getScore }}</view>
+        <view class="mt-5 text-28 text-fore-light">得分</view>
       </view>
       <view class="flex-1 text-center">
-        <view class="text-40 text-fore-title font-bold">68</view>
-        <view class="mt-5 text-28 text-fore-light">答对</view>
+        <view class="text-40 text-fore-title font-bold">{{ rightRate }}%</view>
+        <view class="mt-5 text-28 text-fore-light">正确率</view>
       </view>
     </view>
     <view class="h-1 bg-[#E6E6E6] my-20"></view>
@@ -29,14 +29,55 @@
       <view class="text-20 text-fore-subtitle">答错</view>
     </view>
     <view class="mt-20 grid grid-cols-5 gap-x-10 gap-y-30 place-items-center">
-      <view class="question-item" v-for="(item, index) in list" :key="item.id"
-        :class="item.type === 0 ? 'unanswered' : item.type === 1 ? 'correct' : 'incorrect'">
+      <view class="question-item" v-for="(item, index) in questionList" :key="item.id"
+        :class="[item.isNotAnswer ? 'is-unanswered' : (item.isRight ? 'is-correct' : 'is-incorrect')]">
         <view class="text-36 font-bold">{{ index + 1 }}</view>
       </view>
     </view>
   </view>
 </template>
 <script lang="ts" setup>
+import { EnumQuestionType } from '@/common/enum';
+import { Study } from '@/types';
+
+const props = defineProps<{
+  data: Study.Examinee;
+}>();
+const paperInfo = computed(() => {
+  return props.data.paperInfo || {};
+});
+const rightTotal = computed(() => {
+  return questionList.value.filter(item => item.isRight).length;
+});
+const rightRate = computed(() => {
+  const { totalCount = 0, wrongCount = 0 } = props.data;
+  return Math.round((totalCount - wrongCount) / totalCount * 100) || 0;
+});
+const getScore = computed(() => {
+  return questionList.value.reduce((sum, item) => {
+    return sum + item.score || 0;
+  }, 0);
+})
+const wrongTotal = computed(() => {
+  return questionList.value.filter(item => !item.isRight).length;
+});
+const questionList = computed(() => {
+  return (props.data.questions || []).map(item => {
+    return {
+      ...item,
+      isRight: judgeCorrect(item),
+      isNotAnswer: !item.answers.filter(item => item !== ' ').length
+    }
+  });
+});
+const judgeCorrect = (qs: Study.ExamineeQuestion) => {
+  if (qs.typeId === EnumQuestionType.SINGLE_CHOICE) {
+    return qs.answers.includes(qs.answer1);
+  } else if (qs.typeId === EnumQuestionType.MULTIPLE_CHOICE) {
+    const rightAnswers = qs.answer1.split('').filter(item => item !== ' ');
+    return rightAnswers.length === qs.answers.length && rightAnswers.every(item => qs.answers.includes(item));
+  }
+}
 type QuestionItem = {
   id: number;
   type: number;
@@ -57,17 +98,17 @@ for (let i = 0; i < 100; i++) {
   @apply w-80 h-80 rounded-full overflow-hidden flex items-center justify-center text-20 text-fore-subtitle;
 }
 
-.unanswered {
+.is-unanswered {
   background: #EEF4FA;
   color: #B3B3B3;
 }
 
-.correct {
+.is-correct {
   background: #E7FCF8;
   color: #2CC6A0;
 }
 
-.incorrect {
+.is-incorrect {
   background: #FEEDE9;
   color: #FF5B5C;
 }

+ 1 - 1
src/pagesStudy/pages/simulation-analysis/components/score-stat.vue

@@ -1,5 +1,5 @@
 <template>
-  <view class="px-16 py-20 bg-white rounded-15 mt-20">
+  <view class="sticky bottom-20 px-16 py-20 bg-white rounded-15 mt-20">
     <view class="text-30 text-fore-title font-bold">得分分布</view>
     <view class="mt-20 mx-10 flex items-center bg-back rounded-10 py-30">
       <view class="flex-1 text-center">

+ 43 - 10
src/pagesStudy/pages/simulation-analysis/simulation-analysis.vue

@@ -7,28 +7,28 @@
       <ie-image :is-oss="true" src="/study-title4.png" custom-class="w-282 h-64 absolute top-126 left-72 z-2" />
       <view class="relative z-3 pt-244 pb-20 mx-30">
         <view class="bg-white rounded-15 px-20 pb-1">
-          <rate-chart :value="50" />
+          <rate-chart :value="rightRate" />
           <view class="h-1 bg-[#E6E6E6] my-20"></view>
           <view>
             <view class="my-20 flex items-center justify-between text-24">
               <ie-image src="/pagesStudy/static/image/icon-house.png" custom-class="w-24 h-24" mode="aspectFill" />
               <text class="ml-10 text-fore-light flex-1">考试院校</text>
-              <text class="text-fore-title">长沙民政职业技术学院-大数据与会计</text>
+              <text class="text-fore-title">{{ examineeData.collegeName }}-{{ examineeData.majorName }}</text>
             </view>
             <view class="my-20 flex items-center justify-between text-24">
               <ie-image src="/pagesStudy/static/image/icon-group.png" custom-class="w-24 h-24" mode="aspectFill" />
-              <text class="ml-10 text-fore-light flex-1">测试类型</text>
-              <text class="text-fore-title">职业技能模拟考试</text>
+              <text class="ml-10 text-fore-light flex-1">考试科目</text>
+              <text class="text-fore-title">{{ examineeData.subjectName }}</text>
             </view>
             <view class="my-20 flex items-center justify-between text-24">
               <ie-image src="/pagesStudy/static/image/icon-clock.png" custom-class="w-24 h-24" mode="aspectFill" />
-              <text class="ml-10 text-fore-light flex-1">测试时间</text>
-              <text class="text-fore-title">2025.09.25 10:00</text>
+              <text class="ml-10 text-fore-light flex-1">考试时长</text>
+              <text class="text-fore-title">{{ formatTime(examineeData.duration) }}</text>
             </view>
           </view>
         </view>
-        <exam-stat />
-        <score-stat />
+        <exam-stat :data="examineeData" />
+        <score-stat :data="examineeData" />
       </view>
     </view>
   </ie-page>
@@ -40,11 +40,44 @@ import ExamStat from './components/exam-stat.vue';
 import ScoreStat from './components/score-stat.vue';
 import { getExamineeResult } from '@/api/modules/study';
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { Study } from '@/types';
+import { EnumQuestionType } from '@/common/enum';
 const { prevData } = useTransferPage();
-const examineeResult = ref<any>(null);
+const examineeData = ref<Study.Examinee>({} as Study.Examinee);
+// const questionList = computed(() => {
+//   return (examineeData.value.questions || []).map((item: Study.ExamineeQuestion) => {
+//     return {
+//       ...item,
+//       isRight: judgeCorrect(item)
+//     }
+//   });
+// });
+const rightRate = computed(() => {
+  const { totalCount = 0, wrongCount = 0 } = examineeData.value;
+  return Math.round((totalCount - wrongCount) / totalCount * 100) || 0;
+});
+// const judgeCorrect = (qs: Study.ExamineeQuestion) => {
+//   if (qs.typeId === EnumQuestionType.SINGLE_CHOICE) {
+//     return qs.answers.includes(qs.answer1);
+//   } else if (qs.typeId === EnumQuestionType.MULTIPLE_CHOICE) {
+//     const rightAnswers = qs.answer1.split('').filter(item => item !== ' ');
+//     return rightAnswers.length === qs.answers.length && rightAnswers.every(item => qs.answers.includes(item));
+//   }
+// }
+const formatTime = (time: number) => {
+  const hours = Math.floor(time / 3600);
+  const minutes = Math.floor((time % 3600) / 60);
+  const seconds = time % 60;
+
+  if (hours >= 1) {
+    return `${hours}时${minutes}分${seconds}秒`;
+  } else {
+    return `${minutes}分${seconds}秒`;
+  }
+};
 const loadData = async () => {
   const res = await getExamineeResult(prevData.value.examineeId);
-  examineeResult.value = res.data;
+  examineeData.value = res.data;
 };
 onLoad(() => {
   loadData();

+ 11 - 3
src/pagesStudy/pages/start-exam/start-exam.vue

@@ -106,7 +106,7 @@ import { getOpenExaminee, getPaper, commitExamineePaper, collectQuestion, cancel
 import { useExam } from '@/composables/useExam';
 import { Study } from '@/types';
 import { NEXT_QUESTION, PREV_QUESTION, NEXT_QUESTION_QUICKLY, PREV_QUESTION_QUICKLY } from '@/types/injectionSymbols';
-import { number } from 'echarts';
+
 const userStore = useUserStore();
 // import { Examinee, ExamPaper, ExamPaperSubmit } from '@/types/study';
 const { prevData, transferBack } = useTransferPage();
@@ -160,6 +160,9 @@ const pageTitle = computed(() => {
   const { paperType } = prevData.value;
   return paperType === EnumPaperType.PRACTICE ? '练习' : '考试';
 });
+const isExam = computed(() => {
+  return prevData.value.paperType === EnumPaperType.SIMULATED;
+});
 const pageSubtitle = computed(() => {
   const { name } = prevData.value;
   return name;
@@ -207,6 +210,9 @@ const handleCorrect = () => {
 const handleCorrectClose = () => {
   startTime();
 }
+/**
+ * 收藏
+ */
 const handleFavorite = async () => {
   if (!currentQuestion.value.isFavorite) {
     await collectQuestion(currentQuestion.value.id);
@@ -331,11 +337,13 @@ const handleSubmit = (tempSave: boolean = false) => {
       }),
       examineeId: examineeId.value,
       // examineeId: examineerData.value.examineeId,
-      isDone: isAllDone.value,
+      isDone: tempSave ? isAllDone.value : true,
       duration: practiceDuration.value
     } as Study.ExamPaperSubmit;
     commitExamineePaper(params);
-    uni.navigateBack();
+    setTimeout(() => {
+      uni.navigateBack();
+    }, isExam.value ? 2500 : 0);
   }, 1000);
 }
 

+ 4 - 1
src/pagesStudy/pages/study-history/components/exam-history-student.vue

@@ -32,7 +32,10 @@ const loadData = async (type: string) => {
 watch(() => props.examType, (newVal) => {
   loadData(newVal);
 }, {
-  immediate: true
+  immediate: false
+});
+onShow(() => {
+  loadData(props.examType);
 });
 </script>
 <style lang="scss" scoped></style>

+ 12 - 0
src/types/study.ts

@@ -108,6 +108,10 @@ export interface QuestionState {
  */
 export interface ExamineeQuestion {
   id: number;
+  typeId: number;
+  answer1: string;
+  answer2: string;
+  score: number;
   title: string;
   isFavorite: boolean;
   isMark: boolean;
@@ -123,6 +127,14 @@ export interface Examinee {
   questions: ExamineeQuestion[];
   // 
   paperInfo: ExamineePaperInfo;
+  collegeName: string;
+  majorName: string;
+  collegeId: number;
+  majorId: number;
+  subjectName: string;
+  subjectId: number;
+  totalCount: number;
+  wrongCount: number;
 }
 export interface ExamineePaperInfo {
   score: number;

+ 6 - 0
vite.config.js

@@ -8,7 +8,13 @@ import Components from 'unplugin-vue-components/vite';
 import { resolve } from 'node:path';
 import uniPolyfill from 'vite-plugin-uni-polyfill';
 import viteCompression from "vite-plugin-compression";
+import { env as envConfig } from './src/config';
 // https://vitejs.dev/config/
+const env = JSON.parse(process.env.UNI_CUSTOM_DEFINE);
+const mode = env.IE_ENV || 'development';
+const baseUrl = envConfig[mode]?.serverBaseUrl || '';
+console.log('当前模式:', mode);
+console.log('当前baseUrl:', baseUrl);
 
 export default defineConfig(({ mode }) => ({
   outDir: 'h5',