lsc 1 deň pred
rodič
commit
89f796180f
2 zmenil súbory, kde vykonal 170 pridanie a 33 odobranie
  1. 92 6
      src/views/Editor/Canvas/index.vue
  2. 78 27
      src/views/Student/index.vue

+ 92 - 6
src/views/Editor/Canvas/index.vue

@@ -8,6 +8,14 @@
     v-contextmenu="contextmenus"
     v-click-outside="removeEditorAreaFocus"
   >
+    <!-- 全屏Loading状态 -->
+    <div v-if="isCourseLoading" class="fullscreen-loading-overlay">
+      <div class="loading-content">
+        <div class="loading-spinner"></div>
+        <div class="loading-text">正在加载课程内容...</div>
+      </div>
+    </div>
+
     <ElementCreateSelection
       v-if="creatingElement"
       @created="data => insertElementFromCreateSelection(data)"
@@ -153,6 +161,7 @@ import WebpageLinkEditDialog from './WebpageLinkEditDialog.vue'
 import Modal from '@/components/Modal.vue'
 import api from '@/services/course'
 import useImport from '@/hooks/useImport'
+import message from '@/utils/message'
 
 
 // 定义组件props
@@ -196,6 +205,9 @@ const openWebpageLinkEditDialog = (elementId: string, currentUrl: string) => {
   webpageLinkEditDialogVisible.value = true
 }
 
+// 课程加载loading状态
+const isCourseLoading = ref(false)
+
 watch(handleElementId, () => {
   mainStore.setActiveGroupElementId('')
 })
@@ -226,7 +238,7 @@ const { pasteElement } = useCopyAndPasteElement()
 const { enterScreeningFromStart } = useScreening()
 const { updateSlideIndex } = useSlideHandler()
 const { createTextElement, createShapeElement } = useCreateElement()
-const { readJSON } = useImport()
+const { readJSON, getFile } = useImport()
 
 // 组件渲染时,如果存在元素焦点,需要清除
 // 这种情况存在于:有焦点元素的情况下进入了放映模式,再退出时,需要清除原先的焦点(因为可能已经切换了页面)
@@ -240,11 +252,44 @@ onMounted(() => {
 })
 
 const getCourseDetail = async () => {
-  const res = await api.getCourseDetail(props.courseid as string)
-  console.log(res)
-  const courseDetail = res[0][0]
-  const pptdata = JSON.parse(courseDetail.chapters).pptData ? JSON.parse(courseDetail.chapters).pptData : []
-  readJSON(pptdata, true)
+  // 显示全屏loading
+  isCourseLoading.value = true
+  
+  try {
+    const res = await api.getCourseDetail(props.courseid as string)
+    console.log(res)
+    const courseDetail = res[0][0]
+    const pptJSONUrl = JSON.parse(courseDetail.chapters).pptData ? JSON.parse(courseDetail.chapters).pptData : ''
+    console.log(pptJSONUrl)
+    
+    if (pptJSONUrl) { 
+      const pptdata = await getFile(pptJSONUrl)
+      // pptdata.data 是 ArrayBuffer,需要先转成字符串再解析为 JSON
+      let jsonStr = ''
+      if (pptdata && pptdata.data) {
+        // 先将 ArrayBuffer 转为字符串
+        const uint8Array = new Uint8Array(pptdata.data)
+        jsonStr = new TextDecoder('utf-8').decode(uint8Array)
+        try {
+          const jsonObj = JSON.parse(jsonStr)
+          readJSON(jsonObj, true)
+        }
+        catch (e) {
+          console.error('解析pptdata.data失败:', e)
+          message.error('解析PPT数据失败')
+        }
+      }
+    }
+  }
+  catch (error) {
+    console.error('获取课程详情失败:', error)
+    message.error('获取课程详情失败')
+    isCourseLoading.value = false
+  }
+  finally {
+    // 隐藏loading
+    isCourseLoading.value = false
+  }
 }
 
 
@@ -401,6 +446,47 @@ provide(injectKeySlideScale, canvasScale)
   background-color: $lightGray;
   position: relative;
 }
+
+/* 全屏Loading样式 */
+.fullscreen-loading-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(255, 255, 255, 0.95);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 9999;
+}
+
+.loading-content {
+  text-align: center;
+  color: #666;
+}
+
+.loading-spinner {
+  width: 50px;
+  height: 50px;
+  border: 5px solid #f3f3f3;
+  border-top: 5px solid #1890ff;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+  margin: 0 auto 20px;
+}
+
+.loading-text {
+  font-size: 16px;
+  color: #666;
+  font-weight: 500;
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
 .drag-mask {
   cursor: grab;
   @include absolute-0();

+ 78 - 27
src/views/Student/index.vue

@@ -1,5 +1,12 @@
 <template>
   <div class="pptist-student-viewer" :class="{ 'fullscreen': isFullscreen, 'laser-pen': laserPen }">
+    <!-- Loading状态显示 -->
+    <div v-if="isLoading" class="loading-overlay">
+      <div class="loading-content">
+        <div class="loading-spinner"></div>
+        <div class="loading-text">正在加载课程内容...</div>
+      </div>
+    </div>
     <!-- 左侧导航栏 -->
     <div class="layout-content-left" v-show="type == '1'">
       <div class="thumbnails">
@@ -46,6 +53,7 @@
       </div>
 
       <div class="viewer-canvas" ref="viewerCanvasRef">
+
         <!-- 全屏时:使用放映功能 -->
         <!-- <ScreenSlideList :slideWidth="slideWidth"
                     :slideHeight="slideHeight" :animationIndex="0" :turnSlideToId="() => { }"
@@ -181,6 +189,9 @@ const showSlideList = ref(true)
 const slideWidth = ref(0)
 const slideHeight = ref(0)
 
+// 添加loading状态
+const isLoading = ref(false)
+
 // 计算幻灯片尺寸的函数
 const calculateSlideSize = () => {
   const slideWrapRef = isFullscreen.value ? document.body : viewerCanvasRef.value
@@ -835,29 +846,40 @@ const handleViewportSizeUpdated = (event: any) => {
 }
 
 const getCourseDetail = async () => {
-  const res = await api.getCourseDetail(props.courseid as string)
-  console.log(res)
-  const courseDetail = res[0][0]
-  const pptJSONUrl = JSON.parse(courseDetail.chapters).pptData ? JSON.parse(courseDetail.chapters).pptData : ''
-  console.log(pptJSONUrl)
-  
-  if (pptJSONUrl) { 
-    const pptdata = await getFile(pptJSONUrl)
-    // pptdata.data 是 ArrayBuffer,需要先转成字符串再解析为 JSON
-    let jsonStr = ''
-    if (pptdata && pptdata.data) {
-      // 先将 ArrayBuffer 转为字符串
-      const uint8Array = new Uint8Array(pptdata.data)
-      jsonStr = new TextDecoder('utf-8').decode(uint8Array)
-      try {
-        const jsonObj = JSON.parse(jsonStr)
-        importJSON(jsonObj)
-      }
-      catch (e) {
-        console.error('解析pptdata.data失败:', e)
+  isLoading.value = true
+  try {
+    const res = await api.getCourseDetail(props.courseid as string)
+    console.log(res)
+    const courseDetail = res[0][0]
+    const pptJSONUrl = JSON.parse(courseDetail.chapters).pptData ? JSON.parse(courseDetail.chapters).pptData : ''
+    console.log(pptJSONUrl)
+    
+    if (pptJSONUrl) { 
+      const pptdata = await getFile(pptJSONUrl)
+      // pptdata.data 是 ArrayBuffer,需要先转成字符串再解析为 JSON
+      let jsonStr = ''
+      if (pptdata && pptdata.data) {
+        // 先将 ArrayBuffer 转为字符串
+        const uint8Array = new Uint8Array(pptdata.data)
+        jsonStr = new TextDecoder('utf-8').decode(uint8Array)
+        try {
+          const jsonObj = JSON.parse(jsonStr)
+          importJSON(jsonObj)
+        }
+        catch (e) {
+          console.error('解析pptdata.data失败:', e)
+        }
       }
     }
   }
+  catch (error) {
+    console.error('获取课程详情失败:', error)
+    message.error('获取课程详情失败')
+    isLoading.value = false
+  }
+  finally {
+    isLoading.value = false
+  }
 }
 
 onMounted(() => {
@@ -1373,13 +1395,42 @@ onUnmounted(() => {
   }
 }
 
-@keyframes spin {
-  0% {
-    transform: rotate(0deg);
-  }
+/* Loading状态样式 */
+.loading-overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(255, 255, 255, 0.95);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 1000;
+}
 
-  100% {
-    transform: rotate(360deg);
-  }
+.loading-content {
+    text-align: center;
+    color: #666;
+}
+
+.loading-spinner {
+    width: 40px;
+    height: 40px;
+    border: 4px solid #f3f3f3;
+    border-top: 4px solid #1890ff;
+    border-radius: 50%;
+    animation: spin 1s linear infinite;
+    margin: 0 auto 16px;
+}
+
+.loading-text {
+    font-size: 14px;
+    color: #666;
+}
+
+@keyframes spin {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
 }
 </style>