فهرست منبع

1、组件背景图设置 2、章节后面添加下拉菜单 3、移动组件和章节

dsy 2 روز پیش
والد
کامیت
3977c4489e

+ 47 - 5
src/views/book/courseware/create/components/SelectBackground.vue

@@ -10,21 +10,26 @@
       <div class="select-background-top">
         <span class="tab">{{ title }}</span>
         <SelectUpload :is-show-resource="isResource" type="image" @uploadSuccess="uploadSuccess" />
-        <el-select v-model="imageMode" style="width: 100px; margin-left: 4px">
+        <el-select v-model="imageMode" style="width: 100px; margin-left: 4px" @change="changeImageMode">
           <el-option v-for="item in imageModeList" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
       </div>
-      <div class="background-img">
-        <div v-if="file_url" class="img-set" :style="{ top: `${imgData.top - 9}px`, left: `${imgData.left}px` }">
+      <div class="background-img" :style="computedBackgroundImg()">
+        <div
+          v-if="file_url"
+          class="img-set"
+          :style="{ top: imgTop, left: imgLeft, position: imageMode !== 'repeat' ? 'relative' : '' }"
+        >
           <div class="dot top-left" @mousedown="dragStart($event, 'nwse-resize', 'top-left')"></div>
           <div class="horizontal-line" @mousedown="dragStart($event, 'ns-resize', 'top')"></div>
           <div class="dot top-right" @mousedown="dragStart($event, 'nesw-resize', 'top-right')"></div>
           <div class="vertical-line" @mousedown="dragStart($event, 'ew-resize', 'left')"></div>
           <img
+            ref="img"
             :src="file_url"
             draggable="false"
             :alt="title"
-            :style="{ width: `${imgData.width}px`, height: `${imgData.height}px` }"
+            :style="{ width: imgWidth, height: imgHeight }"
             @mousedown="dragStart($event, 'move', 'move')"
           />
           <div class="vertical-line" @mousedown="dragStart($event, 'ew-resize', 'right')"></div>
@@ -100,6 +105,24 @@ export default {
       imageMode: 'free',
     };
   },
+  computed: {
+    imgTop() {
+      if (this.imageMode === 'repeat') return '-9px';
+      return `${this.imgData.top - 9}px`;
+    },
+    imgLeft() {
+      if (this.imageMode === 'repeat') return '';
+      return `${this.imgData.left}px`;
+    },
+    imgWidth() {
+      if (this.imageMode === 'repeat') return '';
+      return `${this.imgData.width}px`;
+    },
+    imgHeight() {
+      if (this.imageMode === 'repeat') return '';
+      return `${this.imgData.height}px`;
+    },
+  },
   watch: {
     visible(val) {
       if (val) {
@@ -150,6 +173,16 @@ export default {
         image_mode: this.imageMode,
       });
     },
+    // 计算背景图片
+    computedBackgroundImg() {
+      if (this.imageMode === 'repeat') {
+        return {
+          display: 'flex',
+          'justify-content': 'center',
+          'align-items': 'center',
+        };
+      }
+    },
     /**
      * 拖拽开始
      * @param {MouseEvent} event
@@ -173,6 +206,7 @@ export default {
      */
     mouseMove(event) {
       if (!this.drag.dragging) return;
+      if (this.imageMode === 'fill') return; // 填充模式下不允许拖拽改变大小和位置
       const { clientX, clientY } = event;
       const { startX, startY, type } = this.drag;
 
@@ -290,6 +324,15 @@ export default {
         left: 0,
       };
     },
+    // 填充模式改变时,重新计算图片位置和大小
+    changeImageMode() {
+      if (this.imageMode === 'fill') {
+        this.coverComputed(this.maxWidth, this.maxHeight);
+      }
+      if (this.imageMode === 'repeat') {
+        // 如果图片小于最大高度,最大宽度,设置时,按照比例居中显示在显示框中。
+      }
+    },
   },
 };
 </script>
@@ -313,7 +356,6 @@ export default {
     border: 1px dashed rgba(0, 0, 0, 8%);
 
     .img-set {
-      position: relative;
       display: inline-grid;
       grid-template:
         ' . . . ' 2px

+ 9 - 1
src/views/book/courseware/preview/components/common/PreviewMixin.js

@@ -159,7 +159,15 @@ const mixin = {
         const topPct = typeof pos.top === 'undefined' ? '' : pos.top;
 
         backgroundData.backgroundImage = `url(${this.data.property.background_image_url})`;
-        backgroundData.backgroundSize = pos.image_mode === 'fill' ? '100% 100%' : `${widthPct}% ${heightPct}%`;
+        let bcSize = '';
+        if (pos.image_mode === 'fill') {
+          bcSize = '100% 100%';
+        } else if (pos.image_mode === 'repeat') {
+          bcSize = 'auto';
+        } else {
+          bcSize = `${widthPct}% ${heightPct}%`;
+        }
+        backgroundData.backgroundSize = bcSize;
         backgroundData.backgroundPosition = pos.image_mode === 'fill' ? '0% 0%' : `${leftPct}% ${topPct}%`;
         backgroundData.backgroundRepeat = pos.image_mode === 'repeat' ? 'repeat' : 'no-repeat';
       }

+ 17 - 2
src/views/personal_workbench/edit_task/index.vue

@@ -23,12 +23,12 @@
         </ul>
       </div>
       <div class="textbook-chapter">
-        <div class="chapter-header">
+        <div ref="chapterHeader" class="chapter-header">
           <span class="cell">教材内容</span>
           <span class="cell">制作人</span>
           <span class="cell">状态</span>
         </div>
-        <div class="chapter-list">
+        <div ref="chapterList" class="chapter-list">
           <div
             v-for="{ id, name, producer_list, status_name, deep, is_my_edit_task } in node_list"
             :key="id"
@@ -103,6 +103,15 @@ export default {
         is_contain_auditor: 'false',
       }).then(({ node_list }) => {
         this.node_list = node_list;
+
+        this.$nextTick(() => {
+          const chapterList = this.$refs.chapterList;
+          if (chapterList.scrollHeight > chapterList.clientHeight) {
+            this.$refs.chapterHeader.classList.add('has-scrollbar');
+          } else {
+            this.$refs.chapterHeader.classList.remove('has-scrollbar');
+          }
+        });
       });
     },
     /**
@@ -151,6 +160,8 @@ export default {
 .edit-task {
   @include page-base;
 
+  height: 100%;
+
   .edit-main {
     display: flex;
     flex: 1;
@@ -278,6 +289,10 @@ export default {
 
         @include cell;
 
+        &.has-scrollbar {
+          padding-right: 15px;
+        }
+
         .cell {
           font-weight: bold;
           text-align: center;

+ 104 - 20
src/views/personal_workbench/project/ProductionEditorialManage.vue

@@ -14,9 +14,9 @@
         </div>
         <span class="link" @click="visibleAuditSteps = true">设置审校步骤</span>
         <div class="operator flex">
-          <span class="link" @click="openBookUnifiedAttrib">教材样式设置</span>
-          <span class="link" @click="addChapterDialog">添加章节节点</span>
-          <span class="link" @click="addCoursewareDialog">添加教材内容节点</span>
+          <span class="link" @click="openBookUnifiedAttrib()">教材样式设置</span>
+          <span class="link" @click="addChapterDialog()">添加章节节点</span>
+          <span class="link" @click="addCoursewareDialog()">添加教材内容节点</span>
           <span class="link" @click="$router.push({ path: `/personal_workbench/project` })">返回项目列表</span>
         </div>
       </div>
@@ -52,11 +52,22 @@
           @click="selectActiveChapter(id, is_leaf_chapter === 'true')"
         >
           <div
-            :class="['chapter-title', 'nowrap-ellipsis', { courseware: isEnable(is_leaf_chapter) }]"
+            :class="['chapter-title', { courseware: isEnable(is_leaf_chapter) }]"
             :style="computedNameStyle(deep)"
             :title="name"
           >
-            {{ name }}
+            <span class="nowrap-ellipsis">{{ name }}</span>
+            <el-dropdown v-if="!isEnable(is_leaf_chapter)" trigger="click">
+              <span class="el-dropdown-link" style="cursor: pointer"><i class="el-icon-plus"></i> </span>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item>
+                  <span @click="addCoursewareDialog(id)">添加教材内容</span>
+                </el-dropdown-item>
+                <el-dropdown-item>
+                  <span @click="addChapterDialog(id)">添加章节</span>
+                </el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
           </div>
           <div
             class="producer nowrap-ellipsis"
@@ -75,18 +86,13 @@
           <div class="status">{{ status_name }}</div>
           <div class="operator">
             <template v-if="isEnable(is_leaf_chapter)">
-              <span
-                v-if="i > 0 && node_list[i - 1].is_leaf_chapter === 'true'"
-                class="link"
-                @click="moveChapterTreeNode(i, 0)"
-              >
-                上移
-              </span>
-              <span
-                v-if="i < node_list.length - 1 && node_list[i + 1].is_leaf_chapter === 'true'"
-                class="link"
-                @click="moveChapterTreeNode(i, 1)"
-              >
+              <span v-if="i > 0 && computedIsFirst(i)" class="link" @click="moveChapterTreeNode(i, 0)">上移</span>
+              <span v-if="i < node_list.length - 1" class="link" @click="moveChapterTreeNode(i, 1)">下移</span>
+            </template>
+
+            <template v-else>
+              <span v-if="i > 1" class="link" @click="moveChapterNode(i, deep, 0)">上移</span>
+              <span v-if="i > 0 && computedIsLast(id, deep)" class="link" @click="moveChapterNode(i, deep, 1)">
                 下移
               </span>
             </template>
@@ -287,11 +293,25 @@ export default {
     openBookUnifiedAttrib() {
       this.visibleAttr = true;
     },
-    addChapterDialog() {
+    /**
+     * 打开添加章节对话框
+     * @param {string} curId - 当前选中章节ID
+     */
+    addChapterDialog(curId) {
+      if (curId) {
+        this.curSelectId = curId;
+      }
       this.visible = true;
       this.addType = 'chapter';
     },
-    addCoursewareDialog() {
+    /**
+     * 打开添加课件对话框
+     * @param {string} curId - 当前选中章节ID
+     */
+    addCoursewareDialog(curId) {
+      if (curId) {
+        this.curSelectId = curId;
+      }
       this.visible = true;
       this.addType = 'courseware';
     },
@@ -402,13 +422,74 @@ export default {
         });
     },
     /**
+     * 计算是否为第一个教材内容节点
+     * @param {number} i - 当前节点索引
+     */
+    computedIsFirst(i) {
+      const index = this.node_list.findIndex((node) => node.is_leaf_chapter === 'true');
+      return i !== index;
+    },
+    /**
+     * 计算是否为同深度最后一个节点
+     * @param {string} id - 章节ID
+     * @param {number} deep - 节点深度
+     * @returns {boolean} - 是否为最后一个节点
+     */
+    computedIsLast(id, deep) {
+      const index = this.node_list.findIndex((node) => node.id === id);
+      for (let i = index + 1; i < this.node_list.length; i++) {
+        if (this.node_list[i].deep === deep) {
+          return true;
+        } else if (this.node_list[i].deep < deep) {
+          break;
+        }
+      }
+      return false;
+    },
+    /**
      * 章节节点上移下移
      * @param {number} i - 当前节点索引
      * @param {number} dest_position - 目标位置 0上移 1下移
      */
     moveChapterTreeNode(i, dest_position) {
       const id = this.node_list[i].id;
-      const dest_id = this.node_list[dest_position === 0 ? i - 1 : i + 1].id;
+      const is_leaf_chapter = this.node_list[dest_position === 0 ? i - 1 : i + 1].is_leaf_chapter === 'false';
+      const deep = is_leaf_chapter ? 2 : 1;
+      const num = dest_position === 0 ? i - deep : i + deep;
+      const dest_id = this.node_list[num].id;
+
+      ChapterMoveTreeNode({ id, dest_id, dest_position }).then(() => {
+        this.getBookChapterStructExpandList();
+        this.$message.success('章节移动成功');
+      });
+    },
+    /**
+     * 同深度章节节点上移下移
+     * @param {number} i - 当前节点索引
+     * @param {number} deep - 节点深度
+     * @param {number} dest_position - 目标位置 0上移 1下移
+     */
+    moveChapterNode(i, deep, dest_position) {
+      const id = this.node_list[i].id;
+      let num = -1;
+      if (dest_position === 0) {
+        for (let j = i - 1; j >= 0; j--) {
+          if (this.node_list[j].deep === deep) {
+            num = j;
+            break;
+          }
+        }
+      } else {
+        for (let j = i + 1; j < this.node_list.length; j++) {
+          if (this.node_list[j].deep === deep) {
+            num = j;
+            break;
+          }
+        }
+      }
+      if (num === -1) return;
+      const dest_id = this.node_list[num].id;
+
       ChapterMoveTreeNode({ id, dest_id, dest_position }).then(() => {
         this.getBookChapterStructExpandList();
         this.$message.success('章节移动成功');
@@ -514,6 +595,9 @@ export default {
       }
 
       .chapter-title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
         font-weight: bold;
         color: #000;
       }