question-book-item.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <template>
  2. <view class="question-book-item p-24">
  3. <view class="question-title">
  4. <mp-html :content="title" />
  5. </view>
  6. <view class="question-options">
  7. <view class="question-option" v-for="(option, index) in data.options" :class="getStyleClass(option)" :key="index">
  8. <view>
  9. <uv-icon v-if="option.isCorrect" name="checkmark-circle-fill" color="#2CC6A0" size="22" />
  10. <uv-icon v-else-if="!option.isCorrect && option.isSelected" name="close-circle-fill" color="#FF5B5C"
  11. size="22" />
  12. <view v-else class="question-option-index">{{ option.no }}</view>
  13. </view>
  14. <view class="question-option-content">
  15. <mp-html :content="option.name" />
  16. </view>
  17. </view>
  18. </view>
  19. <uv-line margin="15px 0 10px 0" />
  20. <view class="question-toolbar">
  21. <view class="flex items-center gap-6" @click="handleToggleCollect">
  22. <uv-icon :name="data.collect ? 'star-fill' : 'star'" size="20" color="var(--primary-color)" />
  23. <text class="text-26 text-primary">{{ data.collect ? '已收藏' : '收藏' }}</text>
  24. </view>
  25. <view class="flex items-center gap-6" @click="handleCorrect">
  26. <uv-icon name="info-circle" size="18" color="var(--primary-color)" />
  27. <text class="text-26 text-primary">纠错</text>
  28. </view>
  29. <view v-if="!showAnswer" class="flex items-center" @click="toggleShowParse">
  30. <cover-view class="w-60 h-60 flex items-center justify-center">
  31. <cover-image v-show="!showParse" src="@/pagesSystem/static/image/icon/icon-eye.png" mode="widthFix"
  32. class="w-38 h-38" />
  33. <cover-image v-show="showParse" src="@/pagesSystem/static/image/icon/icon-eye-off.png" mode="widthFix"
  34. class="w-38 h-38" />
  35. </cover-view>
  36. <text class="text-26 text-primary">{{ showParse ? '隐藏解析' : '查看解析' }}</text>
  37. </view>
  38. <view v-else class="flex items-center" @click="handleDelete">
  39. <uv-icon name="trash" size="18" color="var(--danger)" />
  40. <text class="text-26 text-danger">删除</text>
  41. </view>
  42. </view>
  43. <view v-show="showParse" class="question-parse">
  44. <view v-if="showAnswer">
  45. <view class="text-28 text-fore-title font-bold">知识点</view>
  46. <view class="mt-10 text-26 text-primary">
  47. <mp-html :content="data.knowledge || '暂无知识点'" />
  48. </view>
  49. </view>
  50. <view>
  51. <view class="text-28 text-fore-title font-bold">答案</view>
  52. <view class="mt-10 text-28 text-primary">
  53. <mp-html :content="data.answer || '略'" />
  54. </view>
  55. </view>
  56. <view>
  57. <view class="text-28 text-fore-title font-bold">解析</view>
  58. <view class="mt-10 text-26 text-primary">
  59. <mp-html :content="data.parse || '暂无解析'" />
  60. </view>
  61. </view>
  62. </view>
  63. </view>
  64. </template>
  65. <script lang="ts" setup>
  66. import { EnumQuestionType } from '@/common/enum';
  67. import type { Study } from '@/types';
  68. const props = defineProps({
  69. showAnswer: {
  70. type: Boolean,
  71. default: false
  72. },
  73. data: {
  74. type: Object as PropType<Study.FavoriteQuestionVO>,
  75. default: () => ({})
  76. }
  77. });
  78. const showParse = ref(props.showAnswer);
  79. const title = computed(() => {
  80. return `[${props.data.qtype}] ${props.data.title}`;
  81. });
  82. const emit = defineEmits<{
  83. (e: 'correct', question: Study.FavoriteQuestionVO): void;
  84. (e: 'toggleCollect', question: Study.FavoriteQuestionVO): void;
  85. (e: 'delete', question: Study.FavoriteQuestionVO): void;
  86. }>();
  87. const handleCorrect = () => {
  88. emit('correct', props.data);
  89. }
  90. const handleToggleCollect = () => {
  91. emit('toggleCollect', props.data);
  92. }
  93. const handleDelete = () => {
  94. emit('delete', props.data);
  95. }
  96. const toggleShowParse = () => {
  97. showParse.value = !showParse.value;
  98. }
  99. const getStyleClass = (option: Study.QuestionOption) => {
  100. let customClass = '';
  101. let { answers, answer, typeId } = props.data;
  102. answers = answers?.filter(item => item !== ' ') || [];
  103. answer = answer || ''
  104. if ([EnumQuestionType.SINGLE_CHOICE, EnumQuestionType.JUDGMENT].includes(typeId)) {
  105. if (option.isCorrect) {
  106. customClass = 'question-option-correct';
  107. } else if (option.isIncorrect) {
  108. customClass = 'question-option-incorrect';
  109. }
  110. } else if ([EnumQuestionType.MULTIPLE_CHOICE].includes(typeId)) {
  111. // 我选择的答案
  112. if (option.isSelected) {
  113. if (option.isCorrect) {
  114. customClass = 'question-option-correct';
  115. } else {
  116. customClass = 'question-option-incorrect';
  117. }
  118. } else {
  119. // 漏选的答案
  120. if (option.isMissed) {
  121. customClass = 'question-option-miss';
  122. }
  123. }
  124. }
  125. return customClass;
  126. };
  127. </script>
  128. <style lang="scss" scoped>
  129. .question-title {
  130. @apply text-28 text-fore-title break-words;
  131. }
  132. .question-options {
  133. @apply mt-20;
  134. }
  135. .question-toolbar {
  136. @apply flex items-center justify-between gap-20;
  137. }
  138. .question-parse {
  139. @apply mt-30 flex flex-col gap-20;
  140. }
  141. .question-option {
  142. @apply flex items-center px-30 py-24 bg-back rounded-8 border border-none border-transparent;
  143. .question-option-index {
  144. @apply w-40 h-40 rounded-full bg-transparent text-30 text-fore-light font-bold flex items-center justify-center flex-shrink-0;
  145. }
  146. .question-option-content {
  147. @apply text-28 text-fore-title ml-20 flex-1 min-w-0;
  148. }
  149. }
  150. .question-option-selected {
  151. @apply bg-[#b5eaff8e];
  152. .question-option-index {
  153. @apply bg-primary text-white;
  154. }
  155. .question-option-content {
  156. @apply text-primary;
  157. }
  158. }
  159. .question-option-not-know {
  160. @apply bg-[#b5eaff8e];
  161. .question-option-content {
  162. @apply text-primary;
  163. }
  164. }
  165. .question-option-correct {
  166. @apply bg-[#E7FCF8] border-[#E7FCF8] text-[#2CC6A0];
  167. .question-option-index {
  168. @apply text-[#2CC6A0];
  169. }
  170. .question-option-content {
  171. @apply text-[#2CC6A0];
  172. }
  173. }
  174. .question-option-miss {
  175. @apply relative overflow-hidden;
  176. &::before {
  177. content: '';
  178. position: absolute;
  179. right: -56rpx;
  180. top: 15rpx;
  181. width: 180rpx;
  182. height: 36rpx;
  183. background: rgba(255, 91, 92, 0.2);
  184. transform: rotate(30deg);
  185. box-shadow: 0 2rpx 4rpx rgba(255, 91, 92, 0.1);
  186. }
  187. &::after {
  188. content: '漏选';
  189. position: absolute;
  190. right: -8rpx;
  191. top: 14rpx;
  192. width: 100rpx;
  193. height: 32rpx;
  194. color: #FF5B5C;
  195. font-size: 20rpx;
  196. // font-weight: bold;
  197. transform: rotate(30deg);
  198. display: flex;
  199. align-items: center;
  200. justify-content: center;
  201. line-height: 1;
  202. }
  203. }
  204. .question-option-incorrect {
  205. @apply bg-[#FEEDE9] border-[#FEEDE9] text-[#FF5B5C];
  206. .question-option-index {
  207. @apply text-[#FF5B5C];
  208. }
  209. .question-option-content {
  210. @apply text-[#FF5B5C];
  211. }
  212. }
  213. .question-option+.question-option {
  214. @apply mt-24;
  215. }
  216. </style>