questionsPopup.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <template>
  2. <u-popup :show="showPopup" mode="bottom" round="30" @close="close">
  3. <view class="ques-content">
  4. <view class="quesTitleViewClass">
  5. <view class="quesTitleLeftClass">
  6. <image src="/static/image/hall/ques_icon.png" mode="aspectFill"></image>
  7. <text class="quesTitleTextClass">问答题</text>
  8. </view>
  9. <view @tap="close">
  10. <u-icon name="close-circle-fill" color="#D8D8D8FF" size="32"></u-icon>
  11. </view>
  12. </view>
  13. <scroll-view class="scroll-y" scroll-y>
  14. <view class="dataItemViewClass" v-for="(item, index) in quesList" :key="index">
  15. <view class="ques-title">
  16. <text>{{ index + 1 }}.</text>
  17. <!-- <view class="ques-type" v-show="item.type == 1 || item.type == 2">
  18. {{item.type == 1 ? '单选' : item.type == 2 ? '多选' : ''}}
  19. </view> -->
  20. <text>{{ item.title }}</text>
  21. </view>
  22. <view :class="isAnswer(item, option.name) ? 'ques-option ques-option-active' : 'ques-option'"
  23. v-for="(option, idx) in item.question" :key="idx" @tap="handleSelectAnswer(item, option)">
  24. <view>
  25. {{ numberToLetter(idx) }}.
  26. </view>
  27. <view>{{ option.name }}</view>
  28. </view>
  29. </view>
  30. <view class="empty" v-if="quesList.length == 0">暂未设置题目~</view>
  31. </scroll-view>
  32. <view v-if="quesList.length > 0" class="bottomBtnViewClass">
  33. <view v-if="~~remainSeconds > 0" class="btnViewClass" @tap.stop="close">再观看{{ formatTime(remainSeconds)
  34. }}可答题</view>
  35. <view v-else :class="isOkAnswer ? 'btnViewClass btnViewClass-active' : 'btnViewClass'"
  36. @tap="isOkAnswer ? confirmAnswer() : null">
  37. 提交答案</view>
  38. </view>
  39. </view>
  40. </u-popup>
  41. </template>
  42. <script>
  43. import {
  44. newVideoDetailsApi
  45. } from '@/api/course'
  46. export default {
  47. props: {
  48. isShow: {
  49. type: Boolean,
  50. default: false
  51. },
  52. typeStatus: {
  53. type: Number,
  54. default: 1, //提交类型:1、可以提交答案,2、需再观看视频
  55. },
  56. // 父组件传:剩余秒数
  57. remainSeconds: {
  58. type: Number,
  59. default: 0
  60. },
  61. videoId: {
  62. type: Number,
  63. default: ''
  64. },
  65. },
  66. data() {
  67. return {
  68. showPopup: false,
  69. quesList: [],
  70. }
  71. },
  72. computed: {
  73. isOkAnswer() {
  74. let newData = false
  75. newData = !(this.quesList.map(item => item.answer).some(itemOne => itemOne == null || itemOne === ''))
  76. return newData
  77. },
  78. isAnswer() {
  79. return (item, name) => {
  80. if (item.type == 1) {
  81. return item.answer == name
  82. } else if (item.type == 2) {
  83. const array = item.answer.split(',')
  84. return array.some(i => i == name)
  85. } else {
  86. return false
  87. }
  88. }
  89. }
  90. },
  91. watch: {
  92. isShow(newVal) {
  93. this.showPopup = newVal
  94. if (newVal) {
  95. this.getNewQuestionsList()
  96. }
  97. }
  98. },
  99. methods: {
  100. // 获取新视频详情
  101. async getNewQuestionsList() {
  102. let res = await newVideoDetailsApi({
  103. videoId: this.videoId
  104. })
  105. if (res.code == 200) {
  106. this.quesList = res.data.questionBankList.length > 0 ? res.data.questionBankList.map(item => ({ ...item, question: JSON.parse(item.question), answer: '' })) : []
  107. }
  108. },
  109. // 秒数 → 分:秒 格式
  110. formatTime(seconds) {
  111. if (seconds <= 0) {
  112. return '00分00秒'
  113. }
  114. const m = Math.floor(seconds / 60)
  115. const s = seconds % 60
  116. return `${m.toString().padStart(2, '0')}分${s.toString().padStart(2, '0')}秒`
  117. },
  118. numberToLetter(num) {
  119. // 将数字转换为字母的 ASCII 码
  120. let letterCode = num + 65;
  121. // 将 ASCII 码转换为大写字母
  122. let letter = String.fromCharCode(letterCode);
  123. return letter;
  124. },
  125. // 点击选择答案
  126. handleSelectAnswer(item, option) {
  127. if (item.type == 1) {
  128. // 单选option
  129. if (item.answer === option.name) {
  130. item.answer = ''
  131. } else {
  132. item.answer = option.name
  133. }
  134. } else if (item.type == 2) {
  135. // 多选
  136. let answer = item.answer ? item.answer.split(',') : []
  137. if (answer.indexOf(option.name) === -1) {
  138. answer.push(option.name)
  139. item.answer = answer.join(',')
  140. } else {
  141. answer.splice(answer.indexOf(option.name), 1)
  142. item.answer = answer.join(',')
  143. }
  144. }
  145. },
  146. // 提交答案
  147. confirmAnswer() {
  148. this.$emit('submitAnswer', this.quesList)
  149. },
  150. // 关闭弹框
  151. close() {
  152. this.$emit('closeMethod')
  153. },
  154. },
  155. }
  156. </script>
  157. <style lang="scss" scoped>
  158. @mixin u-flex($flexD, $alignI, $justifyC) {
  159. display: flex;
  160. flex-direction: $flexD;
  161. align-items: $alignI;
  162. justify-content: $justifyC;
  163. }
  164. .empty {
  165. @include u-flex(row, center, center);
  166. padding: 24rpx 50rpx;
  167. color: #999999;
  168. }
  169. .scroll-y {
  170. width: 100%;
  171. height: calc(60vh - 300rpx)
  172. }
  173. .quesTitleViewClass {
  174. @include u-flex(row, center, space-between);
  175. padding-bottom: 26rpx;
  176. border-bottom: 2rpx solid #D8D8D8;
  177. margin-bottom: 24rpx;
  178. .quesTitleLeftClass {
  179. @include u-flex(row, center, center);
  180. image {
  181. width: 28rpx;
  182. height: 28rpx;
  183. margin-right: 10rpx;
  184. }
  185. .quesTitleTextClass {
  186. font-weight: 600;
  187. font-size: 36rpx;
  188. color: #222222;
  189. }
  190. }
  191. }
  192. .ques-content {
  193. width: 100vw;
  194. height: 60vh;
  195. background-color: #fff;
  196. padding: 24rpx 24rpx 10rpx 24rpx;
  197. box-sizing: border-box;
  198. background: #FFFFFF;
  199. border-radius: 30rpx 30rpx 0rpx 0rpx;
  200. font-weight: 400;
  201. font-size: 28rpx;
  202. color: #222222;
  203. }
  204. .dataItemViewClass {
  205. width: 100%;
  206. margin-bottom: 25rpx;
  207. }
  208. .ques-title {
  209. font-weight: 600;
  210. font-size: 40rpx;
  211. margin-bottom: 24rpx;
  212. color: rgba(0, 0, 0, 0.85);
  213. white-space: normal;
  214. }
  215. .ques-type {
  216. flex-shrink: 0;
  217. min-width: 72rpx;
  218. min-height: 40rpx;
  219. padding: 0 12rpx;
  220. margin: 0 12rpx;
  221. box-sizing: border-box;
  222. background: #FF5C03;
  223. border-radius: 8rpx 8rpx 8rpx 8rpx;
  224. line-height: 40rpx;
  225. text-align: center;
  226. font-family: PingFang SC, PingFang SC;
  227. font-weight: 400;
  228. font-size: 24rpx;
  229. color: #FFFFFF;
  230. display: inline-block;
  231. }
  232. .ques-option {
  233. min-height: 104rpx;
  234. padding: 24rpx;
  235. box-sizing: border-box;
  236. margin-bottom: 24rpx;
  237. background: #F5F7FAFF;
  238. border-radius: 20rpx;
  239. display: flex;
  240. align-items: center;
  241. font-weight: 400;
  242. font-size: 40rpx;
  243. color: rgba(0, 0, 0, 0.85);
  244. &-active {
  245. color: #FF233CFF !important;
  246. background: #FFF0F1FF !important;
  247. }
  248. }
  249. .bottomBtnViewClass {
  250. width: 100%;
  251. height: 130rpx;
  252. @include u-flex(row, center, center);
  253. .btnViewClass {
  254. width: 630rpx;
  255. height: 96rpx;
  256. background: #00000040;
  257. border-radius: 48rpx;
  258. font-family: PingFangSC, PingFang SC;
  259. font-weight: 600;
  260. font-size: 40rpx;
  261. color: #FFFFFF;
  262. line-height: 90rpx;
  263. text-align: center;
  264. &-active {
  265. background: linear-gradient(135deg, #FF5267 0%, #FF233C 100%);
  266. }
  267. }
  268. }
  269. </style>