当前版本: v${latestVersion}
${updateMessage || '建议更新以获得更好体验'}
${line}
`) .join(""); try { const ueditor = this._window.UE.getEditor(textareaElement.name); if (ueditor && typeof ueditor.setContent === "function") { ueditor.setContent(htmlContent); } else { textareaElement.value = answerText; textareaElement.dispatchEvent(new Event("input", { bubbles: true })); } } catch (e) { textareaElement.value = answerText; textareaElement.dispatchEvent(new Event("input", { bubbles: true })); } } else if (question.type === "13") { const sortSelects = question.element.querySelectorAll(".sortQuesSelect .dept_select"); if (sortSelects.length === 0) return; let answers = question.answer; if (typeof question.answer === 'string') { if (question.answer.startsWith('[')) { try { answers = JSON.parse(question.answer); } catch (e) { answers = question.answer.split(/[,,]/).map(a => a.trim()).filter(a => a); } } else { answers = question.answer.split(/[,,]/).map(a => a.trim()).filter(a => a); } } sortSelects.forEach((select, index) => { if (index >= answers.length) return; const answerValue = answers[index].toUpperCase(); select.value = answerValue; const chosenContainer = select.nextElementSibling; if (chosenContainer && chosenContainer.classList.contains("chosen-container")) { const chosenSingle = chosenContainer.querySelector(".chosen-single span"); if (chosenSingle) { chosenSingle.textContent = answerValue; } select.dispatchEvent(new Event("change", { bubbles: true })); const chosenResults = chosenContainer.querySelectorAll(".chosen-results li"); chosenResults.forEach(li => { li.classList.remove("result-selected"); if (li.textContent.trim() === answerValue) { li.classList.add("result-selected"); } }); } }); const answerInput = question.element.querySelector('input[name^="answer"]'); if (answerInput) { answerInput.value = answers.join(""); } } } catch (error) { this.addLog(`答题过程发生错误:${error.message}`, "danger"); } finally { this.isFilling = false; } }); this.type = type; if (iframe) { this._document = iframe.contentDocument; this._window = iframe.contentWindow; decodeCipherFont(this._document); } else { decodeCipherFont(this._document); } } extractOptions(optionElements, optionSelector) { const optionsObject = {}; const optionTexts = []; optionElements.forEach((optionElement) => { var _a; const optionTextContent = this.stripTags(((_a = optionElement.querySelector(optionSelector)) == null ? void 0 : _a.innerHTML) || ""); optionsObject[optionTextContent] = optionElement; optionTexts.push(optionTextContent); }); return [optionsObject, optionTexts]; } addQuestions(questionElements) { questionElements.forEach((questionElement) => { var _a, _b, _c, _d; let questionTitle = ""; let questionTypeText = ""; let optionElements; let optionsObject = {}; let optionTexts = []; if (["zy", "ks"].includes(this.type)) { const h3Element = questionElement.querySelector("h3"); const colorShallowElement = questionElement.querySelector(".colorShallow"); if (["zy"].includes(this.type)) { questionTypeText = (questionElement == null ? void 0 : questionElement.getAttribute("typename")) || ""; } else if (["ks"].includes(this.type)) { questionTypeText = colorShallowElement ? this.stripTags(colorShallowElement.outerHTML).slice(1, 4) : ""; } const fullText = h3Element ? h3Element.textContent : ""; const typeText = colorShallowElement ? colorShallowElement.textContent : ""; questionTitle = fullText.replace(typeText, "").replace(/^\d+\.\s*/, "").replace(/_+/g, "").trim(); optionElements = questionElement.querySelectorAll(SELECTORS.CX_OPTION_ZY_KS); [optionsObject, optionTexts] = this.extractOptions(optionElements, ".answer_p"); } else if (["zj"].includes(this.type)) { questionTitle = this.stripTags(((_c = questionElement.querySelector(".fontLabel")) == null ? void 0 : _c.innerHTML) || ""); questionTypeText = this.stripTags(((_d = questionElement.querySelector(".newZy_TItle")) == null ? void 0 : _d.innerHTML) || ""); optionElements = questionElement.querySelectorAll(SELECTORS.CX_OPTION_ZJ); [optionsObject, optionTexts] = this.extractOptions(optionElements, ".fl.after"); } this.questions.push({ element: questionElement, type: this.typeMap.get(questionTypeText.replace(/[\[\]【】]/g, "")) || (questionTitle.match(/[\[\【](.+?)[\]\】]/)?.[1] && this.typeMap.get(questionTitle.match(/[\[\【](.+?)[\]\】]/)[1])) || "999", title: this.trimTitle(questionTitle), optionsText: optionTexts, options: optionsObject, answer: [], workType: this.type, refer: this._window.location.href }); }); } } const useCxChapterLogic = () => { const logStore = useLogStore(); const progressStore = useProgressStore(); const getCookieLocal = (name) => { const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); return match ? match[2] : ''; }; let cachedUid = ''; if (_unsafeWindow?.getCookie) { cachedUid = _unsafeWindow.getCookie("UID"); } if (!cachedUid) { cachedUid = getCookieLocal('UID'); } if (!cachedUid) { cachedUid = getCookieLocal('_uid'); } if (!cachedUid && _unsafeWindow?.uid) { cachedUid = _unsafeWindow.uid; } const init = () => { const currentUrl = window.location.href; if (!currentUrl.includes("&mooc2=1")) { window.location.href = currentUrl + "&mooc2=1"; } logStore.addLog(`检测到用户进入到章节学习页面`, "primary"); logStore.addLog(`正在解析任务点,请稍等5-10秒(如果长时间没有反应,请刷新页面)`, "warning"); }; const configStore = useConfigStore(); let _justClickedNext = false; let _currentIframe = null; let _urlBackupTimer = null; const monitorIframes = () => { if (_urlBackupTimer) { clearTimeout(_urlBackupTimer); _urlBackupTimer = null; } const documentElement = document.documentElement; const iframe = documentElement.querySelector("iframe"); if (!iframe) { setTimeout(() => monitorIframes(), 2000); return; } const currentSrc = iframe.src || ''; const srcChanged = currentSrc !== _lastIframeSrc; if (iframe !== _currentIframe) { _currentIframe = iframe; iframe.addEventListener("load", function onIframeLoad() { if (_urlBackupTimer) { clearTimeout(_urlBackupTimer); _urlBackupTimer = null; } monitorIframes(); }); } try { if (iframe.contentDocument && iframe.contentDocument.readyState === 'complete') { if (srcChanged) { _lastIframeSrc = currentSrc; watchIframe(documentElement); } } } catch (e) { } }; const watchUrlChanges = () => { let currentUrl = window.location.href; const afkEnabled = configStore.platformParams.cx.parts[4]?.params[0]?.value || false; const checkUrlChange = () => { if (currentUrl !== window.location.href) { currentUrl = window.location.href; _justClickedNext = false; _globalTaskId++; if (_urlBackupTimer) { clearTimeout(_urlBackupTimer); } _urlBackupTimer = setTimeout(() => { _urlBackupTimer = null; monitorIframes(); }, 2000); } }; if (afkEnabled) { BackgroundWorker.start('url_watcher', checkUrlChange, 2000); } else { setInterval(checkUrlChange, 2000); } }; let _globalTaskId = 0; let hasLoggedSkipTip = false; let _currentWatchSubscription = null; const _processedIframeTasks = new WeakMap(); let _lastIframeSrc = null; window.__getAnswerTaskId__ = () => _globalTaskId; const watchIframe = async (documentElement) => { if (_currentWatchSubscription) { _currentWatchSubscription.unsubscribe(); _currentWatchSubscription = null; } forceStopSimulatePlay(); const thisTaskId = ++_globalTaskId; hasLoggedSkipTip = false; await new Promise(resolve => setTimeout(resolve, 3000)); if (thisTaskId !== _globalTaskId) { return; } logStore.addLog(`等待页面加载完成,开始扫描任务点`, "info"); FrameScanner.collectDeep(documentElement).subscribe((allIframes) => { _currentWatchSubscription = rxjs.from(allIframes).pipe(concatMap((iframe) => handleSingleFrame(iframe))).subscribe({ complete: async () => { if (thisTaskId === _globalTaskId) { const autoSwitch = configStore.platformParams.cx.parts[2].params[1].value; logStore.addLog(`本页任务点已全部完成,${autoSwitch ? "正前往下一章节" : "自动切换已关闭"}`, "success"); if (autoSwitch) { const nextBtn1 = documentElement.querySelector("#prevNextFocusNext"); const nextBtn2 = document.querySelector(".jb_btn.jb_btn_92.fr.fs14.nextChapter"); const nextBtn3 = document.querySelector("#nextBtn"); const nextBtn4 = document.querySelector(".nextChapter"); let targetBtn = null; if (nextBtn1 && nextBtn1.style.display !== "none") { targetBtn = nextBtn2 || nextBtn1; } else if (nextBtn2 && nextBtn2.style.display !== "none") { targetBtn = nextBtn2; } else if (nextBtn3 && nextBtn3.style.display !== "none") { targetBtn = nextBtn3; } else if (nextBtn4 && nextBtn4.style.display !== "none") { targetBtn = nextBtn4; } if (!targetBtn) { } else { _justClickedNext = true; await delay(3); if (thisTaskId !== _globalTaskId) { return; } targetBtn.click(); } } } } }); }); }; const calculateEnc = (classId, uid, jobId, objectId, playTime, duration) => { const str = `[${classId}][${uid}][${jobId}][${objectId}][${playTime * 1000}][d_yHJ!$pdA~5][${duration * 1000}][0_${duration}]`; return md5(str); }; const formatDuration = (seconds) => { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; const hasVideoOrAudio = () => { try { const allIframes = FrameScanner.collectDeepSync(document.documentElement); for (const iframe of allIframes) { const src = iframe.src || ''; if (src.includes('video') || src.includes('audio')) { const parent = iframe.parentElement; const ansJobIcon = parent?.querySelector('.ans-job-icon'); if (ansJobIcon) { return true; } } } return false; } catch (e) { console.log('[检测视频] 检测失败:', e); return false; } }; const completedSimulatedIds = new Set(); const processingSimulatedId = { current: null }; const clearOverlayAndBanner = () => { if (window._blockPlayInterval) { clearInterval(window._blockPlayInterval); window._blockPlayInterval = null; } if (window._blockOverlay) { window._blockOverlay.forEach(o => { try { if (document.contains(o)) o.remove(); } catch(e) {} }); window._blockOverlay = []; } const banner = document.getElementById('_simulate_banner_'); if (banner) banner.remove(); if (window._blockPlayScroll) { window.removeEventListener('scroll', window._blockPlayScroll, true); window.removeEventListener('resize', window._blockPlayScroll); window._blockPlayScroll = null; } }; const showOverlayAndBanner = () => { clearOverlayAndBanner(); if (!window._blockOverlay) window._blockOverlay = []; const getVideoIframes = () => { try { const allIframes = FrameScanner.collectDeepSync(document.documentElement); return allIframes.filter(fr => { const src = fr.src || ''; return src.includes('video') || src.includes('audio'); }); } catch (e) { return []; } }; const createOverlay = () => { try { window._blockOverlay.forEach(o => { try { o.remove(); } catch(e) {} }); window._blockOverlay = []; const videoIframes = getVideoIframes(); for (const iframe of videoIframes) { const rect = iframe.getBoundingClientRect(); if (rect.width > 50 && rect.height > 50) { const overlay = document.createElement('div'); overlay.className = '_simulate_block_overlay_'; overlay.style.cssText = `position:fixed;top:${rect.top}px;left:${rect.left}px;width:${rect.width}px;height:${rect.height}px;z-index:2147483647;cursor:not-allowed;background:rgba(0,0,0,0.25);border-radius:8px;`; const ownerDoc = iframe.ownerDocument; if (ownerDoc && ownerDoc.body && ownerDoc.body !== document.body) { ownerDoc.body.appendChild(overlay); } else { document.body.appendChild(overlay); } window._blockOverlay.push(overlay); } } } catch (e) {} }; const updateOverlayPositions = () => {}; createOverlay(); window._blockPlayScroll = updateOverlayPositions; window.addEventListener('scroll', updateOverlayPositions, true); window.addEventListener('resize', updateOverlayPositions); window._blockPlayInterval = setInterval(createOverlay, 1000); if (!document.getElementById('_simulate_banner_')) { const banner = document.createElement('div'); banner.id = '_simulate_banner_'; banner.style.cssText = 'position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:100002;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;padding:12px 28px;border-radius:30px;font-size:15px;font-weight:600;text-align:center;box-shadow:0 4px 20px rgba(102,126,234,0.4);cursor:not-allowed;white-space:nowrap;pointer-events:none;'; banner.innerHTML = '🎬 模拟播放模式下无需播放视频,播放进度可在主页信息查看'; document.body.appendChild(banner); } }; const stopSimulatePlayIfNeeded = () => { if (window._simulateActive) { const hasMedia = hasVideoOrAudio(); if (!hasMedia) { window._simulateActive = false; logStore.addLog('检测到视频/音频已消失,停止模拟播放', 'warning'); if (window._currentMediaInterval) { clearInterval(window._currentMediaInterval); window._currentMediaInterval = null; } if (window._simulateLoopId) { BackgroundWorker.stop(window._simulateLoopId); window._simulateLoopId = null; } progressStore.update({ isPlaying: false }); processingSimulatedId.current = null; simulateVideoPlay._currentObjectId = null; clearOverlayAndBanner(); } } }; const forceStopSimulatePlay = () => { if (window._simulateActive) { window._simulateActive = false; if (window._currentMediaInterval) { clearInterval(window._currentMediaInterval); window._currentMediaInterval = null; } if (window._simulateLoopId) { BackgroundWorker.stop(window._simulateLoopId); window._simulateLoopId = null; } progressStore.update({ isPlaying: false }); processingSimulatedId.current = null; simulateVideoPlay._currentObjectId = null; clearOverlayAndBanner(); } }; const getVideoInfo = async (objectId) => { return new Promise((resolve, reject) => { const host = window.location.host; const protocol = window.location.protocol; const FID = _unsafeWindow.FID || ''; const statusUrl = `${protocol}//${host}/ananas/status/${objectId}?k=${FID}&flag=normal&_dc=${Date.now()}`; let vrefer = ''; try { const videoIframe = _unsafeWindow.document.querySelector('.ans-attach-online.ans-insertvideo-online'); if (videoIframe) { vrefer = videoIframe.src; } } catch (e) {} if (!vrefer) { vrefer = `${protocol}//${host}/ananas/modules/video/index.html?v=2022-1118-1729`; } _GM_xmlhttpRequest({ method: "get", url: statusUrl, headers: { 'Host': host, 'Referer': vrefer, 'Sec-Fetch-Site': 'same-origin' }, onload: function(res) { try { if (res.status === 200) { const data = JSON.parse(res.responseText); resolve(data); } else { reject(new Error(`HTTP ${res.status}`)); } } catch (e) { reject(e); } }, onerror: function(err) { reject(err); } }); }); }; const getTaskParams = () => { try { const topWindow = _unsafeWindow.top; if (topWindow.margs) { return topWindow.margs; } const urlParams = new URLSearchParams(window.location.search); return { clazzId: urlParams.get('clazzId') || urlParams.get('classId'), courseId: urlParams.get('courseId'), knowledgeid: urlParams.get('knowledgeid') || urlParams.get('chapterId') }; } catch (e) { console.error('[模拟播放] 获取任务参数失败:', e); return null; } }; const getEvasionRate = (baseRate) => { const evasionRate = baseRate * (0.95 + Math.random() * 0.1); return Math.max(0.5, Math.min(baseRate * 1.05, evasionRate)); }; let smartSpeedState = { currentSpeed: 0, lastChangeTime: 0, changeInterval: 30000 }; const getSmartSpeed = (baseRate) => { const now = Date.now(); if (now - smartSpeedState.lastChangeTime > smartSpeedState.changeInterval) { const variation = (Math.random() - 0.5) * baseRate * 0.1; smartSpeedState.currentSpeed = Math.max(0.8, Math.min(baseRate * 1.05, baseRate + variation)); smartSpeedState.lastChangeTime = now; } return smartSpeedState.currentSpeed; }; let behaviorState = { lastMouseMove: 0 }; const simulateUserBehavior = () => { const now = Date.now(); if (now - behaviorState.lastMouseMove > 10000 + Math.random() * 20000) { const x = Math.random() * window.innerWidth; const y = Math.random() * window.innerHeight; document.dispatchEvent(new MouseEvent('mousemove', { clientX: x, clientY: y, bubbles: true })); behaviorState.lastMouseMove = now; } }; const compensateDuration = (playTime, duration, baseRate) => playTime; const BackgroundWorker = (() => { let worker = null; const callbacks = new Map(); const workerCode = ` self.onmessage = function(e) { if (e.data.type === 'start') { const id = e.data.id; const interval = e.data.interval || 1000; let timerId = setInterval(() => { self.postMessage({ type: 'tick', id: id }); }, interval); self._timers = self._timers || {}; self._timers[id] = timerId; self.postMessage({ type: 'started', id: id }); } else if (e.data.type === 'stop') { const id = e.data.id; if (self._timers && self._timers[id]) { clearInterval(self._timers[id]); delete self._timers[id]; } self.postMessage({ type: 'stopped', id: id }); } }; `; function ensure() { if (worker) return; const blob = new Blob([workerCode], { type: 'application/javascript' }); worker = new Worker(URL.createObjectURL(blob)); worker.onmessage = (e) => { const { type, id } = e.data; if (type === 'tick' && callbacks.has(id)) { callbacks.get(id)(); } }; worker.onerror = (e) => { console.error('[BackgroundWorker] Error:', e.message); }; } return { start(id, callback, interval = 1000) { ensure(); callbacks.set(id, callback); worker.postMessage({ type: 'start', id, interval }); }, stop(id) { if (!worker) return; callbacks.delete(id); worker.postMessage({ type: 'stop', id }); }, isActive() { return worker !== null; }, destroy() { if (worker) { worker.terminate(); worker = null; callbacks.clear(); } } }; })(); const simulateVideoPlay = async (iframe, iframeDocument, mediaType) => { return new Promise(async (resolve) => { const releaseLock = () => { if (processingSimulatedId.current === (simulateVideoPlay._currentObjectId)) { processingSimulatedId.current = null; simulateVideoPlay._currentObjectId = null; } }; const safeResolve = (val) => { releaseLock(); resolve(val); }; logStore.addLog(`发现一个${mediaType},当前播放模式: 模拟播放`, "primary"); try { const mediaElement = iframeDocument?.documentElement?.querySelector(mediaType); if (mediaElement) { mediaElement.pause(); mediaElement.muted = true; mediaElement.autoplay = false; } } catch (e) { console.log('[模拟播放] 暂停视频失败:', e); } try { let objectId = null; let jobId = null; let otherInfo = ''; let reportUrl = ''; let classId = null; let uid = null; let duration = null; let dtoken = null; let iframeSrc = iframe.src || ''; let videoName = mediaType === 'video' ? '视频' : '音频'; try { let prevTitleElement = null; let parent = iframe.parentElement; while (parent && !prevTitleElement) { prevTitleElement = parent.querySelector('.prev_title'); parent = parent.parentElement; if (parent === document.body || parent === document.documentElement) break; } if (!prevTitleElement) { prevTitleElement = document.querySelector('.prev_title'); } if (prevTitleElement) { const titleText = prevTitleElement.innerText || prevTitleElement.textContent || ''; videoName = titleText.replace(/【上】|【下】|【上$|【下$/g, '').trim(); if (videoName && videoName !== (mediaType === 'video' ? '视频' : '音频')) { logStore.addLog(`获取到视频名称: ${videoName}`, "primary"); } else { logStore.addLog(`从.prev_title获取到的文本: "${titleText}"`, "warning"); } } else { logStore.addLog(`未找到.prev_title元素`, "warning"); } } catch (e) { logStore.addLog(`从标题获取失败: ${e.message}`, "danger"); } const getStr = (str, start, end) => { const startIndex = str.indexOf(start); if (startIndex === -1) return null; const content = str.substring(startIndex + start.length); const endIndex = content.indexOf(end); if (endIndex === -1) return null; return content.substring(0, endIndex); }; let pageData = null; try { if (_unsafeWindow.param) { try { const parsed = JSON.parse(_unsafeWindow.param); if (parsed?.attachments) pageData = parsed; } catch (e) {} } if (!pageData && _unsafeWindow.mArg?.attachments) { pageData = _unsafeWindow.mArg; } } catch (e) {} if (!pageData) { try { const allIframes = FrameScanner.collectDeepSync(document.documentElement); for (const ifr of allIframes) { try { if (!ifr.contentWindow) continue; const win = ifr.contentWindow; if (win.param) { try { const parsed = JSON.parse(win.param); if (parsed?.attachments) { pageData = parsed; break; } } catch (e) {} } if (!pageData && win.mArg?.attachments) { pageData = win.mArg; break; } } catch (e) {} } } catch (e) {} } const documentsToTry = []; if (iframeDocument) { documentsToTry.push({ name: 'iframeDocument', doc: iframeDocument }); } documentsToTry.push({ name: 'currentWindow', doc: _unsafeWindow.document }); try { if (_unsafeWindow.top && _unsafeWindow.top.document) { documentsToTry.push({ name: 'topWindow', doc: _unsafeWindow.top.document }); } } catch (e) {} for (const { name, doc } of documentsToTry) { if (pageData) break; try { const scripts = doc.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { const scriptContent = scripts[i].innerHTML; if (scriptContent.indexOf('mArg = "";') !== -1 && scriptContent.indexOf('==UserScript==') === -1) { const param = getStr(scriptContent, 'try{\n mArg = ', ';\n}catch(e){'); if (param) { pageData = JSON.parse(param); break; } } if (!pageData && scriptContent.indexOf('mArg=') !== -1 && scriptContent.indexOf('==UserScript==') === -1) { const match = scriptContent.match(/mArg\s*=\s*(\{[\s\S]*?\});/); if (match) { try { pageData = JSON.parse(match[1]); break; } catch (e) {} } } } if (!pageData && name === 'topWindow') { for (let i = 0; i < scripts.length; i++) { const content = scripts[i].innerHTML; if (content.indexOf('clazzId') !== -1 && content.length > 10000) { const attachmentsMatch = content.match(/["']attachments["']\s*:\s*(\[[\s\S]*?\])/); if (attachmentsMatch) { try { const jsonStr = `{"attachments": ${attachmentsMatch[1]}}`; const parsed = JSON.parse(jsonStr); if (parsed.attachments && parsed.attachments.length > 0) { pageData = { attachments: parsed.attachments }; break; } } catch (e) { logStore.addLog(`[${name}] 解析attachments失败: ${e.message}`, "warning"); } } const clazzIdMatch = content.match(/stu_clazzId\s*=\s*["'](\d+)["']/); const courseIdMatch = content.match(/stu_CourseId\s*=\s*["'](\d+)["']/); const knowledgeIdMatch = content.match(/stu_knowledgeId\s*=\s*["'](\d+)["']/); if (clazzIdMatch || courseIdMatch) { if (!pageData) pageData = {}; pageData.defaults = { clazzId: clazzIdMatch ? clazzIdMatch[1] : null, courseId: courseIdMatch ? courseIdMatch[1] : null, knowledgeId: knowledgeIdMatch ? knowledgeIdMatch[1] : null }; } } if (!pageData && content.indexOf('"attachments"') !== -1) { const jsonMatch = content.match(/\{[\s\S]*?"attachments"[\s\S]*?\}/); if (jsonMatch) { try { pageData = JSON.parse(jsonMatch[0]); break; } catch (e) {} } } } } } catch (e) { logStore.addLog(`[${name}] 获取失败: ${e.message}`, "warning"); } } if (!pageData || (pageData.defaults && !pageData.defaults.reportUrl)) { try { const windowsToTry = [_unsafeWindow]; try { if (_unsafeWindow.top) windowsToTry.push(_unsafeWindow.top); } catch (e) {} try { if (_unsafeWindow.parent) windowsToTry.push(_unsafeWindow.parent); } catch (e) {} for (const ifr of FrameScanner.collectDeepSync(document.documentElement)) { try { if (ifr.contentWindow) windowsToTry.push(ifr.contentWindow); } catch (e) {} } for (const win of windowsToTry) { const props = ['margs', 'mArg', 'pageData', 'courseData']; for (const prop of props) { try { const val = win[prop]; if (val && typeof val === 'object') { if (val.attachments) { pageData = val; logStore.addLog(`从window.${prop}获取完整数据成功(含attachments)`, "success"); break; } if (!pageData && val.defaults && val.defaults.reportUrl) { pageData = val; logStore.addLog(`从window.${prop}获取数据成功(含reportUrl)`, "success"); break; } } } catch (e) {} } if (pageData && pageData.defaults && pageData.defaults.reportUrl) break; } } catch (e) { logStore.addLog(`获取window数据失败: ${e.message}`, "warning"); } } if (!pageData) { logStore.addLog(`无法获取mArg数据,回退到普通播放模式`, "warning"); } if (pageData) { if (pageData.defaults) { classId = pageData.defaults.clazzId; uid = pageData.defaults.userid || getUid(); reportUrl = pageData.defaults.reportUrl || ''; } if (pageData.attachments && pageData.attachments.length > 0) { const iframeSrc = iframe.src; let srcObjectId = null; const srcMatch = iframeSrc.match(REGEX.OBJECT_ID); if (srcMatch) { srcObjectId = srcMatch[1]; } if (!srcObjectId) { srcObjectId = iframe.getAttribute('objectid') || iframe.getAttribute('data-objectid') || null; } for (const attachment of pageData.attachments) { if (attachment.isPassed === true) continue; if (completedSimulatedIds.has(attachment.property?.objectid)) continue; const moduleType = attachment.property?.module || ''; const isVideo = moduleType === 'video' || moduleType === 'insertvideo' || attachment.type === 'video'; const isAudio = moduleType === 'audio' || moduleType === 'insertaudio' || attachment.type === 'audio'; if ((mediaType === 'video' && isVideo) || (mediaType === 'audio' && isAudio)) { if (srcObjectId && attachment.property?.objectid === srcObjectId) { objectId = attachment.property.objectid; jobId = attachment.jobid; otherInfo = attachment.otherInfo || ''; videoName = attachment.property?.name || videoName; break; } if (!objectId && attachment.job === true) { objectId = attachment.property?.objectid; jobId = attachment.jobid; otherInfo = attachment.otherInfo || ''; videoName = attachment.property?.name || videoName; } } } if (!objectId) { for (const attachment of pageData.attachments) { if (attachment.isPassed === true) continue; if (completedSimulatedIds.has(attachment.property?.objectid)) continue; const moduleType = attachment.property?.module || ''; const isVideo = moduleType === 'video' || moduleType === 'insertvideo' || attachment.type === 'video'; const isAudio = moduleType === 'audio' || moduleType === 'insertaudio' || attachment.type === 'audio'; if ((mediaType === 'video' && isVideo) || (mediaType === 'audio' && isAudio)) { objectId = attachment.property?.objectid; jobId = attachment.jobid; otherInfo = attachment.otherInfo || ''; videoName = attachment.property?.name || videoName; break; } } } } } if (!objectId) { const iframeSrc = iframe.src || ''; const objectIdMatch = iframeSrc.match(REGEX.OBJECT_ID); objectId = objectIdMatch ? objectIdMatch[1] : null; if (!objectId) { const dataAttrs = ['data-objectid', 'data-object-id', 'objectid']; for (const attr of dataAttrs) { const val = iframe.getAttribute(attr); if (val) { objectId = val; break; } } } if (!objectId) { const nameOrId = iframe.name || iframe.id || ''; const nameMatch = nameOrId.match(/([a-f0-9]{24,})/i); if (nameMatch) { objectId = nameMatch[1]; } } } if (!objectId) { logStore.addLog(`无法获取objectId,回退到普通播放模式`, "danger"); return safeResolve(await playMediaDirectly(mediaType, iframeDocument)); } if (processingSimulatedId.current) { logStore.addLog(`该视频正在模拟播放中,跳过重复处理`, "warning"); return safeResolve(); } processingSimulatedId.current = objectId; simulateVideoPlay._currentObjectId = objectId; if (!dtoken || !duration) { const videoInfo = await getVideoInfo(objectId); duration = videoInfo.duration; dtoken = videoInfo.dtoken; } if (!duration || !dtoken) { logStore.addLog(`获取视频信息失败,回退到普通播放模式`, "danger"); return safeResolve(await playMediaDirectly(mediaType, iframeDocument)); } logStore.addLog(`视频时长: ${formatDuration(duration)}秒`, "primary"); if (!uid) { uid = cachedUid; } if (!classId) { const taskParams = getTaskParams(); classId = taskParams?.clazzId || taskParams?.classId; } if (!classId) { const pageUrl = window.location.href; const classIdMatch = pageUrl.match(/clazzId=(\d+)/) || pageUrl.match(/classId=(\d+)/); classId = classIdMatch ? classIdMatch[1] : null; } if (!jobId) { const jobIdAttrs = ['data-jobid', 'data-job-id', 'jobid', 'data-workid']; for (const attr of jobIdAttrs) { const val = iframe.getAttribute(attr); if (val) { jobId = val; break; } } if (!jobId) { const urlMatch = window.location.href.match(/[?&]jobid=([^&]+)/i); jobId = urlMatch ? urlMatch[1] : null; } if (!jobId && iframe.src) { const srcMatch = iframe.src.match(REGEX.JOB_ID); jobId = srcMatch ? srcMatch[1] : null; } if (!jobId) { jobId = objectId; } } if (!classId || !uid || !jobId) { logStore.addLog(`缺少必要参数: classId=${classId}, uid=${uid}, jobId=${jobId}`, "danger"); logStore.addLog(`回退到普通播放模式`, "warning"); return safeResolve(await playMediaDirectly(mediaType, iframeDocument)); } const autoMaxRate = configStore.platformParams.cx.parts[0].params[5].value || false; let playbackRate = configStore.platformParams.cx.parts[0].params[6].value || 1; const maxRate = getMaxPlaybackRate(iframeDocument, false); const speedDisabled = maxRate === 1; window.__maxPlaybackRate = maxRate; const playbackRateParam = configStore.platformParams.cx.parts[0].params[6]; playbackRateParam.max = autoMaxRate ? maxRate : 3; if (speedDisabled) { logStore.addLog(`⚠️ 此视频已被学习通禁用倍速,使用>1x倍速可能会导致学习进度被清空`, "warning"); } if (autoMaxRate) { playbackRate = maxRate; logStore.addLog(`自动倍速: ${playbackRate}x`, "success"); } else { const simulateMaxRate = 3; if (playbackRate > simulateMaxRate) { playbackRate = simulateMaxRate; logStore.addLog(`模拟播放倍速已调整为最大值: ${playbackRate}x`, "warning"); } } logStore.addLog(`播放倍速: ${playbackRate}x`, "primary"); const directComplete = configStore.platformParams.cx.parts[0].params[3].value || false; const host = window.location.host; const protocol = window.location.protocol; const videojs_id = String(parseInt(Math.random() * 9999999)); document.cookie = 'videojs_id=' + videojs_id + ';path=/'; const evasionEnabled = configStore.platformParams.cx.parts[0].params[1].value || false; const reportProgress = async (currentTime, isComplete) => { return new Promise((resolveReport) => { const enc = calculateEnc(classId, uid, jobId, objectId, currentTime, duration); if (enc.length !== 32) { logStore.addLog(`加密字符串计算失败`, "danger"); return resolveReport(false); } const currentIsdrag = isComplete ? '4' : (currentTime > 0 ? '0' : '3'); const baseUrl = reportUrl.startsWith('http') ? reportUrl : `${protocol}//${host}${reportUrl}`; const reportsUrl = `${baseUrl}/${dtoken}?clazzId=${classId}&playingTime=${currentTime}&duration=${duration}&clipTime=0_${duration}&objectId=${objectId}&otherInfo=${otherInfo}&jobid=${jobId}&userid=${uid}&isdrag=${currentIsdrag}&view=pc&enc=${enc}&rt=0.9&dtype=${mediaType === 'video' ? 'Video' : 'Audio'}&_t=${Date.now()}`; _GM_xmlhttpRequest({ method: "get", url: reportsUrl, headers: { 'Host': host, 'Referer': iframeSrc, 'Sec-Fetch-Site': 'same-origin', 'Content-Type': 'application/json' }, onload: function(res) { try { const result = JSON.parse(res.responseText); if (result.isPassed) { logStore.addLog(`✅ 视频任务已完成`, "success"); resolveReport(true); } else { resolveReport(false); } } catch (e) { logStore.addLog(`上报响应解析失败(status=${res.status}): ${res.responseText.substring(0, 200)}`, "danger"); resolveReport(false); } }, onerror: function(err) { logStore.addLog(`上报请求失败`, "danger"); resolveReport(false); } }); }); }; if (!reportUrl) { logStore.addLog(`⚠️ reportUrl为空,将使用默认路径/multimedia/v2`, "warning"); reportUrl = '/multimedia/v2'; } if (directComplete) { logStore.addLog(`⚠️ 直接上报中...`, "warning"); progressStore.update({ taskName: `[${mediaType === 'video' ? '视频' : '音频'}] ${videoName}`, percent: 100, currentTime: duration, totalTime: duration, type: mediaType === 'video' ? '视频' : '音频', detail: '直接上报', isPlaying: true, speedDisabled: speedDisabled }); const isComplete = await reportProgress(duration, true); if (isComplete) { completedSimulatedIds.add(objectId); logStore.addLog(`🎬 ${mediaType}直接上报`, "success"); progressStore.update({ percent: 100, currentTime: duration, detail: '已完成', isPlaying: false }); return safeResolve(); } else { logStore.addLog(`直接上报失败,回退到模拟播放`, "danger"); } } if (window._currentMediaInterval) { clearInterval(window._currentMediaInterval); window._currentMediaInterval = null; } progressStore.update({ taskName: `[${mediaType === 'video' ? '视频' : '音频'}] ${videoName}`, percent: 0, currentTime: 0, totalTime: duration, type: mediaType === 'video' ? '视频' : '音频', detail: `${formatDuration(0)}/${formatDuration(duration)}`, isPlaying: true, speedDisabled: speedDisabled }); let playTime = 0; let playsTime = 0; let isFirst = true; let nextReportTime = 0; let isdrag = '3'; let completeRetryCount = 0; const maxCompleteRetries = 10; const reportInterval = 50; let isReporting = false; const afkEnabled = configStore.platformParams.cx.parts[4]?.params[0]?.value || false; const simulateLoopId = 'simulate_' + Date.now(); let loopInterval = null; const loopCallback = async () => { if (isReporting) return; if (!window._simulateActive) { logStore.addLog('模拟播放已被停止,结束处理', 'warning'); if (afkEnabled) { BackgroundWorker.stop(simulateLoopId); } else { clearInterval(loopInterval); } window._currentMediaInterval = null; progressStore.update({ isPlaying: false }); releaseLock(); safeResolve(); return; } const hasMedia = hasVideoOrAudio(); if (!hasMedia) { window._simulateActive = false; logStore.addLog('检测到视频/音频已消失,停止模拟播放(未完成)', 'warning'); if (afkEnabled) { BackgroundWorker.stop(simulateLoopId); } else { clearInterval(loopInterval); } window._currentMediaInterval = null; progressStore.update({ isPlaying: false }); clearOverlayAndBanner(); releaseLock(); safeResolve(); return; } const autoMaxRateNow = configStore.platformParams.cx.parts[0].params[5].value || false; let playbackRateNow = autoMaxRateNow ? maxRate : Math.min(playbackRateParam.value || playbackRate, 3); let effectiveRate = playbackRateNow; if (evasionEnabled) { effectiveRate = getEvasionRate(effectiveRate); effectiveRate = getSmartSpeed(effectiveRate); simulateUserBehavior(); } playsTime += effectiveRate; playTime = Math.ceil(playsTime); playTime = compensateDuration(playTime, duration, playbackRateNow); if (playTime > duration) { playTime = duration; } const progress = Math.floor((playTime / duration) * 100); progressStore.update({ percent: progress, currentTime: playTime, totalTime: duration, detail: `${formatDuration(playTime)}/${formatDuration(duration)}`, isPlaying: true, speedDisabled: speedDisabled }); let shouldReport = false; if (playTime >= duration) { shouldReport = true; } else if (isFirst) { shouldReport = true; } else if (nextReportTime > 0 && playTime >= nextReportTime) { shouldReport = true; } if (shouldReport) { isReporting = true; if (isFirst) { playTime = 0; isFirst = false; } if (playTime >= duration) { playTime = duration; isdrag = '4'; } else if (playTime > 0) { isdrag = '0'; } nextReportTime = Math.min(playTime + reportInterval, duration); logStore.addLog(`📤 上报进度: ${Math.round(playTime / duration * 100)}%(${Math.round(playTime)}/${Math.round(duration)}s)`); const isComplete = await reportProgress(playTime, isdrag === '4'); isReporting = false; if (isComplete) { if (afkEnabled) { BackgroundWorker.stop(simulateLoopId); } else { clearInterval(loopInterval); } window._simulateActive = false; completedSimulatedIds.add(objectId); logStore.addLog(`🎬 ${mediaType}模拟播放完成`, "success"); progressStore.update({ percent: 100, currentTime: duration, detail: '播放完成', isPlaying: false }); window._currentMediaInterval = null; safeResolve(); } else if (isdrag === '4') { completeRetryCount++; if (completeRetryCount >= maxCompleteRetries) { if (afkEnabled) { BackgroundWorker.stop(simulateLoopId); } else { clearInterval(loopInterval); } window._simulateActive = false; logStore.addLog(`完成上报重试${maxCompleteRetries}次仍未通过,请检查视频是否需要其他操作`, "danger"); progressStore.update({ percent: 100, currentTime: duration, detail: '上报未通过', isPlaying: false }); window._currentMediaInterval = null; safeResolve(); } else { logStore.addLog(`完成上报未通过,重试中(${completeRetryCount}/${maxCompleteRetries})...`, "warning"); } } } }; if (afkEnabled) { window._simulateLoopId = simulateLoopId; window._simulateActive = true; showOverlayAndBanner(); BackgroundWorker.start(simulateLoopId, loopCallback, 1000); logStore.addLog(`🖥️ 挂机模式已启用,使用后台Worker计时`, "success"); } else { loopInterval = setInterval(loopCallback, 1000); window._currentMediaInterval = loopInterval; window._simulateActive = true; showOverlayAndBanner(); } } catch (e) { window._currentMediaInterval = null; window._simulateActive = false; logStore.addLog(`模拟播放出错: ${e.message}`, "danger"); logStore.addLog(`回退到普通播放模式`, "warning"); safeResolve(await playMediaDirectly(mediaType, iframeDocument)); } }); }; const playMediaDirectly = async (mediaType, iframeDocument) => { return new Promise((resolve) => { logStore.addLog(`正在尝试播放${mediaType},请稍等5s`, "primary"); const autoMaxRate = configStore.platformParams.cx.parts[0].params[5].value || false; const playbackRateParam = configStore.platformParams.cx.parts[0].params[6]; let playbackRate = playbackRateParam.value || 1; const maxRate = getMaxPlaybackRate(iframeDocument); const videoQuizEnabled = configStore.platformParams.cx.parts[0].params[4]?.value || false; if (videoQuizEnabled) { const loop = async () => { try { const submitBtn = iframeDocument?.querySelector("#videoquiz-submit"); if (submitBtn) { const list = Array.from(iframeDocument.querySelectorAll(".ans-videoquiz-opt label")); if (list.length > 0) { const answer = list[Math.floor(Math.random() * list.length)]; answer?.click(); submitBtn?.click(); await delay(3); const container = iframeDocument.querySelector("#video .ans-videoquiz"); if (container) { container.remove(); } const components = Array.from(iframeDocument.querySelectorAll(".x-component-default")); if (components.length) { for (const com of components) { com.style.display = "none"; } } logStore.addLog("已处理视频内题目", "success"); } } } catch (e) { console.log("处理视频内题目失败:", e); } await delay(3); loop(); }; loop(); } const simulatePlayEnabled = configStore.platformParams.cx.parts[0].params[0].value || false; if (!simulatePlayEnabled) { playbackRateParam.max = maxRate; if (playbackRate > maxRate) { playbackRateParam.value = maxRate; playbackRate = maxRate; } } if (autoMaxRate) { playbackRate = maxRate; logStore.addLog(`自动倍速: ${playbackRate}x`, "success"); } else { if (playbackRate > maxRate) { playbackRate = maxRate; logStore.addLog(`倍速已调整为播放器最大值: ${playbackRate}x`, "warning"); } } logStore.addLog(`播放倍速: ${playbackRate}x`, "primary"); let isExecuted = false; logStore.addLog("播放成功", "success"); const intervalId = setInterval(async () => { const mediaElement = iframeDocument.documentElement.querySelector(mediaType); if (mediaElement && !isExecuted) { await mediaElement.pause(); mediaElement.muted = true; await mediaElement.play(); mediaElement.playbackRate = playbackRate; const listener = async () => { await delay(3); await mediaElement.play(); mediaElement.playbackRate = playbackRate; }; mediaElement.addEventListener("pause", listener); mediaElement.addEventListener("ended", () => { logStore.addLog(`${mediaType}已播放完成`, "success"); mediaElement.removeEventListener("pause", listener); resolve(); }); isExecuted = true; clearInterval(intervalId); } }, 2500); }); }; const getMaxPlaybackRate = (iframeDocument, showLog = true) => { try { const menuItems = iframeDocument.querySelectorAll('.vjs-playback-rate .vjs-menu-content .vjs-menu-item'); if (menuItems.length === 0) { return 1; } let maxRate = 1; menuItems.forEach(item => { const text = item.textContent.trim(); const rate = parseFloat(text.replace('x', '')); if (!isNaN(rate) && rate > maxRate) { maxRate = rate; } }); if (showLog) { logStore.addLog(`播放器最大倍速: ${maxRate}x`, "info"); } return maxRate; } catch (e) { logStore.addLog(`获取最大倍速失败,使用默认值1x`, "warning"); return 1; } }; const handleMediaContent = async (mediaType, iframeDocument, iframe) => { const useSimulatePlay = configStore.platformParams.cx.parts[0]?.params[0]?.value || false; if (useSimulatePlay) { return simulateVideoPlay(iframe, iframeDocument, mediaType); } else { return playMediaDirectly(mediaType, iframeDocument); } }; const handleAssignment = async (iframe, iframeDocument, iframeWindow) => { logStore.addLog("发现一个作业,正在解析", "warning"); const taskId = _globalTaskId; return new Promise(async (resolve) => { if (!iframeDocument) return resolve(); const ansJobIcon = iframe.parentElement?.querySelector(".ans-job-icon"); if (ansJobIcon) { const ariaLabel = ansJobIcon.getAttribute("aria-label") || ""; if (ariaLabel.includes("已完成")) { logStore.addLog("任务点已完成,跳过", "success"); return resolve(); } } decodeCipherFont(iframeDocument); logStore.addLog(`题目列表获取成功`, "primary"); const correctRate = await new CxQuestionHandler("zj", iframe).init(); if (taskId !== _globalTaskId) { return resolve(); } iframeWindow.alert = () => { }; if (configStore.platformParams.cx.parts[2].params[0].value) { logStore.addLog("自动提交已开启,尝试提交", "primary"); const answerParamsPart = configStore.platformParams.cx.parts.find(p => p.name === "答题参数"); const correctRateThreshold = answerParamsPart?.params.find(p => p.name === "正确阈值")?.value || 85; if (correctRate < Number(correctRateThreshold)) { logStore.addLog(`正确率小于${correctRateThreshold}%,暂存`, "danger"); await iframeWindow.noSubmit(); } else { logStore.addLog(`正确率大于${correctRateThreshold}%,提交`, "success"); await iframeWindow.btnBlueSubmit(); await delay(1.5); await iframeWindow.submitCheckTimes(); logStore.addLog("提交成功", "success"); } } else { logStore.addLog("未开启自动提交,暂存", "primary"); await iframeWindow.noSubmit(); } logStore.addLog("作业已完成", "success"); return resolve(); }); }; const handleSlideshow = async (iframeWindow) => { const pptWindow = iframeWindow.document.querySelector("#panView").contentWindow; logStore.addLog("发现一个PPT,正在解析", "warning"); await pptWindow.scrollTo( { top: pptWindow.document.body.scrollHeight, behavior: "smooth" } ); logStore.addLog("阅读完成", "success"); return Promise.resolve(); }; const handleEbook = async (iframeWindow) => { logStore.addLog("发现一个电子书,正在解析", "warning"); _unsafeWindow.top.onchangepage(iframeWindow.getFrameAttr("end")); logStore.addLog("阅读完成", "success"); return Promise.resolve(); }; const awaitFrameReady = async (iframe) => { return new Promise((resolve) => { const intervalId = setInterval(async () => { var _a; if (iframe.contentDocument && ((_a = iframe.contentDocument) == null ? void 0 : _a.readyState) == "complete") { resolve(); clearInterval(intervalId); } }, 500); }); }; const handleSingleFrame = async (iframe) => { var _a, _b; stopSimulatePlayIfNeeded(); const iframeSrc = iframe.src; const iframeDocument = iframe.contentDocument; const iframeWindow = iframe.contentWindow; if (!iframeDocument || !iframeWindow) { return Promise.resolve(); } if (iframeSrc.includes("javascript:")) { return Promise.resolve(); } const lastTaskId = _processedIframeTasks.get(iframe); const currentTaskId = _globalTaskId; if (lastTaskId === currentTaskId) { return Promise.resolve(); } _processedIframeTasks.set(iframe, currentTaskId); await awaitFrameReady(iframe); const parentClass = ((_a = iframe.parentElement) == null ? void 0 : _a.className) || ""; if (parentClass.includes("ans-job-finished")) { } else { const _src = iframe.getAttribute('_src') || ''; const matchSrc = iframeSrc.includes("api/work"); const matchSrcAttr = _src.includes("api/work"); const normalMode = configStore.platformParams.cx.parts[2].params[2].value; const onlyVideo = configStore.platformParams.cx.parts[2].params[3].value; const onlyAnswer = configStore.platformParams.cx.parts[2].params[4].value; if (matchSrcAttr && !matchSrc) { return Promise.resolve(); } if (matchSrc || matchSrcAttr) { if (onlyVideo) { if (!hasLoggedSkipTip) { logStore.addLog("仅视频模式,跳过答题", "primary"); hasLoggedSkipTip = true; } return Promise.resolve(); } return handleAssignment(iframe, iframeDocument, iframeWindow); } const ansJobIcon = (_b = iframe.parentElement) == null ? void 0 : _b.querySelector(".ans-job-icon"); if (ansJobIcon) { if (onlyAnswer) { if (!hasLoggedSkipTip) { logStore.addLog("仅答题模式,跳过视频等其他内容", "primary"); hasLoggedSkipTip = true; } return Promise.resolve(); } if (iframeSrc.includes("video")) { return handleMediaContent("video", iframeDocument, iframe); } else if (iframeSrc.includes("audio")) { return handleMediaContent("audio", iframeDocument, iframe); } else if (["ppt", "doc", "pptx", "docx", "pdf"].some((type) => iframeSrc.includes("modules/" + type))) { return handleSlideshow(iframeWindow); } else if (["innerbook"].some((type) => iframeSrc.includes("modules/" + type))) { return handleEbook(iframeWindow); } } } return Promise.resolve(); }; init(); monitorIframes(); watchUrlChanges(); }; const clickOption = (element) => { element.focus(); element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true })); element.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true })); element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); }; const useStuActiveLogic = async () => { try { window.parent.postMessage({ source: 'chaoxing-helper-iframe', action: 'closePanel' }, '*'); } catch (e) {} const logStore = useLogStore(); const questionStore = useQuestionStore(); const configStore = useConfigStore(); logStore.addLog(`进入随堂练习答题页面`, "primary"); logStore.addLog(`等待Vue渲染题目...`, "warning"); decodeCipherFont(document); let questionItems = null; for (let i = 0; i < 60; i++) { await delay(0.5); questionItems = document.querySelectorAll(".question-item"); if (questionItems.length > 0) break; } if (!questionItems || questionItems.length === 0) { logStore.addLog("未解析到题目,可能页面尚未加载完成", "danger"); logStore.addLog("请刷新页面重试", "warning"); return; } const questionTypeMapping = { "单选题": "0", "多选题": "1", "判断题": "3", "填空题": "2" }; const questions = []; questionItems.forEach((questionItem) => { const questionNameEl = questionItem.querySelector(".question-name"); const questionText = questionNameEl ? questionNameEl.innerText.trim() : ""; let questionTypeText = "单选题"; if (questionItem.classList.contains("multiple-choice")) { questionTypeText = "多选题"; } else if (questionText.includes("判断题")) { questionTypeText = "判断题"; } else if (questionText.includes("填空题")) { questionTypeText = "填空题"; } const cleanTitle = questionText .replace(/^\d+\.\s*/, "") .replace(/\[单选题\]|\[多选题\]|\[判断题\]|\[填空题\]/g, "") .trim(); const optionLis = questionItem.querySelectorAll(".option-list li"); const optionsObject = {}; const optionTexts = []; optionLis.forEach((li) => { const result = li.querySelector(".option-result")?.innerText?.trim() || ""; optionsObject[result] = li; optionTexts.push(result); }); questions.push({ element: questionItem, type: questionTypeMapping[questionTypeText] || "0", title: cleanTitle, optionsText: optionTexts, options: optionsObject, answer: [], workType: "stuActive", refer: window.location.href }); }); logStore.addLog(`成功解析到${questions.length}道题目`, "success"); const answerParamsPart = configStore.platformParams.cx?.parts.find(p => p.name === "答题参数"); const skipAnswered = answerParamsPart?.params.find(p => p.name === "跳过已答")?.value || false; const answerInterval = answerParamsPart?.params.find(p => p.name === "答题间隔")?.value || 1; const useSimilarity = answerParamsPart?.params.find(p => p.name === "相似匹配")?.value || false; const simulateDelay = answerParamsPart?.params.find(p => p.name === "模拟延迟")?.value ?? true; let skippedCount = 0; for (const [index, question] of questions.entries()) { const isAnswered = Array.from(question.element.querySelectorAll(".option-list li")).some(li => li.classList.contains("active")); if (skipAnswered && isAnswered) { logStore.addLog(`第${index + 1}题已作答,跳过`, "warning"); skippedCount += 1; questionStore.addQuestion(question); continue; } logStore.addLog(`正在查找第${index + 1}道题目答案...`, "primary"); const answerData = await queryAnswer(question); if (answerData.code === 499) { break; } if (answerData.code === 200) { question.answer = answerData.data.answer; question.source = answerData.data.source; if (question.type === "0" || question.type === "1" || question.type === "3") { const selectedKeys = new Set(); for (const answer of question.answer) { const cleanAnswer = answer.replace(/<[^>]*>/g, "").trim(); let matched = false; for (const key in question.options) { if (key === cleanAnswer && !selectedKeys.has(key)) { matched = true; selectedKeys.add(key); clickOption(question.options[key]); await randomDelay(0.1, 0.15); break; } } if (!matched && useSimilarity) { const bestMatch = pickBestOption(cleanAnswer, question.options); if (bestMatch && !selectedKeys.has(bestMatch.key)) { selectedKeys.add(bestMatch.key); clickOption(question.options[bestMatch.key]); await randomDelay(0.1, 0.15); } } } } const aiSources = ['hunyuan-standard', 'hunyuan-t1', 'DeepSeek-V3.2-Think', 'DeepSeek-V3.2', 'DeepSeek-R1-0528', 'qwen3-235b-a22b', 'minimax-m2.5', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gemini-3.1-flash-lite-preview', 'Pro/zai-org/GLM-5', 'Pro/zai-org/GLM-4.7']; const isFromAI = aiSources.includes(answerData.data.source); const sourceHint = isFromAI ? `(${answerData.data.source})` : ""; const msgHint = answerData.msg ? ` - ${answerData.msg}` : ""; logStore.addLog(`第${index + 1}道题搜索成功${sourceHint}${msgHint}`, "success"); logStore.addLog(`剩余次数:${answerData.data.num}`, "primary"); } else { if (answerData.code === 403 && answerData.data && answerData.data.limitedMode) { logStore.addLog(`第${index + 1}道题搜索失败: 免费题库无答案`, "danger"); question.answer[0] = answerData.msg; } else if (answerData.code === 429 && answerData.data && answerData.data.limitedMode) { logStore.addLog(`第${index + 1}道题搜索失败: 今日免费查题已达上限`, "danger"); logStore.addLog('💎 点击购买Token,享无限查题', 'warning'); question.answer[0] = answerData.msg; } else { logStore.addLog(`第${index + 1}道题搜索失败: ${answerData.msg}`, "danger"); question.answer[0] = answerData.msg; } } questionStore.addQuestion(question); await (simulateDelay ? randomDelay(Number(answerInterval), 0.5) : delay(Number(answerInterval))); } if (skippedCount > 0) { logStore.addLog(`共跳过${skippedCount}道已答题目`, "primary"); } logStore.addLog("随堂练习答题完成", "success"); const autoSubmit = configStore.platformParams.cx.parts[2]?.params.find(p => p.name === "自动提交")?.value; if (autoSubmit) { logStore.addLog("自动提交已开启,3秒后提交...", "warning"); await delay(3); const submitBtn = document.querySelector(".submit-btn"); if (submitBtn) { clickOption(submitBtn); logStore.addLog("已点击提交", "success"); } else { logStore.addLog("未找到提交按钮,请手动提交", "danger"); } } else { logStore.addLog("未开启自动提交,请手动提交", "info"); } }; const useCxWorkLogic = async () => { const logStore = useLogStore(); useConfigStore(); logStore.addLog(`进入新版作业页面,开始准备答题`, "primary"); logStore.addLog(`正在解析题目, 请等待5s`, "warning"); await new CxQuestionHandler("zy").init(); }; const useCxExamLogic = async () => { var _a; const logStore = useLogStore(); const configStore = useConfigStore(); logStore.addLog(`进入新版考试页面,开始准备答题`, "primary"); logStore.addLog(`正在解析题目, 请等待5s`, "warning"); await new CxQuestionHandler("ks").init(); if (configStore.platformParams.cx.parts[3].params[0].value) { const currentQuestionNum = parseInt(((_a = _unsafeWindow.document.querySelector(".topicNumber_list .current")) == null ? void 0 : _a.innerText) || "0"); const totalQuestions = _unsafeWindow.document.querySelectorAll(".topicNumber_list li").length; if (currentQuestionNum >= totalQuestions) { logStore.addLog("当前已是最后一题,不再自动切换", "warning"); logStore.addLog("请手动检查答案后提交试卷", "primary"); } else { logStore.addLog("自动切换已开启,正在前往下一题", "success"); await delay(3); _unsafeWindow.getTheNextQuestion(1); } } else { logStore.addLog("已经关闭自动切换,在设置里可更改", "danger"); } }; class ZhsQuestionHandler extends QuestionProcessor { constructor() { super(); __publicField(this, "isZhsQuestionAnswered", (question) => { const reverseTypeMapping = { "0": "单选题", "1": "多选题", "2": "填空题", "3": "判断题", "4": "简答题", "5": "名词解释", "6": "论述题", "7": "计算题" }; const questionTypeText = reverseTypeMapping[question.type] || question.type; if (questionTypeText === "单选题" || questionTypeText === "多选题") { for (const key in question.options) { const optionElement = question.options[key]; if (optionElement.classList.contains("cur") || optionElement.classList.contains("selected") || optionElement.querySelector(".onChecked") || optionElement.querySelector(".cur") || optionElement.querySelector(".selected")) { return true; } } return false; } if (questionTypeText === "判断题") { for (const key in question.options) { const optionElement = question.options[key]; if (optionElement.classList.contains("cur") || optionElement.classList.contains("selected") || optionElement.querySelector(".onChecked")) { return true; } } return false; } return false; }); __publicField(this, "init", async () => { var _a; this.questions = []; this.parseHtml(); if (this.questions.length) { this.addLog(`成功解析到${this.questions.length}个题目`, "primary"); const configStore = useConfigStore(); const _answerParamsPart2 = configStore.platformParams[configStore.platformName]?.parts.find(p => p.name === "答题参数"); const skipAnswered = _answerParamsPart2?.params.find(p => p.name === "跳过已答")?.value || false; let skippedCount = 0; for (const [index, question] of this.questions.entries()) { if (skipAnswered && this.isZhsQuestionAnswered(question)) { this.addLog(`第${index + 1}道题已作答,跳过`, "warning"); skippedCount += 1; this.addQuestion(question); await ((_a = this._document) == null ? void 0 : _a.querySelectorAll(".switch-btn-box > button")[1]).click(); await new Promise(r => setTimeout(r, 1500)); continue; } this.addLog(`正在查找第${index + 1}道题目答案...`, "primary"); const answerData = await queryAnswer(question); if (answerData.code === 200) { question.answer = answerData.data.answer; question.source = answerData.data.source; this.addQuestion(question); await new Promise(r => setTimeout(r, 100)); await this.fillQuestion(question); const aiSources = ['hunyuan-standard', 'hunyuan-t1', 'DeepSeek-V3.2-Think', 'DeepSeek-V3.2', 'DeepSeek-R1-0528', 'qwen3-235b-a22b', 'minimax-m2.5', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gemini-3.1-flash-lite-preview', 'Pro/zai-org/GLM-5', 'Pro/zai-org/GLM-4.7']; const isFromAI = aiSources.includes(answerData.data.source); const sourceHint = isFromAI ? `(${answerData.data.source})` : ""; const msgHint = answerData.msg ? ` - ${answerData.msg}` : ""; this.addLog(`第${index + 1}道题搜索成功${sourceHint}${msgHint}`, "success"); this.addLog(`剩余次数:${answerData.data.num}`, "primary"); } else { this.addLog(`第${index + 1}道题搜索失败:${answerData.msg}`, "danger"); question.answer[0] = answerData.msg; this.addQuestion(question); await new Promise(r => setTimeout(r, 100)); } await ((_a = this._document) == null ? void 0 : _a.querySelectorAll(".switch-btn-box > button")[1]).click(); await new Promise(r => setTimeout(r, 1500)); } if (skippedCount > 0) { this.addLog(`共跳过${skippedCount}道已答题目`, "primary"); } } else this.addLog("未解析到题目,请刷新重试或进入答题页面", "danger"); }); __publicField(this, "parseHtml", () => { if (!this._document) return []; const questionElements = this._document.querySelectorAll(SELECTORS.ZHS_QUESTION); this.addQuestions(questionElements); }); __publicField(this, "fillQuestion", async (question) => { if (!this._window) return; try { const typeNum = question.type; if (typeNum === "0" || typeNum === "1") { const configStore = useConfigStore(); const useSimilarity = configStore.platformParams[configStore.platformName]?.parts.find(p => p.name === "答题参数")?.params.find(p => p.name === "相似匹配")?.value || false; let hasDeselected = false; for (const key in question.options) { const optionElement = question.options[key]; if (optionElement.classList.contains("cur") || optionElement.classList.contains("selected") || optionElement.querySelector(".onChecked") || optionElement.querySelector(".cur") || optionElement.querySelector(".selected")) { hasDeselected = true; optionElement.click(); } } if (hasDeselected) { await new Promise(r => setTimeout(r, 500)); } const selectedKeys = new Set(); console.log("🔍 答案格式:", typeof question.answer, Array.isArray(question.answer), question.answer); console.log("🔍 选项keys:", Object.keys(question.options)); let answers = question.answer; if (typeof question.answer === 'string') { if (question.answer.startsWith('[')) { try { answers = JSON.parse(question.answer); } catch (e) { answers = question.answer.split(/[,,]/).map(a => a.trim()).filter(a => a); } } else { answers = question.answer.split(/[,,]/).map(a => a.trim()).filter(a => a); } console.log("🔍 答案已分割:", answers); } answers.forEach((answer) => { const cleanAnswer = this.stripTags(answer).trim(); let matched = false; for (const key in question.options) { const cleanKey = key.trim(); console.log(`🔍 匹配: "${cleanAnswer}" vs "${cleanKey}" = ${cleanKey === cleanAnswer}`); if (cleanKey === cleanAnswer && !selectedKeys.has(cleanKey)) { matched = true; selectedKeys.add(key); const optionElement = question.options[key]; const isAlreadySelected = optionElement.classList.contains("cur") || optionElement.classList.contains("selected") || optionElement.querySelector(".onChecked") || optionElement.querySelector(".cur") || optionElement.querySelector(".selected"); if (!isAlreadySelected) { optionElement.setAttribute("data-filling", "true"); optionElement.click(); setTimeout(() => optionElement.removeAttribute("data-filling"), 200); } break; } } if (!matched && useSimilarity) { const bestMatch = pickBestOption(cleanAnswer, question.options); if (bestMatch && !selectedKeys.has(bestMatch.key)) { selectedKeys.add(bestMatch.key); const optionElement = question.options[bestMatch.key]; const isAlreadySelected = optionElement.classList.contains("cur") || optionElement.classList.contains("selected") || optionElement.querySelector(".onChecked") || optionElement.querySelector(".cur") || optionElement.querySelector(".selected"); if (!isAlreadySelected) { optionElement.setAttribute("data-filling", "true"); optionElement.click(); setTimeout(() => optionElement.removeAttribute("data-filling"), 200); } } } }); } else if (typeNum === "3") { let answer = "错"; if (REGEX.JUDGE_FALSE.test(question.answer[0])) { answer = "错"; } else if (REGEX.JUDGE_TRUE.test(question.answer[0])) { answer = "对"; } for (const key in question.options) { const optionElement = question.options[key]; const isTrueOption = REGEX.JUDGE_TRUE.test(key); const isFalseOption = REGEX.JUDGE_FALSE.test(key); if ((isTrueOption && answer === "对") || (isFalseOption && answer === "错")) { optionElement.setAttribute("data-filling", "true"); optionElement.click(); setTimeout(() => optionElement.removeAttribute("data-filling"), 200); break; } } } else if (typeNum === "2") { const textareaElements = question.element.querySelectorAll("textarea"); if (textareaElements.length > 0 && question.answer && question.answer.length > 0) { textareaElements.forEach((textarea, index) => { if (index < question.answer.length) { const answerText = this.stripTags(question.answer[index] || question.answer[0]); textarea.value = answerText; textarea.dispatchEvent(new Event("input", { bubbles: true })); console.log(`🔍 填空题填入: 第${index + 1}空 = "${answerText}"`); } }); } } else if (["4", "5", "6", "7"].includes(typeNum)) { const textareaElement = question.element.querySelector("textarea"); if (textareaElement && question.answer && question.answer.length > 0) { const answerText = question.answer.map(a => this.stripTags(a)).join("\n"); textareaElement.value = answerText; textareaElement.dispatchEvent(new Event("input", { bubbles: true })); console.log(`🔍 简答题填入: "${answerText.substring(0, 50)}..."`); } } else { console.log(`⚠️ 未处理的题型: ${typeNum}`); } } catch (error) { this.addLog(`答题过程发生错误:${error.message}`, "danger"); } }); } extractOptions(optionElements, optionSelector) { const optionsObject = {}; const optionTexts = []; optionElements.forEach((optionElement) => { var _a; const optionTextContent = this.stripTags(((_a = optionElement.querySelector(optionSelector)) == null ? void 0 : _a.innerHTML) || ""); optionsObject[optionTextContent] = optionElement; optionTexts.push(optionTextContent); }); return [optionsObject, optionTexts]; } addQuestions(questionElements) { questionElements.forEach((questionElement) => { var _a, _b; const questionTitle = (questionElement == null ? void 0 : questionElement.querySelector(".subject_describe div,.smallStem_describe p")).__Ivue__._data.shadowDom.textContent; const questionTypeText = ((_b = (_a = questionElement == null ? void 0 : questionElement.querySelector(".subject_type span")) == null ? void 0 : _a.textContent) == null ? void 0 : _b.slice(1, 4)) || ""; const zhsTypeMapping = { "单选": "单选题", "多选": "多选题", "填空": "填空题", "判断": "判断题", "简答": "简答题", "名词": "名词解释", "论述": "论述题", "计算": "计算题" }; const fullQuestionType = zhsTypeMapping[questionTypeText] || questionTypeText; const numericType = this.typeMap.get(fullQuestionType) || "999"; const [optionsObject, optionTexts] = this.extractOptions(questionElement == null ? void 0 : questionElement.querySelectorAll(SELECTORS.ZHS_OPTION), ".node_detail"); this.questions.push({ element: questionElement, type: numericType, title: questionTitle, optionsText: optionTexts, options: optionsObject, answer: [], workType: "zhs", refer: this._window.location.href }); }); } } const hookError = () => { console.log("hookError"); const oldset = _unsafeWindow.setInterval; const oldout = _unsafeWindow.setTimeout; _unsafeWindow.setInterval = function(...args) { const err = new Error(); if (err.stack && err.stack.indexOf("checkoutNotTrustScript") !== -1) { return -1; } return oldset.call(this, ...args); }; _unsafeWindow.setTimeout = function(...args) { const err = new Error(); if (err.stack && err.stack.indexOf("checkoutNotTrustScript") !== -1) { return -1; } return oldout.call(this, ...args); }; }; class XMLHttpRequestInterceptor { constructor(urlList, callback) { __publicField(this, "xhr"); __publicField(this, "originalOpen"); __publicField(this, "originalSend"); __publicField(this, "callback"); this.xhr = new XMLHttpRequest(); this.originalOpen = this.xhr.open; this.originalSend = this.xhr.send; this.callback = callback; this.intercept(urlList); } intercept(urlList) { const self = this; XMLHttpRequest.prototype.open = function(method, url2) { self.originalOpen.apply(this, [method, url2]); const shouldIntercept = urlList.some((urlItem) => url2.includes(urlItem)); if (shouldIntercept) { self.callback(this.responseText); } }; } } const useZhsAnswerLogic = async () => { hookError(); const logStore = useLogStore(); useConfigStore(); logStore.addLog(`进入答题页面,开始准备答题`, "primary"); logStore.addLog(`正在解析题目, 请等待5s`, "warning"); new XMLHttpRequestInterceptor(["gateway/t/v1/answer/hasAnswer"], async () => { await delay(1); _unsafeWindow.document.getSelection = function() { return { removeAllRanges: function() { } }; }; _unsafeWindow.document.onselectstart = true; _unsafeWindow.document.oncontextmenu = true; _unsafeWindow.document.oncut = true; _unsafeWindow.document.oncopy = true; _unsafeWindow.document.onpaste = true; await new ZhsQuestionHandler().init(); return true; }); }; const _sfc_main$3 = vue.defineComponent({ __name: "Index", emits: ["customEvent"], setup(__props, { emit: __emit }) { var _a; const cardWidth = vue.ref("100%"); const isShow = vue.ref(false); (_a = document.querySelector("li>a.experience:not([onclick])")) == null ? void 0 : _a.click(); const configStore = useConfigStore(); const logStore = useLogStore(); const questionStore = useQuestionStore(); const url2 = window.location.href; logStore.addLog("用户悉知:使用脚本即为完全同意用户协议", "success"); logStore.addLog("脚本加载成功,正在解析网页", "primary"); logStore.addLog("请不要多个脚本同时使用,会有脚本冲突问题", "warning"); logStore.addLog("如果脚本出现异常,请用谷歌、火狐等浏览器", "warning"); const urlLogicPairs = [ { keyword: "/mycourse/studentstudy", logic: useCxChapterLogic }, { keyword: "/mooc2/work/dowork", logic: useCxWorkLogic }, { keyword: "/exam-ans/", logic: useCxExamLogic }, { keyword: "/work/index", logic: useCxWorkLogic }, { keyword: "/work/doTest", logic: useCxWorkLogic }, { keyword: "/work/calcAnswer", logic: useCxWorkLogic }, { keyword: "/knowledge/start", logic: useCxChapterLogic }, { keyword: "mycourse/stu?courseid", logic: () => { logStore.addLog("该页面无任务,请进入章节或答题页面使用", "danger"); } }, { keyword: "/stuExamWeb.html", logic: useZhsAnswerLogic }, { keyword: "answerQuestion2", logic: useStuActiveLogic } ]; const executeLogicByUrl = (url22) => { for (const { keyword, logic } of urlLogicPairs) { if (url22.includes(keyword)) { logic(); isShow.value = true; return; } } isShow.value = false; }; executeLogicByUrl(url2); const emit = __emit; emit("customEvent", isShow.value); const tabs = [ { label: "🏡主页信息", id: "main-log", component: ScriptHome, props: { "log-list": logStore.logList, "server-config": CURRENT_SERVER_CONFIG } }, { label: "📝答题记录", id: "question-record", component: QuestionTable, props: { "question-list": questionStore.questionList } }, { label: "⚙️脚本配置", id: "config-panel", component: ScriptSetting, props: { "global-config": configStore } }, { label: "📋脚本说明", id: "script-info", component: ScriptInfo }, { label: "💬作者的话", id: "author-words", component: AuthorWords }, { label: "🎁推广奖励", id: "referral-panel", component: ReferralPanel } ]; const activeTab = vue.ref("main-log"); const switchTab = (tabId) => { activeTab.value = tabId; }; return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", { style: vue.normalizeStyle({ width: cardWidth.value }), class: "card_content" }, [ vue.createElementVNode("div", { class: "config-tabs-container" }, [ (vue.openBlock(), vue.createElementBlock(vue.Fragment, null, vue.renderList(tabs, (tab) => { const isUnverified = tab.id === "config-panel" && !configStore.tokenVerified; const isActive = activeTab.value === tab.id; return vue.createElementVNode("button", { key: tab.id, class: vue.normalizeClass(["config-tab", { active: isActive }]), style: isUnverified && !isActive ? { "background": "linear-gradient(135deg, #dc3545 0%, #c82333 100%)", "border-color": "#dc3545", "color": "#fff" } : {}, onClick: () => switchTab(tab.id) }, vue.toDisplayString(tab.label), 13, ["onClick", "class", "style"]); }), 64)) ]), (vue.openBlock(), vue.createElementBlock(vue.Fragment, null, vue.renderList(tabs, (tab) => { return vue.withDirectives(vue.createElementVNode("div", { key: tab.id === "author-words" ? "author-words-" + changelogRefreshKey.value : tab.id, class: "config-panel" }, [ tab.component ? (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(tab.component), vue.mergeProps({ key: 0, ref_for: true }, tab.props), null, 16)) : vue.createCommentVNode("", true) ], 512), [ [vue.vShow, activeTab.value === tab.id] ]); }), 128)) ], 4); }; } }); const _sfc_main$2 = vue.defineComponent({ __name: "ZoomButtons", emits: ["toggleZoom"], setup(__props, { emit: __emit }) { const emit = __emit; const configStore = useConfigStore(); const toggleZoom = () => { const newValue = !configStore.isMinus; emit("toggleZoom", newValue); }; return (_ctx, _cache) => { const _component_el_icon = vue.resolveComponent("el-icon"); return vue.openBlock(), vue.createElementBlock("div", { onMousedown: _cache[1] || (_cache[1] = vue.withModifiers(() => { }, ["stop"])) }, [ vue.createVNode(_component_el_icon, { onClick: _cache[0] || (_cache[0] = () => toggleZoom()), size: "small", style: { "cursor": "pointer" } }, { default: vue.withCtx(() => [ vue.createVNode(vue.unref(configStore.isMinus ? full_screen_default : minus_default)) ]), _: 1 }) ], 32); }; } }); const _hoisted_1 = { class: "overlay" }; const _hoisted_2 = { class: "title" }; const _hoisted_3 = { class: "minus" }; const _sfc_main$1 = vue.defineComponent({ __name: "layout", setup(__props) { const isShow = vue.ref(false); const configStore = useConfigStore(); vue.watch(configStore, (newVal) => { _GM_setValue("config", JSON.stringify(newVal)); }, { deep: true }); const isDragging = vue.ref(false); const offsetX = vue.ref(0); const offsetY = vue.ref(0); const moveStyle = vue.computed(() => { return { left: configStore.position.x, top: configStore.position.y, height: configStore.isMinus ? "auto" : "560px", maxHeight: configStore.isMinus ? "none" : "560px", width: configStore.isMinus ? "280px" : "720px", maxWidth: configStore.isMinus ? "280px" : "720px" }; }); const startDrag = (event) => { isDragging.value = true; offsetX.value = event.clientX - event.target.getBoundingClientRect().left; offsetY.value = event.clientY - event.target.getBoundingClientRect().top; document.addEventListener("mousemove", drag); document.addEventListener("mouseup", endDrag); }; const drag = (event) => { if (!isDragging.value) return; const x = event.clientX - offsetX.value; const y = event.clientY - offsetY.value; configStore.position.x = `${x - 11}px`; configStore.position.y = `${y - 11}px`; if (x < 0) { configStore.position.x = "0px"; } if (y < 0) { configStore.position.y = "0px"; } if (x > window.innerWidth - 720) { configStore.position.x = `${window.innerWidth - 720}px`; } if (y > window.innerHeight - 560) { configStore.position.y = `${window.innerHeight - 560}px`; } }; const endDrag = () => { isDragging.value = false; document.removeEventListener("mousemove", drag); document.removeEventListener("mouseup", endDrag); }; const startDragTouch = (event) => { if (event.touches.length === 1) { isDragging.value = true; const touch = event.touches[0]; offsetX.value = touch.clientX - event.target.getBoundingClientRect().left; offsetY.value = touch.clientY - event.target.getBoundingClientRect().top; document.addEventListener("touchmove", dragTouch, { passive: false }); document.addEventListener("touchend", endDragTouch); } }; const dragTouch = (event) => { if (!isDragging.value || event.touches.length !== 1) return; event.preventDefault(); const touch = event.touches[0]; const x = touch.clientX - offsetX.value; const y = touch.clientY - offsetY.value; configStore.position.x = `${x - 11}px`; configStore.position.y = `${y - 11}px`; if (x < 0) { configStore.position.x = "0px"; } if (y < 0) { configStore.position.y = "0px"; } if (x > window.innerWidth - 720) { configStore.position.x = `${window.innerWidth - 720}px`; } if (y > window.innerHeight - 560) { configStore.position.y = `${window.innerHeight - 560}px`; } }; const endDragTouch = () => { isDragging.value = false; document.removeEventListener("touchmove", dragTouch); document.removeEventListener("touchend", endDragTouch); }; return (_ctx, _cache) => { const _component_el_icon = vue.resolveComponent("el-icon"); const _component_el_tooltip = vue.resolveComponent("el-tooltip"); const _component_el_tag = vue.resolveComponent("el-tag"); const _component_el_text = vue.resolveComponent("el-text"); const _component_el_divider = vue.resolveComponent("el-divider"); const _component_el_card = vue.resolveComponent("el-card"); return vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", { style: vue.normalizeStyle(moveStyle.value), class: "main-page" }, [ vue.withDirectives(vue.createElementVNode("div", _hoisted_1, null, 512), [ [vue.vShow, isDragging.value] ]), vue.createVNode(_component_el_card, { style: { "border": "0" }, "close-on-click-modal": false, "lock-scroll": false, modal: false, "show-close": false, "modal-class": "modal" }, { header: vue.withCtx(() => [ vue.createElementVNode("div", { class: "card-header", onMousedown: startDrag, onTouchstart: startDragTouch }, [ vue.createElementVNode("div", _hoisted_2, [ vue.createElementVNode("span", null, vue.toDisplayString(vue.unref(configStore).isMinus ? `超星网课助手 v${vue.unref(configStore).version}` : vue.unref(configStore).platformParams?.[vue.unref(configStore).platformName]?.name || "超星网课助手"), 1), vue.createVNode(_component_el_tooltip, { teleported: "", effect: "dark", placement: "top-start", content: "注意事项: