useExam.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { EnumQuestionType } from "@/common/enum";
  2. import { getPaper } from '@/api/modules/study';
  3. import { Study } from "@/types";
  4. import { Question } from "@/types/study";
  5. export const useExam = () => {
  6. const questionTypeDesc: Record<EnumQuestionType, string> = {
  7. [EnumQuestionType.SINGLE_CHOICE]: '单选题',
  8. [EnumQuestionType.MULTIPLE_CHOICE]: '多选题',
  9. [EnumQuestionType.JUDGMENT]: '判断题',
  10. [EnumQuestionType.FILL_IN_THE_BLANK]: '填空题',
  11. [EnumQuestionType.SUBJECTIVE]: '主观题',
  12. [EnumQuestionType.SHORT_ANSWER]: '简答题',
  13. [EnumQuestionType.ESSAY]: '问答题',
  14. [EnumQuestionType.ANALYSIS]: '分析题',
  15. [EnumQuestionType.OTHER]: '阅读题'
  16. }
  17. // 题型顺序
  18. const questionTypeOrder = [
  19. EnumQuestionType.SINGLE_CHOICE,
  20. EnumQuestionType.MULTIPLE_CHOICE,
  21. EnumQuestionType.JUDGMENT,
  22. EnumQuestionType.FILL_IN_THE_BLANK,
  23. EnumQuestionType.SUBJECTIVE,
  24. EnumQuestionType.SHORT_ANSWER,
  25. EnumQuestionType.ESSAY,
  26. EnumQuestionType.ANALYSIS,
  27. EnumQuestionType.OTHER
  28. ];
  29. let interval: NodeJS.Timeout | null = null;
  30. const countDownCallback = ref<() => void>(() => { });
  31. // 练习时长
  32. const practiceDuration = ref<number>(0);
  33. const formatPracticeDuration = computed(() => {
  34. const hours = Math.floor(practiceDuration.value / 3600);
  35. const minutes = Math.floor((practiceDuration.value % 3600) / 60);
  36. const seconds = practiceDuration.value % 60;
  37. return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  38. });
  39. // 考试时长
  40. const examDuration = ref<number>(0);
  41. const formatExamDuration = computed(() => {
  42. const hours = Math.floor(examDuration.value / 3600);
  43. const minutes = Math.floor((examDuration.value % 3600) / 60);
  44. const seconds = examDuration.value % 60;
  45. return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  46. });
  47. const swiperDuration = ref<number>(300);
  48. const questionList = ref<Study.Question[]>([]);
  49. // 收藏列表
  50. const favoriteList = ref<Study.Question[]>([]);
  51. // 不会列表
  52. const notKnowList = ref<Study.Question[]>([]);
  53. // 重点标记列表
  54. const markList = ref<Study.Question[]>([]);
  55. // 包含状态的问题列表
  56. const stateQuestionList = computed(() => {
  57. return questionList.value.map(item => {
  58. return {
  59. ...item,
  60. isDone: isDone(item)
  61. }
  62. });
  63. });
  64. const groupedQuestionList = computed(() => {
  65. // 状态:已做、未做、是否不会、是否标记,整体按照题型分组
  66. const state = questionTypeOrder.map(type => {
  67. return {
  68. type,
  69. list: [] as {
  70. question: Study.Question;
  71. index: number
  72. }[]
  73. }
  74. });
  75. for (let i = 0; i <= stateQuestionList.value.length - 1; i++) {
  76. const qs = stateQuestionList.value[i];
  77. state.forEach(item => {
  78. qs.progress = calcProgress(qs);
  79. if (qs.typeId === item.type) {
  80. // qs.isDone = isDone(qs);
  81. item.list.push({
  82. question: qs,
  83. index: i
  84. });
  85. }
  86. });
  87. }
  88. return state;
  89. });
  90. const isAllDone = computed(() => {
  91. return questionList.value.every(q => isDone(q));
  92. });
  93. // 当前下标
  94. const currentIndex = ref<number>(0);
  95. const totalCount = computed(() => {
  96. return questionList.value.length;
  97. });
  98. const doneCount = computed(() => {
  99. // 有答案的或者不会做的,都认为是做了
  100. // return groupedQuestionList.value.reduce((acc, item) => acc + item.list.filter(q => q.question.isDone || q.question.isNotKnow).length, 0);
  101. return stateQuestionList.value.filter(q => q.isDone || q.isNotKnow).length;
  102. });
  103. const notDoneCount = computed(() => {
  104. // return questionList.value.length - doneCount.value;
  105. return stateQuestionList.value.length - doneCount.value;
  106. });
  107. const notKnowCount = computed(() => {
  108. // return groupedQuestionList.value.reduce((acc, item) => acc + item.list.filter(q => q.question.isNotKnow).length, 0);
  109. return stateQuestionList.value.filter(q => q.isNotKnow).length;
  110. });
  111. const markCount = computed(() => {
  112. // return groupedQuestionList.value.reduce((acc, item) => acc + item.list.filter(q => q.question.isMark).length, 0);
  113. return stateQuestionList.value.filter(q => q.isMark).length;
  114. });
  115. // 包含子题的题目计算整体做题进度
  116. const calcProgress = (qs: Study.Question): number => {
  117. if (qs.subQuestions && qs.subQuestions.length > 0) {
  118. return qs.subQuestions.reduce((acc, q) => acc + calcProgress(q), 0) / qs.subQuestions.length;
  119. }
  120. return qs.isDone ? 100 : 0;
  121. }
  122. const isDone = (qs: Study.Question): boolean => {
  123. // console.log(qs.answers, qs.answers && qs.answers.filter(item => !!item).length > 0 || !!qs.isNotKnow)
  124. if (qs.subQuestions && qs.subQuestions.length > 0) {
  125. return qs.subQuestions.every(q => isDone(q));
  126. }
  127. return qs.answers && qs.answers.filter(item => !!item).length > 0 || !!qs.isNotKnow;
  128. }
  129. const nextQuestion = () => {
  130. if (currentIndex.value >= questionList.value.length - 1) {
  131. return;
  132. }
  133. currentIndex.value++;
  134. }
  135. const prevQuestion = () => {
  136. if (currentIndex.value <= 0) {
  137. return;
  138. }
  139. currentIndex.value--;
  140. }
  141. const nextQuestionQuickly = () => {
  142. if (currentIndex.value >= questionList.value.length - 1) {
  143. return;
  144. }
  145. swiperDuration.value = 0;
  146. setTimeout(() => {
  147. nextQuestion();
  148. setTimeout(() => {
  149. swiperDuration.value = 300;
  150. }, 0);
  151. }, 0);
  152. }
  153. const prevQuestionQuickly = () => {
  154. if (currentIndex.value <= 0) {
  155. return;
  156. }
  157. swiperDuration.value = 0;
  158. setTimeout(() => {
  159. prevQuestion();
  160. setTimeout(() => {
  161. swiperDuration.value = 300;
  162. }, 0);
  163. }, 0);
  164. }
  165. const changeIndex = (index: number) => {
  166. swiperDuration.value = 0;
  167. setTimeout(() => {
  168. currentIndex.value = index;
  169. setTimeout(() => {
  170. swiperDuration.value = 300;
  171. }, 0);
  172. }, 0);
  173. }
  174. // 开始计时
  175. const startPracticeDuration = () => {
  176. interval = setInterval(() => {
  177. practiceDuration.value += 1;
  178. }, 1000);
  179. }
  180. // 停止计时
  181. const stopPracticeDuration = () => {
  182. interval && clearInterval(interval);
  183. interval = null;
  184. }
  185. // 开始倒计时
  186. const startExamDuration = () => {
  187. interval = setInterval(() => {
  188. if (examDuration.value <= 0) {
  189. console.log('停止倒计时')
  190. stopExamDuration();
  191. return;
  192. }
  193. examDuration.value -= 1;
  194. }, 1000);
  195. }
  196. // 停止倒计时
  197. const stopExamDuration = () => {
  198. interval && clearInterval(interval);
  199. interval = null;
  200. countDownCallback.value && countDownCallback.value();
  201. }
  202. const setExamDuration = (duration: number) => {
  203. examDuration.value = duration;
  204. }
  205. const setPracticeDuration = (duration: number) => {
  206. practiceDuration.value = duration;
  207. }
  208. const setCountDownCallback = (callback: () => void) => {
  209. countDownCallback.value = callback;
  210. }
  211. const setDuration = (duration: number) => {
  212. examDuration.value = duration;
  213. practiceDuration.value = duration;
  214. }
  215. const setQuestionList = (list: Study.ApiQuestion[]) => {
  216. const orders = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
  217. // 数据预处理
  218. // 1、给每个项目补充额外字段
  219. const parseQuestion = (item: Study.ApiQuestion): Study.Question => {
  220. return {
  221. title: item.title,
  222. // 处理没有题型的大题,统一作为阅读题
  223. typeId: (item.typeId === null || item.typeId === undefined) ? EnumQuestionType.OTHER : item.typeId,
  224. id: item.id,
  225. answers: item.answers || [],
  226. subQuestions: item.subQuestions?.map(parseQuestion) || [],
  227. options: item.options?.map((option, index) => {
  228. return {
  229. name: option,
  230. no: orders[index],
  231. id: index,
  232. isAnswer: false
  233. } as Study.QuestionOption
  234. }) || [],
  235. isDone: false,
  236. isMark: item.isMark,
  237. isNotKnow: item.isNotKnow,
  238. isFavorite: item.isFavorite
  239. } as Study.Question
  240. }
  241. const arr: Study.Question[] = list.map(item => {
  242. return parseQuestion(item);
  243. });
  244. console.log('处理后的题目', arr)
  245. questionList.value = arr;
  246. }
  247. const reset = () => {
  248. questionList.value = questionList.value.map(item => {
  249. return {
  250. ...item,
  251. answers: [],
  252. isMark: false,
  253. isNotKnow: false
  254. } as Study.Question;
  255. });
  256. changeIndex(0);
  257. practiceDuration.value = 0;
  258. examDuration.value = 0;
  259. interval && clearInterval(interval);
  260. interval = null;
  261. }
  262. return {
  263. questionList,
  264. groupedQuestionList,
  265. stateQuestionList,
  266. favoriteList,
  267. notKnowList,
  268. markList,
  269. currentIndex,
  270. isAllDone,
  271. totalCount,
  272. doneCount,
  273. notDoneCount,
  274. notKnowCount,
  275. markCount,
  276. questionTypeDesc,
  277. nextQuestion,
  278. prevQuestion,
  279. nextQuestionQuickly,
  280. prevQuestionQuickly,
  281. swiperDuration,
  282. practiceDuration,
  283. examDuration,
  284. formatExamDuration,
  285. formatPracticeDuration,
  286. startPracticeDuration,
  287. stopPracticeDuration,
  288. setDuration,
  289. startExamDuration,
  290. stopExamDuration,
  291. setExamDuration,
  292. setPracticeDuration,
  293. setCountDownCallback,
  294. setQuestionList,
  295. changeIndex,
  296. reset
  297. }
  298. }