TestShow.vue 10 KB

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