TestShow.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <template>
  2. <div class="TestShow"
  3. :style="localOptions.style">
  4. <q-card v-if="!answerBook.loading && answerBook.id">
  5. <q-card-section class="flex justify-between">
  6. <div>{{ answerBook.test_info.title }}</div>
  7. </q-card-section>
  8. <q-separator />
  9. <q-banner>
  10. <div class="row">
  11. <div class="col-md-3 col-12">
  12. {{ toShamsi(answerBook.attending_start_time) }}
  13. </div>
  14. <div class="col-md-3 col-12">
  15. {{ answerBook.test_info.test_questions_info.list.length }}
  16. سوال
  17. </div>
  18. <div class="col-md-3 col-12">
  19. {{ answerBook.test_info.duration_deadline }}
  20. دقیقه
  21. </div>
  22. <div class="col-md-3 col-12">
  23. نمره نهایی:
  24. {{ answerBook.total_score }}
  25. </div>
  26. </div>
  27. </q-banner>
  28. <q-list separator>
  29. <q-item v-for="(answerSheet, answerSheetIndex) in answerBook.answer_sheet_info.list"
  30. :key="answerSheetIndex"
  31. clickable>
  32. <div class="full-width">
  33. <div class="flex justify-end">
  34. <q-badge v-if="answerSheet.score !== null">
  35. {{ answerSheet.score }}
  36. از
  37. {{ answerSheet.test_question_info.question_info.mark }}
  38. نمره
  39. </q-badge>
  40. <q-badge v-else>
  41. {{ answerSheet.test_question_info.question_info.mark }}
  42. نمره
  43. </q-badge>
  44. </div>
  45. <div>
  46. <div v-html="'<span>' + (answerSheetIndex + 1) + ' - </span>' + answerSheet.test_question_info.question_info.text" />
  47. <q-card class="q-mt-sm">
  48. <q-card-section>
  49. <div>
  50. متن پاسخ:
  51. </div>
  52. <div>
  53. {{ answerSheet.answer_text }}
  54. </div>
  55. <div v-if="answerSheet.answer_attachment">
  56. <q-separator class="q-my-md" />
  57. <q-btn outline
  58. color="primary"
  59. icon="attachment"
  60. label="فایل پیوست"
  61. :href="answerSheet.answer_attachment"
  62. target="_blank" />
  63. </div>
  64. </q-card-section>
  65. </q-card>
  66. </div>
  67. </div>
  68. </q-item>
  69. </q-list>
  70. <q-separator class="q-my-lg" />
  71. <div class="q-pa-lg">
  72. <entity-show v-if="mounted"
  73. ref="entityShow"
  74. v-model:value="overallAnswerInput"
  75. :loaded-data="answerBook"
  76. :show-close-button="false"
  77. :show-edit-button="false"
  78. :show-expand-button="false"
  79. :show-save-button="false"
  80. :show-index-button="false"
  81. :show-reload-button="false"
  82. :default-layout="false" />
  83. </div>
  84. <template v-if="$route.query.send_objection && $route.query.send_objection.toString() === '1' && !answerBook.objection_request && answerBook.is_enabled_objecting">
  85. <q-separator class="q-my-xl" />
  86. <q-card-section>
  87. <entity-edit v-if="mounted"
  88. ref="objectionEntityEdit"
  89. v-model:value="objectionInputs"
  90. title="ثبت اعتراض"
  91. :api="objectionApi"
  92. :loaded-data="{}"
  93. :show-close-button="false"
  94. :show-edit-button="false"
  95. :show-expand-button="false"
  96. :show-save-button="false"
  97. :show-reload-button="false">
  98. <template #after-form-builder>
  99. <div class="flex justify-end q-mt-md">
  100. <q-btn color="primary"
  101. label="ثبت اعتراض"
  102. @click="sendObjection" />
  103. </div>
  104. </template>
  105. </entity-edit>
  106. </q-card-section>
  107. </template>
  108. <template v-else-if="answerBook.objection_request">
  109. <q-separator class="q-my-lg" />
  110. <q-card-section>
  111. <div>
  112. متن اعتراض:
  113. </div>
  114. <div v-html="answerBook.objection_request.toString().replace(/(?:\r\n|\r|\n)/g, '<br>')" />
  115. </q-card-section>
  116. </template>
  117. <template v-if="answerBook.objection_result">
  118. <div>
  119. نتیجه اعتراض:
  120. </div>
  121. <div v-html="answerBook.objection_result" />
  122. </template>
  123. </q-card>
  124. <div v-else>
  125. کمی صبر کنید...
  126. </div>
  127. </div>
  128. </template>
  129. <script>
  130. import Assist from 'src/assets/js/Assist.js'
  131. import { mixinWidget } from 'src/mixin/Mixins.js'
  132. import { APIGateway } from 'src/api/APIGateway.js'
  133. import { EntityShow, EntityEdit } from 'quasar-crud'
  134. import { AnswerBook } from 'src/models/AnswerBook.js'
  135. import { FormBuilderAssist } from 'quasar-form-builder'
  136. export default {
  137. name: 'TestShow',
  138. components: { EntityShow, EntityEdit },
  139. mixins: [mixinWidget],
  140. data () {
  141. return {
  142. mounted: false,
  143. overallAnswerInput: [
  144. // { type: 'input', name: 'overall_answer_text', responseKey: 'overall_answer_text', label: 'متن پاسخ جامع', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
  145. { type: 'file', name: 'overall_answer_attachment', responseKey: 'overall_answer_attachment', label: 'فایل پیوست جامع', placeholder: ' ', col: 'col-12' }
  146. ],
  147. questionInput: [
  148. { type: 'input', name: 'answer_text', responseKey: 'answer_text', label: 'متن پاسخ', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
  149. { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'فایل پیوست', placeholder: ' ', col: 'col-12' }
  150. ],
  151. questionInputs: [],
  152. objectionInputs: [
  153. { type: 'input', name: 'objection_request', responseKey: 'objection_request', label: 'متن اعتراض', placeholder: ' ', inputType: 'textarea', col: 'col-12' }
  154. ],
  155. objectionApi: APIGateway.answerBook.APIAdresses.submitObjectionRequest(this.$route.params.answer_book_id),
  156. participateType: null,
  157. participateTypeDialog: false,
  158. answerBook: new AnswerBook(),
  159. remainingTime: 0,
  160. timerInterval: null
  161. }
  162. },
  163. computed: {
  164. formattedRemainingTime () {
  165. const hours = Math.floor(this.remainingTime / 3600)
  166. const minutes = Math.floor((this.remainingTime % 3600) / 60)
  167. const seconds = this.remainingTime % 60
  168. // Add zero padding for hours, minutes, and seconds
  169. const formattedHours = hours.toString().padStart(2, '0')
  170. const formattedMinutes = minutes.toString().padStart(2, '0')
  171. const formattedSeconds = seconds.toString().padStart(2, '0')
  172. return formattedHours + ':' + formattedMinutes + ':' + formattedSeconds
  173. }
  174. },
  175. mounted () {
  176. // this.getAnswerBook()
  177. this.getTestQuestions()
  178. this.mounted = true
  179. },
  180. methods: {
  181. sendObjection () {
  182. this.answerBook.loading = true
  183. this.$refs.objectionEntityEdit.editEntity(false)
  184. .then(() => {
  185. this.getTestQuestions()
  186. })
  187. .catch(() => {
  188. this.getTestQuestions()
  189. })
  190. },
  191. onAllQuestionsSending () {
  192. this.answerBook.loading = true
  193. },
  194. onAllQuestionsSentSuccess () {
  195. this.answerBook.loading = false
  196. this.backToClassList()
  197. },
  198. onAllQuestionsSentFailed () {
  199. this.answerBook.loading = false
  200. },
  201. onSingleQuestionsSending () {
  202. this.answerBook.loading = true
  203. },
  204. onSingleQuestionsSentSuccess () {
  205. this.answerBook.loading = false
  206. this.backToClassList()
  207. },
  208. onSingleQuestionsSentFailed () {
  209. this.answerBook.loading = false
  210. },
  211. toShamsi (miladi) {
  212. return Assist.miladiToShamsi(miladi)
  213. },
  214. startTest () {
  215. this.$router.push({ name: this.participateType, params: { question_id: 1 } })
  216. },
  217. getRemainingTimeInSeconds (serverTimeStr, attendingStartTimeStr, durationInMinutes) {
  218. // Convert the strings to Date objects
  219. const serverTime = new Date(serverTimeStr)
  220. const attendingStartTime = new Date(attendingStartTimeStr)
  221. // Calculate the difference in milliseconds
  222. const timeDiffInMilliseconds = serverTime - attendingStartTime
  223. // Convert the difference to seconds
  224. const timeDiffInSeconds = timeDiffInMilliseconds / 1000
  225. // Calculate the remaining time in seconds considering the duration
  226. return (durationInMinutes * 60) - timeDiffInSeconds
  227. },
  228. backToClassList () {
  229. // this.$router.push({ name: 'UserPanel.Profile.AllClassrooms' })
  230. },
  231. getAnswerBook () {
  232. this.answerBook.loading = true
  233. APIGateway.answerBook.get(this.$route.params.answer_book_id)
  234. .then((answerBook) => {
  235. this.answerBook = new AnswerBook(answerBook)
  236. this.answerBook.loading = false
  237. })
  238. .catch(() => {
  239. this.answerBook.loading = false
  240. this.backToClassList()
  241. })
  242. },
  243. getTestQuestions () {
  244. this.answerBook.loading = true
  245. APIGateway.answerBook.getTestQuestions(this.$route.params.answer_book_id)
  246. .then((answerBook) => {
  247. this.answerBook = new AnswerBook(answerBook)
  248. this.answerBook.loading = false
  249. this.answerBook.answer_sheet_info.list.forEach(() => {
  250. this.questionInputs.push(this.questionInput)
  251. })
  252. this.remainingTime = this.getRemainingTimeInSeconds(this.answerBook.server_time, this.answerBook.attending_start_time, this.answerBook.duration)
  253. FormBuilderAssist.setAttributeByName(this.objectionInputs, 'objection_request', 'value', this.answerBook.objection_request)
  254. })
  255. .catch(() => {
  256. this.answerBook.loading = false
  257. this.backToClassList()
  258. })
  259. }
  260. }
  261. }
  262. </script>
  263. <style scoped lang="scss">
  264. .TestShow {
  265. :deep(.q-banner) {
  266. background: #FCF9F4 0 0 no-repeat padding-box;
  267. padding: 20px;
  268. .q-banner__content {
  269. font-size: 16px;
  270. font-weight: normal;
  271. }
  272. }
  273. :deep(.q-list) {
  274. .q-item {
  275. .q-item__section {
  276. display: inline-block;
  277. .entity-crud-formBuilder {
  278. margin-top: 8px;
  279. .outsideLabel {
  280. margin-bottom: 8px;
  281. }
  282. }
  283. }
  284. .q-badge {
  285. background: #ACBCAE 0 0 no-repeat padding-box;
  286. border-radius: 18px;
  287. height: 32px;
  288. padding: 4px 16px;
  289. }
  290. }
  291. }
  292. }
  293. </style>