dsy 2 هفته پیش
والد
کامیت
71bf43ed61
5فایلهای تغییر یافته به همراه242 افزوده شده و 87 حذف شده
  1. BIN
      src/assets/icon/back-top.png
  2. 6 0
      src/icons/svg/catalogue.svg
  3. 1 1
      src/layouts/default/index.vue
  4. 3 0
      src/styles/variables.scss
  5. 232 86
      src/web_preview/index.vue

BIN
src/assets/icon/back-top.png


+ 6 - 0
src/icons/svg/catalogue.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" width="52.5" height="52.5" style="" filter="none">
+    
+    <g>
+    <path d="M8 6h-2v2h2v-2zM28 6h-18v2h18v-2zM8 14h-2v2h2v-2zM28 14h-18v2h18v-2zM8 22h-2v2h2v-2zM28 22h-18v2h18v-2z" fill="rgba(64,149,229,1)"></path>
+    </g>
+  </svg>

+ 1 - 1
src/layouts/default/index.vue

@@ -2,7 +2,7 @@
   <div class="app">
     <LayoutHeader />
     <LayoutBreadcrumb :is-show-breadcrumb.sync="isShowBreadcrumb" />
-    <div class="app-container" :style="{ height: `calc(100vh - 54px ${isShowBreadcrumb ? '- 56px' : ''})` }">
+    <div class="app-container" :style="{ height: `calc(100vh - 64px ${isShowBreadcrumb ? '- 56px' : ''})` }">
       <router-view />
     </div>
   </div>

+ 3 - 0
src/styles/variables.scss

@@ -28,4 +28,7 @@ $courseware-right-margin: 100px; // 教材右页边距
 $courseware-top-margin: 65px; // 教材顶部页边距
 $courseware-bottom-margin: 65px; // 教材底部页边距
 $component-spacing: 24px; // 组件间间距
+$title-content-spacing: 65px; // 标题与内容间距
 $border-component-spacing: 10px; // 组件边框与组件内容间距
+$catalogue-width: 300px; // 目录宽度
+$sidebar-width: 300px; // 工具栏宽度

+ 232 - 86
src/web_preview/index.vue

@@ -1,11 +1,7 @@
 <template>
   <div class="common-preview">
     <div class="common-preview__header">
-      <div class="menu-container">
-        <MenuPopover :node-list="node_list" :book-name="courseware_info.book_name" @selectNode="selectNode" />
-      </div>
       <div class="courseware">
-        <span class="name-path">{{ courseware_info.name_path }}</span>
         <span class="flow-nodename">{{ courseware_info.cur_audit_flow_node_name }}</span>
         <slot name="middle" :courseware="courseware_info"></slot>
         <div class="group">
@@ -26,12 +22,44 @@
     </div>
 
     <div class="audit-content">
+      <!-- 左侧菜单栏 - 收缩 -->
+      <aside v-if="navigationShow" class="left-menu">
+        <div class="courseware-info">
+          <div class="cover-image"></div>
+          <div class="info-content">
+            <div class="catalogue-icon">
+              <SvgIcon icon-class="catalogue" size="54" />
+            </div>
+            <div class="courseware">
+              <div class="name nowrap-ellipsis" :title="courseware_info.book_name">
+                {{ courseware_info.book_name }}
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- 教材章节树 -->
+        <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>
+      </aside>
+
       <div ref="previewMain" class="main-container">
+        <div v-if="!navigationShow" class="catalogue-bar">
+          <SvgIcon icon-class="catalogue" size="54" />
+        </div>
+
         <main :class="['preview-main']">
-          <span class="title" :style="{ backgroundColor: unified_attrib?.topic_color }">
-            <SvgIcon icon-class="menu-2" size="24" />
-            <span>{{ courseware_info.name_path }}</span>
-          </span>
+          <div class="preview-left"></div>
           <CoursewarePreview
             v-if="courseware_info.book_name"
             ref="courserware"
@@ -46,15 +74,19 @@
             :component-remark-obj="remark_list_obj"
             @computeScroll="computeScroll"
           />
+          <div class="preview-right"></div>
         </main>
       </div>
+
+      <div class="back-top" @click="backTop">
+        <img :src="require(`@/assets/icon/back-top.png`)" alt="返回顶部" />
+      </div>
     </div>
   </div>
 </template>
 
 <script>
 import CoursewarePreview from '@/views/book/courseware/preview/CoursewarePreview.vue';
-import MenuPopover from '@/views/personal_workbench/common/MenuPopover.vue';
 import { isTrue } from '@/utils/validate';
 import * as OpenCC from 'opencc-js';
 
@@ -71,7 +103,6 @@ export default {
   name: 'CommonPreview',
   components: {
     CoursewarePreview,
-    MenuPopover,
   },
   provide() {
     return {
@@ -115,37 +146,6 @@ export default {
       remark_content: '',
       submit_loading: false,
       isTrue,
-      menuPosition: {
-        x: -1,
-        y: -1,
-        componentId: 'WHOLE',
-      },
-      sidebarIconList: [
-        { icon: 'search', title: '搜索', handle: '', param: {} },
-        { icon: 'mindmap', title: '思维导图', handle: 'openMindMap', param: {} },
-        { icon: 'connect', title: '连接', handle: '', param: {} },
-        { icon: 'audio', title: '音频', handle: 'openDrawer', param: { type: '1' } },
-        { icon: 'image', title: '图片', handle: 'openDrawer', param: { type: '0' } },
-        { icon: 'video', title: '视频', handle: 'openDrawer', param: { type: '2' } },
-        { icon: 'text', title: '文本', handle: '', param: {} },
-        { icon: 'file', title: '文件', handle: '', param: {} },
-        { icon: 'collect', title: '收藏', handle: '', param: {} },
-        { icon: 'setting', title: '设置', handle: '', param: {} },
-      ],
-      visibleMindMap: false,
-      isChildDataLoad: false,
-      mindMapJsonData: {}, // 思维导图json数据
-      drawerType: '', // 抽屉类型
-      drawerStyle: {
-        top: '0',
-        height: '0',
-        right: '0',
-      },
-      page_capacity: 10,
-      cur_page: 1,
-      file_list: [],
-      total_count: 0,
-      loading: false,
       isShowGroup: false,
       groupShowAll: true,
       opencc: OpenCC.Converter({ from: 'cn', to: 'tw' }),
@@ -155,16 +155,10 @@ export default {
       isJudgeCorrect: false,
       isShowAnswer: false,
       unified_attrib: {},
+      curSelectId: this.id,
+      navigationShow: true,
     };
   },
-  computed: {
-    disabled() {
-      return this.loading || this.noMore;
-    },
-    noMore() {
-      return this.file_list.length >= this.total_count && this.total_count > 0;
-    },
-  },
   watch: {
     isJudgeCorrect(newVal) {
       if (!newVal) {
@@ -309,6 +303,44 @@ export default {
     simulateAnswer(disabled = true) {
       this.$refs.courserware.simulateAnswer(this.isJudgeCorrect, this.isShowAnswer, disabled);
     },
+
+    /**
+     * 选择节点
+     * @param {string} nodeId - 节点ID
+     * @param {boolean} isLeaf - 是否是叶子节点
+     */
+    selectChapterNode(nodeId, isLeaf) {
+      if (!isLeaf) return;
+      if (this.curSelectId === nodeId) return;
+      this.curSelectId = nodeId;
+      this.selectNode(nodeId);
+    },
+    /**
+     * 计算章节名称样式
+     * @param {number} deep - 节点深度
+     * @param {boolean} isLeaf - 是否是叶子节点
+     * @returns {Object} - 样式对象
+     */
+    computedNameStyle(deep, isLeaf) {
+      return {
+        'padding-left': `${(deep - 1) * 8}px`,
+        cursor: isLeaf ? 'pointer' : 'auto',
+      };
+    },
+    /**
+     * 切换左侧导航栏显示与隐藏
+     */
+    toggleNavigationShow() {
+      this.navigationShow = !this.navigationShow;
+    },
+
+    backTop() {
+      this.$refs.previewMain.scrollTo({
+        top: 0,
+        left: 0,
+        behavior: 'smooth',
+      });
+    },
   },
 };
 </script>
@@ -316,6 +348,8 @@ export default {
 <style lang="scss" scoped>
 @use '@/styles/mixin.scss' as *;
 
+$total-width: $courseware-width + $courseware-left-margin + $courseware-right-margin;
+
 .common-preview {
   &__header {
     position: sticky;
@@ -331,14 +365,6 @@ export default {
     border-top: $border;
     border-bottom: $border;
 
-    > .menu-container {
-      display: flex;
-      justify-content: space-between;
-      width: 360px;
-      padding: 4px 8px;
-      border-right: $border;
-    }
-
     > .courseware {
       display: flex;
       flex-grow: 1;
@@ -347,15 +373,6 @@ export default {
       justify-content: space-between;
       height: 40px;
 
-      .name-path {
-        min-width: 200px;
-        height: 40px;
-        padding: 4px 8px;
-        font-size: 14px;
-        line-height: 32px;
-        border-right: $border;
-      }
-
       .lang-select {
         :deep .el-input {
           width: 100px;
@@ -402,45 +419,174 @@ export default {
   }
 
   .main-container {
+    position: relative;
     flex: 1;
     min-width: 1110px;
+    padding: 15px 0;
     overflow: auto;
+    background: url('@/assets/preview-bg.png') repeat;
+
+    &.fullscreen {
+      display: flex;
+
+      & .catalogue-bar {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 54px;
+        height: 54px;
+        margin: -9px 6px 0 240px;
+        cursor: pointer;
+        background-color: #fff;
+        border-radius: 2px;
+        box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 40%);
+      }
+    }
+  }
+
+  .back-top {
+    position: absolute;
+    right: 24px;
+    bottom: 0;
+    display: flex;
+    place-content: center center;
+    align-items: center;
+    width: 60px;
+    height: 60px;
+    cursor: pointer;
+    background-color: #fff;
   }
 
   main.preview-main {
     display: flex;
     flex: 1;
-    flex-direction: column;
-    row-gap: 5px;
-    width: 1100px;
-    min-width: 1100px;
+    width: calc($total-width);
+    min-width: calc($total-width);
+    max-width: calc($total-width);
     min-height: 100%;
-    padding: 5px;
     margin: 0 auto;
     background-color: #fff;
     border-radius: 4px;
-    box-shadow: 0 2px 4px rgba(0, 0, 0, 10%);
+    box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 40%);
 
-    .title {
-      display: inline-flex;
-      column-gap: 24px;
-      align-items: center;
-      width: 100%;
-      min-width: 280px;
-      height: 64px;
-      padding: 18px 24px;
-      font-size: 20px;
-      color: #fff;
-      background-color: #f44444;
-      border-top-left-radius: 12px;
-      border-top-right-radius: 16px;
+    .preview-left {
+      width: $courseware-left-margin;
+      min-width: $courseware-left-margin;
+      max-width: $courseware-left-margin;
+      background-color: #ecf0f1;
+    }
+
+    .preview-right {
+      width: $courseware-right-margin;
+      min-width: $courseware-right-margin;
+      max-width: $courseware-right-margin;
+      background-color: #ecf0f1;
+    }
+
+    &.no-audit {
+      margin: 0 auto;
     }
   }
 
   .audit-content {
     display: flex;
     min-width: 1400px;
-    height: calc(100vh - 56px);
+    height: calc(100vh - 46px);
+
+    .left-menu {
+      display: flex;
+      flex-direction: column;
+      width: $catalogue-width;
+      font-family: 'Microsoft YaHei', 'Arial', sans-serif;
+      background-color: #fff;
+
+      .courseware-info {
+        display: flex;
+        column-gap: 18px;
+        width: 100%;
+        height: 186px;
+        padding: 6px 6px 24px;
+        border-bottom: $border;
+
+        .cover-image {
+          width: 111px;
+          height: 157px;
+          background-color: rgba(229, 229, 229, 100%);
+        }
+
+        .info-content {
+          display: flex;
+          flex-direction: column;
+          justify-content: space-between;
+
+          .catalogue-icon {
+            text-align: right;
+
+            .svg-icon {
+              cursor: pointer;
+            }
+          }
+
+          .courseware {
+            width: 159px;
+            height: 64px;
+            font-size: 16px;
+
+            .name {
+              font-weight: bold;
+            }
+          }
+        }
+      }
+
+      .courseware-tree {
+        display: flex;
+        flex: 1;
+        flex-direction: column;
+        row-gap: 8px;
+        padding: 12px;
+        margin-top: 12px;
+        overflow: auto;
+
+        .menu-item {
+          display: flex;
+          align-items: center;
+
+          &:not(.courseware) {
+            font-weight: bold;
+          }
+
+          &.courseware {
+            &:hover {
+              .name {
+                background-color: #f3f3f3;
+              }
+            }
+          }
+
+          .svg-icon {
+            margin-left: 4px;
+
+            &.my-edit-task {
+              color: $right-color;
+            }
+          }
+
+          .name {
+            flex: 1;
+            padding: 4px 8px 4px 4px;
+            border-radius: 4px;
+          }
+
+          &.active {
+            .name {
+              font-weight: bold;
+              color: #4095e5;
+            }
+          }
+        }
+      }
+    }
   }
 }
 </style>