lex пре 2 година
родитељ
комит
17fd7eb081

+ 1 - 1
src/school/train-planning/modal/timer/index.tsx

@@ -197,7 +197,7 @@ export default defineComponent({
               value={`${dayjs(item.startTime).format('HH:mm')}~${dayjs(item.endTime).format(
                 'HH:mm'
               )}`}
-              title="可选时间"
+              title="可排课时间段"
             ></Cell>
           ))}
 

+ 4 - 1
src/student/my-orchestra/apply-withdrawal.tsx

@@ -1,5 +1,5 @@
 import OHeader from '@/components/o-header'
-import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue'
 import iconStudent from '@common/images/icon_student.png'
 import styles from './apply-withdrawal.module.less'
 import { Button, Field, Image, showToast } from 'vant'
@@ -72,6 +72,9 @@ export default defineComponent({
       postMessage({ api: 'setStatusBarTextColor', content: { statusBarTextColor: true } })
       getDetails()
     })
+    onUnmounted(() => {
+      postMessage({ api: 'setStatusBarTextColor', content: { statusBarTextColor: false } })
+    })
     return () => (
       <div class={styles.applyWithdrawal}>
         <div class={styles.headers}>

+ 57 - 27
src/views/unit-test/examination-mode/index.tsx

@@ -10,7 +10,7 @@ import {
   SwipeItem,
   Tag
 } from 'vant'
-import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import styles from './index.module.less'
 import iconQuestionNums from '../images/icon-question-nums.png'
@@ -27,6 +27,7 @@ import request from '@/helpers/request'
 import dayjs from 'dayjs'
 import ResultFinish from '../model/result-finish'
 import { QuestionType } from '../unit'
+import { useRect } from '@vant/use'
 
 export default defineComponent({
   name: 'unit-detail',
@@ -47,7 +48,8 @@ export default defineComponent({
       resultInfo: {} as any,
       resultStatusType: 'SUCCESS', // 'SUCCESS' | 'FAIL'
       visiableExam: false, // 考试已结束
-      nextStatus: false
+      nextStatus: false,
+      swipeHeight: 'auto' as any
     })
 
     const getExamDetails = async () => {
@@ -109,6 +111,22 @@ export default defineComponent({
     }
 
     /**
+     * @description 重置当前的题目高度
+     */
+    const resizeSwipeItemHeight = () => {
+      nextTick(() => {
+        window.scrollTo(0, 0)
+        setTimeout(() => {
+          const currentItemDom: Element =
+            document.querySelectorAll('.swipe-item-question')[state.currentIndex]
+          console.log(currentItemDom, state.currentIndex)
+          const rect = useRect(currentItemDom)
+          state.swipeHeight = rect.height
+        }, 100)
+      })
+    }
+
+    /**
      * @description 下一题 | 测试完成
      */
     const onNextQuestion = async () => {
@@ -208,6 +226,9 @@ export default defineComponent({
 
     onMounted(() => {
       getExamDetails()
+
+      // 初始化高度
+      resizeSwipeItemHeight()
     })
 
     return () => (
@@ -246,16 +267,16 @@ export default defineComponent({
           ref={swipeRef}
           duration={300}
           touchable={false}
+          height={state.swipeHeight}
           lazyRender
-          // initialSwipe={state.currentIndex}
           onChange={(index: number) => {
             state.currentIndex = index
+            resizeSwipeItemHeight()
           }}
         >
           {state.questionList.map((item: any, index: number) => (
             // item.questionTypeCode === QuestionType.PLAY && (
             //   <SwipeItem>
-            //     <KeepLookQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
             //     <PlayQuestion
             //       v-model:value={item.userAnswer}
             //       data={item}
@@ -265,29 +286,38 @@ export default defineComponent({
             //   </SwipeItem>
             // )
             <SwipeItem>
-              {item.questionTypeCode === QuestionType.RADIO && (
-                <ChoiceQuestion
-                  v-model:value={item.userAnswer}
-                  index={index + 1}
-                  data={item}
-                  type="radio"
-                />
-              )}
-              {item.questionTypeCode === QuestionType.CHECKBOX && (
-                <ChoiceQuestion
-                  v-model:value={item.userAnswer}
-                  index={index + 1}
-                  data={item}
-                  type="checkbox"
-                />
-              )}
-              {item.questionTypeCode === QuestionType.SORT && (
-                <DragQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
-              )}
-              {item.questionTypeCode === QuestionType.LINK && (
-                <KeepLookQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
-              )}
-              {item.questionTypeCode === QuestionType.PLAY && <PlayQuestion />}
+              <div class="swipe-item-question">
+                {item.questionTypeCode === QuestionType.RADIO && (
+                  <ChoiceQuestion
+                    v-model:value={item.userAnswer}
+                    index={index + 1}
+                    data={item}
+                    type="radio"
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.CHECKBOX && (
+                  <ChoiceQuestion
+                    v-model:value={item.userAnswer}
+                    index={index + 1}
+                    data={item}
+                    type="checkbox"
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.SORT && (
+                  <DragQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
+                )}
+                {item.questionTypeCode === QuestionType.LINK && (
+                  <KeepLookQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
+                )}
+                {item.questionTypeCode === QuestionType.PLAY && (
+                  <PlayQuestion
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    unitId={state.id as any}
+                  />
+                )}
+              </div>
             </SwipeItem>
           ))}
         </Swipe>

BIN
src/views/unit-test/images/icon-analysis.png


BIN
src/views/unit-test/images/icon-success.png


+ 6 - 0
src/views/unit-test/index.tsx

@@ -105,6 +105,12 @@ export default defineComponent({
     // 查看测验
     const onUnitTestLook = (item: any) => {
       //
+      router.push({
+        path: '/unit-detail',
+        query: {
+          id: item.id
+        }
+      })
     }
 
     const onExamStart = async () => {

+ 26 - 0
src/views/unit-test/model/anser-title/index.module.less

@@ -0,0 +1,26 @@
+.unitSubjectTitle {
+  font-size: 16px;
+  font-weight: 500;
+  color: #333333;
+  line-height: 26px;
+  .unitScore {
+    color: #777777;
+  }
+  :global {
+    .van-tag {
+      vertical-align: middle;
+      margin-top: -3px;
+    }
+  }
+}
+.unitDetail {
+  padding-top: 20px;
+  padding-bottom: 10px;
+  font-size: 14px;
+  img {
+    width: 100%;
+  }
+}
+.unitTitleImg {
+  width: 100%;
+}

+ 67 - 0
src/views/unit-test/model/anser-title/index.tsx

@@ -0,0 +1,67 @@
+import { Tag, Image } from 'vant'
+import { computed, defineComponent } from 'vue'
+import { QuestionType, QuestionTypeName } from '../../unit'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'answer-title',
+  props: {
+    index: {
+      type: Number,
+      default: 1
+    },
+    name: {
+      // 题目名称
+      type: String,
+      default: ''
+    },
+    score: {
+      type: Number,
+      default: 0
+    },
+    answerType: {
+      // 类型
+      type: String,
+      default: ''
+    },
+    extra: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  setup(props) {
+    const answerTypeName = computed(() => {
+      if (props.answerType === QuestionType.CHECKBOX) {
+        return QuestionTypeName.CHECKBOX
+      } else if (props.answerType === QuestionType.LINK) {
+        return QuestionTypeName.LINK
+      } else if (props.answerType === QuestionType.SORT) {
+        return QuestionTypeName.SORT
+      } else if (props.answerType === QuestionType.PLAY) {
+        return QuestionTypeName.PLAY
+      }
+      return QuestionTypeName.RADIO
+    })
+
+    const mediaUrls = computed(() =>
+      props.extra.mediaUrls ? props.extra.mediaUrls.split(',') : ''
+    )
+    return () => (
+      <>
+        <div class={styles.unitSubjectTitle}>
+          {props.index}、{props.name} <span class={styles.unitScore}>({props.score || 0}分)</span>
+          <Tag type="primary">{answerTypeName.value}</Tag>
+        </div>
+        {props.extra.questionDetail || mediaUrls.value ? (
+          <div class={styles.unitDetail}>
+            <div v-html={props.extra.questionDetail}></div>
+            {mediaUrls.value &&
+              mediaUrls.value.map((url: any) => <Image class={styles.unitTitleImg} src={url} />)}
+          </div>
+        ) : (
+          ''
+        )}
+      </>
+    )
+  }
+})

+ 32 - 0
src/views/unit-test/model/answer-analysis/index.module.less

@@ -0,0 +1,32 @@
+.answerAnalysis {
+  .analysisResult {
+    display: flex;
+    align-items: center;
+    // line-height: 22px;
+    font-size: 16px;
+    font-weight: 600;
+    color: #333333;
+
+    .aImg {
+      font-size: 18px;
+      margin-right: 6px;
+    }
+  }
+  .success {
+    color: #3b8df3;
+  }
+  .error {
+    color: #f44541;
+  }
+  .analysisTitle {
+    padding-top: 20px;
+    padding-bottom: 10px;
+  }
+  .analysisMessage {
+    font-size: 15px;
+    color: #333333;
+    line-height: 21px;
+    text-align: justify;
+    padding-bottom: 5px;
+  }
+}

+ 62 - 0
src/views/unit-test/model/answer-analysis/index.tsx

@@ -0,0 +1,62 @@
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import iconError from '../../images/icon-error.png'
+import iconSuccess from '../../images/icon-success.png'
+import iconAnalysis from '../../images/icon-analysis.png'
+import { Icon } from 'vant'
+
+// 答案解析
+export default defineComponent({
+  name: 'answer-analysis',
+  props: {
+    topic: {
+      // 状态
+      type: Boolean,
+      default: true
+    },
+    userResult: {
+      tyep: Boolean,
+      default: false
+    },
+    answerAnalysis: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    return () => (
+      <div class={styles.answerAnalysis}>
+        {props.topic ? (
+          <>
+            {props.userResult ? (
+              <div class={[styles.analysisResult, styles.success]}>
+                <Icon name={iconSuccess} class={styles.aImg} />
+                回答正确
+              </div>
+            ) : (
+              <div class={[styles.analysisResult, styles.error]}>
+                <Icon name={iconError} class={styles.aImg} />
+                回答错误
+              </div>
+            )}
+          </>
+        ) : (
+          ''
+          // <div class={[styles.analysisResult]}>
+          //   正确答案: <span class={styles.success}>A</span>
+          // </div>
+        )}
+
+        {props.answerAnalysis && (
+          <>
+            <div class={[styles.analysisResult, styles.analysisTitle]}>
+              <Icon name={iconAnalysis} class={styles.aImg} />
+              答案解析
+            </div>
+            <div class={styles.analysisMessage}>{props.answerAnalysis}</div>
+          </>
+        )}
+      </div>
+    )
+  }
+})

+ 46 - 10
src/views/unit-test/model/answer-list/index.tsx

@@ -9,6 +9,10 @@ export default defineComponent({
       type: Array,
       default: () => []
     },
+    look: {
+      type: Boolean,
+      default: false
+    },
     statusList: {
       type: Array,
       default: () => [
@@ -21,11 +25,29 @@ export default defineComponent({
           color: '#EAEAEA'
         }
       ]
+    },
+    // 结果
+    answerResult: {
+      type: Array,
+      default: () => []
     }
   },
   emits: ['select'],
   setup(props, { emit }) {
-    console.log(props.value)
+    /**
+     * @description 检查用户是否答对
+     * @returns Boolean
+     */
+    const formatUserResult = (id: string) => {
+      let result = false
+      props.answerResult.forEach((item: any) => {
+        if (item.questionId === id) {
+          result = item.rightFlag
+        }
+      })
+      return result
+    }
+
     return () => (
       <div class={styles.anserList}>
         <div class={styles.status}>
@@ -37,15 +59,29 @@ export default defineComponent({
           ))}
         </div>
 
-        <Grid class={styles.aList} columnNum={6} border={false}>
-          {props.value.map((item: any, index: number) => (
-            <GridItem onClick={() => emit('select', index)}>
-              <span class={item.userAnswer && item.userAnswer.length > 0 && styles.answered}>
-                {index + 1}
-              </span>
-            </GridItem>
-          ))}
-        </Grid>
+        <div
+          style={{
+            maxHeight: '40vh',
+            minHeight: '20vh',
+            overflowX: 'auto'
+          }}
+        >
+          <Grid class={styles.aList} columnNum={6} border={false}>
+            {props.value.map((item: any, index: number) => (
+              <GridItem onClick={() => emit('select', index)}>
+                {/* 判断是否答题了 */}
+                <span
+                  class={[
+                    item.userAnswer && item.userAnswer.length > 0 && styles.answered,
+                    props.look && (formatUserResult(item.id) ? styles.yes : styles.no)
+                  ]}
+                >
+                  {index + 1}
+                </span>
+              </GridItem>
+            ))}
+          </Grid>
+        </div>
       </div>
     )
   }

+ 3 - 29
src/views/unit-test/model/choice-question/index.module.less

@@ -4,39 +4,13 @@
   background-color: #fff;
   overflow: hidden;
   border-radius: 10px;
-}
-.unitSubjectTitle {
-  // display: flex;
-  // align-items: center;
-  // flex-wrap: wrap;
-  font-size: 16px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 26px;
-  .unitScore {
-    color: #777777;
-  }
-  :global {
-    .van-tag {
-      vertical-align: middle;
-      margin-top: -3px;
-    }
+  & + .unitSubject {
+    margin-top: 12px;
   }
 }
-.unitDetail {
-  padding-top: 20px;
-  padding-bottom: 10px;
-  font-size: 14px;
-  img {
-    width: 100%;
-  }
-}
-.unitTitleImg {
-  width: 100%;
-}
 
 .unitAnswers {
-  padding-bottom: 30px;
+  // padding-bottom: 30px;
   .unitAnswer {
     margin-top: 15px;
     background: #f6f6f6;

+ 58 - 33
src/views/unit-test/model/choice-question/index.tsx

@@ -1,6 +1,8 @@
 import { Tag, Image } from 'vant'
 import { computed, defineComponent, PropType, reactive } from 'vue'
-import { AnswerType, labelOptions } from '../../unit'
+import { AnswerType, labelOptions, QuestionType } from '../../unit'
+import AnserTitle from '../anser-title'
+import AnswerAnalysis from '../answer-analysis'
 import styles from './index.module.less'
 
 // 单选题 - 多选题
@@ -27,6 +29,19 @@ export default defineComponent({
     readOnly: {
       type: Boolean,
       default: false
+    },
+    showAnalysis: {
+      // 是否显示解析
+      type: Boolean,
+      default: false
+    },
+    analysis: {
+      type: Object,
+      default: () => ({
+        message: '',
+        topic: false, // 是否显示结果
+        userResult: true // 用户答题对错
+      })
     }
   },
   emits: ['update:value'],
@@ -63,44 +78,54 @@ export default defineComponent({
         )
         item.checked = tempIndex !== -1 ? true : false
       })
-      console.log(list)
       return list
     })
-    const mediaUrls = computed(() => (props.data.mediaUrls ? props.data.mediaUrls.split(',') : ''))
 
     return () => (
-      <div class={styles.unitSubject}>
-        <div class={styles.unitSubjectTitle}>
-          {props.index}、{props.data.name}{' '}
-          <span class={styles.unitScore}>({props.data.totalScore || 0}分)</span>
-          <Tag type="primary">{props.type === 'radio' ? '单选题' : '多选题'}</Tag>
-        </div>
-        <div class={styles.unitDetail}>
-          <div v-html={props.data.questionDetail}></div>
-          {mediaUrls.value &&
-            mediaUrls.value.map((url: any) => <Image class={styles.unitTitleImg} src={url} />)}
-        </div>
+      <>
+        <div class={styles.unitSubject}>
+          {/* 标题 */}
+          <AnserTitle
+            index={props.index}
+            name={props.data.name}
+            score={props.data.totalScore}
+            answerType={props.type === 'radio' ? QuestionType.RADIO : QuestionType.CHECKBOX}
+            extra={{
+              questionDetail: props.data.questionDetail,
+              mediaUrls: props.data.mediaUrls
+            }}
+          />
 
-        <div class={styles.unitAnswers}>
-          {/* styles.active */}
-          {answers.value.map((item: any, index: number) => (
-            <div
-              class={[styles.unitAnswer, item.checked && styles.active]}
-              onClick={() => onSelect(item)}
-            >
-              <span class={styles.option}>{labelOptions[index + 1]}.</span>
-              {item.questionAnswerTypeCode === AnswerType.IMAGE && (
-                <div class={styles.value}>
-                  <Image src={item.questionAnswer} />
-                </div>
-              )}
-              {item.questionAnswerTypeCode === AnswerType.TXT && (
-                <div class={styles.value}>{item.questionAnswer}</div>
-              )}
-            </div>
-          ))}
+          <div class={styles.unitAnswers}>
+            {answers.value.map((item: any, index: number) => (
+              <div
+                class={[styles.unitAnswer, item.checked && styles.active]}
+                onClick={() => onSelect(item)}
+              >
+                <span class={styles.option}>{labelOptions[index + 1]}.</span>
+                {item.questionAnswerTypeCode === AnswerType.IMAGE && (
+                  <div class={styles.value}>
+                    <Image src={item.questionAnswer} />
+                  </div>
+                )}
+                {item.questionAnswerTypeCode === AnswerType.TXT && (
+                  <div class={styles.value}>{item.questionAnswer}</div>
+                )}
+              </div>
+            ))}
+          </div>
         </div>
-      </div>
+
+        {props.showAnalysis && (
+          <div class={styles.unitSubject}>
+            <AnswerAnalysis
+              answerAnalysis={props.analysis.message}
+              topic={props.analysis.topic}
+              userResult={props.analysis.userResult}
+            />
+          </div>
+        )}
+      </>
     )
   }
 })

+ 3 - 32
src/views/unit-test/model/drag-question/index.module.less

@@ -4,40 +4,11 @@
   background-color: #fff;
   // overflow: hidden;
   border-radius: 10px;
-}
-
-.unitSubjectTitle {
-  // display: flex;
-  // align-items: center;
-  // flex-wrap: wrap;
-  font-size: 16px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 26px;
-  .unitScore {
-    color: #777777;
-  }
-  :global {
-    .van-tag {
-      vertical-align: middle;
-      margin-top: -3px;
-    }
+  & + .unitSubject {
+    margin-top: 12px;
   }
 }
-.unitTitleSection {
-  margin-top: 20px;
-  border-radius: 6px;
-  border: 1px solid #d5d5d5;
-  padding: 25px 5px;
-}
-.unitDetail {
-  padding-top: 20px;
-  padding-bottom: 10px;
-  font-size: 14px;
-}
-.unitTitleImg {
-  width: 100%;
-}
+
 .unitAnswers {
   padding-bottom: 30px;
   :global {

+ 82 - 61
src/views/unit-test/model/drag-question/index.tsx

@@ -1,10 +1,12 @@
 import { Tag, Image, Button } from 'vant'
 import { computed, defineComponent, nextTick, onMounted, PropType, reactive } from 'vue'
-import { AnswerType, labelOptions } from '../../unit'
+import { AnswerType, labelOptions, QuestionType } from '../../unit'
 import Draggable from 'vuedraggable'
 import styles from './index.module.less'
 import Sortable from 'sortablejs'
 import deepClone from '@/helpers/deep-clone'
+import AnserTitle from '../anser-title'
+import AnswerAnalysis from '../answer-analysis'
 
 // 单选和多选题
 export default defineComponent({
@@ -27,6 +29,19 @@ export default defineComponent({
     readOnly: {
       type: Boolean,
       default: false
+    },
+    showAnalysis: {
+      // 是否显示解析
+      type: Boolean,
+      default: false
+    },
+    analysis: {
+      type: Object,
+      default: () => ({
+        message: '',
+        topic: false, // 是否显示结果
+        userResult: true // 用户答题对错
+      })
     }
   },
   emits: ['update:value'],
@@ -79,8 +94,6 @@ export default defineComponent({
       emit('update:value', result)
     }
 
-    const mediaUrls = computed(() => (props.data.mediaUrls ? props.data.mediaUrls.split(',') : ''))
-
     const initOptions = () => {
       const answers = props.data.answers || []
       const userAnswer = props.data.userAnswer || [] // 用户填写的答案
@@ -119,7 +132,7 @@ export default defineComponent({
           state.options.push(tmp)
         })
         // 进页面就默认初始化答案
-        emit('update:value', resultValue)
+        // emit('update:value', resultValue)
       }
     }
 
@@ -128,67 +141,75 @@ export default defineComponent({
     })
 
     return () => (
-      <div class={styles.unitSubject}>
-        <div class={styles.unitSubjectTitle}>
-          {props.index}、{props.data.name}{' '}
-          <span class={styles.unitScore}>({props.data.totalScore || 0}分)</span>
-          <Tag type="primary">排序题</Tag>
-        </div>
-
-        {props.data.questionDetail || mediaUrls.value ? (
-          <div class={styles.unitDetail}>
-            <div v-html={props.data.questionDetail}></div>
-            {mediaUrls.value &&
-              mediaUrls.value.map((url: any) => <Image class={styles.unitTitleImg} src={url} />)}
-          </div>
-        ) : (
-          ''
-        )}
+      <>
+        <div class={styles.unitSubject}>
+          {/* 标题 */}
+          <AnserTitle
+            index={props.index}
+            name={props.data.name}
+            score={props.data.totalScore}
+            answerType={QuestionType.SORT}
+            extra={{
+              questionDetail: props.data.questionDetail,
+              mediaUrls: props.data.mediaUrls
+            }}
+          />
 
-        <div class={[styles.unitAnswers, 'van-hairline--top']}>
-          <div class={styles.sortReset}>
-            <span class={styles.tips}>请长按拖拽答案进行排序</span>
-            <Button
-              type="primary"
-              round
-              onClick={() => {
-                const ids: any = []
-                const answers = props.data.answers || []
-                answers.forEach((item: any) => {
-                  ids.push(item.examinationQuestionAnswerId)
-                })
-                state.sortable.sort(
-                  ids.sort((a: any, b: any) => a - b),
-                  true
-                )
+          <div class={[styles.unitAnswers, 'van-hairline--top']}>
+            <div class={styles.sortReset}>
+              <span class={styles.tips}>请长按拖拽答案进行排序</span>
+              <Button
+                type="primary"
+                round
+                disabled={props.readOnly}
+                onClick={() => {
+                  const ids: any = []
+                  const answers = props.data.answers || []
+                  answers.forEach((item: any) => {
+                    ids.push(item.examinationQuestionAnswerId)
+                  })
+                  state.sortable.sort(
+                    ids.sort((a: any, b: any) => a - b),
+                    true
+                  )
 
-                onSelect()
-              }}
-            >
-              重置
-            </Button>
-          </div>
-          <div id={state.domId}>
-            {state.options.map((item: any) => (
-              <>
-                {item.leftType === AnswerType.TXT && (
-                  <Tag class={[styles.items]} data-id={item.index}>
-                    {item.leftValue}
-                  </Tag>
-                )}
-                {item.leftType === AnswerType.IMAGE && (
-                  <Image
-                    src={item.leftValue}
-                    data-id={item.index}
-                    class={styles.imgs}
-                    fit="cover"
-                  />
-                )}
-              </>
-            ))}
+                  onSelect()
+                }}
+              >
+                重置
+              </Button>
+            </div>
+            <div id={state.domId}>
+              {state.options.map((item: any) => (
+                <>
+                  {item.leftType === AnswerType.TXT && (
+                    <Tag class={[styles.items]} data-id={item.index}>
+                      {item.leftValue}
+                    </Tag>
+                  )}
+                  {item.leftType === AnswerType.IMAGE && (
+                    <Image
+                      src={item.leftValue}
+                      data-id={item.index}
+                      class={styles.imgs}
+                      fit="cover"
+                    />
+                  )}
+                </>
+              ))}
+            </div>
           </div>
         </div>
-      </div>
+        {props.showAnalysis && (
+          <div class={styles.unitSubject}>
+            <AnswerAnalysis
+              answerAnalysis={props.analysis.message}
+              topic={props.analysis.topic}
+              userResult={props.analysis.userResult}
+            />
+          </div>
+        )}
+      </>
     )
   }
 })

+ 3 - 31
src/views/unit-test/model/keep-look-question/index.module.less

@@ -4,42 +4,14 @@
   background-color: #fff;
   // overflow: hidden;
   border-radius: 10px;
-}
-
-.unitSubjectTitle {
-  // display: flex;
-  // align-items: center;
-  // flex-wrap: wrap;
-  font-size: 16px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 26px;
-  .unitScore {
-    color: #777777;
-  }
-  :global {
-    .van-tag {
-      vertical-align: middle;
-      margin-top: -3px;
-    }
+  & + .unitSubject {
+    margin-top: 12px;
   }
 }
-.unitDetail {
-  padding-top: 20px;
-  padding-bottom: 10px;
-  font-size: 14px;
-  img {
-    width: 100%;
-  }
-}
-.unitTitleImg {
-  width: 100%;
-  padding-top: 20px;
-  padding-bottom: 10px;
-}
 
 .unitAnswers {
   position: relative;
+  padding-top: 20px;
   padding-bottom: 20px;
 
   .answerItem {

+ 94 - 67
src/views/unit-test/model/keep-look-question/index.tsx

@@ -2,7 +2,9 @@ import { Tag, Image, Button } from 'vant'
 import { computed, defineComponent, nextTick, onMounted, PropType, reactive, ref } from 'vue'
 import styles from './index.module.less'
 import { useRect } from '@vant/use'
-import { AnswerType } from '../../unit'
+import { AnswerType, QuestionType } from '../../unit'
+import AnserTitle from '../anser-title'
+import AnswerAnalysis from '../answer-analysis'
 
 // 单选和多选题
 export default defineComponent({
@@ -25,6 +27,19 @@ export default defineComponent({
     readOnly: {
       type: Boolean,
       default: false
+    },
+    showAnalysis: {
+      // 是否显示解析
+      type: Boolean,
+      default: false
+    },
+    analysis: {
+      type: Object,
+      default: () => ({
+        message: '',
+        topic: false, // 是否显示结果
+        userResult: true // 用户答题对错
+      })
     }
   },
   emits: ['update:value'],
@@ -326,7 +341,7 @@ export default defineComponent({
         state.drawLineList.push(temps)
       })
 
-      console.log(state.drawLineList, state.options)
+      // console.log(state.drawLineList, state.options)
       // 反显答案-连线
       nextTick(() => {
         state.drawLineList.forEach((draw: any) => {
@@ -360,76 +375,88 @@ export default defineComponent({
       })
     })
 
-    const mediaUrls = computed(() => (props.data.mediaUrls ? props.data.mediaUrls.split(',') : ''))
     return () => (
-      <div class={styles.unitSubject}>
-        <div class={styles.unitSubjectTitle}>
-          {props.index}、{props.data.name}{' '}
-          <span class={styles.unitScore}>({props.data.totalScore || 0}分)</span>
-          <Tag type="primary">连连看</Tag>
-        </div>
-        <div class={styles.unitDetail}>
-          <div v-html={props.data.questionDetail}></div>
-          {mediaUrls.value &&
-            mediaUrls.value.map((url: any) => <Image class={styles.unitTitleImg} src={url} />)}
-        </div>
+      <>
+        <div class={styles.unitSubject}>
+          {/* 标题 */}
+          <AnserTitle
+            index={props.index}
+            name={props.data.name}
+            score={props.data.totalScore}
+            answerType={QuestionType.LINK}
+            extra={{
+              questionDetail: props.data.questionDetail,
+              mediaUrls: props.data.mediaUrls
+            }}
+          />
 
-        <div class={[styles.unitAnswers]} id={state.answerDomId}>
-          <canvas
-            ref={canvasRef}
-            class={styles.canvasSection}
-            width={state.answerRect.width || 0}
-            height={state.answerRect.height || 0}
-          ></canvas>
-          {state.options.map((item: any) => (
-            <div class={styles.answerItem}>
-              <div
-                class={[styles.unitItem, item.left && styles.active]}
-                id={item.index + '-left'}
-                onClick={(e: any) => onLeftClick(e, item)}
-              >
-                {item.leftType === AnswerType.TXT && item.leftValue}
-                {item.leftType === AnswerType.IMAGE && (
-                  <Image src={item.leftValue} class={styles.img} />
-                )}
-              </div>
-              <div
-                class={[styles.unitItem, item.right && styles.active]}
-                id={item.index + '-right'}
-                onClick={(e: any) => onRightClick(e, item)}
-              >
-                {item.rightType === AnswerType.TXT && item.rightValue}
-                {item.rightType === AnswerType.IMAGE && (
-                  <Image src={item.rightValue} class={styles.img} />
-                )}
+          <div class={[styles.unitAnswers]} id={state.answerDomId}>
+            <canvas
+              ref={canvasRef}
+              class={styles.canvasSection}
+              width={state.answerRect.width || 0}
+              height={state.answerRect.height || 0}
+            ></canvas>
+            {state.options.map((item: any) => (
+              <div class={styles.answerItem}>
+                <div
+                  class={[styles.unitItem, item.left && styles.active]}
+                  id={item.index + '-left'}
+                  onClick={(e: any) => onLeftClick(e, item)}
+                >
+                  {item.leftType === AnswerType.TXT && item.leftValue}
+                  {item.leftType === AnswerType.IMAGE && (
+                    <Image src={item.leftValue} class={styles.img} />
+                  )}
+                </div>
+                <div
+                  class={[styles.unitItem, item.right && styles.active]}
+                  id={item.index + '-right'}
+                  onClick={(e: any) => onRightClick(e, item)}
+                >
+                  {item.rightType === AnswerType.TXT && item.rightValue}
+                  {item.rightType === AnswerType.IMAGE && (
+                    <Image src={item.rightValue} class={styles.img} />
+                  )}
+                </div>
               </div>
-            </div>
-          ))}
-        </div>
-        <div class={styles.resetBtnGroup}>
-          <Button
-            round
-            type="primary"
-            disabled={props.readOnly}
-            onClick={() => {
-              state.drawLineList = []
-              renderDrawLine(canvasRef.value)
+            ))}
+          </div>
+          <div class={styles.resetBtnGroup}>
+            <Button
+              round
+              type="primary"
+              disabled={props.readOnly}
+              onClick={() => {
+                state.drawLineList = []
+                renderDrawLine(canvasRef.value)
 
-              // 清除所有的选中状态
-              state.options.forEach((item: any) => {
-                item.left = false
-                item.right = false
-                item.leftLocked = false
-                item.rightLocked = false
-              })
-              // 清除所有的选择的内容
-              state.selectItem = []
-            }}
-          >
-            重置
-          </Button>
+                // 清除所有的选中状态
+                state.options.forEach((item: any) => {
+                  item.left = false
+                  item.right = false
+                  item.leftLocked = false
+                  item.rightLocked = false
+                })
+                // 清除所有的选择的内容
+                state.selectItem = []
+              }}
+            >
+              重置
+            </Button>
+          </div>
         </div>
-      </div>
+
+        {props.showAnalysis && (
+          <div class={styles.unitSubject}>
+            <AnswerAnalysis
+              answerAnalysis={props.analysis.message}
+              topic={props.analysis.topic}
+              userResult={props.analysis.userResult}
+            />
+          </div>
+        )}
+      </>
     )
   }
 })

+ 2 - 35
src/views/unit-test/model/play-question/index.module.less

@@ -4,43 +4,10 @@
   background-color: #fff;
   // overflow: hidden;
   border-radius: 10px;
-}
-
-.unitSubjectTitle {
-  // display: flex;
-  // align-items: center;
-  // flex-wrap: wrap;
-  font-size: 16px;
-  font-weight: 500;
-  color: #333333;
-  line-height: 26px;
-  .unitScore {
-    color: #777777;
-  }
-  :global {
-    .van-tag {
-      vertical-align: middle;
-      margin-top: -3px;
-    }
-  }
-}
-.unitTitleSection {
-  margin-top: 20px;
-  border-radius: 6px;
-  border: 1px solid #d5d5d5;
-  padding: 25px 5px;
-}
-.unitDetail {
-  padding-top: 20px;
-  padding-bottom: 10px;
-  font-size: 14px;
-  img {
-    width: 100%;
+  & + .unitSubject {
+    margin-top: 12px;
   }
 }
-.unitTitleImg {
-  width: 100%;
-}
 
 .unitAnswers {
   padding-top: 20px;

+ 83 - 56
src/views/unit-test/model/play-question/index.tsx

@@ -9,6 +9,9 @@ import {
 } from '@/helpers/native-message'
 import deepClone from '@/helpers/deep-clone'
 import iconSong from '../../images/icon-song.png'
+import AnserTitle from '../anser-title'
+import { QuestionType } from '../../unit'
+import AnswerAnalysis from '../answer-analysis'
 
 // 单选和多选题
 export default defineComponent({
@@ -16,7 +19,7 @@ export default defineComponent({
   props: {
     value: {
       type: Array,
-      default: []
+      default: () => []
     },
     index: {
       // 题目是第几道
@@ -36,31 +39,29 @@ export default defineComponent({
     readOnly: {
       type: Boolean,
       default: false
+    },
+    showAnalysis: {
+      // 是否显示解析
+      type: Boolean,
+      default: false
+    },
+    analysis: {
+      type: Object,
+      default: () => ({
+        message: '',
+        topic: false, // 是否显示结果
+        userResult: true // 用户答题对错
+      })
     }
   },
   emits: ['update:value'],
   setup(props, { emit }) {
+    console.log(props)
     const state = reactive({
       list: [] as any,
       score: 0
     })
 
-    // const onSelect = (item: any) => {
-    //   if (props.type === 'checkbox') {
-    //     // 判断是否已选过
-    //     const value: any = props.value
-    //     if (value.includes(item.index)) {
-    //       const index = value.findIndex((v: any) => v === item.index)
-    //       value.splice(index, 1)
-    //       emit('update:value', [...value])
-    //     } else {
-    //       emit('update:value', [item.index, ...value])
-    //     }
-    //   } else {
-    //     emit('update:value', item.index)
-    //   }
-    // }
-
     // const mediaUrls = computed(() => (props.data.mediaUrls ? props.data.mediaUrls.split(',') : ''))
 
     const questionExtendsInfo = computed(() =>
@@ -77,8 +78,8 @@ export default defineComponent({
       postMessage({
         api: 'openAccompanyWebView',
         content: {
-          // url: `https://ponline.colexiu.com/orchestra-music-score/?id=${info.musicSheetId}&modelType=evaluation&unitId=${props.unitId}`,
-          url: `${location.origin}/orchestra-music-score/?id=${info.musicSheetId}&modelType=evaluation&unitId=${props.unitId}`,
+          url: `https://ponline.colexiu.com/orchestra-music-score/?id=${info.musicSheetId}&modelType=evaluation&unitId=${props.unitId}`,
+          // url: `${location.origin}/orchestra-music-score/?id=${info.musicSheetId}&modelType=evaluation&unitId=${props.unitId}`,
           orientation: 0,
           isHideTitle: true,
           statusBarTextColor: false,
@@ -92,7 +93,22 @@ export default defineComponent({
           (res: any) => {
             const content = res.content
             if (content.value) {
-              state.score = content.value.score
+              const result = content.value ? JSON.parse(content.value) : {}
+              if (result.unitId === props.unitId) {
+                const tempScore = result.score || 0
+                // 跟上一次分数对比
+                if (state.score < tempScore) {
+                  state.score = result.score || 0
+                }
+              }
+              const answer = props.data.answer || []
+              emit('update:value', [
+                {
+                  answerId: answer[0].examinationQuestionId,
+                  answer: state.score,
+                  extra: ''
+                }
+              ])
             }
 
             // 关闭页面监听
@@ -105,46 +121,57 @@ export default defineComponent({
     }
 
     return () => (
-      <div class={styles.unitSubject}>
-        <div class={styles.unitSubjectTitle}>
-          {props.index}、{props.data.name}{' '}
-          <span class={styles.unitScore}>({props.data.totalScore || 0}分)</span>
-          <Tag type="primary">演奏题</Tag>
-        </div>
-
-        {props.data.questionDetail ? (
-          <div class={styles.unitDetail}>
-            <div v-html={props.data.questionDetail}></div>
-            {/* {mediaUrls.value &&
-              mediaUrls.value.map((url: any) => <Image class={styles.unitTitleImg} src={url} />)} */}
-          </div>
-        ) : (
-          ''
-        )}
+      <>
+        <div class={styles.unitSubject}>
+          {/* 标题 */}
+          <AnserTitle
+            index={props.index}
+            name={props.data.name}
+            score={props.data.totalScore}
+            answerType={QuestionType.PLAY}
+            extra={{
+              questionDetail: props.data.questionDetail,
+              mediaUrls: ''
+            }}
+          />
 
-        <div class={[styles.unitAnswers]}>
-          {questionExtendsInfo.value && (
-            <Cell class={styles.playSection} center titleClass={['van-ellipsis', styles.playTitle]}>
-              {{
-                icon: () => <Image class={styles.img} src={iconSong} />,
-                title: () => <>{questionExtendsInfo.value.musicName}</>,
-                value: () => (
-                  <Button round class={styles.playBtn} type="primary" onClick={onEvaluation}>
-                    点击评测
-                    <Icon name="play" />
-                  </Button>
-                )
-              }}
-            </Cell>
-          )}
+          <div class={[styles.unitAnswers]}>
+            {questionExtendsInfo.value && (
+              <Cell
+                class={styles.playSection}
+                center
+                titleClass={['van-ellipsis', styles.playTitle]}
+              >
+                {{
+                  icon: () => <Image class={styles.img} src={iconSong} />,
+                  title: () => <>{questionExtendsInfo.value.musicName}</>,
+                  value: () => (
+                    <Button round class={styles.playBtn} type="primary" onClick={onEvaluation}>
+                      点击评测
+                      <Icon name="play" />
+                    </Button>
+                  )
+                }}
+              </Cell>
+            )}
 
-          <div class={['van-hairline--top', styles.unitScoreNum]}>
-            <div class={styles.score}>{state.score}</div>
-            <div class={styles.scoreTitle}>评测分数</div>
-            <div class={styles.scoreTips}>多次评测取完整评测的最高分数</div>
+            <div class={['van-hairline--top', styles.unitScoreNum]}>
+              <div class={styles.score}>{state.score}</div>
+              <div class={styles.scoreTitle}>评测分数</div>
+              <div class={styles.scoreTips}>多次评测取完整评测的最高分数</div>
+            </div>
           </div>
         </div>
-      </div>
+        {props.showAnalysis && (
+          <div class={styles.unitSubject}>
+            <AnswerAnalysis
+              answerAnalysis={props.analysis.message}
+              topic={props.analysis.topic}
+              userResult={props.analysis.userResult}
+            />
+          </div>
+        )}
+      </>
     )
   }
 })

+ 20 - 10
src/views/unit-test/practice-mode/index.module.less

@@ -15,26 +15,36 @@
     color: #333333;
   }
 
+  :global {
+    .van-cell__value {
+      flex: 0 auto;
+    }
+  }
+
   .unitCount {
     padding-top: 10px;
+    padding-left: 8px;
     display: flex;
     align-items: center;
     justify-content: space-between;
   }
-  .qNums {
+  .countSection {
     display: flex;
     align-items: center;
-    font-size: 14px;
+    font-size: 12px;
+    line-height: 17px;
     color: #333333;
-    line-height: 20px;
-    .num {
-      color: #f67146;
+    flex-direction: column;
+    .nums {
+      font-family: 'DIAN';
+      font-weight: bold;
+      color: #333333;
+      font-size: 20px;
+      padding-bottom: 3px;
+    }
+    & + .countSection {
+      margin-left: 25px;
     }
-  }
-  .icon {
-    width: 14px;
-    height: 14px;
-    margin-right: 4px;
   }
 }
 

+ 118 - 54
src/views/unit-test/practice-mode/index.tsx

@@ -10,7 +10,7 @@ import {
   SwipeItem,
   Tag
 } from 'vant'
-import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import styles from './index.module.less'
 import iconQuestionNums from '../images/icon-question-nums.png'
@@ -28,6 +28,8 @@ import ErrorMode from '../model/error-mode'
 import ResultFinish from '../model/result-finish'
 import { QuestionType } from '../unit'
 import request from '@/helpers/request'
+import { useRect } from '@vant/use'
+import OHeader from '@/components/o-header'
 
 export default defineComponent({
   name: 'unit-detail',
@@ -49,24 +51,29 @@ export default defineComponent({
       resultInfo: {} as any,
       resultStatusType: 'SUCCESS', // 'SUCCESS' | 'FAIL'
       visiableExam: false, // 考试已结束
-      nextStatus: false
+      nextStatus: false,
+      swipeHeight: 'auto' as any
     })
 
     const getExamDetails = async () => {
       try {
-        const { data } = await request.post(
-          '/api-student/studentUnitExamination/startExamination',
-          {
-            requestType: 'form',
-            data: {
-              studentUnitExaminationId: state.id
-            }
+        const { data } = await request.post('/api-student/examinationQuestion/page', {
+          data: {
+            page: 1,
+            row: 50,
+            categoryId: state.id
           }
-        )
+        })
         const { questionJson, studentAnswerJson, ...res } = data
-        const temp = questionJson || []
+        const temp = data.rows || []
         temp.forEach((item: any) => {
-          item.userAnswer = []
+          item.showAnalysis = false // 默认不显示解析
+          item.analysis = {
+            message: item.answerAnalysis,
+            topic: true, // 是否显示结果
+            userResult: false // 用户答题对错
+          }
+          item.userAnswer = [] // 用户答题
         })
         state.questionList = temp
 
@@ -106,33 +113,62 @@ export default defineComponent({
       }
     }
 
+    /**
+     * @description 重置当前的题目高度
+     */
+    const resizeSwipeItemHeight = () => {
+      nextTick(() => {
+        window.scrollTo(0, 0)
+        setTimeout(() => {
+          const currentItemDom: Element =
+            document.querySelectorAll('.swipe-item-question')[state.currentIndex]
+          const rect = useRect(currentItemDom)
+          state.swipeHeight = rect.height
+        }, 100)
+      })
+    }
+
     onMounted(() => {
       getExamDetails()
+
+      resizeSwipeItemHeight()
     })
     return () => (
       <div class={styles.unitDetail}>
+        <OSticky position="top">
+          <OHeader
+            v-slots={{
+              right: () => (
+                <span
+                  style="color: var(--van-primary-color)"
+                  onClick={() => {
+                    // router.push('/create-message')
+                  }}
+                >
+                  结束练习
+                </span>
+              )
+            }}
+          />
+        </OSticky>
         <Cell center class={styles.unitSection}>
           {{
-            title: () => <div class={styles.unitTitle}>{state.examDetail.unitExaminationName}</div>,
-            label: () => (
+            title: () => (
+              <div class={[styles.unitTitle]}>
+                {state.examDetail.unitExaminationName}i门口看门口看看门口看看门口看看门口看看看
+              </div>
+            ),
+            value: () => (
               <div class={styles.unitCount}>
-                <div class={styles.qNums}>
-                  <Icon class={styles.icon} name={iconQuestionNums} />
-                  题目数量{' '}
-                  <span class={styles.num} style={{ paddingLeft: '6px' }}>
-                    {state.currentIndex + 1}
-                  </span>
-                  /{state.examDetail.questionNum}
+                <div class={styles.countSection}>
+                  <span class={styles.nums}>12</span>
+                  <span>答对</span>
                 </div>
-                <div class={styles.qNums}>
-                  <Icon class={styles.icon} name={iconCountDown} />
-                  剩余时长:
-                  <CountDown
-                    ref={countDownRef}
-                    v-model:time={state.time}
-                    format={'mm:ss'}
-                    autoStart={false}
-                  />
+                <div class={styles.countSection}>
+                  <span class={styles.nums} style={{ color: '#F44541' }}>
+                    12
+                  </span>
+                  <span>答错</span>
                 </div>
               </div>
             )
@@ -146,9 +182,10 @@ export default defineComponent({
           duration={300}
           touchable={false}
           lazyRender
-          // initialSwipe={state.currentIndex}
+          height={state.swipeHeight}
           onChange={(index: number) => {
             state.currentIndex = index
+            resizeSwipeItemHeight()
           }}
         >
           {state.questionList.map((item: any, index: number) => (
@@ -164,29 +201,56 @@ export default defineComponent({
             //   </SwipeItem>
             // )
             <SwipeItem>
-              {item.questionTypeCode === QuestionType.RADIO && (
-                <ChoiceQuestion
-                  v-model:value={item.userAnswer}
-                  index={index + 1}
-                  data={item}
-                  type="radio"
-                />
-              )}
-              {item.questionTypeCode === QuestionType.CHECKBOX && (
-                <ChoiceQuestion
-                  v-model:value={item.userAnswer}
-                  index={index + 1}
-                  data={item}
-                  type="checkbox"
-                />
-              )}
-              {item.questionTypeCode === QuestionType.SORT && (
-                <DragQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
-              )}
-              {item.questionTypeCode === QuestionType.LINK && (
-                <KeepLookQuestion v-model:value={item.userAnswer} data={item} index={index + 1} />
-              )}
-              {item.questionTypeCode === QuestionType.PLAY && <PlayQuestion />}
+              <div class="swipe-item-question">
+                {item.questionTypeCode === QuestionType.RADIO && (
+                  <ChoiceQuestion
+                    v-model:value={item.userAnswer}
+                    index={index + 1}
+                    data={item}
+                    type="radio"
+                    showAnalysis={item.showAnalysis}
+                    analysis={item.analysis}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.CHECKBOX && (
+                  <ChoiceQuestion
+                    v-model:value={item.userAnswer}
+                    index={index + 1}
+                    data={item}
+                    type="checkbox"
+                    showAnalysis={item.showAnalysis}
+                    analysis={item.analysis}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.SORT && (
+                  <DragQuestion
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    showAnalysis={item.showAnalysis}
+                    analysis={item.analysis}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.LINK && (
+                  <KeepLookQuestion
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    showAnalysis={item.showAnalysis}
+                    analysis={item.analysis}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.PLAY && (
+                  <PlayQuestion
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    unitId={state.id as any}
+                    showAnalysis={item.showAnalysis}
+                    analysis={item.analysis}
+                  />
+                )}
+              </div>
             </SwipeItem>
           ))}
         </Swipe>

+ 226 - 74
src/views/unit-test/unit-detail/index.tsx

@@ -1,71 +1,143 @@
-import {
-  ActionSheet,
-  Button,
-  Cell,
-  CountDown,
-  Icon,
-  Image,
-  Popup,
-  Swipe,
-  SwipeItem,
-  Tag
-} from 'vant'
-import { defineComponent, onMounted, reactive, ref } from 'vue'
+import { ActionSheet, Button, Cell, Icon, Image, Swipe, SwipeItem } from 'vant'
+import { defineComponent, onMounted, reactive, ref, nextTick } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
-import NoticeStart from '../model/notice-start'
 import styles from './index.module.less'
 import iconQuestionNums from '../images/icon-question-nums.png'
-import iconCountDown from '../images/icon-count-down.png'
 import iconButtonList from '../images/icon-button-list.png'
 import OSticky from '@/components/o-sticky'
 import ChoiceQuestion from '../model/choice-question'
 import AnswerList from '../model/answer-list'
-import ODialog from '@/components/o-dialog'
 import DragQuestion from '../model/drag-question'
 import KeepLookQuestion from '../model/keep-look-question'
 import PlayQuestion from '../model/play-question'
+import request from '@/helpers/request'
+import { QuestionType } from '../unit'
+import { useRect } from '@vant/use'
 
 export default defineComponent({
   name: 'unit-detail',
   setup() {
     const route = useRoute()
     const router = useRouter()
-    const countDownRef = ref()
     const swipeRef = ref()
     const state = reactive({
+      id: route.query.id,
+      examDetail: {} as any,
       visiableAnswer: false,
       currentIndex: 0,
-      questionList: [5],
-      answerList: {},
-      time: 30 * 60 * 1000,
-      visiableSure: false,
-      childs: [
-        { name: 'John', id: 0 },
-        { name: 'Joao', id: 1 },
-        { name: 'Jean', id: 2 }
-      ]
+      questionList: [],
+      time: 0,
+      resultInfo: {} as any,
+      answerResult: [] as any,
+      nextStatus: false,
+      swipeHeight: 'auto' as any
+    })
+
+    const getExamDetails = async () => {
+      try {
+        const { data } = await request.post('/api-school/studentUnitExamination/detail', {
+          requestType: 'form',
+          data: {
+            studentUnitExaminationId: state.id
+          }
+        })
+        const { questionJson, studentAnswerJson, answerResult, ...res } = data
+        const temp = questionJson || []
+        temp.forEach((item: any) => {
+          item.userAnswer = formatUserAnswers(item, studentAnswerJson)
+        })
+        // 问题列表
+        state.questionList = temp
+        // 正确答案
+        state.answerResult = answerResult ? JSON.parse(answerResult) : []
+
+        // 详情
+        state.examDetail = { ...res } || {}
+      } catch {
+        //
+      }
+    }
+
+    /**
+     * @description 初始化用户答案
+     */
+    const formatUserAnswers = (item: any, userAnswer: any) => {
+      // 判断是否有结果
+      if (!userAnswer) return []
+      const answers = JSON.parse(userAnswer) || []
+
+      const questionItem = answers.find((child: any) => child.questionId === item.id)
+      return questionItem ? questionItem.details : []
+    }
+
+    /**
+     * @description 检查用户是否答对
+     * @returns Boolean
+     */
+    const formatUserResult = (id: string) => {
+      let result = false
+      state.answerResult.forEach((item: any) => {
+        if (item.questionId === id) {
+          result = item.rightFlag
+        }
+      })
+      return result
+    }
+
+    /**
+     * @description 重置当前的题目高度
+     */
+    const resizeSwipeItemHeight = () => {
+      nextTick(() => {
+        window.scrollTo(0, 0)
+        setTimeout(() => {
+          const currentItemDom: Element =
+            document.querySelectorAll('.swipe-item-question')[state.currentIndex]
+          console.log(currentItemDom, state.currentIndex)
+          const rect = useRect(currentItemDom)
+          state.swipeHeight = rect.height
+        }, 100)
+      })
+    }
+
+    /**
+     * @description 下一题 | 测试完成
+     */
+    const onNextQuestion = async () => {
+      try {
+        state.nextStatus = true
+        if (state.questionList.length === state.currentIndex + 1) {
+          router.back()
+        }
+        swipeRef.value?.next()
+        state.nextStatus = false
+      } catch {
+        //
+        state.nextStatus = false
+      }
+    }
+
+    onMounted(async () => {
+      await getExamDetails()
+
+      // 初始化高度
+      resizeSwipeItemHeight()
     })
 
     return () => (
       <div class={styles.unitDetail}>
         <Cell center class={styles.unitSection}>
           {{
-            title: () => <div class={styles.unitTitle}>长笛level1上册测验一</div>,
+            title: () => <div class={styles.unitTitle}>{state.examDetail.unitExaminationName}</div>,
             label: () => (
               <div class={styles.unitCount}>
                 <div class={styles.qNums}>
                   <Icon class={styles.icon} name={iconQuestionNums} />
-                  题目数量 <span class={styles.num}>1</span>/4
-                </div>
-                <div class={styles.qNums}>
-                  <Icon class={styles.icon} name={iconCountDown} />
-                  剩余时长:
-                  <CountDown
-                    ref={countDownRef}
-                    time={state.time}
-                    format={'mm:ss'}
-                    autoStart={false}
-                  />
+                  题目数量{' '}
+                  <span class={styles.num} style={{ paddingLeft: '6px' }}>
+                    {state.currentIndex + 1}
+                  </span>
+                  /{state.examDetail.questionNum || 0}
                 </div>
               </div>
             )
@@ -79,26 +151,109 @@ export default defineComponent({
           duration={300}
           touchable={false}
           lazyRender
-          // initialSwipe={state.currentIndex}
+          style={{ paddingBottom: '12px' }}
+          height={state.swipeHeight}
           onChange={(index: number) => {
             state.currentIndex = index
+            resizeSwipeItemHeight()
           }}
         >
-          <SwipeItem>
-            <ChoiceQuestion v-model:value={state.answerList[0]} type="checkbox" />
-          </SwipeItem>
-          <SwipeItem>
-            <ChoiceQuestion v-model:value={state.answerList[1]} type="radio" />
-          </SwipeItem>
-          <SwipeItem>
-            <DragQuestion />
-          </SwipeItem>
-          <SwipeItem>
-            <KeepLookQuestion />
-          </SwipeItem>
-          <SwipeItem>
-            <PlayQuestion />
-          </SwipeItem>
+          {state.questionList.map((item: any, index: number) => (
+            // item.questionTypeCode === QuestionType.CHECKBOX && (
+            //   <SwipeItem>
+            //     <ChoiceQuestion
+            //       v-model:value={item.userAnswer}
+            //       index={index + 1}
+            //       data={item}
+            //       readOnly
+            //       type="radio"
+            //       showAnalysis
+            //       analysis={{
+            //         message: item.answerAnalysis,
+            //         topic: true, // 是否显示结果
+            //         userResult: formatUserResult(item.id) // 用户答题对错
+            //       }}
+            //     />
+            //   </SwipeItem>
+            // )
+            <SwipeItem>
+              <div class="swipe-item-question">
+                {item.questionTypeCode === QuestionType.RADIO && (
+                  <ChoiceQuestion
+                    v-model:value={item.userAnswer}
+                    index={index + 1}
+                    data={item}
+                    readOnly
+                    type="radio"
+                    showAnalysis
+                    analysis={{
+                      message: item.answerAnalysis,
+                      topic: true, // 是否显示结果
+                      userResult: formatUserResult(item.id) // 用户答题对错
+                    }}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.CHECKBOX && (
+                  <ChoiceQuestion
+                    v-model:value={item.userAnswer}
+                    index={index + 1}
+                    data={item}
+                    readOnly
+                    type="checkbox"
+                    showAnalysis
+                    analysis={{
+                      message: item.answerAnalysis,
+                      topic: true, // 是否显示结果
+                      userResult: formatUserResult(item.id) // 用户答题对错
+                    }}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.SORT && (
+                  <DragQuestion
+                    readOnly
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    showAnalysis
+                    analysis={{
+                      message: item.answerAnalysis,
+                      topic: true, // 是否显示结果
+                      userResult: formatUserResult(item.id) // 用户答题对错
+                    }}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.LINK && (
+                  <KeepLookQuestion
+                    readOnly
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    showAnalysis
+                    analysis={{
+                      message: item.answerAnalysis,
+                      topic: true, // 是否显示结果
+                      userResult: formatUserResult(item.id) // 用户答题对错
+                    }}
+                  />
+                )}
+                {item.questionTypeCode === QuestionType.PLAY && (
+                  <PlayQuestion
+                    readOnly
+                    v-model:value={item.userAnswer}
+                    data={item}
+                    index={index + 1}
+                    unitId={state.id as any}
+                    showAnalysis
+                    analysis={{
+                      message: item.answerAnalysis,
+                      topic: true, // 是否显示结果
+                      userResult: formatUserResult(item.id) // 用户答题对错
+                    }}
+                  />
+                )}
+              </div>
+            </SwipeItem>
+          ))}
         </Swipe>
 
         <OSticky position="bottom" background="white">
@@ -120,16 +275,11 @@ export default defineComponent({
               block
               round
               type="primary"
-              onClick={() => {
-                // if (state.questionList.length - 1 === state.currentIndex) {
-                //   state.visiableSure = true
-                // } else {
-                swipeRef.value?.next()
-                // }
-              }}
+              onClick={onNextQuestion}
+              loading={state.nextStatus}
+              disabled={state.nextStatus}
             >
-              下一题
-              {/* {state.questionList.length === state.currentIndex + 1 ? '测试完成' : '下一题'} */}
+              {state.questionList.length === state.currentIndex + 1 ? '确定' : '下一题'}
             </Button>
             <Image
               src={iconButtonList}
@@ -142,7 +292,19 @@ export default defineComponent({
         {/* 题目集合 */}
         <ActionSheet v-model:show={state.visiableAnswer} title="题目列表" safeAreaInsetBottom>
           <AnswerList
-            value={[1, 3, 4]}
+            value={state.questionList}
+            answerResult={state.answerResult}
+            look
+            statusList={[
+              {
+                text: '答对',
+                color: '#71B0FF'
+              },
+              {
+                text: '答错',
+                color: '#FF8486'
+              }
+            ]}
             onSelect={(item: any) => {
               // 跳转,并且跳过动画
               swipeRef.value?.swipeTo(item, {
@@ -152,16 +314,6 @@ export default defineComponent({
             }}
           />
         </ActionSheet>
-
-        <ODialog
-          v-model:show={state.visiableSure}
-          title="测验完成"
-          message="确认本次测验的题目都完成了吗?\n提交后不可修改哦"
-          messageAlign="left"
-          showCancelButton
-          cancelButtonText="再等等"
-          confirmButtonText="确认完成"
-        />
       </div>
     )
   }

+ 7 - 0
src/views/unit-test/unit.ts

@@ -19,6 +19,13 @@ export const QuestionType = {
   SORT: 'SORT',
   PLAY: 'PLAY'
 }
+export const QuestionTypeName = {
+  RADIO: '单选题',
+  CHECKBOX: '多选题',
+  LINK: '连线题',
+  SORT: '排序题',
+  PLAY: '演奏题'
+}
 
 export const AnswerType = {
   IMAGE: 'IMAGE',