index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import { ActionSheet, Button, Cell, Icon, Image, Swipe, SwipeItem } from 'vant'
  2. import { defineComponent, onMounted, reactive, ref, nextTick } from 'vue'
  3. import { useRoute, useRouter } from 'vue-router'
  4. import styles from './index.module.less'
  5. import iconQuestionNums from '../images/icon-question-nums.png'
  6. import iconButtonList from '../images/icon-button-list.png'
  7. import OSticky from '@/components/o-sticky'
  8. import ChoiceQuestion from '../model/choice-question'
  9. import AnswerList from '../model/answer-list'
  10. import DragQuestion from '../model/drag-question'
  11. import KeepLookQuestion from '../model/keep-look-question'
  12. import PlayQuestion from '../model/play-question'
  13. import request from '@/helpers/request'
  14. import { QuestionType } from '../unit'
  15. import { useRect } from '@vant/use'
  16. import { state as baseState } from '@/state'
  17. export default defineComponent({
  18. name: 'unit-detail',
  19. setup() {
  20. const route = useRoute()
  21. const router = useRouter()
  22. const swipeRef = ref()
  23. const state = reactive({
  24. id: route.query.id,
  25. examDetail: {} as any,
  26. visiableAnswer: false,
  27. currentIndex: 0,
  28. questionList: [],
  29. time: 0,
  30. resultInfo: {} as any,
  31. answerResult: [] as any,
  32. nextStatus: false,
  33. swipeHeight: 'auto' as any
  34. })
  35. // 学生端查看详情
  36. const getExamDetails = async () => {
  37. try {
  38. const { data } = await request.post('/api-student/studentUnitExamination/detail', {
  39. requestType: 'form',
  40. data: {
  41. studentUnitExaminationId: state.id
  42. }
  43. })
  44. const { questionJson, studentAnswerJson, answerResult, ...res } = data
  45. const temp = questionJson || []
  46. temp.forEach((item: any) => {
  47. item.userAnswer = formatUserAnswers(item, studentAnswerJson)
  48. item.showAnalysis = true
  49. item.analysis = {
  50. message: item.answerAnalysis,
  51. topic: true, // 是否显示结果
  52. userResult: formatUserResult(item.id) // 用户答题对错
  53. }
  54. })
  55. // 问题列表
  56. state.questionList = temp
  57. // 正确答案
  58. state.answerResult = answerResult ? JSON.parse(answerResult) : []
  59. // 详情
  60. state.examDetail = { ...res } || {}
  61. } catch {
  62. //
  63. }
  64. }
  65. const getExamTeaherDetails = async () => {
  66. try {
  67. const { data } = await request.post(
  68. baseState.platformApi + '/classGroupUnitExamination/report',
  69. {
  70. requestType: 'form',
  71. data: {
  72. classGroupUnitExaminationId: state.id,
  73. level: route.query.level
  74. }
  75. }
  76. )
  77. console.log(data)
  78. // const { questionJson, studentAnswerJson, answerResult, ...res } = data
  79. state.examDetail = {
  80. unitExaminationName: data.unitExaminationName,
  81. questionNum: data.questionNum || 0
  82. }
  83. // 问题列表
  84. const temp = data.examinationQuestionAdds || []
  85. temp.forEach((item: any) => {
  86. item.userAnswer = formatTeacherAnswer(item.answers || [])
  87. item.showAnalysis = true
  88. item.showRate = true
  89. item.analysis = {
  90. message: item.answerAnalysis,
  91. topic: false // 是否显示结果
  92. }
  93. })
  94. // 问题列表
  95. state.questionList = temp
  96. // 正确答案
  97. } catch {
  98. //
  99. }
  100. }
  101. /**
  102. * @description 初始化正确答案
  103. */
  104. const formatTeacherAnswer = (answers: any) => {
  105. console.log(answers)
  106. const result: any = []
  107. answers.forEach((answer: any) => {
  108. // rightAnswerFlag 说明是正确的
  109. if (answer.rightAnswerFlag) {
  110. const rightOption = answers.find(
  111. (item: any) => item.questionExtra === answer.questionExtra
  112. )
  113. result.push({
  114. answer: answer.questionAnswer,
  115. answerId: answer.examinationQuestionAnswerId,
  116. answerExtra: rightOption ? rightOption.questionExtra : null
  117. })
  118. }
  119. })
  120. return result || []
  121. }
  122. /**
  123. * @description 初始化用户答案
  124. */
  125. const formatUserAnswers = (item: any, userAnswer: any) => {
  126. // 判断是否有结果
  127. if (!userAnswer) return []
  128. const answers = userAnswer || []
  129. return answers[item.id] ? answers[item.id] : []
  130. }
  131. /**
  132. * @description 检查用户是否答对
  133. * @returns Boolean
  134. */
  135. const formatUserResult = (id: string) => {
  136. let result = false
  137. state.answerResult.forEach((item: any) => {
  138. if (item.questionId === id) {
  139. result = item.rightFlag
  140. }
  141. })
  142. return result
  143. }
  144. /**
  145. * @description 重置当前的题目高度
  146. */
  147. const resizeSwipeItemHeight = () => {
  148. nextTick(() => {
  149. window.scrollTo(0, 0)
  150. setTimeout(() => {
  151. const currentItemDom: any = document
  152. .querySelectorAll('.van-swipe-item')
  153. [state.currentIndex].querySelector('.swipe-item-question')
  154. const rect = useRect(currentItemDom)
  155. console.log('🚀 ~ setTimeout ~ currentItemDom', currentItemDom)
  156. console.log('🚀 ~ setTimeout ~ rect', rect, state.currentIndex)
  157. state.swipeHeight = rect.height
  158. }, 100)
  159. })
  160. }
  161. /**
  162. * @description 下一题 | 测试完成
  163. */
  164. const onNextQuestion = async () => {
  165. try {
  166. state.nextStatus = true
  167. if (state.questionList.length === state.currentIndex + 1) {
  168. router.back()
  169. }
  170. swipeRef.value?.next()
  171. state.nextStatus = false
  172. } catch {
  173. //
  174. state.nextStatus = false
  175. }
  176. }
  177. onMounted(async () => {
  178. if (baseState.platformType === 'TEACHER' || baseState.platformType === 'SCHOOL') {
  179. await getExamTeaherDetails()
  180. } else {
  181. await getExamDetails()
  182. }
  183. // 初始化高度
  184. resizeSwipeItemHeight()
  185. })
  186. return () => (
  187. <div class={styles.unitDetail}>
  188. <Cell center class={styles.unitSection} border={false}>
  189. {{
  190. title: () => <div class={styles.unitTitle}>{state.examDetail.unitExaminationName}</div>,
  191. label: () => (
  192. <div class={styles.unitCount}>
  193. <div class={styles.qNums}>
  194. <Icon class={styles.icon} name={iconQuestionNums} />
  195. 题目数量{' '}
  196. <span class={styles.num} style={{ paddingLeft: '6px' }}>
  197. {state.currentIndex + 1}
  198. </span>
  199. /{state.examDetail.questionNum || 0}
  200. </div>
  201. </div>
  202. )
  203. }}
  204. </Cell>
  205. <Swipe
  206. loop={false}
  207. showIndicators={false}
  208. ref={swipeRef}
  209. duration={300}
  210. touchable={false}
  211. lazyRender
  212. style={{ paddingBottom: '12px' }}
  213. height={state.swipeHeight}
  214. onChange={(index: number) => {
  215. state.currentIndex = index
  216. // .swipe-item-question
  217. // const t = setInterval(() => {
  218. // console.log(document.querySelectorAll('.van-swipe-item')[state.currentIndex])
  219. // console.log(
  220. // document
  221. // .querySelectorAll('.van-swipe-item')
  222. // [state.currentIndex].querySelector('.swipe-item-question')
  223. // )
  224. // if (
  225. // document
  226. // .querySelectorAll('.van-swipe-item')
  227. // [state.currentIndex].querySelector('.swipe-item-question')
  228. // ) {
  229. // clearInterval(t)
  230. // }
  231. // }, 100)
  232. resizeSwipeItemHeight()
  233. }}
  234. >
  235. {state.questionList.map((item: any, index: number) => (
  236. // item.questionTypeCode === QuestionType.CHECKBOX && (
  237. // <SwipeItem>
  238. // <ChoiceQuestion
  239. // v-model:value={item.userAnswer}
  240. // index={index + 1}
  241. // data={item}
  242. // readOnly
  243. // type="checkbox"
  244. // showRate={item.showRate}
  245. // showAnalysis={item.showAnalysis}
  246. // analysis={item.analysis}
  247. // />
  248. // </SwipeItem>
  249. // )
  250. <SwipeItem>
  251. <div class="swipe-item-question">
  252. {item.questionTypeCode === QuestionType.RADIO && (
  253. <ChoiceQuestion
  254. v-model:value={item.userAnswer}
  255. index={index + 1}
  256. data={item}
  257. readOnly
  258. type="radio"
  259. showRate={item.showRate}
  260. showAnalysis={item.showAnalysis}
  261. analysis={item.analysis}
  262. />
  263. )}
  264. {item.questionTypeCode === QuestionType.CHECKBOX && (
  265. <ChoiceQuestion
  266. v-model:value={item.userAnswer}
  267. index={index + 1}
  268. data={item}
  269. readOnly
  270. type="checkbox"
  271. showRate={item.showRate}
  272. showAnalysis={item.showAnalysis}
  273. analysis={item.analysis}
  274. />
  275. )}
  276. {item.questionTypeCode === QuestionType.SORT && (
  277. <DragQuestion
  278. readOnly
  279. v-model:value={item.userAnswer}
  280. data={item}
  281. index={index + 1}
  282. showRate={item.showRate}
  283. showAnalysis={item.showAnalysis}
  284. analysis={item.analysis}
  285. />
  286. )}
  287. {item.questionTypeCode === QuestionType.LINK && (
  288. <KeepLookQuestion
  289. readOnly
  290. v-model:value={item.userAnswer}
  291. data={item}
  292. index={index + 1}
  293. showRate={item.showRate}
  294. showAnalysis={item.showAnalysis}
  295. analysis={item.analysis}
  296. />
  297. )}
  298. {item.questionTypeCode === QuestionType.PLAY && (
  299. <PlayQuestion
  300. readOnly
  301. v-model:value={item.userAnswer}
  302. data={item}
  303. index={index + 1}
  304. unitId={state.id as any}
  305. showRate={item.showRate}
  306. showAnalysis={item.showAnalysis}
  307. analysis={item.analysis}
  308. />
  309. )}
  310. </div>
  311. </SwipeItem>
  312. ))}
  313. </Swipe>
  314. <OSticky position="bottom" background="white">
  315. <div class={['btnGroup btnMore']}>
  316. {state.currentIndex > 0 && (
  317. <Button
  318. round
  319. block
  320. type="primary"
  321. plain
  322. onClick={() => {
  323. swipeRef.value?.prev()
  324. }}
  325. >
  326. 上一题
  327. </Button>
  328. )}
  329. <Button
  330. block
  331. round
  332. type="primary"
  333. onClick={onNextQuestion}
  334. loading={state.nextStatus}
  335. disabled={state.nextStatus}
  336. >
  337. {state.questionList.length === state.currentIndex + 1 ? '确定' : '下一题'}
  338. </Button>
  339. <Image
  340. src={iconButtonList}
  341. class={[styles.wapList, 'van-haptics-feedback']}
  342. onClick={() => (state.visiableAnswer = true)}
  343. />
  344. </div>
  345. </OSticky>
  346. {/* 题目集合 */}
  347. <ActionSheet v-model:show={state.visiableAnswer} title="题目列表" safeAreaInsetBottom>
  348. <AnswerList
  349. value={state.questionList}
  350. answerResult={state.answerResult}
  351. index={state.currentIndex}
  352. lookType={baseState.platformType === 'STUDENT' ? 'RESULT' : 'CLICK'}
  353. statusList={
  354. baseState.platformType === 'STUDENT'
  355. ? [
  356. {
  357. text: '答对',
  358. color: '#71B0FF'
  359. },
  360. {
  361. text: '答错',
  362. color: '#FF8486'
  363. }
  364. ]
  365. : []
  366. }
  367. onSelect={(item: any) => {
  368. // 跳转,并且跳过动画
  369. swipeRef.value?.swipeTo(item, {
  370. immediate: true
  371. })
  372. state.visiableAnswer = false
  373. }}
  374. />
  375. </ActionSheet>
  376. </div>
  377. )
  378. }
  379. })