Bläddra i källkod

审核添加批注功能

natasha 1 dag sedan
förälder
incheckning
cd97b020e9

+ 16 - 80
src/components/CommonPreview.vue

@@ -113,13 +113,6 @@
 
         <main :class="['preview-main', { 'no-audit': !isShowAudit }]">
           <div class="preview-left"></div>
-          <!-- <div
-            id="selectable-area-preview"
-            @mousedown="startSelection"
-            @mousemove="updateSelection"
-            @mouseup="endSelection"
-            @mouseleave="endSelection"
-          > -->
           <CoursewarePreview
             v-if="courseware_info.book_name"
             ref="courserware"
@@ -143,18 +136,6 @@
             @editFeedback="handEditFeedback"
             @selectedComponent="$emit('selectedComponent', $event)"
           />
-          <!-- <div
-              v-if="isSelecting"
-              :style="{
-                position: 'absolute',
-                top: `${menuPosition.startY}px`,
-                left: `${menuPosition.startX}px`,
-                width: `${menuPosition.endX - menuPosition.startX}px`,
-                height: `${menuPosition.endY - menuPosition.startY}px`,
-                border: '2px solid #165DFF',
-              }"
-            ></div>
-          </div> -->
 
           <div class="preview-right"></div>
         </main>
@@ -435,11 +416,12 @@
       width="680px"
       :close-on-click-modal="false"
       class="remark-dialog"
-      @close="visibleRemark = false"
+      @close="closeVisibleRemark"
+      v-if="visibleRemark"
     >
       <Remark :remark="remark" :project-id="projectId" :courseware-id="select_node ? select_node : id" />
       <div slot="footer">
-        <el-button @click="visibleRemark = false">取消</el-button>
+        <el-button @click="closeVisibleRemark">取消</el-button>
         <el-button type="primary" :loading="submit_loading" @click="addCoursewareAuditRemark(select_node)">
           确定
         </el-button>
@@ -660,7 +642,6 @@ export default {
         endX: null,
         endY: null,
       },
-      isSelecting: false,
 
       curToolbarIcon: this.isShowAudit ? 'audit' : '',
       sidebarIconList,
@@ -969,7 +950,7 @@ export default {
         }, {});
       });
     },
-    addRemark(selectNode, x, y, componentId, content_select) {
+    addRemark(selectNode, x, y, br_x, br_y, componentId, content_select) {
       this.remark = {
         remark_content: '',
         file_id_list: [],
@@ -979,6 +960,8 @@ export default {
         this.menuPosition = {
           x,
           y,
+          br_x,
+          br_y,
           componentId,
           content_select,
         };
@@ -986,6 +969,8 @@ export default {
         this.menuPosition = {
           x: -1,
           y: -1,
+          br_x: -1,
+          br_y: -1,
           componentId: 'WHOLE',
           content_select,
         };
@@ -1003,6 +988,8 @@ export default {
         component_id: this.menuPosition.componentId,
         position_x: this.menuPosition.x,
         position_y: this.menuPosition.y,
+        position_br_x: this.menuPosition.br_x,
+        position_br_y: this.menuPosition.br_y,
         content_select: this.menuPosition.content_select,
         file_id_list: this.remark.file_id_list,
       })
@@ -1010,11 +997,17 @@ export default {
           this.submit_loading = false;
           this.visibleRemark = false;
           this.getCoursewareAuditRemarkList(id || this.id);
+          this.$refs.courserware.resetRemark();
         })
         .catch(() => {
           this.submit_loading = false;
         });
     },
+    // 关闭审核批注弹窗
+    closeVisibleRemark() {
+      this.visibleRemark = false;
+      this.$refs.courserware.resetRemark();
+    },
     // 删除批注
     deleteRemarks(id) {
       this.$confirm('确定要删除此条批注吗?', '提示', {
@@ -1738,64 +1731,7 @@ export default {
     computedSelectedGroupCoursewareInfo() {
       return this.$refs.courserware.computedSelectedGroupCoursewareInfo();
     },
-    startSelection(event) {
-      if (
-        this.isTrue(this.courseware_info.is_my_audit_task) &&
-        this.isTrue(this.courseware_info.is_can_add_audit_remark)
-      ) {
-        this.isSelecting = true;
 
-        let clientRect = document.getElementById(`selectable-area-preview`).getBoundingClientRect();
-        this.menuPosition.startX = event.clientX - clientRect.left;
-        this.menuPosition.startY = event.clientY - clientRect.top;
-      }
-    },
-    updateSelection(event) {
-      if (!this.isSelecting) return;
-      let clientRect = document.getElementById(`selectable-area-preview`).getBoundingClientRect();
-
-      this.menuPosition.endX = event.clientX - clientRect.left;
-      this.menuPosition.endY = event.clientY - clientRect.top;
-    },
-    endSelection() {
-      if (!this.isSelecting || this.menuPosition.startX === this.menuPosition.endX || !this.menuPosition.endX) return;
-      this.isSelecting = false;
-      const width = Math.abs(this.menuPosition.endX - this.menuPosition.startX);
-      const height = Math.abs(this.menuPosition.endY - this.menuPosition.startY);
-      const x =
-        this.menuPosition.endX > this.menuPosition.startX
-          ? `${this.menuPosition.startX}px`
-          : `${this.menuPosition.endX}px`;
-      const y =
-        this.menuPosition.endY > this.menuPosition.startY
-          ? `${this.menuPosition.startY}px`
-          : `${this.menuPosition.endY}px`;
-      let obj = {
-        id: Math.random().toString(36).substring(2, 10),
-        width: `${width}px`,
-        height: `${height}px`,
-        x,
-        y,
-        text: '',
-      };
-      this.menuPosition.startX = null;
-      this.menuPosition.endX = null;
-      this.menuPosition.startY = null;
-      this.menuPosition.endY = null;
-      if (width && height && this.isText) {
-        this.data.text_list.push(obj);
-        this.activeType = 'text';
-        this.activeIndex = this.data.text_list.length - 1;
-
-        this.hotspotsActiveIndex = this.data.text_list.length - 1;
-      } else if (width && height && !this.isText) {
-        this.data.input_list.push(obj);
-        this.activeType = 'input';
-        this.activeIndex = this.data.input_list.length - 1;
-
-        this.inputActiveIndex = this.data.input_list.length - 1;
-      }
-    },
     async submitChapterAllCoursewareToAuditFlow(chapter_id) {
       try {
         await this.$confirm('确定要提交审核吗?', '提示', {

+ 5 - 2
src/components/Remark.vue

@@ -10,6 +10,7 @@
     <SoundRecord @updateFileList="updateFileList"></SoundRecord>
     <UploadFile
       key="upload_remark"
+      type="upload_remark"
       :total-size="200"
       :file-list="file_list"
       :file-id-list="remark.file_id_list"
@@ -66,10 +67,12 @@ export default {
       this.file_list.push({
         id,
         file_name: '录制音频',
+        file_id: id,
       });
     },
-    updateFileLists({ file_list, file_id_list, file_info_list, file_info }) {
-      this.file_list = file_list;
+    updateFileLists(val) {
+      this.file_list = val.file_list;
+      let file_id_list = val.file_list.map((item) => item.file_id);
       this.remark.file_id_list = file_id_list;
     },
   },

+ 33 - 6
src/components/UploadFile.vue

@@ -4,6 +4,7 @@
       <span class="label-text" v-if="labelText">{{ labelText }}</span>
       <div class="upload-box">
         <el-upload
+          v-show="false"
           ref="upload"
           class="file-uploader"
           action="no"
@@ -16,9 +17,9 @@
           :on-exceed="handleExceed"
           :limit="limit"
         >
-          <el-button>上传附件</el-button>
+          <!-- <el-button>上传附件</el-button> -->
         </el-upload>
-        <el-button size="small" type="primary" @click="uploadFiles"> 上传 </el-button>
+        <el-button size="small" type="primary" @click="selectAndUpload"> 本地上传 </el-button>
         <el-button size="small" type="primary" @click="useResource"> 使用资源 </el-button>
       </div>
     </div>
@@ -225,6 +226,13 @@ export default {
     },
   },
   methods: {
+    selectAndUpload() {
+      const uploadInput = this.$refs.upload.$el.querySelector('input[type="file"]');
+      if (uploadInput) {
+        uploadInput.value = '';
+        uploadInput.click();
+      }
+    },
     // 显示自定义样式文件列表
     onFileChange(file, fileList) {
       this.afterSelectFile(file);
@@ -246,6 +254,8 @@ export default {
         return;
       }
       this.content.file_list = [...this.content.file_list, ...files];
+      // 选中文件后自动上传
+      this.uploadFiles();
     },
 
     handleExceed(files, fileList) {
@@ -328,6 +338,25 @@ export default {
       } else if (this.type === '3DModel') {
         fileType = ['fbx', 'zip', 'gltf', 'glb'];
         typeTip = '3D模型文件只能是 fbx、gltf、glb、zip 格式!';
+      } else if (this.type === 'upload_remark') {
+        fileType = [
+          'png',
+          'jpg',
+          'jpeg',
+          'mp3',
+          'mp4',
+          'zip',
+          'rar',
+          'txt',
+          'pdf',
+          'doc',
+          'docx',
+          'xls',
+          'xlsx',
+          'ppt',
+          'pptx',
+        ];
+        typeTip = '文件不支持';
       }
       const isNeedType = fileType.includes(suffix);
       if (!isNeedType) {
@@ -404,8 +433,6 @@ export default {
             }
           });
         });
-      // 选中文件后自动上传
-      this.uploadFiles();
     },
 
     // 显示弹窗
@@ -474,8 +501,8 @@ export default {
 }
 
 .upload-box {
-  display: flex;
-  justify-content: space-between;
+  // display: flex;
+  // justify-content: space-between;
 
   .el-button + .el-button {
     margin-left: 2px;

+ 90 - 21
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -1,5 +1,14 @@
 <template>
-  <div ref="courserware" class="courserware" :style="computedCourserwareStyle()" @mouseup="handleTextSelection">
+  <div
+    ref="courserware"
+    class="courserware"
+    id="selectable-area-preview"
+    :style="computedCourserwareStyle()"
+    @mouseup="handleTextSelection"
+    @mousedown="startSelection"
+    @mousemove="updateSelection"
+    @mouseleave="endSelection"
+  >
     <template v-for="(row, i) in data.row_list">
       <div v-show="computedRowVisibility(row.row_id)" :key="i" class="row" :style="getMultipleColStyle(i)">
         <el-checkbox
@@ -33,7 +42,7 @@
                 @handleHeightChange="handleHeightChange"
               />
 
-              <div
+              <!-- <div
                 v-if="showMenu && componentId === grid.id"
                 :key="'menu' + grid.id + k"
                 class="custom-context-menu"
@@ -41,7 +50,7 @@
                 @click="handleMenuItemClick"
               >
                 添加批注
-              </div>
+              </div> -->
               <div
                 v-if="showRemark && Object.keys(componentRemarkObj).length !== 0 && componentRemarkObj[grid.id]"
                 :key="'show' + grid.id + k"
@@ -70,13 +79,23 @@
         </template>
       </div>
     </template>
-
+    <div
+      v-if="menuPosition.endX - menuPosition.startX > 3 && menuPosition.endY - menuPosition.startY > 3"
+      :style="{
+        position: 'absolute',
+        top: `${menuPosition.startY}px`,
+        left: `${menuPosition.startX}px`,
+        width: `${menuPosition.endX - menuPosition.startX}px`,
+        height: `${menuPosition.endY - menuPosition.startY}px`,
+        border: '2px solid #165DFF',
+      }"
+    ></div>
     <!-- 选中文本的工具栏 -->
     <div v-show="showToolbar" class="contentmenu" :style="contentmenu">
       <template v-if="canRemark">
-        <span class="button" @click="handleMenuItemClick($event, 'tool')">
+        <!-- <span class="button" @click="handleMenuItemClick($event, 'tool')">
           <SvgIcon icon-class="sidebar-pushpin" size="14" /> 添加批注
-        </span>
+        </span> -->
       </template>
       <template v-else>
         <span class="button" @click="setNote"><SvgIcon icon-class="sidebar-text" size="14" /> 笔记</span>
@@ -166,7 +185,7 @@ export default {
         left: 0,
         top: 0,
       }, // courserware盒子原始距离页面顶部和左边的距离
-      menuPosition: { x: 0, y: 0, select_node: '' }, // 用于存储菜单的位置
+      menuPosition: { x: 0, y: 0, select_node: '', startX: null, startY: null, endX: null, endY: null }, // 用于存储菜单的位置
       componentId: '', // 添加批注的组件id
       rowCheckList: {},
       showToolbar: false,
@@ -177,6 +196,8 @@ export default {
       selectedInfo: null,
       selectHandleInfo: null,
       curSelectId: '', // 当前选中组件id
+
+      isSelecting: false, // 是否开始框选内容
     };
   },
   watch: {
@@ -612,11 +633,10 @@ export default {
       }
     },
     handleResult(top, left, select_node) {
-      this.menuPosition = {
-        x: this.menuPosition.x + left,
-        y: this.menuPosition.y + top,
-        select_node,
-      }; // 设置菜单位置
+      this.menuPosition.x = this.menuPosition.x + left;
+      this.menuPosition.y = this.menuPosition.y + top;
+      this.menuPosition.select_node = select_node;
+      // 设置菜单位置
       this.showMenu = true; // 显示菜单
     },
     handleMenuItemClick(event, type) {
@@ -631,17 +651,21 @@ export default {
         this.componentId = info.blockId;
         text = info.text;
 
-        this.$emit('computeScroll');
         this.showMenu = false;
       }
-      this.$emit(
-        'addRemark',
-        this.menuPosition.select_node,
-        this.menuPosition.x,
-        this.menuPosition.y,
-        this.componentId,
-        text,
-      );
+      this.$emit('computeScroll');
+      setTimeout(() => {
+        this.$emit(
+          'addRemark',
+          this.menuPosition.select_node,
+          this.menuPosition.startX,
+          this.menuPosition.startY,
+          this.menuPosition.endX,
+          this.menuPosition.endY,
+          this.componentId,
+          text,
+        );
+      }, 10);
     },
     handleMouseDown(event) {
       if (event.button === 0 && event.target.className !== 'custom-context-menu') {
@@ -726,6 +750,9 @@ export default {
           top: `${Math.round(selectRect.top - boxRect.top + selectRect.height)}px`, // 向上偏移10px
         };
       }, 100);
+      if (this.canRemark) {
+        this.endSelection();
+      }
     },
     // 笔记
     setNote() {
@@ -999,6 +1026,48 @@ export default {
         });
       }
     },
+    startSelection(event) {
+      if (this.canRemark) {
+        this.isSelecting = true;
+
+        let clientRect = document.getElementById(`selectable-area-preview`).getBoundingClientRect();
+        this.menuPosition.startX = event.clientX - clientRect.left;
+        this.menuPosition.startY = event.clientY - clientRect.top;
+        this.menuPosition.endX = null;
+        this.menuPosition.endY = null;
+      }
+    },
+    updateSelection(event) {
+      if (!this.isSelecting || !this.canRemark) return;
+      let clientRect = document.getElementById(`selectable-area-preview`).getBoundingClientRect();
+
+      this.menuPosition.endX = event.clientX - clientRect.left;
+      this.menuPosition.endY = event.clientY - clientRect.top;
+    },
+    endSelection() {
+      this.isSelecting = false;
+
+      if (this.menuPosition.startX === this.menuPosition.endX || !this.menuPosition.endX || !this.canRemark) return;
+      const width = this.menuPosition.endX - this.menuPosition.startX;
+      const height = this.menuPosition.endY - this.menuPosition.startY;
+      const x =
+        this.menuPosition.endX > this.menuPosition.startX
+          ? `${this.menuPosition.startX}px`
+          : `${this.menuPosition.endX}px`;
+      const y =
+        this.menuPosition.endY > this.menuPosition.startY
+          ? `${this.menuPosition.startY}px`
+          : `${this.menuPosition.endY}px`;
+      if (width > 3 && height > 3) {
+        this.handleMenuItemClick();
+      } else {
+        this.resetRemark();
+      }
+    },
+    // 重置框选数据
+    resetRemark() {
+      this.menuPosition = { x: 0, y: 0, select_node: '', startX: null, startY: null, endX: null, endY: null };
+    },
   },
 };
 </script>