ali před 2 roky
rodič
revize
32d0752943

+ 51 - 0
src/components/FormBuilderCustumComponents/FormBuilderTextarea.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="form-builder-input"
+       :class="customClass">
+    <div class="outsideLabel">{{ placeholder ? label : null }}</div>
+    <q-input v-model="inputData"
+             :name="name"
+             :filled="filled"
+             :rules="rules"
+             :lazy-rules="lazyRules"
+             :label="placeholder ? null : label"
+             :stack-label="!!placeholder"
+             :type="inputType"
+             :disable="disable"
+             :clearable="clearable"
+             :loading="loading"
+             :readonly="readonly"
+             :rounded="rounded"
+             :outlined="outlined"
+             :placeholder="placeholder"
+             :class="customClass"
+             :input-class="customClass"
+             :autogrow="autogrow"
+             @update:model-value="change($event)"
+             @click="onClick"
+             @keypress="onKeyPress" />
+  </div>
+</template>
+
+<script>
+import inputMixin from '../mixins/inputMixin.js'
+export default {
+  name: 'FormBuilderInput',
+  mixins: [inputMixin],
+  props: {
+    name: {
+      default: '',
+      type: String
+    },
+    autogrow: {
+      type: Boolean,
+      default: false
+    },
+    value: {
+      default: '',
+      type: [String, Number, Boolean]
+    }
+  }
+}
+</script>
+
+<style scoped></style>

+ 19 - 6
src/components/Widgets/Test/TestParticipation/TestParticipation.vue

@@ -96,6 +96,7 @@ export default {
       mounted: false,
       participateType: null,
       participateTypeDialog: false,
+      storeAnswerBook: null,
       answerBook: new AnswerBook(),
       remainingTime: 0,
       timerInterval: null
@@ -124,6 +125,9 @@ export default {
     this.mounted = true
   },
   methods: {
+    loadAnswerBookFromStore () {
+      this.storeAnswerBook = this.$store.getters['Test/answerBook']
+    },
     onAllQuestionsSending () {
       this.answerBook.loading = true
     },
@@ -203,16 +207,25 @@ export default {
           this.backToClassList()
         })
     },
+    initPageFromAnswerBook (answerBook) {
+      this.answerBook = new AnswerBook(answerBook)
+      this.answerBook.loading = false
+      this.answerBook.server_time = '2023-12-15T23:57:46.909211'
+      this.answerBook.attending_start_time = '2023-12-15T23:27:46.909211'
+      this.remainingTime = this.getRemainingTimeInSeconds(this.answerBook.server_time, this.answerBook.attending_start_time, this.answerBook.duration)
+      this.$store.commit('Test/updateAnswerBook', this.answerBook)
+      this.startTimer()
+    },
     getTestQuestions () {
+      this.loadAnswerBookFromStore()
+      if (this.storeAnswerBook && this.storeAnswerBook.id) {
+        this.initPageFromAnswerBook(this.storeAnswerBook)
+        return
+      }
       this.answerBook.loading = true
       APIGateway.answerBook.getTestQuestions(this.$route.params.answer_book_id)
         .then((answerBook) => {
-          this.answerBook = new AnswerBook(answerBook)
-          this.answerBook.loading = false
-          this.answerBook.server_time = '2023-12-15T23:57:46.909211'
-          this.answerBook.attending_start_time = '2023-12-15T23:27:46.909211'
-          this.remainingTime = this.getRemainingTimeInSeconds(this.answerBook.server_time, this.answerBook.attending_start_time, this.answerBook.duration)
-          this.startTimer()
+          this.initPageFromAnswerBook(answerBook)
         })
         .catch(() => {
           this.answerBook.loading = false

+ 12 - 4
src/components/Widgets/Test/TestParticipation/components/AllQuestions.vue

@@ -14,7 +14,7 @@
       </q-item>
     </q-list>
     <q-separator class="q-my-lg" />
-    <q-card-actions class="flex justify-end">
+    <q-card-actions class="flex justify-end AllQuestionsForm">
       <entity-create v-if="mounted"
                      ref="entityCreate"
                      v-model:value="inputs"
@@ -54,19 +54,22 @@ export default {
       mounted: false,
       api: APIGateway.answerBook.APIAdresses.submitOverallAnswer(this.$route.params.answer_book_id),
       inputs: [
-        { type: 'inputEditor', name: 'answer_text', label: 'متن پاسخ', placeholder: ' ', col: 'col-12' },
+        // { type: 'inputEditor', name: 'answer_text', label: 'متن پاسخ', placeholder: ' ', col: 'col-12' },
+        { type: 'input', name: 'answer_text', label: 'متن پاسخ', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
         { type: 'file', name: 'answer_attachment', label: 'فایل جامع پاسخ سوالات', placeholder: ' ', col: 'col-12' },
         { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'اتمام آزمون', placeholder: ' ', atClick: () => {}, col: 'col-12' }
       ]
     }
   },
   mounted () {
-    this.setBtnAction()
+    this.setInputs()
     this.mounted = true
   },
   methods: {
-    setBtnAction () {
+    setInputs () {
       FormBuilderAssist.setAttributeByName(this.inputs, 'btn', 'atClick', this.sendAnswers)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'answer_text', 'value', this.answerBook.overall_answer_text)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'answer_attachment', 'value', this.answerBook.overall_answer_attachment)
     },
     sendAnswers () {
       const answerText = FormBuilderAssist.getInputsByName(this.inputs, 'answer_text').value
@@ -103,5 +106,10 @@ export default {
       }
     }
   }
+  .AllQuestionsForm {
+    & > div {
+      width: 100%;
+    }
+  }
 }
 </style>

+ 8 - 3
src/components/Widgets/Test/TestParticipation/components/SingleQuestion.vue

@@ -17,7 +17,7 @@
               v-html="question.question_info.text" />
       </div>
       <q-separator class="q-my-lg" />
-      <q-card-actions class="flex justify-end">
+      <q-card-actions class="flex justify-end SingleQuestionForm">
         <entity-create v-if="mounted"
                        ref="entityCreate"
                        v-model:value="inputs"
@@ -65,8 +65,8 @@ export default {
       btnNext: { type: BtnControlComp, name: 'btnNext', label: 'سوال بعدی', placeholder: ' ', atClick: () => {}, col: 'col-md-6 col-12 flex justify-end' },
       btnFinal: { type: BtnControlComp, name: 'btnFinal', label: 'اتمام آزمون', placeholder: ' ', atClick: () => {}, col: 'col-md-6 col-12 flex justify-end' },
       inputs: [
-        { type: 'inputEditor', name: 'answer_text', label: 'متن پاسخ', placeholder: ' ', col: 'col-12' },
-        { type: 'file', name: 'answer_attachment', label: 'فایل جامع پاسخ سوالات', placeholder: ' ', col: 'col-12' },
+        { type: 'input', name: 'answer_text', label: 'متن پاسخ', placeholder: ' ', inputType: 'textarea', col: 'col-12' },
+        { type: 'file', name: 'answer_attachment', label: 'فایل پیوست', placeholder: ' ', col: 'col-12' },
         { type: 'hidden', name: 'answer_book', value: this.answerBook.id },
         { type: 'hidden', name: 'test_set_question', value: null }
       ]
@@ -171,5 +171,10 @@ export default {
     height: 32px;
     padding: 4px 16px;
   }
+  .SingleQuestionForm {
+    & > div {
+      width: 100%;
+    }
+  }
 }
 </style>

+ 7 - 0
src/models/AnswerBook.js

@@ -1,6 +1,7 @@
 import { User } from 'src/models/User.js'
 import { Test } from 'src/models/Test.js'
 import { Collection, Model } from 'js-abstract-model'
+import { AnswerSheetList } from 'src/models/AnswerSheet.js'
 
 class AnswerBook extends Model {
   constructor (data) {
@@ -35,6 +36,12 @@ class AnswerBook extends Model {
       { key: 'objection_request' },
       { key: 'objection_result' },
       { key: 'objection_time' },
+      {
+        key: 'answer_sheet_info',
+        relatedModel: AnswerSheetList
+      },
+      { key: 'overall_answer_text' },
+      { key: 'overall_answer_attachment' },
       { key: 'registration' },
       { key: 'registration_info' },
       { key: 'server_time' },

+ 6 - 96
src/models/AnswerSheet.js

@@ -1,105 +1,15 @@
-import { User } from 'src/models/User.js'
-import { Test } from 'src/models/Test.js'
 import { Collection, Model } from 'js-abstract-model'
 
 class AnswerSheet extends Model {
   constructor (data) {
     super(data, [
       { key: 'id' },
-      { key: 'attachment' },
-      { key: 'attending_end_time' },
-      { key: 'attending_start_time' },
-      { key: 'creation_time' },
-      { key: 'creator' },
-      {
-        key: 'creator_info',
-        relatedModel: User
-      },
-      { key: 'owner' },
-      {
-        key: 'owner_info',
-        relatedModel: User
-      },
-      { key: 'test' },
-      {
-        key: 'test_info',
-        relatedModel: Test
-      },
-      { key: 'duration' },
-      { key: 'grader' },
-      { key: 'grader_description' },
-      { key: 'grader_info' },
-      { key: 'is_active' },
-      { key: 'is_deleted' },
-      { key: 'last_modification_time' },
-      { key: 'objection_request' },
-      { key: 'objection_result' },
-      { key: 'objection_time' },
-      { key: 'registration' },
-      { key: 'registration_info' },
-      { key: 'server_time' },
-      { key: 'attending_start_time' },
-      { key: 'status' },
-      {
-        key: 'is_enabled_attending',
-        default: false
-      },
-      {
-        key: 'is_enabled_continuing',
-        default: false
-      },
-      {
-        key: 'is_enabled_objecting',
-        default: false
-      },
-      {
-        key: 'is_enabled_viewing',
-        default: false
-      }
-    ], {
-      status: {
-        infoKey: 'status_info',
-        enumListKey: 'statusEnums',
-        enums: [
-          {
-            label: 'حذف شده',
-            value: 'DELETED'
-          },
-          {
-            label: 'ایجاد شده',
-            value: 'CREATED'
-          },
-          {
-            label: 'ارسال نشده',
-            value: 'FETCHED'
-          },
-          {
-            label: 'ارسال شده',
-            value: 'SUBMITTED'
-          },
-          {
-            label: 'در حال تصحیح',
-            value: 'GRADING'
-          },
-          {
-            label: 'غیر قابل تصحیح',
-            value: 'UNGRADABLE'
-          },
-          {
-            label: 'تصحیح شده',
-            value: 'GRADED'
-          },
-          {
-            label: 'اعتراض شده',
-            value: 'OBJECTED'
-          },
-          {
-            label: 'نهایی شده',
-            value: 'FINALIZED'
-          }
-        ]
-      }
-    })
+      { key: 'answer_text' },
+      { key: 'answer_attachment' },
+      { key: 'score' },
+      { key: 'test_set_question' },
+      { key: 'test_set_question_info' }
+    ])
   }
 }
 

+ 37 - 19
src/pages/User/Profile/classroomInfo.vue

@@ -146,25 +146,43 @@
               {{ inputData.rowNumber }}
             </template>
             <template v-else-if="inputData.col.name === 'action'">
-              <q-btn v-if="inputData.props.row.is_enabled_attending"
-                     size="md"
-                     color="primary"
-                     label="شرکت در آزمون"
-                     :to="{name: 'UserPanel.Test.AnswerBook.Confirmation', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}" />
-              <q-btn v-if="inputData.props.row.is_enabled_continuing"
-                     size="md"
-                     color="primary"
-                     label="ادامه آزمون"
-                     :to="{name: 'UserPanel.Test.AnswerBook.Confirmation', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}" />
-              <q-btn v-if="inputData.props.row.is_enabled_objecting"
-                     size="md"
-                     color="primary"
-                     label="ثبت اعتراض" />
-              <q-btn v-if="inputData.props.row.is_enabled_viewing"
-                     size="md"
-                     color="primary"
-                     label="مشاهده"
-                     :to="{name: 'UserPanel.Test.AnswerBook.Show', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}" />
+
+              <q-btn flat
+                     icon="more_vert">
+                <q-menu>
+                  <q-list separator>
+                    <q-item v-if="inputData.props.row.is_enabled_attending"
+                            v-close-popup
+                            :to="{name: 'UserPanel.Test.AnswerBook.Confirmation', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}"
+                            clickable>
+                      <q-item-section>شرکت در آزمون</q-item-section>
+                    </q-item>
+                    <q-item v-if="inputData.props.row.is_enabled_continuing"
+                            v-close-popup
+                            clickable
+                            :to="{name: 'UserPanel.Test.AnswerBook.Participate.AllQuestions', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}">
+                      <q-item-section>ادامه آزمون(تمام سوالها)</q-item-section>
+                    </q-item>
+                    <q-item v-if="inputData.props.row.is_enabled_continuing"
+                            v-close-popup
+                            clickable
+                            :to="{name: 'UserPanel.Test.AnswerBook.Participate.SingleQuestion', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id, question_number: 1}}">
+                      <q-item-section>ادامه آزمون(تک سوال)</q-item-section>
+                    </q-item>
+                    <q-item v-if="inputData.props.row.is_enabled_objecting"
+                            v-close-popup
+                            clickable>
+                      <q-item-section>ثبت اعتراض</q-item-section>
+                    </q-item>
+                    <q-item v-if="inputData.props.row.is_enabled_viewing"
+                            v-close-popup
+                            clickable
+                            :to="{name: 'UserPanel.Test.AnswerBook.Show', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}">
+                      <q-item-section>مشاهده</q-item-section>
+                    </q-item>
+                  </q-list>
+                </q-menu>
+              </q-btn>
             </template>
             <template v-else>
               {{ inputData.col.value }}

+ 0 - 0
src/store/Test/actions.js


+ 3 - 0
src/store/Test/getters.js

@@ -0,0 +1,3 @@
+export function answerBook (state) {
+  return state.answerBook
+}

+ 12 - 0
src/store/Test/index.js

@@ -0,0 +1,12 @@
+import state from './state'
+import * as getters from './getters'
+import * as mutations from './mutations'
+import * as actions from './actions'
+
+export default {
+  namespaced: true,
+  getters,
+  mutations,
+  actions,
+  state
+}

+ 3 - 0
src/store/Test/mutations.js

@@ -0,0 +1,3 @@
+export function updateAnswerBook (state, newInfo) {
+  state.answerBook = newInfo
+}

+ 5 - 0
src/store/Test/state.js

@@ -0,0 +1,5 @@
+export default function () {
+  return {
+    answerBook: null
+  }
+}

+ 2 - 0
src/store/index.js

@@ -1,6 +1,7 @@
 import SEO from './Seo'
 import Auth from './Auth'
 import Shop from './Shop'
+import Test from './Test'
 import process from 'process'
 import Widgets from './Widgets'
 import { createStore } from 'vuex'
@@ -42,6 +43,7 @@ export default store(function (/* { ssrContext } */) {
     modules: {
       SEO,
       Shop,
+      Test,
       Auth,
       Widgets,
       AppLayout,