index.tsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import { Tag, Image, Button } from 'vant';
  2. import {
  3. computed,
  4. defineComponent,
  5. nextTick,
  6. onMounted,
  7. PropType,
  8. reactive,
  9. watch
  10. } from 'vue';
  11. import { AnswerType, labelOptions, QuestionType } from '../../unit';
  12. import Draggable from 'vuedraggable';
  13. import styles from './index.module.less';
  14. import AnserTitle from '../anser-title';
  15. import AnswerAnalysis from '../answer-analysis';
  16. // 单选和多选题
  17. export default defineComponent({
  18. name: 'choice-question',
  19. props: {
  20. value: {
  21. type: Array,
  22. default: () => []
  23. },
  24. index: {
  25. // 题目是第几道
  26. type: Number,
  27. default: 1
  28. },
  29. data: {
  30. type: Object,
  31. default: () => ({})
  32. },
  33. /* 只读 */
  34. readOnly: {
  35. type: Boolean,
  36. default: false
  37. },
  38. showRate: {
  39. // 是否显示答题的正确率
  40. type: Boolean,
  41. default: false
  42. },
  43. showAnalysis: {
  44. // 是否显示解析
  45. type: Boolean,
  46. default: false
  47. },
  48. analysis: {
  49. type: Object,
  50. default: () => ({
  51. message: '',
  52. topic: false, // 是否显示结果
  53. userResult: true // 用户答题对错
  54. })
  55. }
  56. },
  57. emits: ['update:value'],
  58. setup(props, { emit, slots }) {
  59. const state = reactive({
  60. domId: 'draggableContainer' + +new Date(),
  61. drag: false,
  62. sortable: null as any,
  63. list: [] as any,
  64. options: [] as any,
  65. alst: [] as any
  66. });
  67. // 返回选中的结果
  68. const onSelect = () => {
  69. const list = state.list || [];
  70. const result: any = [];
  71. list.forEach((item: any, index: number) => {
  72. // console.log(item, 'item')
  73. result.push({
  74. answerId: item.answerId,
  75. answer: item.answer,
  76. answerExtra: index + 1
  77. });
  78. });
  79. emit('update:value', result);
  80. };
  81. // 修改题目逻辑
  82. const onSelectAnswer = (item: any) => {
  83. // 判断是否已经选中了
  84. if (item.checked || props.readOnly) return;
  85. const result: any = [];
  86. // console.log(state.options, 'state.options')
  87. state.options.forEach((option: any, index: any) => {
  88. // console.log(option, '------')
  89. result.push({
  90. answerId: option.index,
  91. answer: option.leftValue,
  92. answerExtra: index + 1
  93. });
  94. });
  95. result.push({
  96. answerId: item.examinationQuestionAnswerId,
  97. answer: item.questionAnswer,
  98. answerExtra: state.list.length + 1
  99. });
  100. state.list.push({
  101. answerId: item.examinationQuestionAnswerId,
  102. answer: item.questionAnswer,
  103. answerExtra: state.list.length + 1
  104. });
  105. emit('update:value', result);
  106. nextTick(() => {
  107. initOptions();
  108. });
  109. };
  110. const answers = computed(() => {
  111. const list: any = props.data.answers || [];
  112. const value: any = props.value || [];
  113. // console.log(value, 'answer')
  114. list.forEach((item: any) => {
  115. const tempIndex = value.findIndex(
  116. (c: any) => c.answerId === item.examinationQuestionAnswerId
  117. );
  118. // if (tempIndex !== -1) {
  119. item.checked = tempIndex !== -1 ? true : false;
  120. // }
  121. });
  122. return list;
  123. });
  124. const initOptions = () => {
  125. const answers = props.data.answers || [];
  126. const userAnswer = props.data.userAnswer || []; // 用户填写的答案
  127. // console.log(answers, userAnswer)
  128. // state.options = []
  129. nextTick(() => {
  130. if (userAnswer.length > 0) {
  131. const tempList: any = [];
  132. userAnswer.forEach((answer: any, index: any) => {
  133. const rightOption = answers.find(
  134. (child: any) =>
  135. answer.answerId === child.examinationQuestionAnswerId
  136. );
  137. const rightValue = answers.find(
  138. (child: any) => answer.answerExtra == child.questionExtra
  139. );
  140. const tmp = {
  141. itemIndex: index,
  142. index: answer.answerId, // 左边的值
  143. leftValue: answer.answer, // 左边的值
  144. rightValue: answer.answerExtra, // 右边的值
  145. leftType: rightOption
  146. ? rightOption.questionAnswerTypeCode || 'TXT'
  147. : 'TXT', // 左边类型
  148. rightType: rightOption
  149. ? rightOption.questionExtraTypeCode || 'TXT'
  150. : 'TXT', // 右边类型
  151. rightIndex: rightValue
  152. ? rightValue.examinationQuestionAnswerId
  153. : ''
  154. };
  155. // state.options.push(tmp)
  156. tempList.push(tmp);
  157. });
  158. state.options = tempList;
  159. }
  160. // console.log(props.data, 'data');
  161. });
  162. };
  163. watch(
  164. () => state.options,
  165. () => {
  166. const list = state.options || [];
  167. const result: any = [];
  168. list.forEach((item: any, index: number) => {
  169. result.push({
  170. answerId: item.index,
  171. answer: item.leftValue,
  172. answerExtra: index + 1
  173. });
  174. });
  175. emit('update:value', result);
  176. }
  177. );
  178. onMounted(() => {
  179. initOptions();
  180. });
  181. return () => (
  182. <>
  183. <div class={styles.unitSubject}>
  184. {slots.title && slots.title()}
  185. {/* 标题 */}
  186. <AnserTitle
  187. index={props.index}
  188. name={props.data.name}
  189. showRate={props.showRate}
  190. score={props.data.totalScore}
  191. answerType={QuestionType.SORT}
  192. extra={{
  193. rightRate: props.data.rightRate,
  194. questionDetail: props.data.questionDetail,
  195. mediaUrls: props.data.mediaUrls
  196. }}
  197. />
  198. <div class={[styles.unitAnswers]}>
  199. {answers.value.map((item: any, index: number) => (
  200. <div
  201. class={[styles.unitAnswer, item.checked && styles.active]}
  202. onClick={() => onSelectAnswer(item)}>
  203. <div class={styles.answerContent}>
  204. <span class={styles.option}>{labelOptions[index + 1]}.</span>
  205. {item.questionAnswerTypeCode === AnswerType.IMAGE && (
  206. <div class={styles.value}>
  207. <Image src={item.questionAnswer} />
  208. </div>
  209. )}
  210. {item.questionAnswerTypeCode === AnswerType.TXT && (
  211. <div class={styles.value}>{item.questionAnswer}</div>
  212. )}
  213. </div>
  214. </div>
  215. ))}
  216. <div class={[styles.sortReset, 'van-hairline--top']}>
  217. <span class={styles.tips}>我的回答(可拖拽调整顺序)</span>
  218. <Button
  219. type="primary"
  220. round
  221. disabled={props.readOnly}
  222. onClick={() => {
  223. state.options = [];
  224. state.list = [];
  225. onSelect();
  226. initOptions();
  227. }}>
  228. 重置
  229. </Button>
  230. </div>
  231. {props.readOnly ? (
  232. state.options.map((item: any) => (
  233. <div class={styles.itemsContainer}>
  234. {item.leftType === AnswerType.TXT && (
  235. <Tag class={[styles.items]} data-id={item.itemIndex}>
  236. {item.leftValue}
  237. </Tag>
  238. )}
  239. {item.leftType === AnswerType.IMAGE && (
  240. <Image
  241. src={item.leftValue}
  242. data-id={item.itemIndex}
  243. class={[styles.imgs, 'van-hairline--surround']}
  244. fit="cover"
  245. />
  246. )}
  247. </div>
  248. ))
  249. ) : (
  250. <Draggable
  251. v-model:modelValue={state.options}
  252. itemKey="itemIndex"
  253. componentData={{
  254. itemKey: 'id',
  255. tag: 'div',
  256. animation: 200,
  257. group: 'description'
  258. }}>
  259. {{
  260. item: (element: any) => {
  261. const item = element.element;
  262. return (
  263. <div class={styles.itemsContainer}>
  264. {item.leftType === AnswerType.TXT && (
  265. <Tag class={[styles.items]} data-id={item.itemIndex}>
  266. {item.leftValue}
  267. </Tag>
  268. )}
  269. {item.leftType === AnswerType.IMAGE && (
  270. <Image
  271. src={item.leftValue}
  272. data-id={item.itemIndex}
  273. class={[styles.imgs, 'van-hairline--surround']}
  274. fit="cover"
  275. />
  276. )}
  277. </div>
  278. );
  279. }
  280. }}
  281. </Draggable>
  282. )}
  283. </div>
  284. </div>
  285. {props.showAnalysis && (
  286. <AnswerAnalysis
  287. answerAnalysis={props.analysis.message}
  288. topic={props.analysis.topic}
  289. userResult={props.analysis.userResult}
  290. />
  291. )}
  292. </>
  293. );
  294. }
  295. });