ソースを参照

教材内容资源查看并跳转对应位置(音频、图片、视频)

zq 1 日 前
コミット
bd668b6d58
2 ファイル変更193 行追加13 行削除
  1. 8 0
      src/api/book.js
  2. 185 13
      src/components/CommonPreview.vue

+ 8 - 0
src/api/book.js

@@ -190,3 +190,11 @@ export function MangerGenerateMindMapByBookContent(data) {
 export function MangerSaveBookMindMap(data) {
   return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_content_manager-SaveBookMindMap`, data);
 }
+
+/**
+ * @description 分页查询教材资源列表
+ * @param {object} data
+ */
+export function PageQueryBookResourceList(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=page_query-PageQueryBookResourceList`, data);
+}

+ 185 - 13
src/components/CommonPreview.vue

@@ -49,18 +49,52 @@
         </ul>
         <p v-else style="text-align: center">暂无批注</p>
       </div>
-
-      <div class="sidebar">
+      <div ref="sidebarMenu" class="sidebar">
         <div
-          v-for="{ icon, title, handle } in sidebarIconList"
-          :key="icon"
-          :title="title"
+          v-for="item in sidebarIconList"
+          :key="item.icon"
+          :title="item.title"
           class="sidebar-icon"
-          @click="handleSidebarClick(handle)"
+          @click="handleSidebarClick(item)"
         >
-          <SvgIcon :icon-class="`sidebar-${icon}`" size="24" />
+          <SvgIcon :icon-class="`sidebar-${item.icon}`" size="24" />
         </div>
       </div>
+      <el-drawer
+        custom-class="custom-drawer"
+        :visible="drawerType.length > 0"
+        :with-header="false"
+        :modal="false"
+        size="25%"
+        :style="drawerStyle"
+      >
+        <div class="infinite-list-wrapper" style="overflow: auto">
+          <ul v-infinite-scroll="loadMore" class="scroll-container" infinite-scroll-disabled="disabled">
+            <li
+              v-for="(item, index) in file_list"
+              :key="index"
+              class="list-item"
+              @click="handleFileClick(item?.component_id)"
+            >
+              <template v-if="parseInt(drawerType) === 0"> <el-image :src="item.file_url" fit="contain" /></template>
+              <template v-else-if="parseInt(drawerType) === 1">
+                <AudioPlay
+                  view-size="middle"
+                  :file-id="item.file_id"
+                  :file-name="item.file_name.slice(0, item.file_name.lastIndexOf('.'))"
+                  :show-slider="true"
+                  :audio-index="index"
+                />
+              </template>
+              <template v-else-if="parseInt(drawerType) === 2">
+                <VideoPlay view-size="big" :file-id="item.file_id" :video-index="index" />
+              </template>
+            </li>
+          </ul>
+          <p v-if="loading">加载中...</p>
+          <p v-if="noMore">没有更多了</p>
+        </div>
+      </el-drawer>
     </div>
 
     <el-dialog
@@ -104,6 +138,8 @@ import MenuPopover from '@/views/personal_workbench/common/MenuPopover.vue';
 import RichText from '@/components/RichText.vue';
 import { isTrue } from '@/utils/common';
 import MindMap from '@/components/MindMap.vue';
+import VideoPlay from '@/views/book/courseware/preview/components/common/VideoPlay.vue';
+import AudioPlay from '@/views/book/courseware/preview/components/common/AudioPlay.vue';
 
 import {
   GetBookCoursewareInfo,
@@ -117,6 +153,7 @@ import {
   ChapterGetBookChapterStructExpandList,
   GetBookBaseInfo,
   MangerGetBookMindMap,
+  PageQueryBookResourceList,
 } from '@/api/book';
 
 export default {
@@ -126,6 +163,8 @@ export default {
     MenuPopover,
     RichText,
     MindMap,
+    AudioPlay,
+    VideoPlay,
   },
   props: {
     projectId: {
@@ -191,9 +230,9 @@ export default {
         { icon: 'search', title: '搜索', handle: '' },
         { icon: 'mindmap', title: '思维导图', handle: 'openMindMap' },
         { icon: 'connect', title: '连接', handle: '' },
-        { icon: 'audio', title: '音频', handle: '' },
-        { icon: 'image', title: '图片', handle: '' },
-        { icon: 'video', title: '视频', handle: '' },
+        { icon: 'audio', title: '音频', handle: 'openDrawer', type: '1' },
+        { icon: 'image', title: '图片', handle: 'openDrawer', type: '0' },
+        { icon: 'video', title: '视频', handle: 'openDrawer', type: '2' },
         { icon: 'text', title: '文本', handle: '' },
         { icon: 'collect', title: '收藏', handle: '' },
         { icon: 'setting', title: '设置', handle: '' },
@@ -201,8 +240,27 @@ export default {
       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,
     };
   },
+  computed: {
+    disabled() {
+      return this.loading || this.noMore;
+    },
+    noMore() {
+      return this.file_list.length >= this.total_count && this.total_count > 0;
+    },
+  },
   created() {
     if (this.id) {
       this.getBookCoursewareInfo(this.id);
@@ -213,6 +271,9 @@ export default {
     }
     this.getBookChapterStructExpandList();
   },
+  mounted() {
+    this.calcDrawerPosition();
+  },
   methods: {
     getProjectBaseInfo() {
       GetProjectBaseInfo({ id: this.projectId }).then(({ project_info }) => {
@@ -361,11 +422,16 @@ export default {
      * @param {string} handle - 处理函数名
      * @param {any} param - 处理函数参数
      */
-    handleSidebarClick(handle, param) {
-      if (typeof handle === 'string' && handle && typeof this[handle] === 'function') {
-        this[handle](param);
+    handleSidebarClick(item) {
+      let handle = item.handle;
+      if (typeof this[handle] === 'function') {
+        this[handle](item.type);
       }
+      // if (typeof handle === 'string' && handle && typeof this[handle] === 'function') {
+      //   this[handle](param);
+      // }
     },
+
     openMindMap() {
       MangerGetBookMindMap({ book_id: this.projectId }).then(({ content }) => {
         if (content) {
@@ -392,6 +458,66 @@ export default {
       }
       this.visibleMindMap = false;
     },
+
+    // 计算抽屉滑出位置
+    calcDrawerPosition() {
+      const menu = this.$refs.sidebarMenu;
+      if (menu) {
+        const rect = menu.getBoundingClientRect();
+        this.drawerStyle = {
+          top: `${rect.top}px`,
+          height: `${rect.height}px`,
+          right: `${window.innerWidth - rect.left + 1}px`,
+        };
+      }
+    },
+    // 打开抽屉并初始化加载
+    openDrawer(type) {
+      if (this.drawerType === type) {
+        this.drawerType = '';
+        return;
+      }
+      this.drawerType = type;
+      this.drawerVisible = true;
+      this.$nextTick(() => {
+        this.cur_page = 1;
+        this.file_list = [];
+        this.loadMore();
+      });
+    },
+    // 加载更多数据
+    loadMore() {
+      if (this.disabled) return;
+      this.loading = true;
+      const params = {
+        page_capacity: this.page_capacity,
+        cur_page: this.cur_page,
+        book_id: this.projectId,
+        type: parseInt(this.drawerType),
+      };
+      PageQueryBookResourceList(params)
+        .then(({ total_count, resource_list }) => {
+          this.total_count = total_count;
+          this.file_list = this.cur_page === 1 ? resource_list : [...this.file_list, ...resource_list];
+          this.cur_page += this.cur_page;
+        })
+        .finally(() => {
+          this.loading = false;
+        });
+    },
+    async handleFileClick(component_id) {
+      if (component_id) {
+        let node = await this.$refs.courserware.findChildComponentByKey(component_id);
+        if (node) {
+          await this.$nextTick();
+          this.$refs.previewMain.scrollTo({
+            top: node.offsetTop - 50,
+            left: node.offsetLeft - 50,
+            behavior: 'smooth',
+          });
+        }
+      }
+    },
   },
 };
 </script>
@@ -571,6 +697,52 @@ export default {
         cursor: pointer;
       }
     }
+
+    .el-drawer__body {
+      .scroll-container {
+        display: flex;
+        flex-direction: column;
+        row-gap: 8px;
+        margin: 6px;
+
+        .list-item {
+          cursor: pointer;
+          border: 1px solid #ccc;
+          border-radius: 8px;
+
+          :deep .el-slider {
+            .el-slider__runway {
+              background-color: #eee;
+            }
+          }
+
+          :deep .audio-middle {
+            width: 100%;
+            border: none;
+            border-radius: 8px;
+          }
+
+          .el-image {
+            display: flex;
+            width: 30%;
+            height: 90px;
+            margin: 6px;
+            background-color: #ccc;
+            border-radius: 8px;
+          }
+
+          .video-play {
+            width: 30%;
+            margin: 6px;
+          }
+        }
+      }
+
+      p {
+        color: #999;
+        text-align: center;
+      }
+    }
   }
 }