lsc 12 tuntia sitten
vanhempi
commit
1a25aba3e6

+ 24 - 0
src/services/course.ts

@@ -33,7 +33,31 @@ export const submitWork = (params: SubmitWorkParams): Promise<any> => {
   return axios.post(`${API_URL}addCourseWorks_workPage`, [params])
 }
 
+/**
+ * 查看作业接口
+ * @param cid 课程ID
+ * @param s s参数,传0
+ * @param t 第几页幻灯片
+ * @returns Promise<any>
+ */
+export const selectSWorks = (cid: string, s: string, t: string): Promise<any> => {
+  return axios.get(`${API_URL}selectSWorks`, {
+    params: {
+      cid,
+      s,
+      t,
+    },
+  })
+}
+
+export const getHTML = (url: string): Promise<any> => {
+  return axios.get(`${url}`)
+}
+
+
 export default {
   getCourseDetail,
   submitWork,
+  selectSWorks,
+  getHTML,
 }

+ 1 - 0
src/types/slides.ts

@@ -644,6 +644,7 @@ export interface PPTAudioElement extends PPTBaseElement {
 export interface PPTFrameElement extends PPTBaseElement {
   type: 'frame'
   url: string
+  isHTML?: boolean
 }
 
 export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement | PPTLatexElement | PPTVideoElement | PPTAudioElement | PPTFrameElement

+ 159 - 82
src/views/Student/index.vue

@@ -275,6 +275,7 @@ const elementList = computed(() => {
 
 // 检测当前幻灯片是否包含iframe元素
 const currentSlideHasIframe = computed(() => {
+  console.log('elementList.value', elementList.value)
   return elementList.value.some(element => element.type === ElementTypes.FRAME)
 })
 
@@ -282,24 +283,43 @@ const currentSlideHasIframe = computed(() => {
 const goToSlide = (index: number) => {
   console.log('goToSlide 被调用,目标索引:', index)
   console.log('当前索引:', slideIndex.value)
-  slidesStore.updateSlideIndex(index)
-  console.log('更新后的索引:', slideIndex.value)
+  
+  if (index >= 0 && index < slides.value.length) {
+    slidesStore.updateSlideIndex(index)
+    console.log('更新后的索引:', slideIndex.value)
+  }
+  else {
+    console.warn('goToSlide: 无效的索引:', index)
+  }
 }
 
 // 上一页
 const previousSlide = () => {
   if (slideIndex.value > 0) {
-    slidesStore.updateSlideIndex(slideIndex.value - 1)
+    const newIndex = slideIndex.value - 1
+    console.log('上一页,从', slideIndex.value, '到', newIndex)
+    slidesStore.updateSlideIndex(newIndex)
   }
 }
 
 // 下一页
 const nextSlide = () => {
   if (slideIndex.value < slides.value.length - 1) {
-    slidesStore.updateSlideIndex(slideIndex.value + 1)
+    const newIndex = slideIndex.value + 1
+    console.log('下一页,从', slideIndex.value, '到', newIndex)
+    slidesStore.updateSlideIndex(newIndex)
   }
 }
 
+// 监听slideIndex变化,调用getWork
+watch(() => slideIndex.value, (newIndex, oldIndex) => {
+  console.log('slideIndex变化,调用getWork', { newIndex, oldIndex })
+  if (newIndex !== oldIndex && typeof newIndex === 'number') {
+    console.log('触发getWork,当前幻灯片索引:', newIndex)
+    getWork()
+  }
+}, { immediate: false, deep: false })
+
 // 全屏
 const enterFullscreen = () => {
   if (document.fullscreenElement) {
@@ -380,80 +400,98 @@ const findSlideIndexByIframeUrl = (iframeUrl: string): number => {
 }
 
 // 处理iframe链接,为包含workPage的iframe添加必要参数
-const processIframeLinks = () => {
+// 处理iframe链接,为包含workPage的iframe添加必要参数
+const processIframeLinks = async () => {
   try {
     console.log('开始处理iframe链接')
     console.log('当前props:', { courseid: props.courseid, userid: props.userid })
 
     // 从slides数据中查找包含iframe的元素
     let hasIframe = false
-    const updatedSlides = slides.value.map((slide, slideIndex) => {
-      if (slide.elements && slide.elements.length > 0) {
-        const updatedElements = slide.elements.map(element => {
-          // 检查是否是iframe元素
-          if (element.type === ElementTypes.FRAME && element.url) {
-            const iframeSrc = element.url
-            
-            if (iframeSrc.includes('workPage')) {
-              hasIframe = true
-              console.log(`处理幻灯片 ${slideIndex + 1} 中的iframe链接:`, iframeSrc)
-
-              try {
-                // 解析URL,处理hash部分
-                let baseUrl = iframeSrc
-                let hashPart = ''
-
-                // 分离base URL和hash部分
-                if (iframeSrc.includes('#')) {
-                  const parts = iframeSrc.split('#')
-                  baseUrl = parts[0]
-                  hashPart = parts[1]
-                }
-
-                // 构建新的hash部分,添加参数
-                // 使用当前幻灯片索引作为task参数
-                let newHash = hashPart
-                if (newHash.includes('?')) {
-                  // 如果hash中已经有查询参数,添加&
-                  newHash += `&courseid=${props.courseid || ''}&userid=${props.userid || ''}&stage=0&task=${slideIndex}&tool=0`
-                }
-                else {
-                  // 如果hash中没有查询参数,添加?
-                  newHash += `?courseid=${props.courseid || ''}&userid=${props.userid || ''}&stage=0&task=${slideIndex}&tool=0`
+    // 由于有异步操作,需整体用Promise.all处理
+    const updatedSlides = await Promise.all(
+      slides.value.map(async (slide, slideIndex) => {
+        if (slide.elements && slide.elements.length > 0) {
+          // 这里不能直接用async map,否则会导致类型不对
+          const updatedElements = await Promise.all(
+            slide.elements.map(async (element) => {
+              // 检查是否是iframe元素
+              if (element.type === ElementTypes.FRAME && element.url) {
+                const iframeSrc = element.url
+
+                if (iframeSrc.includes('workPage')) {
+                  hasIframe = true
+                  console.log(`处理幻灯片 ${slideIndex + 1} 中的iframe链接:`, iframeSrc)
+
+                  try {
+                    // 解析URL,处理hash部分
+                    let baseUrl = iframeSrc
+                    let hashPart = ''
+
+                    // 分离base URL和hash部分
+                    if (iframeSrc.includes('#')) {
+                      const parts = iframeSrc.split('#')
+                      baseUrl = parts[0]
+                      hashPart = parts[1]
+                    }
+
+                    // 构建新的hash部分,添加参数
+                    // 使用当前幻灯片索引作为task参数
+                    let newHash = hashPart
+                    if (newHash.includes('?')) {
+                      // 如果hash中已经有查询参数,添加&
+                      newHash += `&courseid=${props.courseid || ''}&userid=${props.userid || ''}&stage=0&task=${slideIndex}&tool=0`
+                    } 
+                    else {
+                      // 如果hash中没有查询参数,添加?
+                      newHash += `?courseid=${props.courseid || ''}&userid=${props.userid || ''}&stage=0&task=${slideIndex}&tool=0`
+                    }
+
+                    // 构建新的URL
+                    const newUrl = `${baseUrl}#${newHash}`
+
+                    console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
+
+                    // 返回更新后的元素
+                    return {
+                      ...element,
+                      url: newUrl
+                    }
+                  } 
+                  catch (error) {
+                    console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
+                    return element
+                  }
+                } 
+                else if (iframeSrc.includes('.html')) {
+                  hasIframe = true
+                  const html = await api.getHTML(iframeSrc)
+                  console.log('html', html)
+                  console.log(`处理幻灯片 ${slideIndex + 1} 中的iframe链接:`, iframeSrc)
+                  return {
+                    ...element,
+                    isHTML: true,
+                    url: html
+                  }
                 }
+              }
 
-                // 构建新的URL
-                const newUrl = `${baseUrl}#${newHash}`
-
-                console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
+              // 不是iframe元素或不需要处理,直接返回
+              return element
+            })
+          )
 
-                // 返回更新后的元素
-                return {
-                  ...element,
-                  url: newUrl
-                }
-              }
-              catch (error) {
-                console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
-                return element
-              }
-            }
+          // 返回更新后的幻灯片
+          return {
+            ...slide,
+            elements: updatedElements
           }
-          
-          // 不是iframe元素或不需要处理,直接返回
-          return element
-        })
-
-        // 返回更新后的幻灯片
-        return {
-          ...slide,
-          elements: updatedElements
         }
-      }
-      
-      // 没有元素的幻灯片直接返回
-      return slide
-    })
+
+        // 没有元素的幻灯片直接返回
+        return slide
+      })
+    )
 
     if (hasIframe) {
       console.log('找到iframe元素,更新slides数据')
@@ -697,17 +735,31 @@ const handleHomeworkSubmit = async () => {
         }
       }
       else {
+        // message.warning('当前页面暂不支持作业提交')
         console.log('尝试截图当前页面并提交')
-        return
-        /*
+        // return
         try {
           // 尝试使用html2canvas,对iframe支持更好
           let imageData: string
-          
+          const screenSlides = document.querySelectorAll('.viewer-canvas .screen-slide')
+          let iframe: HTMLElement | null = null
+          if (
+            screenSlides &&
+            screenSlides[slideIndex.value] &&
+            screenSlides[slideIndex.value].querySelector('iframe') &&
+            (screenSlides[slideIndex.value].querySelector('iframe') as HTMLIFrameElement).contentWindow &&
+            (screenSlides[slideIndex.value].querySelector('iframe') as HTMLIFrameElement).contentWindow!.document &&
+            (screenSlides[slideIndex.value].querySelector('iframe') as HTMLIFrameElement).contentWindow!.document.body
+          ) {
+            iframe = (screenSlides[slideIndex.value].querySelector('iframe') as HTMLIFrameElement).contentWindow!.document.body as HTMLElement
+          }
+          else {
+            throw new Error('未能获取到iframe的body元素,无法截图')
+          }
           try {
             // 首先尝试使用html2canvas
             const html2canvas = await import('html2canvas')
-            const canvas = await html2canvas.default(document.querySelectorAll('.viewer-canvas .screen-slide')[slideIndex.value] as HTMLElement, {
+            const canvas = await html2canvas.default(iframe, {
               useCORS: true,
               allowTaint: true,
               scale: 1,
@@ -725,7 +777,7 @@ const handleHomeworkSubmit = async () => {
             
             // 回退到html-to-image
             const { toPng } = await import('html-to-image')
-            imageData = await toPng(document.querySelectorAll('.viewer-canvas .screen-slide')[slideIndex.value] as HTMLElement, {
+            imageData = await toPng(iframe, {
               quality: 0.95,
               backgroundColor: '#ffffff'
             })
@@ -755,7 +807,7 @@ const handleHomeworkSubmit = async () => {
         catch (error) {
           console.error('截图提交失败:', error)
           message.error('截图提交失败')
-        }*/
+        }
       }
     }
 
@@ -882,6 +934,29 @@ const getCourseDetail = async () => {
   }
 }
 
+const getWork = async () => {
+  try {
+    console.log('getWork 开始执行,参数:', {
+      courseid: props.courseid,
+      slideIndex: slideIndex.value,
+      type: props.type
+    })
+    
+    if (!props.courseid) {
+      console.warn('getWork: courseid 未提供,跳过执行')
+      return
+    }
+    
+    const res = await api.selectSWorks(props.courseid, '0', slideIndex.value.toString())
+    console.log('getWork 执行成功,结果:', res)
+    return res
+  }
+  catch (error) {
+    console.error('getWork 执行失败:', error)
+    message.error('获取作业信息失败')
+  }
+}
+
 onMounted(() => {
   document.addEventListener('keydown', handleKeydown)
 
@@ -902,6 +977,7 @@ onMounted(() => {
     }
   }
   getCourseDetail()
+  getWork()
 
   // 计算初始缩放比例
   nextTick(() => {
@@ -1407,6 +1483,16 @@ onUnmounted(() => {
     align-items: center;
     justify-content: center;
     z-index: 1000;
+
+    .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-content {
@@ -1414,15 +1500,6 @@ onUnmounted(() => {
     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;

+ 6 - 0
src/views/components/element/FrameElement/BaseFrameElement.vue

@@ -12,7 +12,13 @@
       :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
     >
       <div class="element-content">
+        <iframe :srcdoc="elementInfo.url" v-if="elementInfo.isHTML"
+          :width="elementInfo.width"
+          :height="elementInfo.height"
+          :frameborder="0" 
+          :allowfullscreen="true"></iframe>
         <iframe 
+          v-else
           :src="elementInfo.url"
           :width="elementInfo.width"
           :height="elementInfo.height"

+ 6 - 1
src/views/components/element/FrameElement/index.vue

@@ -17,7 +17,12 @@
         @mousedown="$event => handleSelectElement($event)"
         @touchstart="$event => handleSelectElement($event)"
       >
-        <iframe 
+        <iframe :srcdoc="elementInfo.url" v-if="elementInfo.isHTML"
+          :width="elementInfo.width"
+          :height="elementInfo.height"
+          :frameborder="0" 
+          :allowfullscreen="true"></iframe>
+        <iframe v-else
           :src="elementInfo.url"
           :width="elementInfo.width"
           :height="elementInfo.height"