TestShow.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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'">
  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. <div>
  98. متن اعتراض:
  99. </div>
  100. <div v-html="answerBook.objection_request" />
  101. </template>
  102. <template v-if="answerBook.objection_result">
  103. <div>
  104. نتیجه اعتراض:
  105. </div>
  106. <div v-html="answerBook.objection_result" />
  107. </template>
  108. </q-card>
  109. <div v-else>
  110. کمی صبر کنید...
  111. </div>
  112. </div>
  113. </template>
  114. <script>
  115. import Assist from 'src/assets/js/Assist.js'
  116. import { mixinWidget } from 'src/mixin/Mixins.js'
  117. import { APIGateway } from 'src/api/APIGateway.js'
  118. import { EntityShow, EntityEdit } from 'quasar-crud'
  119. import { AnswerBook } from 'src/models/AnswerBook.js'
  120. import { FormBuilderAssist } from 'quasar-form-builder'
  121. export default {
  122. name: 'TestShow',
  123. components: { EntityShow, EntityEdit },
  124. mixins: [mixinWidget],
  125. data () {
  126. return {
  127. mounted: false,
  128. overallAnswerInput: [
  129. { type: 'input', name: 'overall_answer_text', responseKey: 'overall_answer_text', label: 'متن پاسخ جامع', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
  130. { type: 'file', name: 'overall_answer_attachment', responseKey: 'overall_answer_attachment', label: 'فایل پیوست جامع', placeholder: ' ', col: 'col-12' }
  131. ],
  132. questionInput: [
  133. { type: 'input', name: 'answer_text', responseKey: 'answer_text', label: 'متن پاسخ', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
  134. { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'فایل پیوست', placeholder: ' ', col: 'col-12' }
  135. ],
  136. questionInputs: [],
  137. objectionInputs: [
  138. { type: 'input', name: 'objection_request', responseKey: 'objection_request', label: 'متن اعتراض', placeholder: ' ', inputType: 'textarea', col: 'col-12' }
  139. ],
  140. objectionApi: APIGateway.answerBook.APIAdresses.submitObjectionRequest(this.$route.params.answer_book_id),
  141. participateType: null,
  142. participateTypeDialog: false,
  143. answerBook: new AnswerBook(),
  144. remainingTime: 0,
  145. timerInterval: null
  146. }
  147. },
  148. computed: {
  149. formattedRemainingTime () {
  150. const hours = Math.floor(this.remainingTime / 3600)
  151. const minutes = Math.floor((this.remainingTime % 3600) / 60)
  152. const seconds = this.remainingTime % 60
  153. // Add zero padding for hours, minutes, and seconds
  154. const formattedHours = hours.toString().padStart(2, '0')
  155. const formattedMinutes = minutes.toString().padStart(2, '0')
  156. const formattedSeconds = seconds.toString().padStart(2, '0')
  157. return formattedHours + ':' + formattedMinutes + ':' + formattedSeconds
  158. }
  159. },
  160. mounted () {
  161. // this.getAnswerBook()
  162. this.getTestQuestions()
  163. this.mounted = true
  164. },
  165. methods: {
  166. sendObjection () {
  167. this.answerBook.loading = true
  168. this.$refs.objectionEntityEdit.editEntity(false)
  169. .then(() => {
  170. this.getTestQuestions()
  171. })
  172. .catch(() => {
  173. this.getTestQuestions()
  174. })
  175. },
  176. onAllQuestionsSending () {
  177. this.answerBook.loading = true
  178. },
  179. onAllQuestionsSentSuccess () {
  180. this.answerBook.loading = false
  181. this.backToClassList()
  182. },
  183. onAllQuestionsSentFailed () {
  184. this.answerBook.loading = false
  185. },
  186. onSingleQuestionsSending () {
  187. this.answerBook.loading = true
  188. },
  189. onSingleQuestionsSentSuccess () {
  190. this.answerBook.loading = false
  191. this.backToClassList()
  192. },
  193. onSingleQuestionsSentFailed () {
  194. this.answerBook.loading = false
  195. },
  196. toShamsi (miladi) {
  197. return Assist.miladiToShamsi(miladi)
  198. },
  199. startTest () {
  200. this.$router.push({ name: this.participateType, params: { question_id: 1 } })
  201. },
  202. getRemainingTimeInSeconds (serverTimeStr, attendingStartTimeStr, durationInMinutes) {
  203. // Convert the strings to Date objects
  204. const serverTime = new Date(serverTimeStr)
  205. const attendingStartTime = new Date(attendingStartTimeStr)
  206. // Calculate the difference in milliseconds
  207. const timeDiffInMilliseconds = serverTime - attendingStartTime
  208. // Convert the difference to seconds
  209. const timeDiffInSeconds = timeDiffInMilliseconds / 1000
  210. // Calculate the remaining time in seconds considering the duration
  211. return (durationInMinutes * 60) - timeDiffInSeconds
  212. },
  213. backToClassList () {
  214. // this.$router.push({ name: 'UserPanel.Profile.AllClassrooms' })
  215. },
  216. getAnswerBook () {
  217. this.answerBook.loading = true
  218. APIGateway.answerBook.get(this.$route.params.answer_book_id)
  219. .then((answerBook) => {
  220. this.answerBook = new AnswerBook(answerBook)
  221. this.answerBook.loading = false
  222. })
  223. .catch(() => {
  224. this.answerBook.loading = false
  225. this.backToClassList()
  226. })
  227. },
  228. getTestQuestions () {
  229. this.answerBook.loading = true
  230. APIGateway.answerBook.getTestQuestions(this.$route.params.answer_book_id)
  231. .then((answerBook) => {
  232. this.answerBook = new AnswerBook(answerBook)
  233. this.answerBook.loading = false
  234. this.answerBook.answer_sheet_info.list.forEach(() => {
  235. this.questionInputs.push(this.questionInput)
  236. })
  237. this.remainingTime = this.getRemainingTimeInSeconds(this.answerBook.server_time, this.answerBook.attending_start_time, this.answerBook.duration)
  238. FormBuilderAssist.setAttributeByName(this.objectionInputs, 'objection_request', 'value', this.answerBook.objection_request)
  239. })
  240. .catch(() => {
  241. this.answerBook.loading = false
  242. this.backToClassList()
  243. })
  244. }
  245. }
  246. }
  247. </script>
  248. <style scoped lang="scss">
  249. .TestShow {
  250. :deep(.q-banner) {
  251. background: #FCF9F4 0 0 no-repeat padding-box;
  252. padding: 20px;
  253. .q-banner__content {
  254. font-size: 16px;
  255. font-weight: normal;
  256. }
  257. }
  258. :deep(.q-list) {
  259. .q-item {
  260. .q-item__section {
  261. display: inline-block;
  262. .entity-crud-formBuilder {
  263. margin-top: 8px;
  264. .outsideLabel {
  265. margin-bottom: 8px;
  266. }
  267. }
  268. }
  269. .q-badge {
  270. background: #ACBCAE 0 0 no-repeat padding-box;
  271. border-radius: 18px;
  272. height: 32px;
  273. padding: 4px 16px;
  274. }
  275. }
  276. }
  277. }
  278. </style>