Browse Source

refactor(FrameElement): 重构iframe脚本注入逻辑

1. 移除本地processedSrcdoc计算属性,改为运行时注入依赖脚本
2. 将全局$替换为$query避免全局污染
3. 调整脚本注入时机和方式,修复变量冲突问题
lsc 13 hours ago
parent
commit
612c8989f2

+ 72 - 70
src/views/components/element/FrameElement/BaseFrameElement.vue

@@ -64,7 +64,7 @@
         <!-- 延迟加载iframe:只有在可见且不是缩略图时才加载 -->
         <iframe
           :key="`html-${iframeKey}`"
-          :srcdoc="processedSrcdoc"
+          :srcdoc="elementInfo.url"
           v-else-if="elementInfo.isHTML && !isThumbnail && isVisible"
           :width="width"
           :height="height"
@@ -121,10 +121,6 @@ import {
 } from '@/configs/englishSpeakingTools'
 import { ref, watch, nextTick } from 'vue'
 import { computed } from 'vue'
-import jqueryRaw from './jquery-3.6.0.min.js?raw'
-import awsSdkRaw from './aws-sdk-2.235.1.min.js?raw'
-import jietuRaw from './jietu.js?raw'
-import html2canvasRaw from 'html2canvas/dist/html2canvas.min.js?raw'
 
 const props = defineProps({
   elementInfo: {
@@ -148,65 +144,6 @@ const props = defineProps({
 // 用于强制刷新iframe的key
 const iframeKey = ref(0)
 
-// 处理 srcdoc:将页面自定义的 $ 改名为 _$,注入 jQuery 等脚本
-const processedSrcdoc = computed(() => {
-  if (!props.elementInfo.isHTML) return props.elementInfo.url
-
-  const html = props.elementInfo.url
-  try {
-    const parser = new DOMParser()
-    const doc = parser.parseFromString(html, 'text/html')
-
-    // 1. 重命名页面已有的 $ 变量为 _$,避免与 jQuery 冲突
-    // 支持 var/let/const 声明,以及 $(...) 调用和 $.xxx 属性访问
-    const scripts = doc.querySelectorAll('script:not([src])')
-    scripts.forEach(script => {
-      const content = script.textContent || ''
-      if (!content) return
-      const modified = content
-        // 重命名声明:var $ = / let $ = / const $ =
-        .replace(/\b(var|let|const)\s+\$\s*=/g, '$1 _$ =')
-        // 重命名调用:$('id') → _$('id')
-        .replace(/(^|[^a-zA-Z0-9_$])\$\s*\(/g, '$1_$(')
-        // 重命名属性访问:$.ajax → _$.ajax
-        .replace(/(^|[^a-zA-Z0-9_$])\$\./g, '$1_$.')
-      script.textContent = modified
-    })
-
-    // 2. 在 head 开头注入 jQuery、html2canvas、aws-sdk
-    const head = doc.head || doc.getElementsByTagName('head')[0] || doc.createElement('head')
-    if (!head.parentNode) {
-      doc.documentElement.insertBefore(head, doc.body || null)
-    }
-
-    const injectScript = (content: string, prepend = false) => {
-      const s = doc.createElement('script')
-      s.textContent = content
-      if (prepend) head.insertBefore(s, head.firstChild)
-      else head.appendChild(s)
-    }
-
-    // 按顺序注入:jQuery → html2canvas → aws-sdk
-    injectScript(jqueryRaw, true)
-    injectScript(html2canvasRaw)
-    injectScript(awsSdkRaw)
-
-    // 3. 在 body 末尾追加 jietu.js(此时 $ 是 jQuery)
-    const body = doc.body || doc.getElementsByTagName('body')[0]
-    if (body) {
-      const jietuScript = doc.createElement('script')
-      jietuScript.textContent = jietuRaw
-      body.appendChild(jietuScript)
-    }
-
-    return '<!DOCTYPE html>' + doc.documentElement.outerHTML
-  }
-  catch (e) {
-    console.error('处理 srcdoc 失败:', e)
-    return html
-  }
-})
-
 // 监听elementInfo.url的变化
 watch(
   () => props.elementInfo.url,
@@ -261,13 +198,78 @@ const getTypeLabel = (type: number): string => {
 
 const frameloading = ref(false)
 
-// 处理iframe加载完成事件(脚本已在 srcdoc 中内联注入,这里只需隐藏 loading)
-const handleIframeLoad = async () => {
+// 处理iframe加载完成事件
+const handleIframeLoad = async (event: Event) => {
+  const iframe = event.target as HTMLIFrameElement
   frameloading.value = true
-  await nextTick()
-  setTimeout(() => {
+  try {
+    // 等待iframe完全加载
+    await nextTick()
+    setTimeout(async () => {
+      // 检查iframe是否可访问(同源检查)
+      if (iframe.contentWindow && iframe.contentDocument) {
+        const iframeDoc = iframe.contentDocument
+        const iframeHead =
+          iframeDoc.head || iframeDoc.getElementsByTagName('head')[0]
+
+        if (iframeHead) {
+          // 使用动态导入获取JS文件内容
+          const jsFiles = [
+            {
+              id: 'aws-sdk',
+              importPath: () => import('./aws-sdk-2.235.1.min.js?raw'),
+            },
+            {
+              id: 'jquery',
+              importPath: () => import('./jquery-3.6.0.min.js?raw'),
+            },
+            { id: 'jietu', importPath: () => import('./jietu.js?raw') },
+          ]
+
+          for (const jsFile of jsFiles) {
+            try {
+              // 检查是否已经注入过
+              if (!iframeDoc.getElementById(jsFile.id)) {
+                const jsModule = await jsFile.importPath()
+                const jsContent = jsModule.default || jsModule
+
+                const scriptElement = iframeDoc.createElement('script')
+                scriptElement.id = jsFile.id
+                scriptElement.textContent = jsContent
+                iframeHead.appendChild(scriptElement)
+
+                console.log(`已注入 ${jsFile.id} 到iframe中`)
+              }
+            }
+            catch (fetchError) {
+              console.error(`获取 ${jsFile.id} 失败:`, fetchError)
+            }
+          }
+          frameloading.value = false
+
+          // 可选:在iframe中执行一些初始化代码
+          try {
+            iframe.contentWindow.eval(`
+                console.log('iframe中的JS环境已准备就绪');
+                // 这里可以添加一些初始化代码
+              `)
+          }
+          catch (evalError) {
+            frameloading.value = false
+            console.warn('无法在iframe中执行代码:', evalError)
+          }
+        }
+      }
+      else {
+        frameloading.value = false
+        console.warn('无法访问iframe内容,可能是跨域限制')
+      }
+    }, 1000)
+  }
+  catch (error) {
     frameloading.value = false
-  }, 500)
+    console.error('注入JS到iframe失败:', error)
+  }
 }
 </script>
   
@@ -432,4 +434,4 @@ const handleIframeLoad = async () => {
     transform: rotate(360deg);
   }
 }
-</style>
+</style>

+ 3 - 3
src/views/components/element/FrameElement/jietu.js

@@ -1,5 +1,5 @@
 function jietu_shishi(uid, cid, page, type) {
-  html2canvas($('body')[0]).then(function(canvas) {
+  html2canvas($query('body')[0]).then(function(canvas) {
     // 获取截取图片路径
 
     const base64Url = canvas.toDataURL('image/png')
@@ -8,7 +8,7 @@ function jietu_shishi(uid, cid, page, type) {
     const base64 = '<img src=' + base64Url + ' />'
     const file = dataURLtoFile_shishi(base64Url, '截图')
     beforeUpload_shishi(file, uid, cid, page, type)
-    // $("body")[0].innerHTML = base64
+    // $query("body")[0].innerHTML = base64
   })
 }
 
@@ -82,7 +82,7 @@ function addSWork_shishi(uid, cid, a, task, tool, atool, type) {
     content: a.url,
     type: type,
   }
-  $.ajax({
+  $query.ajax({
     type: 'GET',
     url: 'https://pbl.cocorobo.cn/api/pbl/addCourseWorks_workPage2',
     data: params,

File diff suppressed because it is too large
+ 0 - 0
src/views/components/element/FrameElement/jquery-3.6.0.min.js


Some files were not shown because too many files changed in this diff