lsc 5 days ago
parent
commit
efea85465b

+ 12 - 2
src/tools/aiChat.ts

@@ -45,7 +45,12 @@ export const chat_no_stream = (msg: string, agentId: string, userId: string, lan
     const params: ChatParams = {
       ...DEFAULT_PARAMS,
       id: agentId,
-      message: msg,
+      message: `Language: ${language === 'en'
+        ? 'English'
+        : language === 'hk'
+          ? 'Traditional Chinese'
+          : 'Chinese'
+      } ${msg}`,
       uid: uuidv4(),
       stream: false,
       model: agentData?.modelType || 'open-doubao',
@@ -105,7 +110,12 @@ export const chat_stream = async (
     ...DEFAULT_PARAMS,
     id: agentId,
     file_ids: file_ids || [],
-    message: msg,
+    message: `Language: ${language === 'en'
+      ? 'English'
+      : language === 'hk'
+        ? 'Traditional Chinese'
+        : 'Chinese'
+    } ${msg}`,
     uid: uuidv4(),
     stream: true,
     model: agentData?.modelType || 'open-doubao',

+ 261 - 236
src/views/Student/index.vue

@@ -1452,243 +1452,13 @@ const processIframeLinks = async () => {
             slide.elements.map(async (element) => {
               // 检查是否是iframe元素
               if (element.type === ElementTypes.FRAME && element.url) {
-                let iframeSrc = element.url
-                const toolType = element.toolType
-                console.log('当前版本:', currentVersion)
-                // 替换beta环境域名
-                iframeSrc = iframeSrc.replace(/https?:\/\/beta\.pbl\.cocorobo\.cn/g, 'https://pbl.cocorobo.cn')
-
-                // 根据当前版本统一域名
-                const versionMap = {
-                  cn: /cocorobo\.(hk|com)/g,
-                  hk: /cocorobo\.(cn|com)/g,
-                  com: /cocorobo\.(cn|hk)/g
+                const { element: updatedElement, hasIframe: updatedHasIframe } = await elementDone(element, slideIndex)
+                hasIframe = updatedHasIframe
+                console.log('更新后的iframe元素:', updatedElement)
+                return {
+                  ...updatedElement,
+                  isDone: true
                 }
-
-                const targetDomain = `cocorobo.${currentVersion}`
-                iframeSrc = iframeSrc.replace(versionMap[currentVersion], targetDomain)
-
-                if (iframeSrc.includes('setWorkPage')) {
-                  iframeSrc = iframeSrc.replace(/setWorkPage/g, 'workPageNew')
-                }
-
-                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
-                    let newUrl = `${baseUrl}#${newHash}`
-
-                    console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
-                      
-                    if (window.location.href.includes('beta') && !newUrl.includes('beta')) {
-                      newUrl = newUrl.replace('pbl.cocorobo.cn', 'beta.pbl.cocorobo.cn')
-                    }
-                    else if (newUrl.includes('beta') && !window.location.href.includes('beta')) {
-                      newUrl = newUrl.replace('beta.pbl.cocorobo.cn', 'pbl.cocorobo.cn')
-                    }
-                    // 返回更新后的元素
-                    return {
-                      ...element,
-                      url: newUrl
-                    }
-                  } 
-                  catch (error) {
-                    console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
-                    return element
-                  }
-                }
-                else if (iframeSrc.includes('aichat.cocorobo') || iframeSrc.includes('knowledge.cocorobo')) {
-                  hasIframe = true
-                  try {
-                    // 解析URL,处理hash部分
-                    let baseUrl = iframeSrc
-                    let hashPart = ''
-                    let isHashPart = false
-                    // 分离base URL和hash部分
-                    if (iframeSrc.includes('#')) {
-                      const parts = iframeSrc.split('#')
-                      baseUrl = parts[0]
-                      hashPart = parts[1]
-                      isHashPart = true
-                    }
-
-                    // 构建新的hash部分,添加参数
-                    // 使用当前幻灯片索引作为task参数
-                    let newHash = hashPart
-                    if (newHash.includes('?')) {
-                      // 如果hash中已经有查询参数,添加&
-                      newHash += `&courseid=${props.courseid || ''}&layout=laptop`
-                    } 
-                    else {
-                      // 如果hash中没有查询参数,添加?
-                      newHash += `?courseid=${props.courseid || ''}&layout=laptop`
-                    }
-
-                    // 构建新的URL
-                    let newUrl = `${baseUrl}#${newHash}`
-                    if (!isHashPart) {
-                      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 (toolType == 76) {
-                  hasIframe = true
-                  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 += `&mode=pptMode`
-                    } 
-                    else {
-                      // 如果hash中没有查询参数,添加?
-                      newHash += `?mode=pptMode`
-                    }
-
-                    // 构建新的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 (toolType == 73) {
-                  hasIframe = true
-                  
-                  // 先尝试获取iframe的contentWindow,如果获取不到再使用HTML方式
-                  try {
-                    // 创建一个临时的iframe来测试是否能获取contentWindow
-                    const tempIframe = document.createElement('iframe')
-                    tempIframe.style.display = 'none'
-                    tempIframe.src = iframeSrc
-                    
-                    // 先将临时iframe添加到body,否则onload事件不会触发
-                    document.body.appendChild(tempIframe)
-                    // 等待iframe加载完成
-                    await new Promise((resolve, reject) => {
-                      tempIframe.onload = resolve
-                      tempIframe.onerror = reject
-                      // 可选:设置超时时间,避免长时间无响应
-                      setTimeout(() => reject(new Error('Timeout')), 5000)
-                    })
-                    
-                    // 尝试获取contentWindow
-                    if (tempIframe.contentWindow && tempIframe.contentWindow.document) {
-                      console.log(`iframe ${iframeSrc} 可以获取contentWindow,使用直接加载方式`)
-                      // 移除临时iframe
-                      document.body.removeChild(tempIframe)
-                      
-                      return {
-                        ...element,
-                        isHTML: false,
-                        url: iframeSrc
-                      }
-                    } 
-                    // 加载完成但无法获取contentWindow,也要移除iframe
-                    document.body.removeChild(tempIframe)
-                    
-                  }
-                  catch (error) {
-                    console.log(`iframe ${iframeSrc} 无法获取contentWindow,使用HTML方式:`, error)
-                  
-                    // 如果无法获取contentWindow,使用HTML方式
-                    let html = null
-                    try {
-                      console.log(`getFile2 失败,尝试使用 getHTML:`, error2)
-                      try {
-                        html = await api.getHTML(iframeSrc)
-                        console.log('getHTML 成功获取内容:', html)
-                      }
-                      catch (htmlError) {
-                        console.error('getHTML 也失败:', htmlError)
-                        console.error('无法获取内容: getFile、getFile2 和 getHTML 都失败了')
-                        // throw new Error(`无法获取内容: getFile、getFile2 和 getHTML 都失败了`)
-                      }
-                    }
-                    catch (error) {
-                      console.log(`getFile 失败,尝试使用 getFile2:`, error)
-                      try {
-                        const fileData = await getFile(iframeSrc)
-                        if (fileData && fileData.data) {
-                          const uint8Array = new Uint8Array(fileData.data)
-                          html = new TextDecoder('utf-8').decode(uint8Array)
-                          console.log('getFile 成功获取内容:', html)
-                        }
-                      }
-                      catch (error2) {
-                        const fileData2 = await getFile2(iframeSrc)
-                        if (fileData2 && fileData2.data) {
-                          const uint8Array = new Uint8Array(fileData2.data)
-                          html = new TextDecoder('utf-8').decode(uint8Array)
-                          console.log('getFile2 成功获取内容:', html)
-                        }
-                      }
-                    }
-                    console.log(`处理幻灯片 ${slideIndex + 1} 中的iframe链接:`, iframeSrc)
-                    return {
-                      ...element,
-                      isHTML: true,  
-                      url: html
-                    }
-                  }
-                }
-
               }
 
               // 不是iframe元素或不需要处理,直接返回
@@ -1725,6 +1495,261 @@ const processIframeLinks = async () => {
   }
 }
 
+const elementDone = async (element: any, slideIndex: number) => {
+  let hasIframe = false
+  let _element = {...element}
+
+  let iframeSrc = element.url
+  const toolType = element.toolType
+  console.log('当前版本:', currentVersion)
+  // 替换beta环境域名
+  iframeSrc = iframeSrc.replace(/https?:\/\/beta\.pbl\.cocorobo\.cn/g, 'https://pbl.cocorobo.cn')
+
+  // 根据当前版本统一域名
+  const versionMap = {
+    cn: /cocorobo\.(hk|com)/g,
+    hk: /cocorobo\.(cn|com)/g,
+    com: /cocorobo\.(cn|hk)/g
+  }
+
+  const targetDomain = `cocorobo.${currentVersion}`
+  iframeSrc = iframeSrc.replace(versionMap[currentVersion], targetDomain)
+
+  if (iframeSrc.includes('setWorkPage')) {
+    iframeSrc = iframeSrc.replace(/setWorkPage/g, 'workPageNew')
+  }
+
+  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
+      let newUrl = `${baseUrl}#${newHash}`
+
+      console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
+                      
+      if (window.location.href.includes('beta') && !newUrl.includes('beta')) {
+        newUrl = newUrl.replace('pbl.cocorobo.cn', 'beta.pbl.cocorobo.cn')
+      }
+      else if (newUrl.includes('beta') && !window.location.href.includes('beta')) {
+        newUrl = newUrl.replace('beta.pbl.cocorobo.cn', 'pbl.cocorobo.cn')
+      }
+      // 返回更新后的元素
+      _element = {
+        ...element,
+        url: newUrl
+      }
+    } 
+    catch (error) {
+      console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
+      return {
+        element: _element,
+        hasIframe
+      }
+    }
+  }
+  else if (iframeSrc.includes('aichat.cocorobo') || iframeSrc.includes('knowledge.cocorobo')) {
+    hasIframe = true
+    try {
+      // 解析URL,处理hash部分
+      let baseUrl = iframeSrc
+      let hashPart = ''
+      let isHashPart = false
+      // 分离base URL和hash部分
+      if (iframeSrc.includes('#')) {
+        const parts = iframeSrc.split('#')
+        baseUrl = parts[0]
+        hashPart = parts[1]
+        isHashPart = true
+      }
+
+      // 构建新的hash部分,添加参数
+      // 使用当前幻灯片索引作为task参数
+      let newHash = hashPart
+      if (newHash.includes('?')) {
+        // 如果hash中已经有查询参数,添加&
+        newHash += `&courseid=${props.courseid || ''}&layout=laptop`
+      } 
+      else {
+        // 如果hash中没有查询参数,添加?
+        newHash += `?courseid=${props.courseid || ''}&layout=laptop`
+      }
+
+      // 构建新的URL
+      let newUrl = `${baseUrl}#${newHash}`
+      if (!isHashPart) {
+        newUrl = `${baseUrl}${newHash}`
+      }
+
+      console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
+      // 返回更新后的元素
+      _element = {
+        ...element,
+        url: newUrl
+      }
+    }
+    catch (error) {
+      console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
+      return {
+        element: _element,
+        hasIframe
+      }
+    }
+  }
+  else if (toolType === 76) {
+    hasIframe = true
+    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 += `&mode=pptMode`
+      } 
+      else {
+        // 如果hash中没有查询参数,添加?
+        newHash += `?mode=pptMode`
+      }
+
+      // 构建新的URL
+      const newUrl = `${baseUrl}#${newHash}`
+
+      console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
+      // 返回更新后的元素
+      _element = {
+        ...element,
+        url: newUrl
+      }
+    }
+    catch (error) {
+      console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
+      return {
+        element: _element,
+        hasIframe
+      }
+    }
+  }
+  else if (toolType === 73) {
+    hasIframe = true
+                  
+    // 先尝试获取iframe的contentWindow,如果获取不到再使用HTML方式
+    try {
+      // 创建一个临时的iframe来测试是否能获取contentWindow
+      const tempIframe = document.createElement('iframe')
+      tempIframe.style.display = 'none'
+      tempIframe.src = iframeSrc
+                    
+      // 先将临时iframe添加到body,否则onload事件不会触发
+      document.body.appendChild(tempIframe)
+      // 等待iframe加载完成
+      await new Promise((resolve, reject) => {
+        tempIframe.onload = resolve
+        tempIframe.onerror = reject
+        // 可选:设置超时时间,避免长时间无响应
+        setTimeout(() => reject(new Error('Timeout')), 5000)
+      })
+                    
+      // 尝试获取contentWindow
+      if (tempIframe.contentWindow && tempIframe.contentWindow.document) {
+        console.log(`iframe ${iframeSrc} 可以获取contentWindow,使用直接加载方式`)
+        // 移除临时iframe
+        document.body.removeChild(tempIframe)
+                      
+        _element = {
+          ...element,
+          isHTML: false,
+          url: iframeSrc
+        }
+      } 
+      // 加载完成但无法获取contentWindow,也要移除iframe
+      document.body.removeChild(tempIframe)
+                    
+    }
+    catch (error) {
+      console.log(`iframe ${iframeSrc} 无法获取contentWindow,使用HTML方式:`, error)
+                  
+      // 如果无法获取contentWindow,使用HTML方式
+      let html = null
+      try {
+        html = await api.getHTML(iframeSrc)
+        console.log('getHTML 成功获取内容:', html)
+      }
+      catch (error) {
+        console.log(`getHTML 失败,尝试使用 getFile:`, error)
+        try {
+          const fileData = await getFile(iframeSrc)
+          if (fileData && fileData.data) {
+            const uint8Array = new Uint8Array(fileData.data)
+            html = new TextDecoder('utf-8').decode(uint8Array)
+            console.log('getFile 成功获取内容:', html)
+          }
+        }
+        catch (error2) {
+          console.log(`getFile 失败,尝试使用 getHTML:`, error2)
+          try {
+            const fileData2 = await getFile2(iframeSrc)
+            if (fileData2 && fileData2.data) {
+              const uint8Array = new Uint8Array(fileData2.data)
+              html = new TextDecoder('utf-8').decode(uint8Array)
+              console.log('getFile2 成功获取内容:', html)
+            }
+          }
+          catch (error3) {
+            console.error('getFile2 也失败:', error3)
+            console.error('无法获取内容: getFile、getFile2 和 getHTML 都失败了')
+          }
+        }
+      }
+      console.log(`处理幻灯片 ${slideIndex + 1} 中的iframe链接:`, iframeSrc)
+      _element = {
+        ...element,
+        isHTML: true,  
+        url: html
+      }
+    }
+  }
+
+  return {
+    element: _element,
+    hasIframe
+  }
+}
+ 
 // 导入JSON功能
 const importJSON = (jsonData: any) => {
   try {

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

@@ -22,6 +22,11 @@
       :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
     >
       <div class="element-content">
+        <div class="fullscreen-spin mask" v-if="!elementInfo.isDone && !isThumbnail && isVisible">
+          <div class="spin">
+            <div class="spinner"></div>
+          </div>
+        </div>
         <!-- 视频类型(type 74):使用 video 标签 -->
         <video
           v-if="elementInfo.toolType === 74 && !isThumbnail && isVisible"
@@ -343,4 +348,54 @@ const handleIframeLoad = async (event: Event) => {
     left: 100%;
   }
 }
+
+.fullscreen-spin {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  z-index: 100;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  &.mask {
+    background-color: rgba($color: #f1f1f1, $alpha: .7);
+  }
+}
+.spin {
+  width: 200px;
+  height: 200px;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  margin-top: -100px;
+  margin-left: -100px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+.spinner {
+  width: 36px;
+  height: 36px;
+  border: 3px solid #f6c82b;
+  border-top-color: transparent;
+  border-radius: 50%;
+  animation: spinner .8s linear infinite;
+}
+.text {
+  margin-top: 20px;
+  color: #f6c82b;
+}
+
+@keyframes spinner {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
 </style>