Bläddra i källkod

填表题修改

dusenyao 1 år sedan
förälder
incheckning
258faf6b70

+ 1 - 0
src/views/exercise_questions/answer/answer.js

@@ -9,4 +9,5 @@ export const subjectiveQuestionList = [
   'answer_question',
   'replace_answer',
   'essay_question',
+  'table_fill',
 ];

+ 3 - 3
src/views/exercise_questions/create/components/exercises/FillQuestion.vue

@@ -5,8 +5,8 @@
         <RichText v-model="data.stem" :font-size="18" placeholder="输入题干" />
 
         <el-input
-          v-if="isEnable(data.property.is_enable_word_fill)"
-          v-model="data.word_fill"
+          v-if="isEnable(data.property.is_enable_word_select_fill)"
+          v-model="data.word_select_fill"
           rows="3"
           resize="none"
           type="textarea"
@@ -69,7 +69,7 @@
           <el-radio
             v-for="{ value, label } in switchOption"
             :key="value"
-            v-model="data.property.is_enable_word_fill"
+            v-model="data.property.is_enable_word_select_fill"
             :label="value"
           >
             {{ label }}

+ 3 - 3
src/views/exercise_questions/create/components/exercises/ListenFillQuestion.vue

@@ -12,8 +12,8 @@
         />
 
         <el-input
-          v-if="isEnable(data.property.is_enable_word_fill)"
-          v-model="data.word_fill"
+          v-if="isEnable(data.property.is_enable_word_select_fill)"
+          v-model="data.word_select_fill"
           rows="3"
           resize="none"
           type="textarea"
@@ -85,7 +85,7 @@
           <el-radio
             v-for="{ value, label } in switchOption"
             :key="value"
-            v-model="data.property.is_enable_word_fill"
+            v-model="data.property.is_enable_word_select_fill"
             :label="value"
           >
             {{ label }}

+ 35 - 141
src/views/exercise_questions/create/components/exercises/TableFillQuestion.vue

@@ -20,7 +20,7 @@
               <span
                 v-if="isEnable(data.property.is_enable_table_header)"
                 class="form-header"
-                :style="{ paddingLeft: isEnable(data.property.is_enable_number_column) ? '40px' : '0' }"
+                :style="{ paddingLeft: isEnable(data.property.is_enable_number_column) && i === 1 ? '40px' : '0' }"
               >
                 <el-input v-model="data.option_header_list[i - 1].text" placeholder="请输入" />
               </span>
@@ -29,17 +29,7 @@
                 <span v-if="i === 1 && isEnable(data.property.is_enable_number_column)" class="serial-number">
                   {{ j }}
                 </span>
-                <div class="rich">
-                  <RichText
-                    ref="modelEssay"
-                    v-model="data.option_list[j - 1][i - 1].text"
-                    :is-fill="true"
-                    :toolbar="false"
-                    :font-size="12"
-                    :wordlimit-num="false"
-                    placeholder="请输入"
-                  />
-                </div>
+                <el-input v-model="data.option_list[j - 1][i - 1].text" placeholder="请输入" />
               </span>
             </div>
             <span
@@ -50,7 +40,14 @@
             ></span>
           </template>
         </div>
-        <div class="tips">在输入框最前插入##,代表输入框中内容作为正确答案在作答时隐藏。</div>
+
+        <el-input
+          v-if="isEnable(data.property.is_enable_reference_answer)"
+          v-model="data.reference_answer"
+          type="textarea"
+          rows="3"
+          placeholder="输入参考答案"
+        />
       </div>
     </template>
 
@@ -87,6 +84,17 @@
           </el-radio>
         </el-form-item>
 
+        <el-form-item label="参考答案">
+          <el-radio
+            v-for="{ value, label } in switchOption"
+            :key="value"
+            v-model="data.property.is_enable_reference_answer"
+            :label="value"
+          >
+            {{ label }}
+          </el-radio>
+        </el-form-item>
+
         <el-form-item label="表头">
           <el-radio
             v-for="{ value, label } in switchOption"
@@ -209,7 +217,7 @@ export default {
             let newValue = newVal[i][j];
             let oldValue = oldVal[i][j];
             if (newValue.text === oldValue.text) return;
-            this.handleOptionChange(newValue, oldValue, i, j);
+            this.$set(this.data.option_list[i][j], 'type', newValue.text.length > 0 ? 'text' : 'input');
           });
         });
       },
@@ -265,118 +273,6 @@ export default {
         width: `${this.data.property.form_width}px`,
       };
     },
-    /**
-     * 处理选项内容变化
-     * @param {object} newValue 新值
-     * @param {object} oldValue 旧值
-     * @param {number} i 选项列表的索引
-     * @param {number} j 选项列表中的索引
-     */
-    handleOptionChange(newValue, oldValue, i, j) {
-      let arr = newValue.text.split(/<p>(.*?)<\/p>/gi).filter((item) => item);
-      // 答案数组中的索引
-      let answerIndex = this.data.answer.answer_list.findIndex(({ mark }) => mark === newValue.mark);
-      if (arr.length === 0) {
-        if (answerIndex !== -1) {
-          this.data.answer.answer_list.splice(answerIndex, 1);
-          answerIndex = -1;
-        }
-      }
-      if (newValue.text.length <= 0) {
-        this.$set(this.data.option_list[i][j], 'content_list', [
-          { type: 'input_any', content: '', mark: getRandomNumber() },
-        ]);
-        return;
-      }
-      let isStart = /^##/.test(arr[0]); // 是否以 ## 开头
-      if (isStart) {
-        this.handleSpecialCharacterStart(arr, answerIndex, newValue.mark, i, j);
-      } else {
-        let str = arr.join().replace(/<span class="rich-fill".*?>(.*?)<\/span>/gi, '###$1###');
-        this.handleFill(str, answerIndex, newValue.mark, i, j);
-      }
-    },
-    /**
-     * 处理填空或其他情况
-     * @param {string} str 处理后的字符串
-     * @param {number} answerIndex 答案数组的索引
-     * @param {string} newValueMark 新值的标识
-     * @param {number} i 选项列表的索引
-     * @param {number} j 选项列表中的索引
-     */
-    handleFill(str, answerIndex, newValueMark, i, j) {
-      let _str = str;
-      let start = 0;
-      let index = 0;
-      let arr = [];
-      let matchNum = 0;
-      while (index !== -1) {
-        index = _str.indexOf('###', start);
-        if (index === -1) break;
-        matchNum += 1;
-        arr.push({ content: _str.slice(start, index), type: 'text', mark: '' });
-        if (matchNum % 2 === 0 && arr.length > 0) {
-          arr[arr.length - 1].type = 'input';
-          let mark = getRandomNumber();
-          arr[arr.length - 1].mark = mark;
-        }
-        start = index + 3;
-      }
-      let last = _str.slice(start);
-      if (last) {
-        arr.push({ content: last, type: 'text', mark: '' });
-      }
-      let value_list = arr
-        .filter(({ type }) => type === 'input')
-        .map(({ content, mark }) => ({ value: content, mark }));
-      if (answerIndex === -1 && value_list.length > 0) {
-        this.data.answer.answer_list.push({
-          value_list,
-          mark: newValueMark,
-        });
-      } else if (answerIndex !== -1) {
-        this.data.answer.answer_list[answerIndex].value_list = value_list;
-      }
-      this.data.option_list[i][j].content_list = arr.map(({ content, type, mark }) => {
-        return {
-          type,
-          mark,
-          content: type === 'input' ? '' : content,
-        };
-      });
-    },
-    /**
-     * 处理特殊字符开头
-     * @param {array} arr 文本数组
-     * @param {number} answerIndex 答案数组的索引
-     * @param {string} newValueMark 新值的标识
-     * @param {number} i 选项列表的索引
-     * @param {number} j 选项列表中的索引
-     */
-    handleSpecialCharacterStart(arr, answerIndex, newValueMark, i, j) {
-      // 去除第一个元素的 ##
-      arr[0] = arr[0].slice(2);
-      let _arr = arr.map((item) => {
-        return item.replace(/<span class="rich-fill".*?>(.*?)<\/span>/gi, '$1');
-      });
-      let mark = getRandomNumber();
-
-      if (answerIndex === -1) {
-        this.data.answer.answer_list.push({
-          value_list: [{ mark, value: _arr.join('') }],
-          mark: newValueMark,
-        });
-      } else {
-        this.data.answer.answer_list[answerIndex].value_list = [{ mark, value: _arr.join('') }];
-      }
-      this.data.option_list[i][j].content_list = [
-        {
-          type: 'fill',
-          mark,
-          content: '',
-        },
-      ];
-    },
   },
 };
 </script>
@@ -399,6 +295,7 @@ export default {
   .option-wrapper {
     display: grid;
     grid-auto-flow: column;
+    margin-bottom: 8px;
 
     .fill-form {
       display: flex;
@@ -433,18 +330,21 @@ export default {
           text-align: center;
         }
 
-        .rich {
-          position: relative;
-          width: 100%;
-          overflow: auto;
-        }
+        .el-input {
+          :deep &__inner {
+            height: 54px;
+            font-size: 16px;
+            line-height: 54px;
+            background-color: #fff;
 
-        :deep .tox-tinymce {
-          border-width: 0;
+            &:focus {
+              border-color: #c0c4cc;
+            }
+          }
         }
 
-        :deep .tox .tox-sidebar-wrap {
-          border-width: 0;
+        :deep .tox-editor-header {
+          display: none;
         }
 
         :deep .tox-editor-header {
@@ -461,12 +361,6 @@ export default {
       border-radius: 4px;
     }
   }
-
-  .tips {
-    margin-top: 8px;
-    font-size: 14px;
-    color: #999;
-  }
 }
 
 .property {

+ 2 - 2
src/views/exercise_questions/data/fill.js

@@ -25,7 +25,7 @@ export const fillData = {
   type: 'fill', // 题型
   stem: '', // 题干
   file_id_list: [], // 文件 id 列表
-  word_fill: '', // 选词
+  word_select_fill: '', // 选词
   description: '', // 描述
   article: '', // 文章
   model_essay: [], // 文章解析后的数据
@@ -35,7 +35,7 @@ export const fillData = {
     stem_type: stemTypeList[1].value, // 题干类型
     question_number: '1', // 题号
     stem_question_number_font_size: fontSizeList[6], // 题干题号
-    is_enable_word_fill: switchOption[0].value, // 选词填空
+    is_enable_word_select_fill: switchOption[0].value, // 选词填空
     is_enable_description: switchOption[0].value, // 描述
     score: 1, // 分值
     score_type: scoreTypeList[0].value, // 分值类型

+ 2 - 2
src/views/exercise_questions/data/listenFill.js

@@ -25,7 +25,7 @@ export const listenFillData = {
   type: 'listen_fill', // 题型
   stem: '', // 题干
   file_id_list: [], // 文件 id 列表
-  word_fill: '', // 选词
+  word_select_fill: '', // 选词
   description: '', // 描述
   article: '', // 文章
   model_essay: [], // 文章解析后的数据
@@ -36,7 +36,7 @@ export const listenFillData = {
     question_number: '1', // 题号
     stem_question_number_font_size: fontSizeList[6], // 题干题号
     is_enable_listening: switchOption[0].value, // 是否听力
-    is_enable_word_fill: switchOption[0].value, // 选词填空
+    is_enable_word_select_fill: switchOption[0].value, // 选词填空
     is_enable_description: switchOption[0].value, // 描述
     score: 1, // 分值
     score_type: scoreTypeList[0].value, // 分值类型

+ 3 - 1
src/views/exercise_questions/data/tableFill.js

@@ -5,7 +5,7 @@ export function getOption() {
   return {
     mark: getRandomNumber(),
     text: '',
-    content_list: [{ type: 'input_any', content: '', mark: getRandomNumber() }],
+    type: 'input',
   };
 }
 
@@ -18,6 +18,7 @@ export function getTableFillData() {
     type: 'table_fill', // 题型
     stem: '', // 题干
     description: '', // 描述
+    reference_answer: '', // 参考答案
     option_header_list: [
       { mark: getRandomNumber(), text: '', width: 50 },
       { mark: getRandomNumber(), text: '', width: 50 },
@@ -35,6 +36,7 @@ export function getTableFillData() {
       stem_question_number_font_size: fontSizeList[6], // 题干题号
       is_enable_table_header: switchOption[0].value, // 是否启用表头
       is_enable_description: switchOption[0].value, // 描述
+      is_enable_reference_answer: switchOption[0].value, // 参考答案
       score: 1, // 分值
       score_type: scoreTypeList[0].value, // 分值类型
       is_enable_number_column: switchOption[0].value, // 是否启用序号列

+ 2 - 2
src/views/exercise_questions/preview/FillPreview.vue

@@ -8,7 +8,7 @@
       <span v-html="sanitizeHTML(data.stem)"></span>
     </div>
 
-    <div v-if="isEnable(data.property.is_enable_word_fill)" class="word-fill">
+    <div v-if="isEnable(data.property.is_enable_word_select_fill)" class="word-fill">
       <span v-for="(text, i) in wordFillList" :key="i" class="word-fill-item" @click="selectedDescription(text)">
         {{ text }}
       </span>
@@ -65,7 +65,7 @@ export default {
   },
   computed: {
     wordFillList() {
-      return this.data.word_fill.split(/\s+/).filter((item) => item);
+      return this.data.word_select_fill.split(/\s+/).filter((item) => item);
     },
   },
   watch: {

+ 2 - 2
src/views/exercise_questions/preview/ListenFillPreview.vue

@@ -7,7 +7,7 @@
       </span>
       <span v-html="sanitizeHTML(data.stem)"></span>
     </div>
-    <div v-if="isEnable(data.property.is_enable_word_fill)" class="word-fill">
+    <div v-if="isEnable(data.property.is_enable_word_select_fill)" class="word-fill">
       <span v-for="(text, i) in wordFillList" :key="i" class="word-fill-item" @click="selectedDescription(text)">
         {{ text }}
       </span>
@@ -71,7 +71,7 @@ export default {
   },
   computed: {
     wordFillList() {
-      return this.data.word_fill.split(/\s+/).filter((item) => item);
+      return this.data.word_select_fill.split(/\s+/).filter((item) => item);
     },
   },
   watch: {

+ 59 - 138
src/views/exercise_questions/preview/TableFillPreview.vue

@@ -17,11 +17,13 @@
       <div class="form" :style="{ width: `${data.property.form_width}px` }">
         <div v-if="isEnable(data.property.is_enable_table_header)" class="form-header">
           <div
-            v-for="{ text, mark, width } in data.option_header_list"
+            v-for="({ text, mark, width }, i) in data.option_header_list"
             :key="mark"
             :style="{
-              width: `${width}%`,
-              paddingLeft: isEnable(data.property.is_enable_number_column) ? '40px' : '0',
+              borderLeft: isEnable(data.property.is_enable_number_column) || i > 0 ? '1px solid #165dff' : 'none',
+              width:
+                isEnable(data.property.is_enable_number_column) && i === 0 ? `calc(${width}% - 40px)` : `${width}%`,
+              marginLeft: isEnable(data.property.is_enable_number_column) && i === 0 ? '40px' : '0',
             }"
             class="header-item"
           >
@@ -30,73 +32,38 @@
         </div>
         <div v-for="(item, i) in optionList" :key="i" class="form-content">
           <div
-            v-for="({ mark, content_list }, j) in item"
-            :key="mark"
+            v-for="(li, j) in item"
+            :key="li.mark"
             :style="{ width: `${data.option_header_list[j].width}%` }"
             class="form-item"
           >
             <span v-if="j === 0 && isEnable(data.property.is_enable_number_column)" class="serial-number">
               {{ i + 1 }}
             </span>
-            <div v-for="(li, k) in content_list" :key="k" class="item-content">
-              <span v-if="li.type === 'text'" v-html="sanitizeHTML(li.content)"></span>
-              <template v-else-if="li.type === 'input_any'">
-                <el-input
-                  :key="k"
-                  v-model="li.content"
-                  :disabled="disabled"
-                  :class="['fill']"
-                  placeholder="请输入"
-                  :style="[
-                    { cursor: disabled ? 'not-allowed' : 'pointer' },
-                    { width: Math.max(80, li.content.length * 12) + 'pt' },
-                  ]"
-                />
-              </template>
-              <template v-else-if="li.type === 'fill'">
-                <el-input
-                  :key="k"
-                  v-model="li.content"
-                  :disabled="disabled"
-                  :class="['fill', ...computedAnswerClass(mark, li.mark)]"
-                  placeholder="请输入"
-                  :style="[
-                    { cursor: disabled ? 'not-allowed' : 'pointer' },
-                    { width: Math.max(80, li.content.length * 12) + 'pt' },
-                  ]"
-                />
-                <span
-                  v-show="computedAnswerText(mark, li.mark).length > 0"
-                  :key="`answer-${i}-${j}-${k}`"
-                  class="right-answer-content"
-                >
-                  {{ computedAnswerText(mark, li.mark) }}
-                </span>
-              </template>
+            <div class="item-content">
+              <span v-if="li.type === 'text'">{{ li.text }}</span>
               <template v-else-if="li.type === 'input'">
                 <el-input
-                  :key="k"
-                  v-model="li.content"
+                  v-model="li.text"
                   :disabled="disabled"
-                  :class="['input', ...computedAnswerClass(mark, li.mark)]"
+                  class="fill"
+                  placeholder="请输入"
                   :style="[
                     { cursor: disabled ? 'not-allowed' : 'pointer' },
-                    { width: Math.max(80, li.content.length * 12) + 'pt' },
+                    { width: Math.max(80, li.text.length * 12) + 'pt' },
                   ]"
                 />
-                <span
-                  v-show="computedAnswerText(mark, li.mark).length > 0"
-                  :key="`answer-${i}-${j}-${k}`"
-                  class="right-answer-fill"
-                >
-                  {{ computedAnswerText(mark, li.mark) }}
-                </span>
               </template>
             </div>
           </div>
         </div>
       </div>
     </div>
+
+    <div v-if="isEnable(data.property.is_enable_reference_answer) && isShowRightAnswer" class="reference-box">
+      <h5 class="reference-title">参考答案</h5>
+      <span class="reference-answer">{{ data.reference_answer }}</span>
+    </div>
   </div>
 </template>
 
@@ -122,106 +89,44 @@ export default {
     },
     optionList: {
       handler(val) {
-        val.forEach((item) => {
-          item.forEach(({ mark, content_list }) => {
-            let value_list = [];
-            let answerIndex = this.answer.answer_list.findIndex((item) => item.mark === mark);
-            content_list.forEach((li) => {
-              if (['fill', 'input', 'input_any'].includes(li.type) && li.content.length > 0) {
-                value_list.push({
+        if (!val) return;
+        this.optionList.forEach((item) => {
+          item.forEach((li) => {
+            if (['input'].includes(li.type)) {
+              let findIndex = this.answer.answer_list.findIndex(({ mark }) => mark === li.mark);
+              if (findIndex === -1 && li.text.length <= 0) {
+                this.answer.answer_list.splice(findIndex, 1);
+                return;
+              }
+              if (findIndex === -1) {
+                this.answer.answer_list.push({
                   mark: li.mark,
-                  value: li.content,
+                  value: li.text,
                 });
+                return;
+              }
+              if (findIndex !== -1) {
+                this.answer.answer_list[findIndex].value = li.text;
+                return;
               }
-            });
-            if (answerIndex === -1 && value_list.length <= 0) return;
-            if (answerIndex !== -1 && value_list.length <= 0) {
-              this.answer.answer_list.splice(answerIndex, 1);
-              return;
-            }
-            if (answerIndex !== -1) {
-              this.answer.answer_list[answerIndex].value_list = value_list;
-              return;
             }
-            this.answer.answer_list.push({
-              mark,
-              value_list,
-            });
           });
         });
       },
+      immediate: true,
       deep: true,
     },
     isJudgingRightWrong(val) {
       if (!val) return;
-      this.answer.answer_list.forEach(({ mark, value_list }) => {
-        this.optionList
-          .find((item) => item.find((li) => li.mark === mark))
-          .forEach((item) => {
-            item.content_list.forEach((li) => {
-              if (['fill', 'input', 'input_any'].includes(li.type)) {
-                let answer = value_list.find((item) => item.mark === li.mark);
-                if (!answer) return;
-                li.content = answer.value;
-              }
-            });
-          });
+      this.answer.answer_list.forEach(({ mark, value }) => {
+        const parentIndex = this.optionList.findIndex((item) => item.find((li) => li.mark === mark));
+        if (parentIndex === -1) return;
+        const childIndex = this.optionList[parentIndex].findIndex((item) => item.mark === mark);
+        this.optionList[parentIndex][childIndex].text = value;
       });
     },
   },
-  methods: {
-    /**
-     * 计算答题对错选项字体颜色
-     * @param {string} mark 选项标识
-     * @param {string} liMark 选项子标识
-     */
-    computedAnswerClass(mark, liMark) {
-      if (!this.isJudgingRightWrong && !this.isShowRightAnswer) {
-        return '';
-      }
-      let selectOption = this.answer.answer_list
-        .find((item) => item.mark === mark)
-        ?.value_list.find((item) => item.mark === liMark);
-      let answerOption = this.data.answer.answer_list
-        .find((item) => item.mark === mark)
-        ?.value_list.find((item) => item.mark === liMark);
-      if (!selectOption) return '';
-      let selectValue = selectOption.value;
-      let answerValue = answerOption.value;
-      let classList = [];
-      let isRight = selectValue === answerValue;
-
-      if (this.isJudgingRightWrong) {
-        isRight ? classList.push('right') : classList.push('wrong');
-      }
-
-      if (this.isShowRightAnswer && !isRight) {
-        classList.push('show-right-answer');
-      }
-      return classList;
-    },
-    /**
-     * 计算正确答案
-     * @param {string} mark 选项标识
-     * @param {string} liMark 选项子标识
-     */
-    computedAnswerText(mark, liMark) {
-      if (!this.isShowRightAnswer) return '';
-      // mark 对应答题选项
-      let selectOption = this.answer.answer_list
-        .find((item) => item.mark === mark)
-        ?.value_list.find((item) => item.mark === liMark);
-      // mark 对应正确答案选项
-      let answerOption = this.data.answer.answer_list
-        .find((item) => item.mark === mark)
-        ?.value_list.find((item) => item.mark === liMark);
-      let selectValue = selectOption?.value;
-      let answerValue = answerOption.value;
-      let isRight = selectValue === answerValue;
-      if (isRight) return '';
-      return `(${answerValue})`;
-    },
-  },
+  methods: {},
 };
 </script>
 
@@ -248,7 +153,6 @@ $table-border: 1px solid #ebebeb;
           padding: 8px 12px;
           font-size: 16px;
           text-align: center;
-          border-left: $table-border;
         }
       }
 
@@ -265,6 +169,7 @@ $table-border: 1px solid #ebebeb;
           display: flex;
           align-items: center;
           min-height: 48px;
+          overflow: auto;
           border-bottom: $table-border;
           border-left: $table-border;
 
@@ -339,11 +244,27 @@ $table-border: 1px solid #ebebeb;
       .serial-number {
         display: flex;
         align-items: center;
+        width: 40px;
+        min-width: 40px;
+        max-width: 40px;
         height: 100%;
         padding: 8px 12px;
         border-right: $table-border;
       }
     }
   }
+
+  .reference-box {
+    padding: 12px;
+    background: #f9f8f9;
+
+    .reference-title {
+      margin: 0 0 10px;
+      font-size: 14px;
+      font-weight: 400;
+      line-height: 32px;
+      color: #4e5969;
+    }
+  }
 }
 </style>