|
@@ -1103,7 +1103,7 @@ const handleHomeworkSubmit = async () => {
|
|
|
const screenSlides = document.querySelectorAll('.viewer-canvas .screen-slide')
|
|
|
let iframeElement: HTMLIFrameElement | null = null
|
|
|
|
|
|
- // 直接获取iframe元素本身,而不是其内部文档
|
|
|
+ // 直接获取iframe元素本身
|
|
|
if (
|
|
|
screenSlides &&
|
|
|
screenSlides[slideIndex.value] &&
|
|
@@ -1116,7 +1116,7 @@ const handleHomeworkSubmit = async () => {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 直接对iframe元素进行截图,而不是其内部文档
|
|
|
+ // 方案1:使用html2canvas的iframe特殊处理
|
|
|
const html2canvas = await import('html2canvas')
|
|
|
const canvas = await html2canvas.default(iframeElement, {
|
|
|
useCORS: true,
|
|
@@ -1124,87 +1124,139 @@ const handleHomeworkSubmit = async () => {
|
|
|
scale: 1,
|
|
|
backgroundColor: '#ffffff',
|
|
|
logging: false,
|
|
|
- // 对iframe元素进行截图
|
|
|
+ // 关键配置:处理iframe内容
|
|
|
foreignObjectRendering: true,
|
|
|
removeContainer: true,
|
|
|
- // 添加iframe特定的配置
|
|
|
- ignoreElements: (element) => {
|
|
|
- // 忽略可能引起问题的元素
|
|
|
- return element.tagName === 'SCRIPT' || element.tagName === 'STYLE'
|
|
|
+ // 特殊处理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)
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
})
|
|
|
imageData = canvas.toDataURL('image/png', 0.95)
|
|
|
- console.log('使用html2canvas截图成功')
|
|
|
+ console.log('使用html2canvas iframe特殊处理截图成功')
|
|
|
}
|
|
|
catch (html2canvasError) {
|
|
|
- console.log('html2canvas截图失败,回退到html-to-image:', html2canvasError)
|
|
|
+ console.log('html2canvas iframe处理失败,尝试html-to-image:', html2canvasError)
|
|
|
|
|
|
try {
|
|
|
- // 回退到html-to-image
|
|
|
+ // 方案2:使用html-to-image
|
|
|
const { toPng } = await import('html-to-image')
|
|
|
imageData = await toPng(iframeElement, {
|
|
|
quality: 0.95,
|
|
|
- backgroundColor: '#ffffff'
|
|
|
+ backgroundColor: '#ffffff',
|
|
|
+ // html-to-image的iframe处理
|
|
|
+ filter: (node) => {
|
|
|
+ // 过滤掉可能引起问题的元素
|
|
|
+ if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
})
|
|
|
console.log('使用html-to-image截图成功')
|
|
|
}
|
|
|
catch (htmlToImageError) {
|
|
|
- console.log('html-to-image也失败了,尝试备用方案:', htmlToImageError)
|
|
|
+ console.log('html-to-image也失败了,使用canvas绘制方案:', htmlToImageError)
|
|
|
|
|
|
- // 最后的备用方案:使用canvas直接绘制iframe
|
|
|
+ // 方案3:使用canvas直接绘制iframe占位符
|
|
|
const canvas = document.createElement('canvas')
|
|
|
const ctx = canvas.getContext('2d')
|
|
|
if (ctx) {
|
|
|
- canvas.width = iframeElement.offsetWidth
|
|
|
- canvas.height = iframeElement.offsetHeight
|
|
|
+ // 设置canvas尺寸
|
|
|
+ canvas.width = iframeElement.offsetWidth || 800
|
|
|
+ canvas.height = iframeElement.offsetHeight || 600
|
|
|
|
|
|
- // 绘制iframe的可见区域
|
|
|
- ctx.fillStyle = '#ffffff'
|
|
|
+ // 绘制背景
|
|
|
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height)
|
|
|
+ gradient.addColorStop(0, '#f8f9fa')
|
|
|
+ gradient.addColorStop(1, '#e9ecef')
|
|
|
+ ctx.fillStyle = gradient
|
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
|
|
|
|
- // 添加一个提示文本
|
|
|
- ctx.fillStyle = '#666666'
|
|
|
- ctx.font = '16px Arial'
|
|
|
+ // 绘制边框
|
|
|
+ ctx.strokeStyle = '#dee2e6'
|
|
|
+ ctx.lineWidth = 3
|
|
|
+ ctx.strokeRect(2, 2, canvas.width - 4, canvas.height - 4)
|
|
|
+
|
|
|
+ // 绘制内边框
|
|
|
+ ctx.strokeStyle = '#ffffff'
|
|
|
+ ctx.lineWidth = 1
|
|
|
+ ctx.strokeRect(5, 5, canvas.width - 10, canvas.height - 10)
|
|
|
+
|
|
|
+ // 绘制iframe图标
|
|
|
+ ctx.fillStyle = '#6c757d'
|
|
|
+ ctx.font = 'bold 48px Arial'
|
|
|
ctx.textAlign = 'center'
|
|
|
- ctx.fillText('无法截图iframe内容', canvas.width / 2, canvas.height / 2)
|
|
|
- ctx.fillText('(跨域限制)', canvas.width / 2, canvas.height / 2 + 20)
|
|
|
+ ctx.fillText('', canvas.width / 2, canvas.height / 2 - 40)
|
|
|
+
|
|
|
+ // 绘制标题
|
|
|
+ ctx.font = 'bold 20px Arial'
|
|
|
+ ctx.fillStyle = '#495057'
|
|
|
+ ctx.fillText('iframe内容', canvas.width / 2, canvas.height / 2 + 20)
|
|
|
+
|
|
|
+ // 绘制URL
|
|
|
+ const src = iframeElement.src
|
|
|
+ if (src) {
|
|
|
+ ctx.font = '14px Arial'
|
|
|
+ ctx.fillStyle = '#6c757d'
|
|
|
+ const url = src.length > 80 ? src.substring(0, 80) + '...' : src
|
|
|
+ ctx.fillText(url, canvas.width / 2, canvas.height / 2 + 50)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 绘制提示信息
|
|
|
+ ctx.font = '12px Arial'
|
|
|
+ ctx.fillStyle = '#adb5bd'
|
|
|
+ ctx.fillText('(跨域限制,无法截图内部内容)', canvas.width / 2, canvas.height / 2 + 80)
|
|
|
+
|
|
|
+ // 绘制装饰性元素
|
|
|
+ ctx.strokeStyle = '#dee2e6'
|
|
|
+ ctx.lineWidth = 1
|
|
|
+ ctx.setLineDash([5, 5])
|
|
|
+ ctx.strokeRect(20, 20, canvas.width - 40, canvas.height - 40)
|
|
|
+ ctx.setLineDash([])
|
|
|
|
|
|
imageData = canvas.toDataURL('image/png', 0.95)
|
|
|
- console.log('使用备用方案截图成功')
|
|
|
+ console.log('使用canvas绘制方案截图成功')
|
|
|
}
|
|
|
else {
|
|
|
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) {
|
|
|
console.error('截图提交失败:', error)
|