Sfoglia il codice sorgente

1、查看答案只显示正确答案 2、使用模板的选择框始终保持顶部 3、保存选择内容为模板

dsy 1 settimana fa
parent
commit
8557d56bc0

+ 7 - 0
src/components/CommonPreview.vue

@@ -1402,6 +1402,13 @@ export default {
         this.visiblePreviewURL = true;
       });
     },
+    /**
+     * 计算选中分组行的课件信息
+     * @returns {object} 选中分组行的课件信息
+     */
+    computedSelectedGroupCoursewareInfo() {
+      return this.$refs.courserware.computedSelectedGroupCoursewareInfo();
+    },
   },
 };
 </script>

+ 111 - 1
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -89,6 +89,8 @@
 <script>
 import { previewComponentList } from '@/views/book/courseware/data/bookType';
 
+import _ from 'lodash';
+
 export default {
   name: 'CoursewarePreview',
   provide() {
@@ -236,6 +238,7 @@ export default {
     /**
      * 计算行的可见性
      * @params {string} rowId 行的ID
+     * @return {boolean} 行是否可见
      */
     computedRowVisibility(rowId) {
       if (this.groupShowAll) return true;
@@ -253,6 +256,113 @@ export default {
       return this.rowCheckList[row_id];
     },
     /**
+     * 计算选中分组行的课件信息
+     * @returns {object} 选中分组行的课件信息
+     */
+    computedSelectedGroupCoursewareInfo() {
+      if (Object.keys(this.rowCheckList).length === 0) {
+        return {};
+      }
+      // 根据 rowCheckList 过滤出选中的行,获取这些行的组件信息
+      let coursewareInfo = structuredClone(this.data);
+      coursewareInfo.row_list = coursewareInfo.row_list.filter((row) => {
+        let groupRow = this.groupRowList.find(({ row_id }) => row_id === row.row_id);
+        if (!groupRow.is_pre_same_group) {
+          return this.rowCheckList[groupRow.row_id];
+        }
+        const index = this.groupRowList.findIndex(({ row_id }) => row_id === row.row_id);
+        if (index === -1) return false;
+        for (let i = index - 1; i >= 0; i--) {
+          if (!this.groupRowList[i].is_pre_same_group) {
+            return this.rowCheckList[this.groupRowList[i].row_id];
+          }
+        }
+        return false;
+      });
+      // 获取选中行的所有组件id列表
+      let component_id_list = coursewareInfo.row_list.flatMap((row) =>
+        row.col_list.flatMap((col) => col.grid_list.map((grid) => grid.id)),
+      );
+      // 获取选中行的分组列表描述
+      let content_group_row_list = this.groupRowList.filter(({ row_id, is_pre_same_group }) => {
+        if (!is_pre_same_group) {
+          return this.rowCheckList[row_id];
+        }
+        const index = this.groupRowList.findIndex(({ row_id: id }) => id === row_id);
+        if (index === -1) return false;
+        for (let i = index - 1; i >= 0; i--) {
+          if (!this.groupRowList[i].is_pre_same_group) {
+            return this.rowCheckList[this.groupRowList[i].row_id];
+          }
+        }
+        return false;
+      });
+
+      let groupIdList = _.cloneDeep(content_group_row_list);
+      let groupList = [];
+      // 通过判断 is_pre_same_group 将组合并
+      for (let i = 0; i < groupIdList.length; i++) {
+        if (groupIdList[i].is_pre_same_group) {
+          groupList[groupList.length - 1].row_id_list.push(groupIdList[i].row_id);
+        } else {
+          groupList.push({
+            name: '',
+            row_id_list: [groupIdList[i].row_id],
+            component_id_list: [],
+          });
+        }
+      }
+
+      // 通过合并后的分组,获取对应的组件 id 和分组名称
+      groupList.forEach(({ row_id_list, component_id_list }, i) => {
+        row_id_list.forEach((row_id, j) => {
+          let row = this.data.row_list.find((row) => {
+            return row.row_id === row_id;
+          });
+          // 当前行所有组件id列表
+          let gridIdList = row.col_list.map((col) => col.grid_list.map((grid) => grid.id)).flat();
+          component_id_list.push(...gridIdList);
+          // 查找每组第一行中第一个包含 describe、label 或 stem 的组件
+          if (j === 0) {
+            let findKey = '';
+            let findType = '';
+            row.col_list.some((col) => {
+              const findItem = col.grid_list.find(({ type }) => {
+                return ['describe', 'label', 'stem'].includes(type);
+              });
+              if (findItem) {
+                findKey = findItem.id;
+                findType = findItem.type;
+                return true;
+              }
+            });
+            let groupName = `组${i + 1}`;
+
+            // 如果有标签类组件,获取对应名称
+            if (findKey) {
+              let item = this.isEdit
+                ? this.findChildComponentByKey(`grid-${findKey}`)
+                : this.$refs.previewEdit.findChildComponentByKey(`preview-${findKey}`);
+              if (['describe', 'stem'].includes(findType)) {
+                groupName = item.data.content.replace(/<[^>]+>/g, '');
+              } else if (findType === 'label') {
+                groupName = item.data.dynamicTags.map((tag) => tag.text).join(', ');
+              }
+            }
+
+            groupList[i].name = groupName;
+          }
+        });
+      });
+
+      return {
+        content: JSON.stringify(coursewareInfo),
+        component_id_list: JSON.stringify(component_id_list),
+        content_group_row_list: JSON.stringify(content_group_row_list),
+        content_group_component_list: JSON.stringify(groupList),
+      };
+    },
+    /**
      * 分割整数为多个 1的倍数
      * @param {number} num
      * @param {number} parts
@@ -865,7 +975,7 @@ export default {
 
     .row-checkbox {
       position: absolute;
-      left: 4px;
+      left: -20px;
     }
   }
 

+ 0 - 6
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -364,14 +364,8 @@ export default {
      * @param {string} mark 选项标识
      */
     computedAnswerText(mark) {
-      if (!this.isShowRightAnswer) return '';
-      let selectOption = this.answer.answer_list.find((item) => item.mark === mark);
       let answerOption = this.data.answer.answer_list.find((item) => item.mark === mark);
-      if (!selectOption) return '';
-      let selectValue = selectOption.value;
       let answerValue = answerOption.value;
-      let isRight = selectValue === answerValue;
-      if (isRight) return '';
       return `${answerValue}`;
     },
     // 重做

+ 2 - 12
src/views/book/courseware/preview/components/judge/JudgePreview.vue

@@ -99,13 +99,7 @@
               v-for="option_type in incertitudeList"
               :key="option_type"
               :style="{ cursor: disabled ? 'not-allowed' : 'pointer' }"
-              :class="[
-                'option-type-item',
-                {
-                  active: isAnswer(mark, option_type),
-                },
-                computedIsShowRightAnswer(mark, option_type),
-              ]"
+              :class="['option-type-item', computedIsShowRightAnswer(mark, option_type)]"
               @click="selectAnswer(mark, option_type)"
             >
               <SvgIcon
@@ -203,13 +197,9 @@ export default {
     // 计算是否显示正确答案的样式
     computedIsShowRightAnswer(mark, option_type) {
       if (!this.isShowRightAnswer) return '';
-      let selectOption = this.answer.answer_list.find((item) => item.mark === mark); // 查找是否已选中的选项
       // 是否是正确的选项类型
       let isCorrectType = this.data.answer.answer_list.find((item) => item.mark === mark)?.option_type === option_type;
-      if (!selectOption) {
-        return isCorrectType ? 'answer-right' : '';
-      }
-      return isCorrectType && !(selectOption.option_type === option_type) ? 'answer-right' : '';
+      return isCorrectType ? 'answer-right' : '';
     },
     retry() {
       this.answer.answer_list = [];

+ 0 - 1
src/views/book/courseware/preview/components/matching/MatchingPreview.vue

@@ -49,7 +49,6 @@
       @closeAnswerAnalysis="closeAnswerAnalysis"
     >
       <div slot="right-answer" class="right-answer">
-        <div class="title">正确答案</div>
         <ul ref="answerList" class="option-list">
           <li v-for="(item, i) in data.option_list" :key="i" class="list-item">
             <div v-for="{ content, mark } in item" :key="mark" :class="['item-wrapper', `answer-item-${mark}`]">

+ 3 - 2
src/views/book/courseware/preview/components/select/SelectPreview.vue

@@ -60,7 +60,7 @@
           v-for="({ content, mark, multilingual, paragraph_list }, i) in data.option_list"
           :key="mark"
           :style="{ cursor: disabled ? 'not-allowed' : 'pointer', borderColor: data.unified_attrib?.topic_color }"
-          :class="['option-item', { active: isAnswer(mark) }, ...computedAnswerClass(mark, 'answer-analysis')]"
+          :class="['option-item', ...computedAnswerClass(mark, 'answer-analysis')]"
           @click="selectAnswer(mark)"
         >
           <span :class="[isSingle ? 'radio' : 'checkbox']">
@@ -165,9 +165,10 @@ export default {
       let isRight = this.data.answer.answer_list.includes(mark); // 是否是正确答案
 
       let answerClass = [];
-      if (!isHas && this.isShowRightAnswer && type === 'answer-analysis') {
+      if (this.isShowRightAnswer && type === 'answer-analysis') {
         answerClass = isRight ? ['answer-right'] : [];
       }
+
       // 判断是否是正确答案
       if (isHas && this.isJudgingRightWrong) {
         answerClass = isRight ? ['answer-right'] : ['wrong'];

+ 31 - 22
src/views/personal_workbench/edit_task/edit/UseTemplate.vue

@@ -44,23 +44,25 @@
       </div>
 
       <div class="courseware-preview">
-        <el-popover placement="bottom-start" width="300" trigger="click" :disabled="curType === 0">
-          <div class="courseware-tree">
-            <div
-              v-for="{ id: nodeId, name, deep, is_leaf_chapter } in node_list"
-              :key="nodeId"
-              :class="['menu-item', { active: curSelectId === nodeId }, { courseware: isTrue(is_leaf_chapter) }]"
-              :style="computedNameStyle(deep, isTrue(is_leaf_chapter))"
-              @click="selectChapterNode(nodeId, isTrue(is_leaf_chapter))"
-            >
-              <span class="name nowrap-ellipsis" :title="name">
-                {{ name }}
-              </span>
+        <div class="preview-top">
+          <el-popover placement="bottom-start" width="300" trigger="click" :disabled="curType === 0">
+            <div class="courseware-tree">
+              <div
+                v-for="{ id: nodeId, name, deep, is_leaf_chapter } in node_list"
+                :key="nodeId"
+                :class="['menu-item', { active: curSelectId === nodeId }, { courseware: isTrue(is_leaf_chapter) }]"
+                :style="computedNameStyle(deep, isTrue(is_leaf_chapter))"
+                @click="selectChapterNode(nodeId, isTrue(is_leaf_chapter))"
+              >
+                <span class="name nowrap-ellipsis" :title="name">
+                  {{ name }}
+                </span>
+              </div>
             </div>
-          </div>
-          <el-button slot="reference" class="courseware-name">{{ coursewareName }}</el-button>
-        </el-popover>
-        <span class="fullpath">{{ fullPath }}</span>
+            <el-button slot="reference" class="courseware-name">{{ coursewareName }}</el-button>
+          </el-popover>
+          <span class="fullpath">{{ fullPath }}</span>
+        </div>
         <CoursewarePreview
           v-if="coursewareData.row_list?.length > 0"
           ref="courserware"
@@ -443,13 +445,20 @@ export default {
       flex: 1;
       overflow: auto;
 
-      .courseware-name {
-        margin: 2px 0 0 4px;
-      }
+      .preview-top {
+        position: sticky;
+        top: 0;
+        left: 0;
+        z-index: 9;
 
-      .fullpath {
-        display: inline-block;
-        margin-left: 24px;
+        .courseware-name {
+          margin: 2px 0 0 4px;
+        }
+
+        .fullpath {
+          display: inline-block;
+          margin-left: 24px;
+        }
       }
     }
   }

+ 12 - 0
src/views/personal_workbench/edit_task/preview/CreateCoursewareAsTemplate.vue

@@ -82,6 +82,18 @@ export default {
       return this.titleList[this.scope];
     },
   },
+  watch: {
+    visible(newVal) {
+      if (newVal) return;
+      this.form = {
+        name: '',
+        label_list: [],
+        memo: '',
+      };
+      this.inputVisible = false;
+      this.inputValue = '';
+    },
+  },
   methods: {
     dialogClose() {
       this.$emit('update:visible', false);

+ 20 - 2
src/views/personal_workbench/edit_task/preview/index.vue

@@ -4,10 +4,11 @@
 
     <CommonPreview :id="id" ref="preview" :project-id="project_id" type="edit_preview">
       <template #operator="{ courseware }">
-        <el-popover placement="bottom" width="125" trigger="click">
+        <el-popover placement="bottom" width="155" trigger="click">
           <el-link type="primary" @click="openCreateTemplateDialog(0)">保存本页为模板</el-link>
           <el-link type="primary" @click="openCreateTemplateDialog(3)">保存本课为模板</el-link>
           <el-link type="primary" @click="openCreateTemplateDialog(1)">保存本书为模板</el-link>
+          <el-link type="primary" @click="openCreateTemplateDialog(0, 'true')">保存选择内容为模板</el-link>
           <span slot="reference" class="link save-template">保存为个人模板</span>
         </el-popover>
         <span class="link"></span>
@@ -50,6 +51,7 @@ export default {
       isTrue,
       visibleTemplate: false,
       templateScope: 0,
+      is_select_part_courseware_mode: 'false',
     };
   },
   methods: {
@@ -71,9 +73,15 @@ export default {
         this.$refs.preview.getBookCoursewareInfo(this.$refs.preview.select_node);
       });
     },
-    openCreateTemplateDialog(scope) {
+    /**
+     * 打开保存为个人模板的弹窗
+     * @param {number} scope 模板范围 0-本页 1-本书 3-本课
+     * @param {'false' | 'true'} part_courseware_mode 是否部分课件模式 模板范围为 0 时有效
+     */
+    openCreateTemplateDialog(scope, part_courseware_mode = 'false') {
       this.visibleTemplate = true;
       this.templateScope = scope;
+      this.is_select_part_courseware_mode = part_courseware_mode;
     },
     /**
      * 保存为个人模板
@@ -90,9 +98,19 @@ export default {
       })
         .then(() => {
           this.visibleTemplate = false;
+          let courseware_info = {};
+          if (this.is_select_part_courseware_mode === 'true') {
+            courseware_info = this.$refs.preview.computedSelectedGroupCoursewareInfo();
+            if (Object.keys(courseware_info).length === 0) {
+              this.$message.warning('请选择要保存的内容');
+              return;
+            }
+          }
           SaveCoursewareAsTemplatePersonal({
             courseware_id: this.$refs.preview.select_node,
             scope: this.templateScope,
+            is_select_part_courseware_mode: this.is_select_part_courseware_mode,
+            courseware_info,
             ...form,
           }).then(() => {
             this.$message.success('已保存为个人模板');