Prechádzať zdrojové kódy

Merge branch 'master' of http://60.205.254.193:3000/GCLS/GCLS_Page_Textbook

# Conflicts:
#	src/views/book/courseware/create/components/base/common/UploadFile.vue
#	src/views/book/courseware/create/components/base/picture/Picture.vue
zq 1 rok pred
rodič
commit
116053eba9

+ 0 - 3
src/icons/svg/components/navigation.svg

@@ -1,3 +0,0 @@
-<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M0 0H18V2H0V0ZM0 7H12V9H0V7ZM0 14H18V16H0V14Z" fill="white"/>
-</svg>

+ 3 - 0
src/icons/svg/courseware/menu-2.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3 4H21V6H3V4ZM3 11H15V13H3V11ZM3 18H21V20H3V18Z" fill="white"/>
+</svg>

+ 10 - 8
src/icons/svg/courseware/menu.svg

@@ -1,10 +1,12 @@
 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<g clip-path="url(#clip0_271_9197)">
-<path d="M5.33333 2.66536H14V3.9987H5.33333V2.66536ZM2 2.33203H4V4.33203H2V2.33203ZM2 6.9987H4V8.9987H2V6.9987ZM2 11.6654H4V13.6654H2V11.6654ZM5.33333 7.33203H14V8.66536H5.33333V7.33203ZM5.33333 11.9987H14V13.332H5.33333V11.9987Z" fill="black"/>
-</g>
-<defs>
-<clipPath id="clip0_271_9197">
-<rect width="16" height="16" fill="white"/>
-</clipPath>
-</defs>
+  <g clip-path="url(#clip0_271_9197)">
+    <path
+      d="M5.33333 2.66536H14V3.9987H5.33333V2.66536ZM2 2.33203H4V4.33203H2V2.33203ZM2 6.9987H4V8.9987H2V6.9987ZM2 11.6654H4V13.6654H2V11.6654ZM5.33333 7.33203H14V8.66536H5.33333V7.33203ZM5.33333 11.9987H14V13.332H5.33333V11.9987Z"
+      fill="black" />
+  </g>
+  <defs>
+    <clipPath id="clip0_271_9197">
+      <rect width="16" height="16" fill="white" />
+    </clipPath>
+  </defs>
 </svg>

+ 2 - 8
src/router/modules/courseware.js

@@ -18,14 +18,8 @@ const CoursewareModulePage = {
  * 课件预览组件
  */
 const PreviewPage = {
-  path: '/preview',
-  component: DEFAULT,
-  children: [
-    {
-      path: 'courseware/:book_id',
-      component: () => import('@/views/book/courseware/preview/index.vue'),
-    },
-  ],
+  path: '/preview/courseware/:book_id',
+  component: () => import('@/views/book/courseware/preview/index.vue'),
 };
 
 export default [CoursewareModulePage, PreviewPage];

+ 2 - 1
src/views/book/chapter.vue

@@ -7,7 +7,7 @@
           <span>{{ getCatalogueName() }}</span>
         </div>
         <el-popover v-model="visibleStatus" placement="bottom" trigger="click">
-          <CatalogueTree :nodes="nodes" @selectNode="selectNode" />
+          <CatalogueTree :nodes="nodes" />
 
           <span slot="reference" class="pointer"><SvgIcon icon-class="menu" /> 目录</span>
         </el-popover>
@@ -54,6 +54,7 @@ export default {
   provide() {
     return {
       selectNode: this.selectNode,
+      getCurChaterId: () => this.curChapterId,
     };
   },
   data() {

+ 6 - 2
src/views/book/components/catalogueTree.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="node-wrapper">
-    <div v-for="node in nodes" :key="node.id" class="node">
+    <div v-for="node in nodes" :key="node.id" :class="['node', { active: getCurChaterId() === node.id }]">
       <div
         :class="['node-name', { content: node.is_leaf_chapter === 'true' }]"
         @click="handleSelectNode(node.is_leaf_chapter, node.id)"
@@ -15,7 +15,7 @@
 <script>
 export default {
   name: 'CatalogueTree',
-  inject: ['selectNode'],
+  inject: ['selectNode', 'getCurChaterId'],
   props: {
     nodes: {
       type: Array,
@@ -36,6 +36,10 @@ export default {
 
 <style lang="scss" scoped>
 .node {
+  &.active {
+    background-color: #f3f3f3;
+  }
+
   &-name {
     padding-bottom: 4px;
 

+ 218 - 27
src/views/book/courseware/create/components/createCanvas.vue

@@ -20,7 +20,13 @@
       <div :key="i" class="row" :style="getMultipleColStyle(i)">
         <!-- 列 -->
         <template v-for="(col, j) in row.col_list">
-          <span v-if="j === 0" :key="`start-${i}-${j}`" class="drag-vertical-line" :data-row="i" :data-col="j"></span>
+          <span
+            v-if="j === 0"
+            :key="`start-${i}-${j}`"
+            class="drag-vertical-line col-start"
+            :data-row="i"
+            :data-col="j"
+          ></span>
           <div
             :key="j"
             :class="['col', `col-${i}-${j}`]"
@@ -35,11 +41,22 @@
             <template v-for="(grid, k) in col.grid_list">
               <span
                 v-if="k === 0"
-                :key="`start-${k}`"
-                class="drag-line grid-line"
+                :key="`start-${i}-${j}-${k}`"
+                class="drag-line grid-line drag-row"
+                :style="{ gridArea: 'grid-top' }"
                 :data-row="i"
                 :data-col="j"
                 :data-grid="k"
+                data-type="row"
+              ></span>
+              <span
+                :key="`left-${k}`"
+                :style="{ gridArea: `left-${grid.grid_area}` }"
+                :data-row="i"
+                :data-col="j"
+                :data-grid="k"
+                data-type="col-left"
+                class="drag-vertical-line grid-line grid-line-left"
               ></span>
               <component
                 :is="componentList[grid.type]"
@@ -47,15 +64,33 @@
                 ref="component"
                 :key="k"
                 :class="[grid.id]"
-                :style="{ gridArea: grid.grid_area, height: grid.height }"
+                :style="{ gridArea: grid.grid_area, height: grid.height, marginTop: grid.row !== 1 ? '16px' : '0' }"
                 :delete-component="deleteComponent(i, j, k)"
                 :component-move="componentMove(i, j, k)"
                 @showSetting="showSetting"
               />
-              <span :key="`end-${k}`" class="drag-line grid-line" :data-row="i" :data-col="j" :data-grid="k + 1"></span>
+              <span
+                :key="`right-${k}`"
+                :style="{ gridArea: `right-${grid.grid_area}` }"
+                :data-row="i"
+                :data-col="j"
+                :data-grid="k + 1"
+                data-type="col-right"
+                class="drag-vertical-line grid-line grid-line-right"
+              ></span>
+              <span
+                v-if="k === col.grid_list.length - 1"
+                :key="`end-${i}-${j}-${k}`"
+                class="drag-line grid-line drag-row"
+                :style="{ gridArea: `grid-bottom` }"
+                :data-row="i"
+                :data-col="j"
+                :data-grid="k + 1"
+                data-type="row"
+              ></span>
             </template>
           </div>
-          <span :key="`end-${i}-${j}`" class="drag-vertical-line" :data-row="i" :data-col="j + 1"></span>
+          <span :key="`end-${i}-${j}`" class="drag-vertical-line col-end" :data-row="i" :data-col="j + 1"></span>
         </template>
       </div>
       <span v-if="i < data.row_list.length - 1" :key="`row-${i}`" class="drag-line" :data-row="i"></span>
@@ -94,6 +129,7 @@ export default {
       curRow: -2,
       curCol: -1,
       curGrid: -1,
+      gridInsertType: '', // 网格插入类型
       enterCanvas: false, // 是否进入画布
       // 拖拽状态
       drag: {
@@ -128,6 +164,7 @@ export default {
         });
         this.curCol = -1;
         this.curGrid = -1;
+        this.gridInsertType = '';
       },
     },
   },
@@ -252,7 +289,10 @@ export default {
      */
     deleteComponent(i, j, k) {
       return () => {
-        this.data.row_list[i].col_list[j].grid_list.splice(k, 1);
+        const gridList = this.data.row_list[i].col_list[j].grid_list;
+        let delRow = gridList[k].row; // 删除的 grid 的 row
+        let delW = gridList[k].width; // 删除的 grid 的 width
+        gridList.splice(k, 1);
 
         const colList = this.data.row_list[i].col_list[j];
         if (colList.grid_list.length === 0) {
@@ -269,14 +309,69 @@ export default {
           this.data.row_list.splice(i, 1);
         }
 
-        const gridList = this.data.row_list[i]?.col_list[j]?.grid_list;
         if (gridList?.length > 0) {
-          colList.grid_template_columns = `100%`;
-
-          const grid_template_areas = gridList.map(({ grid_area }) => grid_area).join(' ');
-          colList.grid_template_areas = `'.' '${grid_template_areas}' '.'`;
+          let maxCol = 0; // 最大列数
+          let rowList = new Map();
+          gridList.forEach(({ row }) => {
+            rowList.set(row, (rowList.get(row) || 0) + 1);
+          });
+          let curMaxRow = 0; // 当前数量最大 row 的值
+          rowList.forEach((value, key) => {
+            if (value > maxCol) {
+              maxCol = value;
+              curMaxRow = key;
+            }
+          });
 
-          colList.grid_template_rows = `0 ${gridList.map(({ height }) => height).join(' 16px ')} 0`;
+          let delNum = gridList.filter(({ row }) => row === delRow).length;
+          let diff = Number(delW.replace('%', '')) / delNum;
+          if (delNum === 0) {
+            // 删除 grid 后面的 row 都减 1
+            gridList.forEach((item) => {
+              if (item.row > delRow) {
+                item.row -= 1;
+              }
+            });
+          } else {
+            gridList.forEach((item) => {
+              if (item.row === delRow) {
+                item.width = `${Number(item.width.replace('%', '')) + diff}%`;
+              }
+            });
+          }
+          // 计算 grid_template_areas 和 grid_template_rows
+          let gridStr = '';
+          let gridArr = [];
+          gridList.forEach(({ grid_area, row }) => {
+            if (!gridArr[row - 1]) {
+              gridArr[row - 1] = [];
+            }
+            if (curMaxRow === row) {
+              gridArr[row - 1].push(`left-${grid_area} ${grid_area} right-${grid_area}`);
+            } else {
+              const str = ` ${grid_area} `.repeat(maxCol * 3 - 2);
+              gridArr[row - 1].push(`left-${grid_area} ${str} right-${grid_area}`);
+            }
+          });
+          gridArr.forEach((item) => {
+            gridStr += `'${item.join(' ')}' `;
+          });
+          colList.grid_template_areas = `'${'grid-top '.repeat(maxCol * 3)}' ${gridStr} '${'grid-bottom '.repeat(maxCol * 3)}'`;
+          colList.grid_template_rows = `0 ${gridList.map(({ height }) => height).join(' ')} 0`;
+
+          // 计算 grid_template_columns
+          let gridTemCols = '';
+          gridList.forEach((item) => {
+            // 如果第一行只有一个 grid 则不需要 - 8px
+            if (maxCol === 1 && item.row === 1) {
+              gridTemCols += `${item.width}`;
+              return;
+            }
+            if (item.row === 1) {
+              gridTemCols += `calc(${item.width} - 8px) 8px 8px `;
+            }
+          });
+          colList.grid_template_columns = `0 ${maxCol === 1 ? gridTemCols : gridTemCols.slice(0, gridTemCols.length - 8)} 0`;
         }
       };
     },
@@ -343,6 +438,7 @@ export default {
           this.curRow = Number(item.getAttribute('data-row'));
           this.curCol = Number(item.getAttribute('data-col') || -1);
           this.curGrid = Number(item.getAttribute('data-grid') || -1);
+          this.gridInsertType = item.getAttribute('data-type') || '';
 
           item.style.opacity = 1;
         } else {
@@ -408,16 +504,90 @@ export default {
       let row = this.data.row_list[this.curRow];
       let col = row.col_list[this.curCol];
       let grid = col.grid_list;
+      let type = this.gridInsertType;
+
+      if (type === 'row') {
+        let rowNum = this.curGrid === 0 ? 1 : grid[this.curGrid - 1].row + 1;
+        grid.splice(this.curGrid, 0, {
+          id,
+          grid_area: letter,
+          width: '100%',
+          height: 'auto',
+          row: rowNum,
+          type: this.curType,
+        });
+        // 在新加入的 grid 后面的 row 都加 1
+        grid.forEach((item, i) => {
+          if (i > this.curGrid) {
+            item.row += 1;
+          }
+        });
+      }
 
-      grid.splice(this.curGrid, 0, {
-        id,
-        grid_area: letter,
-        width: '100%',
-        height: 'auto',
-        type: this.curType,
+      if (['col-left', 'col-right'].includes(type)) {
+        let rowNum = grid[type === 'col-left' ? this.curGrid : this.curGrid - 1].row;
+        grid.splice(this.curGrid, 0, {
+          id,
+          grid_area: letter,
+          width: '100%',
+          height: 'auto',
+          row: rowNum,
+          type: this.curType,
+        });
+
+        let allRowNum = grid.filter(({ row }) => row === rowNum).length;
+        let w = 0;
+        grid.forEach((item, i) => {
+          if (item.row === rowNum && i !== this.curGrid) {
+            let width = Number(item.width.replace('%', ''));
+            let diff = width / allRowNum;
+            item.width = `${width - diff}%`;
+            w += diff;
+          }
+        });
+        grid[this.curGrid].width = `${w}%`;
+      }
+
+      let maxCol = 0; // 最大列数
+      let rowList = new Map();
+      grid.forEach(({ row }) => {
+        rowList.set(row, (rowList.get(row) || 0) + 1);
+      });
+      let curMaxRow = 0; // 当前数量最大 row 的值
+      rowList.forEach((value, key) => {
+        if (value > maxCol) {
+          maxCol = value;
+          curMaxRow = key;
+        }
+      });
+      // 计算 grid_template_areas 和 grid_template_rows
+      let gridStr = '';
+      let gridArr = [];
+      grid.forEach(({ grid_area, row }) => {
+        if (!gridArr[row - 1]) {
+          gridArr[row - 1] = [];
+        }
+        if (curMaxRow === row) {
+          gridArr[row - 1].push(`left-${grid_area} ${grid_area} right-${grid_area}`);
+        } else {
+          const str = ` ${grid_area} `.repeat(maxCol * 3 - 2);
+          gridArr[row - 1].push(`left-${grid_area} ${str} right-${grid_area}`);
+        }
+      });
+      gridArr.forEach((item) => {
+        gridStr += `'${item.join(' ')}' `;
+      });
+      col.grid_template_areas = `'${'grid-top '.repeat(maxCol * 3)}' ${gridStr} '${'grid-bottom '.repeat(maxCol * 3)}'`;
+      col.grid_template_rows = `0 ${grid.map(({ height }) => height).join(' ')} 0`;
+
+      // 计算 grid_template_columns
+      let gridTemCols = '';
+      grid.forEach((item) => {
+        if (item.row === 1) {
+          gridTemCols += `calc(${item.width} - 8px) 8px 8px `;
+        }
       });
-      col.grid_template_areas = `'.' ${grid.map(({ grid_area }) => `'${grid_area}'`).join(" '.' ")} '.'`;
-      col.grid_template_rows = `0 ${grid.map(({ height }) => height).join(' 16px ')} 0`;
+      col.grid_template_columns = `0 ${gridTemCols.slice(0, gridTemCols.length - 8)} 0`;
     },
     /**
      * 计算列插入的对象
@@ -441,13 +611,14 @@ export default {
       col.splice(this.curCol, 0, {
         width: '100%',
         height: 'auto',
-        grid_template_areas: `'.' '${letter}' '.'`,
-        grid_template_columns: '100%',
+        grid_template_areas: `'grid-top grid-top grid-top' 'left-${letter} ${letter} right-${letter}' 'grid-bottom grid-bottom grid-bottom'`,
+        grid_template_columns: '0 100% 0',
         grid_template_rows: '0 auto 0',
         grid_list: [
           {
             id,
             grid_area: letter,
+            row: 1,
             width: '100%',
             height: 'auto',
             type: this.curType,
@@ -468,8 +639,8 @@ export default {
           {
             width: '100%',
             height: 'auto',
-            grid_template_areas: `'.' '${letter}' '.'`,
-            grid_template_columns: '100%',
+            grid_template_areas: `'grid-top grid-top grid-top' 'left-${letter} ${letter} right-${letter}' 'grid-bottom grid-bottom grid-bottom'`,
+            grid_template_columns: '0 100% 0',
             grid_template_rows: '0 auto 0',
             grid_list: [
               {
@@ -477,6 +648,7 @@ export default {
                 grid_area: letter,
                 width: '100%',
                 height: 'auto',
+                row: 1,
                 type: this.curType,
               },
             ],
@@ -526,10 +698,18 @@ export default {
     display: grid;
     row-gap: 16px;
 
-    > .drag-vertical-line:not(:first-child, :last-child) {
+    > .drag-vertical-line:not(:first-child, :last-child, .grid-line) {
       left: 6px;
     }
 
+    .drag-vertical-line.col-start {
+      left: -12px;
+    }
+
+    .drag-vertical-line.col-end {
+      right: -8px;
+    }
+
     .col {
       display: grid;
 
@@ -537,7 +717,7 @@ export default {
         left: -8px;
       }
 
-      .drag-vertical-line:not(:first-child, :last-child) {
+      .drag-vertical-line:not(:first-child, :last-child, .grid-line) {
         left: 6px;
       }
 
@@ -548,6 +728,7 @@ export default {
   }
 
   .drag-line {
+    z-index: 9;
     width: calc(100% - 16px);
     height: 4px;
     margin: 0 8px;
@@ -555,6 +736,10 @@ export default {
     border-radius: 4px;
     opacity: 0;
 
+    &.drag-row {
+      background-color: $right-color;
+    }
+
     &.grid-line:not(:first-child, :last-child) {
       position: relative;
       top: 6px;
@@ -563,11 +748,16 @@ export default {
 
   .drag-vertical-line {
     position: relative;
+    z-index: 9;
     width: 4px;
     height: 100%;
     background-color: #379fff;
     border-radius: 4px;
     opacity: 0;
+
+    &.grid-line {
+      background-color: #f43;
+    }
   }
 }
 </style>
@@ -581,6 +771,7 @@ export default {
   background-color: #eaf5ff;
   border: 1px solid #b5dbff;
   border-radius: 4px;
+  opacity: 0.5;
   transform: translate(-40%, -40%);
 }
 </style>

+ 116 - 114
src/views/book/courseware/preview/index.vue

@@ -1,41 +1,36 @@
 <template>
   <div class="preview">
-    <div>
-      <div v-for="{ id, name, nodes: children } in nodes" :key="id" class="catalogue">
-        <div class="catalogue-title">{{ name }}</div>
-        <template v-for="item in children">
-          <div :key="item.id" :class="['catalogue-item', item.is_leaf_chapter === 'true' ? 'content' : 'subdirectory']">
-            <span class="name">{{ item.name }}</span>
-          </div>
-          <div v-for="li in item.nodes" :key="li.id">
-            <div :class="['catalogue-item', 'children', curChapterId === li.id ? 'active' : '']">
-              <span class="name">{{ li?.name }}</span>
-            </div>
-          </div>
-        </template>
-      </div>
+    <div class="menu">
+      <div class="title">目录</div>
+      <CatalogueTree :nodes="nodes" />
     </div>
-    <div class="content-area">
-      <div class="content-top">
-        <el-button @click="goBack"><SvgIcon icon-class="quit" size="14" /> 退出预览</el-button>
+
+    <div class="content-wrapper">
+      <div class="exit">
+        <el-button icon="el-icon-close" @click="goBack">退出预览</el-button>
       </div>
-      <div
-        class="content"
-        :style="[
-          {
-            backgroundImage: data.background_image_url ? `url(${data.background_image_url})` : '',
-            backgroundSize: data.background_image_url
-              ? `${data.background_position.width}% ${data.background_position.height}%`
-              : '',
-            backgroundPosition: data.background_image_url
-              ? `${data.background_position.left}% ${data.background_position.top}%`
-              : '',
-          },
-        ]"
-      >
-        <div class="navigation">
-          <span class="navigation-label"> <SvgIcon icon-class="navigation" size="14" />{{ getCatalogueName() }} </span>
-        </div>
+      <div v-for="(data, i) in coursewareDataList" :key="`data-${i}`" class="content">
+        <span class="content-title">
+          <SvgIcon icon-class="menu-2" size="24" />
+          <template v-for="(item, m) in menuList">
+            <span :key="m">{{ item }}</span>
+            <span v-if="i < menuList.length - 1" :key="`separator-${m}`" class="separator">/</span>
+          </template>
+        </span>
+        <div
+          class="courserware"
+          :style="[
+            {
+              backgroundImage: data.background_image_url ? `url(${data.background_image_url})` : '',
+              backgroundSize: data.background_image_url
+                ? `${data.background_position.width}% ${data.background_position.height}%`
+                : '',
+              backgroundPosition: data.background_image_url
+                ? `${data.background_position.left}% ${data.background_position.top}%`
+                : '',
+            },
+          ]"
+        ></div>
       </div>
     </div>
   </div>
@@ -49,37 +44,43 @@ import {
   GetCoursewareComponentContent_View,
 } from '@/api/book';
 
+import CatalogueTree from '@/views/book/components/catalogueTree.vue';
+
 export default {
   name: 'PreviewPage',
+  components: {
+    CatalogueTree,
+  },
+  provide() {
+    return {
+      selectNode: this.selectNode,
+      getCurChaterId: () => this.curChapterId,
+    };
+  },
   data() {
     const { chapter_id } = this.$route.query;
     return {
       book_id: this.$route.params.book_id,
       chapter_id,
       curChapterId: chapter_id,
-      data: {
-        background_image_url: '',
-        background_position: {
-          width: 100,
-          height: 100,
-          top: 0,
-          left: 0,
-        },
-        // 组件列表
-        row_list: [],
-      },
       nodes: [],
       curPosition: [],
       courseware_list: [],
+      coursewareDataList: [],
     };
   },
+  computed: {
+    menuList() {
+      return this.curPosition.map((item, index) => this.getNodeName(index));
+    },
+  },
   created() {
     GetBookChapterStruct({ book_id: this.book_id, node_deep_mode: 0 }).then(({ nodes }) => {
       this.nodes = nodes ?? [];
       this.setCurPosition(this.chapter_id);
     });
     this.getCoursewareList_Chapter(this.chapter_id);
-    this.GetCoursewareComponentContent_View();
+    this.getCoursewareComponentContent_View();
   },
   methods: {
     // 获取课件数据
@@ -119,14 +120,25 @@ export default {
     setCurPosition(id) {
       this.curPosition = this.findNodeIndexById(this.nodes, id);
     },
+    selectNode(id) {
+      this.setCurPosition(id);
+      this.getCoursewareList_Chapter(id);
+      this.curChapterId = id;
+    },
     getCoursewareList_Chapter(chapter_id) {
       GetCoursewareList_Chapter({ chapter_id }).then(({ courseware_list }) => {
         this.courseware_list = courseware_list ?? [];
+        this.getCoursewareContent();
       });
     },
-    getCoursewareContent() {
-      GetCoursewareContent({ id: this.courseware_id }).then(({ content }) => {
-        if (content) this.data = JSON.parse(content);
+    async getCoursewareContent() {
+      Promise.all(
+        this.courseware_list.map(async ({ courseware_id }) => {
+          const { content } = await GetCoursewareContent({ id: courseware_id });
+          return content ? JSON.parse(content) : {};
+        }),
+      ).then((coursewareDataList) => {
+        this.coursewareDataList = coursewareDataList;
       });
     },
     getNodeName(index) {
@@ -146,96 +158,86 @@ export default {
 <style lang="scss" scoped>
 .preview {
   display: flex;
-  height: calc(100vh - 66px);
-  background-color: #ececec;
+  height: 100%;
 
-  .catalogue {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
+  .menu {
+    width: 240px;
     padding: 24px;
+    overflow: auto;
     background-color: #fff;
 
-    &-title {
-      padding: 8px 0;
+    .title {
+      margin-bottom: 16px;
       font-weight: bold;
-      color: #000;
     }
 
-    &-item {
-      display: flex;
-      column-gap: 8px;
-      padding: 8px 32px 8px 16px;
-      font-size: 14px;
-      border-bottom: 1px solid #ebebeb;
-
-      &:hover {
-        background-color: #f3f3f3;
-      }
+    :deep .node {
+      &-name {
+        height: 38px;
+        line-height: 38px;
 
-      &.active {
-        background-color: #f3f3f3;
-      }
-
-      &.subdirectory {
-        .name {
-          font-weight: bold;
+        &.content {
+          padding-left: 16px;
+          font-size: 14px;
+          border-bottom: 1px solid $border-color;
         }
       }
 
-      &.children {
-        padding-left: 32px;
-      }
-
-      .name {
-        flex: 1;
-        color: #000;
-      }
-
-      .time,
-      .edit {
-        color: #929292;
-      }
-
-      .edit {
-        margin-left: 24px;
-        cursor: pointer;
+      .node {
+        padding-left: 20px;
+        margin-left: 0;
       }
     }
   }
 
-  .content-area {
-    width: calc(100vw - 320px);
-    margin: 18px 60px;
+  .content-wrapper {
+    flex: 1;
+    padding: 24px 60px;
+    background: url('~@/assets/mask_group.png') repeat 0 0;
 
-    .content-top {
+    .exit {
       display: flex;
-      justify-content: end;
-      width: 100%;
-      margin-bottom: 18px;
-      font-size: 16px;
+      justify-content: flex-end;
+
+      .el-button {
+        height: 40px;
+        font-size: 16px;
+        font-weight: bold;
+        border-width: 0;
+        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 8%);
+      }
     }
 
     .content {
-      display: flex;
-      flex-direction: column;
-      row-gap: 6px;
-      width: 94%;
-      min-height: calc(100% - 56px);
-      padding: 24px;
+      margin-top: 24px;
       background-color: #fff;
-      background-repeat: no-repeat;
       border: 3px solid #f44444;
       border-radius: 16px;
 
-      .row {
-        display: grid;
-        row-gap: 16px;
-        align-items: start;
+      &-title {
+        display: inline-flex;
+        column-gap: 24px;
+        align-items: center;
+        min-width: 280px;
+        height: 64px;
+        padding: 18px 24px;
+        font-size: 20px;
+        color: #fff;
+        background-color: #f44444;
+        border-top-left-radius: 12px;
+        border-bottom-right-radius: 16px;
+      }
 
-        .col {
-          display: grid;
-        }
+      .courserware {
+        display: flex;
+        flex-direction: column;
+        row-gap: 6px;
+        width: 100%;
+        min-height: 500px;
+        background-color: #fff;
+        background-repeat: no-repeat;
+        border-bottom-right-radius: 12px;
+        border-bottom-left-radius: 12px;
       }
 
       .navigation {