exam.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. <template>
  2. <view>
  3. <view class="question_content">
  4. <view class="question_item">
  5. <view class="question_title">第&nbsp;{{ currentQuestionIndex + 1 }}&nbsp;题&nbsp;/&nbsp;共&nbsp;{{ question_Info.question_total }}&nbsp;题</view>
  6. <view class="question_content_text">{{ question_map.question_title }}</view>
  7. <view class="question_tip"> 请从以下选项中选出正确答案 </view>
  8. <view class="question_options">
  9. <view
  10. v-for="(option, cIndex) in question_map.answer_list"
  11. :key="cIndex"
  12. :class="[
  13. 'question_answer',
  14. { active: question_map.selectAnswer == option.id },
  15. { error: question_map.checkAnswer && question_map.checkAnswer == option.id && question_map.checkAnswer !== question_map.answer_id },
  16. ]"
  17. @click="_selectAnswer(option.id)"
  18. >
  19. <view
  20. :class="[
  21. 'question_index',
  22. { active: question_map.selectAnswer == option.id },
  23. { error: question_map.checkAnswer && question_map.checkAnswer == option.id && question_map.checkAnswer !== question_map.answer_id },
  24. ]"
  25. >{{ option.value.substring(0, 1) }}</view
  26. >
  27. {{ option.value.substring(2) }}
  28. </view>
  29. </view>
  30. <view class="question_result" v-if="question_map.checkAnswer">
  31. <icon :type="question_map.answer_id == question_map.checkAnswer ? 'success' : 'cancel'" size="24" />
  32. <view :class="['title', question_map.checkAnswer == question_map.answer_id ? 'green' : 'red']">{{
  33. question_map.answer_id == question_map.checkAnswer ? "恭喜您,答对啦!" : "很遗憾,答错了"
  34. }}</view>
  35. <view class="tip">本题的正确选项为:{{ answer_number[question_map.answer_index] }}</view>
  36. </view>
  37. </view>
  38. </view>
  39. <view class="bottom_btn" v-if="question_Info.question_list.length !== 0">
  40. <!-- <view class="answer_info"
  41. >已答&nbsp;<view style="color: #5045e6">{{ answeredCount }}</view
  42. >&nbsp;题,共&nbsp;{{ question_Info.question_total }}&nbsp;题</view
  43. > -->
  44. <!-- <view class="submit-btn" @click="_handleSubmit">交卷</view> -->
  45. <view
  46. :class="[
  47. 'submit-btn',
  48. {
  49. disabled: !question_map.selectAnswer,
  50. },
  51. ]"
  52. @click="_handleSubmitAnswer"
  53. v-if="!question_map.checkAnswer"
  54. >
  55. 提交
  56. </view>
  57. <view v-if="question_map.checkAnswer" style="display: flex; justify-content: space-between; width: 100%">
  58. <view class="prev_btn" @click="_prevQuestion">上一题</view>
  59. <view class="next_btn" @click="_nextQuestion">{{ currentQuestionIndex !== question_Info.question_list.length - 1 ? "下一题" : "查看报告" }}</view>
  60. </view>
  61. </view>
  62. <Empty v-if="question_Info.question_list.length == 0" text="----- 本课程还没有配置习题 -----" />
  63. <uni-popup ref="tipRef" type="center" @change="_changeTip">
  64. <view class="tip_content">
  65. <view code>&nbsp;{{ "<" }}&nbsp; &nbsp;{{ ">" }}&nbsp;</view>
  66. <view>左右滑动切换题目</view>
  67. </view>
  68. </uni-popup>
  69. </view>
  70. </template>
  71. <script>
  72. import Empty from "@/components/Empty/Empty.vue";
  73. export default {
  74. components: { Empty },
  75. data() {
  76. return {
  77. currentQuestionIndex: 0,
  78. course_id: null,
  79. question_Info: {},
  80. question_map: {}, // 初始化 question_map
  81. answer_number: ["A", "B", "C", "D", "E", "F"],
  82. };
  83. },
  84. onLoad(param) {
  85. this.course_id = param.id;
  86. if (!uni.getStorageSync("isShowExamTip")) {
  87. this.$refs.tipRef.open("center");
  88. }
  89. uni.enableAlertBeforeUnload({
  90. message: "您确定要退出答题吗?",
  91. success: function (res) {
  92. console.log("方法注册成功:", res);
  93. },
  94. fail: function (errMsg) {
  95. console.log("方法注册失败:", errMsg);
  96. },
  97. });
  98. },
  99. onShow() {
  100. this._getDetail();
  101. },
  102. computed: {
  103. answeredCount() {
  104. if (!this.question_Info.question_list) return 0;
  105. return this.question_Info.question_list.filter((item) => item.checkAnswer).length;
  106. },
  107. },
  108. methods: {
  109. _changeTip() {
  110. uni.setStorageSync("isShowExamTip", true);
  111. },
  112. _getDetail() {
  113. this.$http
  114. .request("api/video_exam_question/get_list", {
  115. course_id: this.course_id,
  116. })
  117. .then((re) => {
  118. if (re.code == "success") {
  119. re.data.question_list.map((item) => {
  120. item.answer_index = item.answer_list.findIndex((answer) => answer.is_answer == 1);
  121. item.answer_id = item.answer_list.find((answer) => answer.is_answer == 1).id;
  122. item.selectAnswer = null; // 初始化 selectAnswer 属性
  123. });
  124. console.log(re.data);
  125. this.question_Info = re.data;
  126. this.question_map = re.data.question_list[this.currentQuestionIndex];
  127. }
  128. });
  129. },
  130. _selectAnswer(id) {
  131. //已经回答的题目不能再答了
  132. if (this.question_map.checkAnswer) {
  133. return;
  134. }
  135. // this.question_map.selectAnswer = id;
  136. this.$set(this.question_map, "selectAnswer", id);
  137. console.log(this.question_map);
  138. },
  139. _handleSubmitAnswer() {
  140. if (!this.question_map.selectAnswer) {
  141. return;
  142. }
  143. this.question_map.checkAnswer = this.question_map.selectAnswer;
  144. },
  145. _handleSubmit() {
  146. if (this.answeredCount !== this.question_Info.question_total) {
  147. uni.showModal({
  148. title: "温馨提示",
  149. content: "还有未完成的题目,是否交卷",
  150. confirmText: "继续交卷",
  151. cancelText: "继续答题",
  152. success: (res) => {
  153. if (res.confirm) {
  154. this._handleIn();
  155. }
  156. },
  157. });
  158. } else {
  159. this._handleIn();
  160. }
  161. },
  162. _handleIn() {
  163. const _this = this;
  164. const question_list = this.question_Info.question_list.filter((item) => item.checkAnswer);
  165. const answer_list = question_list.map((item) => {
  166. return { question_id: item.question_id, answer_id: item.checkAnswer };
  167. });
  168. this.$http
  169. .request(
  170. "api/video_exam_record/hand_in",
  171. {
  172. record_id: this.question_Info.record_id,
  173. answer_list: JSON.stringify(answer_list),
  174. },
  175. "POST"
  176. )
  177. .then((re) => {
  178. if (re.code == "success") {
  179. uni.redirectTo({
  180. url: `/pages/video/record?type=exam&record_id=${_this.question_Info.record_id}`,
  181. });
  182. // uni.showModal({
  183. // title: "完成测评",
  184. // content: "恭喜您完成测评,是否查看报告",
  185. // confirmText: "查看报告",
  186. // cancelText: "返回列表",
  187. // success(res) {
  188. // if (res.confirm) {
  189. // uni.redirectTo({
  190. // url: `/pages/video/record?type=exam&record_id=${_this.question_Info.record_id}`,
  191. // });
  192. // } else {
  193. // uni.redirectTo({
  194. // url: `/pages/video/index`,
  195. // });
  196. // }
  197. // },
  198. // });
  199. }
  200. });
  201. },
  202. _prevQuestion() {
  203. if (this.currentQuestionIndex === 0) {
  204. uni.showToast({
  205. title: "已是第一题啦!",
  206. icon: "none",
  207. duration: 2000,
  208. });
  209. return;
  210. }
  211. this.currentQuestionIndex--;
  212. this.question_map = this.question_Info.question_list[this.currentQuestionIndex];
  213. },
  214. _nextQuestion() {
  215. try {
  216. if (this.currentQuestionIndex == this.question_Info.question_list.length - 1) {
  217. this._handleIn();
  218. return;
  219. }
  220. this.question_Info.question_list[this.currentQuestionIndex] = this.question_map;
  221. this.currentQuestionIndex++;
  222. this.question_map = this.question_Info.question_list[this.currentQuestionIndex];
  223. console.clear();
  224. console.log(this.currentQuestionIndex, "currentQuestionIndex");
  225. console.log(this.question_map, "question_map");
  226. console.log(this.question_Info.question_list, "this.question_Info.question_list");
  227. } catch (error) {
  228. console.log(error);
  229. }
  230. },
  231. },
  232. };
  233. </script>
  234. <style lang="less" scoped>
  235. .tip_content {
  236. font-size: 64rpx;
  237. font-weight: bold;
  238. color: #fff;
  239. display: flex;
  240. flex-direction: column;
  241. justify-content: center;
  242. align-items: center;
  243. gap: 20rpx;
  244. }
  245. .question_content {
  246. height: calc(100vh - 200rpx);
  247. background-color: #f2f2f2;
  248. width: 100%;
  249. box-sizing: border-box;
  250. padding: 30rpx 20rpx;
  251. background-color: #f9fbfc;
  252. .question_item {
  253. width: 100%;
  254. padding: 30rpx 20rpx;
  255. box-sizing: border-box;
  256. // border: 2rpx solid #ddd;
  257. border-radius: 8rpx;
  258. // background-color: #fff;
  259. .question_title {
  260. color: #999;
  261. margin-bottom: 40rpx;
  262. }
  263. .question_tip {
  264. color: #999999;
  265. margin-bottom: 40rpx;
  266. }
  267. .question_content_text {
  268. font-size: 32rpx;
  269. margin-bottom: 40rpx;
  270. font-weight: bold;
  271. }
  272. .question_options {
  273. display: flex;
  274. flex-direction: column;
  275. gap: 40rpx;
  276. width: 100%;
  277. .question_index {
  278. border: 2rpx solid #ddd;
  279. border-radius: 50%;
  280. width: 50rpx;
  281. height: 50rpx;
  282. text-align: center;
  283. line-height: 50rpx;
  284. margin-right: 20rpx;
  285. &.active {
  286. border-color: #5045e6;
  287. background-color: #5045e6;
  288. color: #fff;
  289. }
  290. &.error {
  291. background-color: #ef4444;
  292. border-color: #ef4444;
  293. }
  294. }
  295. .question_answer {
  296. height: 80rpx;
  297. width: 100%;
  298. border-radius: 20rpx;
  299. display: flex;
  300. align-items: center;
  301. border: 2rpx solid #ddd;
  302. box-sizing: border-box;
  303. padding: 20rpx 20rpx;
  304. &.active {
  305. color: #5045e6;
  306. border-color: #5045e6;
  307. }
  308. &.error {
  309. border-color: #ef4444;
  310. background-color: #fef2f2;
  311. color: #ef4444;
  312. }
  313. }
  314. }
  315. .question_result {
  316. display: flex;
  317. flex-direction: column;
  318. gap: 20rpx;
  319. width: 100%;
  320. justify-content: center;
  321. align-items: center;
  322. border: 2rpx solid #ddd;
  323. margin-top: 40rpx;
  324. padding: 36rpx;
  325. box-sizing: border-box;
  326. border-radius: 20rpx;
  327. .red {
  328. color: red;
  329. }
  330. .green {
  331. color: green;
  332. }
  333. }
  334. }
  335. }
  336. .bottom_btn {
  337. position: fixed;
  338. bottom: 0;
  339. left: 0;
  340. width: 100%;
  341. height: 200rpx;
  342. border-top: 4rpx solid #ddd;
  343. display: flex;
  344. align-items: center;
  345. justify-content: space-between;
  346. padding: 0 40rpx;
  347. box-sizing: border-box;
  348. .prev_btn {
  349. background-color: #fff;
  350. color: #333;
  351. font-size: 28rpx;
  352. width: 45%;
  353. height: 80rpx;
  354. line-height: 80rpx;
  355. border-radius: 8rpx;
  356. box-shadow: 2rpx 2rpx 8rpx rgba(0, 0, 0, 0.1);
  357. text-align: center;
  358. }
  359. .next_btn {
  360. background-color: #5045e6;
  361. color: #fff;
  362. font-size: 28rpx;
  363. width: 45%;
  364. height: 80rpx;
  365. line-height: 80rpx;
  366. border-radius: 8rpx;
  367. box-shadow: 2rpx 2rpx 8rpx rgba(0, 0, 0, 0.1);
  368. text-align: center;
  369. }
  370. .answer_info {
  371. display: flex;
  372. align-items: baseline;
  373. }
  374. .submit-btn {
  375. background-color: #5045e6;
  376. color: #fff;
  377. font-size: 28rpx;
  378. width: 100%;
  379. height: 80rpx;
  380. line-height: 80rpx;
  381. border-radius: 8rpx;
  382. box-shadow: 2rpx 2rpx 8rpx rgba(0, 0, 0, 0.1);
  383. text-align: center;
  384. &.disabled {
  385. background-color: #ccc;
  386. color: #999;
  387. cursor: not-allowed;
  388. }
  389. }
  390. }
  391. </style>