|
@@ -1101,87 +1101,58 @@ const handleHomeworkSubmit = async () => {
|
|
|
// 尝试使用html2canvas,对iframe支持更好
|
|
|
let imageData: string
|
|
|
const screenSlides = document.querySelectorAll('.viewer-canvas .screen-slide')
|
|
|
- let iframeElement: HTMLElement | null = null
|
|
|
+ let iframeElement: HTMLIFrameElement | null = null
|
|
|
+ let iframeBody: HTMLElement | null = null
|
|
|
+
|
|
|
+ // 获取iframe元素
|
|
|
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
|
|
|
+ screenSlides[slideIndex.value].querySelector('iframe')
|
|
|
) {
|
|
|
- iframeElement = (screenSlides[slideIndex.value].querySelector('iframe') as HTMLIFrameElement).contentWindow!.document.body as HTMLElement
|
|
|
+ iframeElement = screenSlides[slideIndex.value].querySelector('iframe') as HTMLIFrameElement
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ throw new Error('未能获取到iframe元素,无法截图')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取iframe内部的body元素(同源)
|
|
|
+ if (
|
|
|
+ iframeElement.contentWindow &&
|
|
|
+ iframeElement.contentWindow.document &&
|
|
|
+ iframeElement.contentWindow.document.body
|
|
|
+ ) {
|
|
|
+ iframeBody = iframeElement.contentWindow.document.body as HTMLElement
|
|
|
}
|
|
|
else {
|
|
|
throw new Error('未能获取到iframe的body元素,无法截图')
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 方案1:使用html2canvas的iframe特殊处理
|
|
|
+ // 直接对iframe内部的body进行截图
|
|
|
const html2canvas = await import('html2canvas')
|
|
|
- const canvas = await html2canvas.default(iframeElement, {
|
|
|
+ const canvas = await html2canvas.default(iframeBody, {
|
|
|
useCORS: true,
|
|
|
allowTaint: true,
|
|
|
scale: 1,
|
|
|
backgroundColor: '#ffffff',
|
|
|
logging: false,
|
|
|
- // 关键配置:处理iframe内容
|
|
|
foreignObjectRendering: true,
|
|
|
- removeContainer: true,
|
|
|
- // 特殊处理iframe
|
|
|
- onclone: (clonedDoc, element) => {
|
|
|
- // 在克隆的文档中处理iframe
|
|
|
- const clonedIframes = clonedDoc.querySelectorAll('iframe')
|
|
|
- clonedIframes.forEach((iframe) => {
|
|
|
- const src = iframe.getAttribute('src')
|
|
|
- if (src) {
|
|
|
- // 创建一个包含iframe信息的占位符
|
|
|
- const placeholder = clonedDoc.createElement('div')
|
|
|
- placeholder.style.width = iframe.style.width || iframe.getAttribute('width') || '100%'
|
|
|
- placeholder.style.height = iframe.style.height || iframe.getAttribute('height') || '300px'
|
|
|
- placeholder.style.backgroundColor = '#f8f9fa'
|
|
|
- placeholder.style.border = '2px solid #dee2e6'
|
|
|
- placeholder.style.borderRadius = '8px'
|
|
|
- placeholder.style.display = 'flex'
|
|
|
- placeholder.style.flexDirection = 'column'
|
|
|
- placeholder.style.alignItems = 'center'
|
|
|
- placeholder.style.justifyContent = 'center'
|
|
|
- placeholder.style.padding = '20px'
|
|
|
- placeholder.style.fontFamily = 'Arial, sans-serif'
|
|
|
-
|
|
|
- // 创建iframe占位符内容
|
|
|
- placeholder.innerHTML = `
|
|
|
- <div style="font-size: 24px; margin-bottom: 10px;">🌐</div>
|
|
|
- <div style="font-size: 16px; font-weight: bold; color: #495057; margin-bottom: 8px;">iframe内容</div>
|
|
|
- <div style="font-size: 12px; color: #6c757d; text-align: center; word-break: break-all; max-width: 100%;">
|
|
|
- ${src.length > 60 ? src.substring(0, 60) + '...' : src}
|
|
|
- </div>
|
|
|
- <div style="font-size: 10px; color: #adb5bd; margin-top: 8px;">
|
|
|
- (跨域限制,无法截图内部内容)
|
|
|
- </div>
|
|
|
- `
|
|
|
-
|
|
|
- // 替换iframe
|
|
|
- iframe.parentNode?.replaceChild(placeholder, iframe)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
+ removeContainer: true
|
|
|
})
|
|
|
imageData = canvas.toDataURL('image/png', 0.95)
|
|
|
- console.log('使用html2canvas iframe特殊处理截图成功')
|
|
|
+ console.log('成功截图iframe内部内容')
|
|
|
}
|
|
|
catch (html2canvasError) {
|
|
|
- console.log('html2canvas iframe处理失败,尝试html-to-image:', html2canvasError)
|
|
|
+ console.log('html2canvas失败,尝试html-to-image:', html2canvasError)
|
|
|
|
|
|
try {
|
|
|
- // 方案2:使用html-to-image
|
|
|
+ // 回退到html-to-image
|
|
|
const { toPng } = await import('html-to-image')
|
|
|
- imageData = await toPng(iframeElement, {
|
|
|
+ imageData = await toPng(iframeBody, {
|
|
|
quality: 0.95,
|
|
|
backgroundColor: '#ffffff',
|
|
|
- // html-to-image的iframe处理
|
|
|
filter: (node) => {
|
|
|
- // 过滤掉可能引起问题的元素
|
|
|
if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') {
|
|
|
return false
|
|
|
}
|
|
@@ -1193,11 +1164,10 @@ const handleHomeworkSubmit = async () => {
|
|
|
catch (htmlToImageError) {
|
|
|
console.log('html-to-image也失败了,使用canvas绘制方案:', htmlToImageError)
|
|
|
|
|
|
- // 方案3:使用canvas直接绘制iframe占位符
|
|
|
+ // 最后的备用方案:使用canvas绘制
|
|
|
const canvas = document.createElement('canvas')
|
|
|
const ctx = canvas.getContext('2d')
|
|
|
if (ctx) {
|
|
|
- // 设置canvas尺寸
|
|
|
canvas.width = iframeElement.offsetWidth || 800
|
|
|
canvas.height = iframeElement.offsetHeight || 600
|
|
|
|
|
@@ -1241,7 +1211,7 @@ const handleHomeworkSubmit = async () => {
|
|
|
// 绘制提示信息
|
|
|
ctx.font = '12px Arial'
|
|
|
ctx.fillStyle = '#adb5bd'
|
|
|
- ctx.fillText('(跨域限制,无法截图内部内容)', canvas.width / 2, canvas.height / 2 + 80)
|
|
|
+ ctx.fillText('(截图失败)', canvas.width / 2, canvas.height / 2 + 80)
|
|
|
|
|
|
// 绘制装饰性元素
|
|
|
ctx.strokeStyle = '#dee2e6'
|
|
@@ -1257,34 +1227,6 @@ const handleHomeworkSubmit = async () => {
|
|
|
throw new Error('无法创建canvas上下文')
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 将base64字符串转换为File对象
|
|
|
- const base64ToFile = (base64String: string, filename: string): File => {
|
|
|
- const arr = base64String.split(',')
|
|
|
- const mime = arr[0].match(/:(.*?);/)?.[1] || 'image/png'
|
|
|
- const bstr = atob(arr[1])
|
|
|
- let n = bstr.length
|
|
|
- const u8arr = new Uint8Array(n)
|
|
|
- while (n--) {
|
|
|
- u8arr[n] = bstr.charCodeAt(n)
|
|
|
- }
|
|
|
- return new File([u8arr], filename, { type: mime })
|
|
|
- }
|
|
|
-
|
|
|
- const imageFile = base64ToFile(imageData, `screenshot_${Date.now()}.png`)
|
|
|
- const imageUrl = await uploadFile(imageFile)
|
|
|
- // 提交截图
|
|
|
- await submitWork(slideIndex.value, '73', imageUrl, '1') // 73表示截图工具,21表示图片类型
|
|
|
- message.success('页面截图提交成功')
|
|
|
- hasSubmitWork = true
|
|
|
-
|
|
|
- // 发送作业提交成功的socket消息
|
|
|
- sendMessage({
|
|
|
- type: 'homework_submitted',
|
|
|
- courseid: props.courseid,
|
|
|
- slideIndex: slideIndex.value,
|
|
|
- userid: props.userid
|
|
|
- })
|
|
|
}
|
|
|
}
|
|
|
catch (error) {
|