|
@@ -372,7 +372,7 @@ import useImport from '@/hooks/useImport'
|
|
|
import message from '@/utils/message'
|
|
import message from '@/utils/message'
|
|
|
import api, { API_URL } from '@/services/course'
|
|
import api, { API_URL } from '@/services/course'
|
|
|
import axios from '@/services/config'
|
|
import axios from '@/services/config'
|
|
|
-import currentVersion from '@/main'
|
|
|
|
|
|
|
+import {currentVersion, lang} from '@/main'
|
|
|
import ShotWorkModal from './components/ShotWorkModal.vue'
|
|
import ShotWorkModal from './components/ShotWorkModal.vue'
|
|
|
import QAWorkModal from './components/QAWorkModal.vue'
|
|
import QAWorkModal from './components/QAWorkModal.vue'
|
|
|
import ChoiceWorkModal from './components/ChoiceWorkModal.vue'
|
|
import ChoiceWorkModal from './components/ChoiceWorkModal.vue'
|
|
@@ -594,6 +594,7 @@ const connectionStatus = ref<'disconnected' | 'connecting' | 'connected'>('disco
|
|
|
// 认证 token 相关变量
|
|
// 认证 token 相关变量
|
|
|
const authToken = ref<string | null>(null)
|
|
const authToken = ref<string | null>(null)
|
|
|
const authTokenUpdateTimer = ref<NodeJS.Timeout | null>(null)
|
|
const authTokenUpdateTimer = ref<NodeJS.Timeout | null>(null)
|
|
|
|
|
+const socketCheckTimer = ref<NodeJS.Timeout | null>(null)
|
|
|
|
|
|
|
|
// 同步数据最大保留时间(40分钟)
|
|
// 同步数据最大保留时间(40分钟)
|
|
|
const SYNC_DATA_MAX_AGE = 40 * 60 * 1000 // 40分钟 = 40 * 60 * 1000毫秒
|
|
const SYNC_DATA_MAX_AGE = 40 * 60 * 1000 // 40分钟 = 40 * 60 * 1000毫秒
|
|
@@ -1503,16 +1504,16 @@ const processIframeLinks = async () => {
|
|
|
// 如果无法获取contentWindow,使用HTML方式
|
|
// 如果无法获取contentWindow,使用HTML方式
|
|
|
let html = null
|
|
let html = null
|
|
|
try {
|
|
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 都失败了`)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ 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) {
|
|
catch (error) {
|
|
|
console.log(`getFile 失败,尝试使用 getFile2:`, error)
|
|
console.log(`getFile 失败,尝试使用 getFile2:`, error)
|
|
@@ -1906,13 +1907,17 @@ const handleHomeworkSubmit = async () => {
|
|
|
// 直接对iframe内部的body进行截图
|
|
// 直接对iframe内部的body进行截图
|
|
|
const html2canvas = await import('html2canvas')
|
|
const html2canvas = await import('html2canvas')
|
|
|
const canvas = await html2canvas.default(iframeBody, {
|
|
const canvas = await html2canvas.default(iframeBody, {
|
|
|
- useCORS: true,
|
|
|
|
|
- allowTaint: true,
|
|
|
|
|
- scale: 1,
|
|
|
|
|
- backgroundColor: '#ffffff',
|
|
|
|
|
- logging: false,
|
|
|
|
|
- foreignObjectRendering: true,
|
|
|
|
|
- removeContainer: true
|
|
|
|
|
|
|
+ // useCORS: true,
|
|
|
|
|
+ // allowTaint: true,
|
|
|
|
|
+ // scale: 1,
|
|
|
|
|
+ // backgroundColor: '#ffffff',
|
|
|
|
|
+ // logging: false,
|
|
|
|
|
+ // foreignObjectRendering: true,
|
|
|
|
|
+ // removeContainer: true
|
|
|
|
|
+ scale: 2, // 提高清晰度
|
|
|
|
|
+ allowTaint: false, // 是否允许跨域污染画布
|
|
|
|
|
+ useCORS: true, // 尝试跨域加载图片
|
|
|
|
|
+ logging: true,
|
|
|
})
|
|
})
|
|
|
imageData = canvas.toDataURL('image/png', 0.95)
|
|
imageData = canvas.toDataURL('image/png', 0.95)
|
|
|
|
|
|
|
@@ -2083,7 +2088,7 @@ const handleHomeworkSubmit = async () => {
|
|
|
'_js.onload = function(){\n' +
|
|
'_js.onload = function(){\n' +
|
|
|
' var a = document.getElementsByTagName("img")\n' +
|
|
' var a = document.getElementsByTagName("img")\n' +
|
|
|
' for(var i = 0;i<a.length;i++){a[i].crossOrigin="anonymous"}\n' +
|
|
' for(var i = 0;i<a.length;i++){a[i].crossOrigin="anonymous"}\n' +
|
|
|
- ' html2canvas(document.body).then(canvas => {\n' +
|
|
|
|
|
|
|
+ ' html2canvas(document.body, {scale: 2,allowTaint: false,useCORS: true,logging: true,}).then(canvas => {\n' +
|
|
|
' var base64Url = canvas.toDataURL("image/png");\n' +
|
|
' var base64Url = canvas.toDataURL("image/png");\n' +
|
|
|
'var base64 = "<img src=" + base64Url + " />"\n' +
|
|
'var base64 = "<img src=" + base64Url + " />"\n' +
|
|
|
'var file = dataURLtoFile_shishi(base64Url, "截图")\n' +
|
|
'var file = dataURLtoFile_shishi(base64Url, "截图")\n' +
|
|
@@ -3278,6 +3283,7 @@ const addOp3 = async (userTime: any, loadTime: any, object: any, status: any) =>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
|
|
+
|
|
|
document.addEventListener('keydown', handleKeydown)
|
|
document.addEventListener('keydown', handleKeydown)
|
|
|
|
|
|
|
|
// 处理URL参数
|
|
// 处理URL参数
|
|
@@ -3406,11 +3412,17 @@ onUnmounted(() => {
|
|
|
authTokenUpdateTimer.value = null
|
|
authTokenUpdateTimer.value = null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (providerSocket.value) {
|
|
|
|
|
- providerSocket.value.destroy()
|
|
|
|
|
- providerSocket.value = null
|
|
|
|
|
|
|
+ // 清理 socket 连接检查定时器
|
|
|
|
|
+ if (socketCheckTimer.value) {
|
|
|
|
|
+ clearInterval(socketCheckTimer.value)
|
|
|
|
|
+ socketCheckTimer.value = null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // if (providerSocket.value) {
|
|
|
|
|
+ // providerSocket.value.destroy()
|
|
|
|
|
+ // providerSocket.value = null
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
// 清理画图延迟发送定时器
|
|
// 清理画图延迟发送定时器
|
|
|
if (drawingDelayTimer.value) {
|
|
if (drawingDelayTimer.value) {
|
|
|
clearTimeout(drawingDelayTimer.value)
|
|
clearTimeout(drawingDelayTimer.value)
|
|
@@ -3524,7 +3536,7 @@ const manualReconnect = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 创建WebSocket连接
|
|
// 创建WebSocket连接
|
|
|
-const createWebSocketConnection = async () => {
|
|
|
|
|
|
|
+const createWebSocketConnection = async (type = 1) => {
|
|
|
if (!api.yweb_socket || isConnecting.value) return
|
|
if (!api.yweb_socket || isConnecting.value) return
|
|
|
|
|
|
|
|
isConnecting.value = true
|
|
isConnecting.value = true
|
|
@@ -3532,10 +3544,10 @@ const createWebSocketConnection = async () => {
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
// 清理之前的连接
|
|
// 清理之前的连接
|
|
|
- if (providerSocket.value) {
|
|
|
|
|
- providerSocket.value.destroy()
|
|
|
|
|
- providerSocket.value = null
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // if (providerSocket.value && type == 1) {
|
|
|
|
|
+ // providerSocket.value.destroy()
|
|
|
|
|
+ // providerSocket.value = null
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
// 清理之前的 token 更新定时器
|
|
// 清理之前的 token 更新定时器
|
|
|
if (authTokenUpdateTimer.value) {
|
|
if (authTokenUpdateTimer.value) {
|
|
@@ -3639,7 +3651,7 @@ const createWebSocketConnection = async () => {
|
|
|
console.log('👉 WebSocket连接断开')
|
|
console.log('👉 WebSocket连接断开')
|
|
|
connectionStatus.value = 'disconnected'
|
|
connectionStatus.value = 'disconnected'
|
|
|
isConnecting.value = false
|
|
isConnecting.value = false
|
|
|
- handleDisconnection()
|
|
|
|
|
|
|
+ createWebSocketConnection(2)
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -3648,7 +3660,7 @@ const createWebSocketConnection = async () => {
|
|
|
console.error('👉 WebSocket连接错误:', error)
|
|
console.error('👉 WebSocket连接错误:', error)
|
|
|
connectionStatus.value = 'disconnected'
|
|
connectionStatus.value = 'disconnected'
|
|
|
isConnecting.value = false
|
|
isConnecting.value = false
|
|
|
- handleDisconnection()
|
|
|
|
|
|
|
+ createWebSocketConnection(2)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
}
|
|
}
|
|
@@ -3656,8 +3668,11 @@ const createWebSocketConnection = async () => {
|
|
|
console.error('👉 创建WebSocket连接失败:', error)
|
|
console.error('👉 创建WebSocket连接失败:', error)
|
|
|
connectionStatus.value = 'disconnected'
|
|
connectionStatus.value = 'disconnected'
|
|
|
isConnecting.value = false
|
|
isConnecting.value = false
|
|
|
- handleDisconnection()
|
|
|
|
|
|
|
+ createWebSocketConnection(2)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 启动 socket 连接检查定时器
|
|
|
|
|
+ startSocketCheckTimer()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 处理连接断开
|
|
// 处理连接断开
|
|
@@ -3668,7 +3683,7 @@ const handleDisconnection = () => {
|
|
|
|
|
|
|
|
reconnectTimer.value = setTimeout(() => {
|
|
reconnectTimer.value = setTimeout(() => {
|
|
|
createWebSocketConnection()
|
|
createWebSocketConnection()
|
|
|
- }, reconnectInterval.value)
|
|
|
|
|
|
|
+ }, reconnectInterval.value) as unknown as NodeJS.Timeout
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
console.error('👉 WebSocket重连次数已达上限,停止重连')
|
|
console.error('👉 WebSocket重连次数已达上限,停止重连')
|
|
@@ -3677,6 +3692,29 @@ const handleDisconnection = () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 启动 socket 连接检查定时器
|
|
|
|
|
+const startSocketCheckTimer = () => {
|
|
|
|
|
+ // 清理之前的定时器
|
|
|
|
|
+ if (socketCheckTimer.value) {
|
|
|
|
|
+ clearInterval(socketCheckTimer.value)
|
|
|
|
|
+ socketCheckTimer.value = null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 每10秒检查一次 socket 连接状态
|
|
|
|
|
+ socketCheckTimer.value = setInterval(() => {
|
|
|
|
|
+ if (providerSocket.value) {
|
|
|
|
|
+ // 直接检查 providerSocket 的连接状态
|
|
|
|
|
+ // WebsocketProvider 有一个 connected 属性来表示连接状态
|
|
|
|
|
+ const isConnected = (providerSocket.value as any).ws.readyState
|
|
|
|
|
+ console.log('🔍 定时器检查 socket 连接状态:', isConnected)
|
|
|
|
|
+ if (isConnected !== 1) {
|
|
|
|
|
+ console.log('🔍 定时器检查发现 socket 未连接,执行重连')
|
|
|
|
|
+ createWebSocketConnection(2)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 10000) as unknown as NodeJS.Timeout
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 工具函数:格式化时间
|
|
// 工具函数:格式化时间
|
|
|
const formatTime = (totalSec: number) => {
|
|
const formatTime = (totalSec: number) => {
|
|
|
const m = Math.floor(totalSec / 60)
|
|
const m = Math.floor(totalSec / 60)
|
|
@@ -4890,7 +4928,7 @@ const clearTimerState = () => {
|
|
|
/* 作业提交按钮样式 */
|
|
/* 作业提交按钮样式 */
|
|
|
.homework-submit-btn {
|
|
.homework-submit-btn {
|
|
|
position: fixed;
|
|
position: fixed;
|
|
|
- bottom: 60px;
|
|
|
|
|
|
|
+ bottom: 160px;
|
|
|
z-index: 100;
|
|
z-index: 100;
|
|
|
background: #191a19;
|
|
background: #191a19;
|
|
|
color: white;
|
|
color: white;
|
|
@@ -4950,7 +4988,7 @@ const clearTimerState = () => {
|
|
|
/* 刷新网页按钮样式 */
|
|
/* 刷新网页按钮样式 */
|
|
|
.refresh-page-btn {
|
|
.refresh-page-btn {
|
|
|
position: fixed;
|
|
position: fixed;
|
|
|
- bottom: 60px;
|
|
|
|
|
|
|
+ bottom: 160px;
|
|
|
z-index: 100;
|
|
z-index: 100;
|
|
|
color: #000;
|
|
color: #000;
|
|
|
padding: 5px 20px;
|
|
padding: 5px 20px;
|