您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案,能够跳过秒看教学视频,自动阅读文档
当前为
// ==UserScript== // @name 宜宾智慧校园助手 // @namespace 智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案===来自计算机科学与技术学院--修改自若离智慧校园 // @version 11.20 // @description 智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案,能够跳过秒看教学视频,自动阅读文档 // @author 计算机科学与技术学院---软工 // @match https://mooc.yibinu.edu.cn/* // @icon https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.min.js // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/xlsx/0.18.5/xlsx.full.min.js // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js // @resource ANTD_CSS https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.min.css // @run-at document-end // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_getResourceText // ==/UserScript== // 脚本设置对象 const setting = { logs: [ '初始化脚本完成,', '当前脚本版本:V11.01', '由于国内无法访问gf.qytechs.cn,可以进入 https://home.gf.qytechs.cn.cn/ 下载' ], // 日志数据 datas: [], // 答案数据 secretKey: '你好', // 默认密钥 _vt: '274c8b3f2a8c63ffc960dd1e4e3f0eac', // 答题设置 - 默认值 autoSubmit: false, // 默认不自动提交答案 autoEnterExam: true, // 默认自动进入考试 // 学习进度 learningProgress: { isLearning: false, currentChapter: null, currentContent: null, timestamp: null }, // 初始化设置 init() { // 尝试从localStorage加载设置 try { const savedConfig = localStorage.getItem('examConfig'); if (savedConfig) { const config = JSON.parse(savedConfig); this.autoSubmit = config.autoSubmit !== undefined ? config.autoSubmit : true; this.autoEnterExam = config.autoEnterExam !== undefined ? config.autoEnterExam : true; } } catch (e) { console.error('加载答题配置失败:', e); // 使用默认值 this.autoSubmit = true; this.autoEnterExam = true; } } }; // 初始化设置 setting.init(); // 资源加载管理器 const ResourceLoader = { resources: { styles: [ { name: 'ANTD_CSS', type: 'resource' } ], scripts: [ { name: 'vue', url: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js' }, { name: 'antd', url: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.min.js' }, { name: 'xlsx', url: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/xlsx/0.18.5/xlsx.full.min.js' }, { name: 'crypto-js', url: 'https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js' } ] }, // 资源加载状态 loadStatus: new Map(), // 初始化加载 async init() { try { // 先加载样式资源 await this.loadStyles(); // 再加载脚本资源 await this.loadScripts(); console.log('所有资源加载完成'); return true; } catch (error) { console.error('资源加载失败:', error); return false; } }, // 加载样式资源 async loadStyles() { for (const style of this.resources.styles) { try { if (style.type === 'resource') { // 使用GM_getResourceText加载资源 const cssContent = GM_getResourceText(style.name); if (cssContent) { GM_addStyle(cssContent); this.loadStatus.set(style.name, true); console.log(`样式资源 ${style.name} 加载成功`); } else { throw new Error(`无法获取样式资源 ${style.name}`); } } else { // 直接通过URL加载 await this.loadStyle(style.name, style.url); } } catch (error) { console.error(`加载样式 ${style.name} 失败:`, error); this.loadStatus.set(style.name, false); } } }, // 加载脚本资源 async loadScripts() { const promises = this.resources.scripts.map(script => this.loadScript(script.name, script.url) ); return Promise.all(promises); }, // 加载单个样式 loadStyle(name, url) { return new Promise((resolve, reject) => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; link.onload = () => { this.loadStatus.set(name, true); console.log(`样式 ${name} 加载成功`); resolve(); }; link.onerror = (error) => { console.error(`样式 ${name} 加载失败:`, error); this.loadStatus.set(name, false); reject(new Error(`样式 ${name} 加载失败`)); }; document.head.appendChild(link); }); }, // 加载单个脚本 loadScript(name, url) { return new Promise((resolve, reject) => { // 检查是否已加载 if (window[name]) { this.loadStatus.set(name, true); resolve(); return; } const script = document.createElement('script'); script.src = url; script.async = true; script.onload = () => { this.loadStatus.set(name, true); console.log(`脚本 ${name} 加载成功`); resolve(); }; script.onerror = () => { // 加载失败时尝试备用CDN const backupUrl = this.getBackupUrl(url); if (backupUrl) { script.src = backupUrl; console.log(`尝试使用备用CDN加载 ${name}`); } else { this.loadStatus.set(name, false); console.error(`脚本 ${name} 加载失败`); reject(new Error(`脚本 ${name} 加载失败`)); } }; document.head.appendChild(script); }); }, // 获取备用CDN地址 getBackupUrl(url) { const cdnMap = { 'lf26-cdn-tos.bytecdntp.com': 'cdn.jsdelivr.net/npm', 'cdn.jsdelivr.net': 'cdnjs.cloudflare.com/ajax/libs', 'cdnjs.cloudflare.com': 'unpkg.com' }; for (const [current, backup] of Object.entries(cdnMap)) { if (url.includes(current)) { return url.replace(current, backup); } } return null; }, // 检查资源是否都已加载 checkAllLoaded() { return Array.from(this.loadStatus.values()).every(status => status); }, // 获取加载失败的资源 getFailedResources() { return Array.from(this.loadStatus.entries()) .filter(([, status]) => !status) .map(([name]) => name); } }; // 浏览器兼容性检查和处理 const BrowserCompatibility = { // 必需的特性列表 requiredFeatures: { localStorage: { name: 'localStorage', test: () => typeof localStorage !== 'undefined', fallback: this.createMemoryStorage }, promise: { name: 'Promise', test: () => typeof Promise !== 'undefined', fallback: () => this.loadPolyfill('promise-polyfill') }, fetch: { name: 'fetch', test: () => typeof fetch !== 'undefined', fallback: () => this.loadPolyfill('whatwg-fetch') }, customElements: { name: 'Custom Elements', test: () => 'customElements' in window, fallback: () => this.loadPolyfill('@webcomponents/custom-elements') } }, // 浏览器信息 browserInfo: { name: '', version: '', isSupported: false }, // 初始化 async init() { this.detectBrowser(); const compatibility = await this.checkCompatibility(); if (!compatibility.isCompatible) { this.showCompatibilityWarning(compatibility.unsupportedFeatures); await this.loadFallbacks(compatibility.unsupportedFeatures); } return compatibility.isCompatible; }, // 检测浏览器信息 detectBrowser() { const userAgent = navigator.userAgent; let browserName = "未知浏览器"; let browserVersion = "未知版本"; // 检测主流浏览器 if (userAgent.indexOf("Chrome") > -1) { browserName = "Chrome"; browserVersion = userAgent.match(/Chrome\/([0-9.]+)/)[1]; } else if (userAgent.indexOf("Firefox") > -1) { browserName = "Firefox"; browserVersion = userAgent.match(/Firefox\/([0-9.]+)/)[1]; } else if (userAgent.indexOf("Safari") > -1) { browserName = "Safari"; browserVersion = userAgent.match(/Version\/([0-9.]+)/)[1]; } else if (userAgent.indexOf("MSIE") > -1 || userAgent.indexOf("Trident") > -1) { browserName = "Internet Explorer"; browserVersion = userAgent.match(/(?:MSIE |rv:)([0-9.]+)/)[1]; } this.browserInfo = { name: browserName, version: browserVersion, isSupported: this.checkBrowserSupport(browserName, browserVersion) }; }, // 检查浏览器是否支持 checkBrowserSupport(name, version) { const minVersions = { 'Chrome': 49, 'Firefox': 45, 'Safari': 10, 'Internet Explorer': 11 }; const majorVersion = parseInt(version.split('.')[0]); return majorVersion >= (minVersions[name] || 0); }, // 检查特性兼容性 async checkCompatibility() { const unsupportedFeatures = []; for (const [feature, {test}] of Object.entries(this.requiredFeatures)) { if (!test()) { unsupportedFeatures.push(feature); } } return { isCompatible: unsupportedFeatures.length === 0, unsupportedFeatures }; }, // 加载polyfill async loadPolyfill(packageName) { const polyfillUrl = `https://cdn.jsdelivr.net/npm/${packageName}`; return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = polyfillUrl; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); }, // 创建内存存储的fallback createMemoryStorage() { const storage = {}; return { setItem: (key, value) => storage[key] = value, getItem: (key) => storage[key], removeItem: (key) => delete storage[key], clear: () => Object.keys(storage).forEach(key => delete storage[key]) }; }, // 加载所有需要的fallback async loadFallbacks(unsupportedFeatures) { const fallbackPromises = unsupportedFeatures.map(feature => { const {fallback} = this.requiredFeatures[feature]; return fallback(); }); try { await Promise.all(fallbackPromises); console.log('所有fallback加载完成'); } catch (error) { console.error('加载fallback失败:', error); } }, // 显示兼容性警告 showCompatibilityWarning(unsupportedFeatures) { const warning = document.createElement('div'); warning.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; background: #fff3cd; color: #856404; padding: 12px; text-align: center; z-index: 9999; border-bottom: 1px solid #ffeeba; `; warning.innerHTML = ` <strong>浏览器兼容性警告</strong><br> 您的浏览器 (${this.browserInfo.name} ${this.browserInfo.version}) 可能不完全支持本脚本的所有功能。<br> 建议使用最新版本的Chrome、Firefox或Safari浏览器。 `; document.body.appendChild(warning); } }; // 在脚本初始化时使用 async function initScript() { try { // 检查浏览器兼容性 await BrowserCompatibility.init(); // 加载资源 await ResourceLoader.init(); // 如果所有检查都通过,继续初始化脚本 if (BrowserCompatibility.browserInfo.isSupported && ResourceLoader.checkAllLoaded()) { initView(); } else { console.error('脚本初始化失败:', { browser: BrowserCompatibility.browserInfo, failedResources: ResourceLoader.getFailedResources() }); } } catch (error) { console.error('脚本初始化错误:', error); } } // 启动脚本 initScript(); // 时间值 function decryptValidDuration() { try { const validDurations = { 'c51ce410c124a10e0db5e4b97fc2af39': 86400000, '274c8b3f2a8c63ffc960dd1e4e3f0eac': 172800000, 'e2ef524fbf3d9fe611d5a8e90fefdc9c': 259200000, '069059b7ef840f0c74a814ec9237b6ec': 432000000, '7f6ffaa6bb0b408017b62254211691b5': 604800000, '149e9677a5989fd342ae44213df68868': 2592000000 }; return validDurations[setting._vt] || 172800000; // 默认返回2天 } catch (e) { console.error('解密时间值出错:', e); return 172800000; // 解密出错时返回默认值(2天) } } // 简化的验证时间管理 function setValidTime() { try { const validDuration = decryptValidDuration(); const validUntil = Date.now() + validDuration; const data = { time: validUntil, hash: CryptoJS.SHA256(validUntil.toString()).toString() }; localStorage.setItem('scriptValidUntil', JSON.stringify(data)); return true; } catch (e) { console.error('设置验证时间出错:', e); return false; } } // 检查验证时间 function checkValidTime() { try { const data = localStorage.getItem('scriptValidUntil'); if (!data) return false; const { time, hash } = JSON.parse(data); const now = Date.now(); // 验证时间和哈希 if (time && hash && hash === CryptoJS.SHA256(time.toString()).toString() && time > now) { return true; } localStorage.removeItem('scriptValidUntil'); return false; } catch (e) { console.error('检查验证时间出错:', e); localStorage.removeItem('scriptValidUntil'); return false; } } // 将验证函数定义为全局函数 window.verifySecret = function() { const input = document.getElementById('secretInput'); if (!input) { console.error('找不到输入框元素'); return; } const inputValue = input.value.trim(); if (!inputValue) { input.style.borderColor = '#ff4d4f'; input.style.animation = 'shake 0.5s'; setTimeout(() => { input.style.borderColor = '#e8e8e8'; input.style.animation = ''; }, 1000); return; } if (inputValue !== setting.secretKey) { const modalDiv = document.querySelector('#secretModal > div'); if (modalDiv) { modalDiv.style.animation = 'shake 0.5s'; input.style.borderColor = '#ff4d4f'; setTimeout(() => { modalDiv.style.animation = ''; input.style.borderColor = '#e8e8e8'; }, 1000); } return; } try { // 设置验证时间 if (!setValidTime()) { throw new Error('设置验证时间失败'); } // 添加关闭动画 const modal = document.getElementById('secretModal'); if (modal) { modal.style.animation = 'modalFadeOut 0.3s ease'; setTimeout(() => { modal.remove(); continueInit(); // 添加成功提示 if (window.vue) { window.vue.$message.success('验证成功!'); } }, 300); } } catch (error) { console.error('验证过程出错:', error); if (window.vue) { window.vue.$message.error('验证过程出错,请刷新页面重试'); } } }; // 添加键盘事件监听器的函数 function addKeyboardListener() { document.addEventListener('keydown', function(event) { if (event.key === 'Enter') { const secretInput = document.getElementById('secretInput'); if (secretInput && document.activeElement === secretInput) { verifySecret(); } } }); } // 日志 function log(logText){ // 限制日志数量,防止内存占用过大 if (setting.logs.length > 100) { setting.logs = setting.logs.slice(0, 50); } setting.logs.unshift(logText); // 使用Vue的响应式更新机制 if (window.vue) { window.vue.logs = [...setting.logs]; } } // 添加一个清理HTML标签的函数 function cleanHtmlTags(text) { if (!text) return ''; // 将HTML转换为纯文本 let temp = document.createElement('div'); temp.innerHTML = text; let cleanText = temp.textContent || temp.innerText; // 清理多余的空白字符 cleanText = cleanText.replace(/\s+/g, ' ').trim(); return cleanText; } // 从后台获取答案 function getAnswer(url, data){ log('获取答案中'); let id = url.match(/\/examSubmit\/(\d+)\/getExamPaper/)[1]; GM_xmlhttpRequest({ method: "post", url: url, data: data, dataType: 'json', headers: { 'Origin': location.origin, 'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36', 'Content-type': 'application/x-www-form-urlencoded;charset=utf-8', 'Referer': `https://mooc.yibinu.edu.cn/examTest/stuExamList/${id}.mooc` }, onload: function(res){ if(res.status == 200){ try { let response = JSON.parse(res.responseText); if (response && response.paper && response.paper.paperStruct) { log("获取答案成功,正在格式化答案!"); formatAnswer(response.paper.paperStruct); } else { log("答案数据格式异常:" + JSON.stringify(response)); if (window.vue) { window.vue.hasAnswer = false; } } } catch (error) { log("解析答案数据失败:" + error.message); if (window.vue) { window.vue.hasAnswer = false; } } } else { log("获取答案失败,状态码:" + res.status); } }, onerror: function(error) { log("求答案失败:" + error.message); } }); } //格式化答案 function formatAnswer(str) { try { setting.datas = []; // 清空之前的数据 if (!Array.isArray(str)) { log("答案数据格式错误:期望数组类型"); if (window.vue) { window.vue.hasAnswer = false; } return; } str.forEach((listItem, index) => { if (!listItem.quiz) { return; } // 使用cleanHtmlTags清理题目内容 var question = cleanHtmlTags(listItem.quiz.quizContent) || "未知题目"; var options = {}; var optionContents = {}; // 存储选项内容 var answer = []; const questionNum = (index + 1).toString(); // 处理选择题 if (listItem.quiz.quizOptionses && listItem.quiz.quizOptionses.length > 0) { listItem.quiz.quizOptionses.forEach((optionItem, idx) => { if (optionItem && optionItem.optionId !== undefined) { const optionLabel = String.fromCharCode(65 + idx); options[optionItem.optionId] = optionLabel; optionContents[optionItem.optionId] = optionItem.optionContent || ''; } }); // 处理答案 if (listItem.quiz.quizResponses) { listItem.quiz.quizResponses.forEach(answerItem => { if (answerItem && options[answerItem.optionId]) { const label = options[answerItem.optionId]; const content = optionContents[answerItem.optionId]; answer.push(`${label}.${content}`); } }); } // 并序号和选项标签 const answerLabels = listItem.quiz.quizResponses .map(item => options[item.optionId]) .join(''); const idAndOptions = `${questionNum}.${answerLabels}`; setting.datas.push({ 'key': index.toString(), 'idAndOptions': idAndOptions, 'question': question, 'answer': answer.join('\n'), // 每个选项答案换行显示 'originalIndex': index // 保存原始索引,用于后续排序 }); } else { // 处理填空题 if (listItem.quiz.quizResponses) { const fillAnswers = []; listItem.quiz.quizResponses.forEach(answerItem => { if (answerItem && answerItem.responseContent) { fillAnswers.push(answerItem.responseContent); } }); setting.datas.push({ 'key': index.toString(), 'idAndOptions': `${questionNum}.(填空)`, 'question': question, 'answer': fillAnswers.join('\n'), // 多个填空答案换行显示 'originalIndex': index // 保存原始索引,用于后续排序 }); } } }); // 提取题目编号信息(如果有) setting.datas.forEach(item => { // 尝试从题目中提取编号 const numberMatch = item.question.match(/^(\d+)[、..]\s*/); if (numberMatch) { item.questionNumber = numberMatch[1]; // 存储不带编号的题目文本,用于后续匹配 item.cleanQuestion = item.question.replace(/^\d+[、..]\s*/, '').trim(); } else { item.cleanQuestion = item.question; } }); // 获取当前页面上的题目顺序 const pageQuestions = []; const questionContainers = document.querySelectorAll('.view-test'); if (questionContainers && questionContainers.length > 0) { questionContainers.forEach((container, idx) => { const textElement = container.querySelector('.test-text-tutami'); if (textElement) { const questionText = textElement.textContent.trim(); pageQuestions.push({ index: idx, text: questionText }); } }); log(`页面上找到 ${pageQuestions.length} 道题目,准备排序答案列表`); // 如果页面上有题目,则根据页面题目顺序排序答案列表 if (pageQuestions.length > 0) { const sortedDatas = []; const usedAnswerIndices = new Set(); // 第一轮:尝试为每个页面题目找到最匹配的答案 pageQuestions.forEach((pageQuestion, pageIdx) => { let bestMatchIndex = -1; let bestMatchScore = 0; // 遍历所有答案,找到最匹配的 setting.datas.forEach((answerData, answerIdx) => { if (usedAnswerIndices.has(answerIdx)) return; // 跳过已使用的答案 const cleanPageQuestion = cleanHtmlTags(pageQuestion.text); const cleanAnswerQuestion = answerData.cleanQuestion; // 计算匹配分数 let matchScore = 0; if (cleanPageQuestion.includes(cleanAnswerQuestion) || cleanAnswerQuestion.includes(cleanPageQuestion)) { matchScore = Math.min(cleanPageQuestion.length, cleanAnswerQuestion.length) / Math.max(cleanPageQuestion.length, cleanAnswerQuestion.length); } // 如果是更好的匹配,则更新 if (matchScore > bestMatchScore) { bestMatchScore = matchScore; bestMatchIndex = answerIdx; } }); // 如果找到匹配,则添加到排序后的列表 if (bestMatchIndex !== -1 && bestMatchScore > 0.5) { // 设置一个阈值,确保匹配质量 sortedDatas.push(setting.datas[bestMatchIndex]); usedAnswerIndices.add(bestMatchIndex); } }); // 第二轮:添加未匹配的答案到列表末尾 setting.datas.forEach((answerData, idx) => { if (!usedAnswerIndices.has(idx)) { sortedDatas.push(answerData); } }); // 如果成功排序,则更新数据 if (sortedDatas.length > 0) { setting.datas = sortedDatas; log(`答案列表已按当前题目顺序排序`); } } } // 更新 Vue 实例中的数据 if (window.vue && setting.datas.length > 0) { // 更新idAndOptions以反映新的顺序 setting.datas.forEach((item, idx) => { const questionNum = (idx + 1).toString(); // 提取原始idAndOptions中的选项部分 const optionsPart = item.idAndOptions.split('.').slice(1).join('.'); item.idAndOptions = `${questionNum}.${optionsPart}`; item.key = idx.toString(); // 更新key以匹配新顺序 }); window.vue.answerList = [...setting.datas]; window.vue.hasAnswer = true; // 设置答案获取状态为 true } log(`成功处理 ${setting.datas.length} 道题目`); log('答案获取完成,可以切换到答案列表查看'); } catch (error) { log("格式化答案时出错:" + error.message); if (window.vue) { window.vue.hasAnswer = false; } } } //初始化界面 function initView(){ // 检查验证是否有效 if (checkValidTime()) { // 验证仍然有效,直接继续初始化 continueInit(); return; } // 创建验证界面的HTML const createModal = () => { const modalHtml = ` <div id="secretModal" style=" position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 10000; "> <div style=" background: white; padding: 30px; border-radius: 15px; box-shadow: 0 8px 24px rgba(0,0,0,0.2); width: 320px; text-align: center; animation: modalFadeIn 0.3s ease; "> <img src="https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png" style=" width: 64px; height: 64px; margin-bottom: 15px; "> <h2 style=" margin: 0 0 20px 0; color: #333; font-size: 20px; font-weight: 500; ">请输入暗号---你好</h2> <input type="text" id="secretInput" style=" width: 100%; padding: 12px; margin-bottom: 15px; border: 2px solid #e8e8e8; border-radius: 8px; font-size: 16px; outline: none; transition: all 0.3s; box-sizing: border-box; " placeholder="请输入暗号..."> <button id="verifyButton" style=" width: 100%; padding: 12px; border: none; background-color: #1890ff; color: white; border-radius: 8px; font-size: 16px; cursor: pointer; transition: all 0.3s; box-sizing: border-box; ">验证</button> </div> </div> `; document.body.insertAdjacentHTML('beforeend', modalHtml); // 添加事件监听器 const button = document.getElementById('verifyButton'); if (button) { button.addEventListener('click', window.verifySecret); } // 添加键盘事件监听 addKeyboardListener(); // 自动聚焦输入框 setTimeout(() => { const input = document.getElementById('secretInput'); if (input) { input.focus(); } }, 100); }; // 添加动画样式 const style = document.createElement('style'); style.textContent = ` @keyframes shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } 20%, 40%, 60%, 80% { transform: translateX(5px); } } @keyframes modalFadeIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } @keyframes modalFadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-20px); } } `; document.head.appendChild(style); createModal(); } // 将原来的初始化代码移动到新的函数中 function continueInit() { var $div =$('<div class="rlBox minimized">' + ' <a-card title="宜宾学院智慧校园助手" style="width: 100%;height: 100%;">' + ' <template slot="extra">' + ' <span v-show="!close" style="margin-right: 10px; font-size: 12px; color: #999;">{{validTimeRemaining}}</span>' + ' <a-button :type="buttonColor" shape="circle" :icon="buttonIcon" @click="toClose" size="small"/>' + ' </template>' + ' <div style="margin-bottom: 15px;" v-show="!close">' + ' <div class="button-container">' + ' <a-button :type="autoLearning ? \'danger\' : \'warning\'" class="main-button" @click="startAutoLearning">{{autoLearning ? "停止学习" : "自动学习"}}</a-button>' + ' <a-button type="primary" class="main-button" @click="autoAnswer()">自动答题</a-button>' + ' <a-button type="default" class="main-button" @click="showMoreMenu">' + ' <span>更多功能</span>' + ' <a-icon type="down" />'+ ' </a-button>' + ' </div>' + ' </div>' + ' <a-tabs default-active-key="1" @change="callback" v-show="!close">' + ' <a-tab-pane key="1" tab="运行日志">' + ' <div class="rl-panel log">' + ' <p v-for="item in logs" class="log_content">' + ' {{item}}' + ' </p>' + ' </div>' + ' </a-tab-pane>' + ' <a-tab-pane key="2" :tab="answerTabTitle" :disabled="!hasAnswer">' + ' <div class="rl-panel">' + ' <a-table id="rlTable"' + ' :pagination="false" bordered size="small" :columns="columns" :data-source="answerList">' + ' </a-table>' + ' </div>' + ' </a-tab-pane>' + ' </a-tabs>' + ' </a-card>' + '</div>'); // 更新样式 const customStyle = ` .rlBox { position: fixed; top: 10px; right: 10px; width: 400px; z-index: 9999; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 10px; transition: all 0.3s ease; transform: none !important; overflow: hidden; } /* 优化最小化状态 */ .rlBox.minimized { width: 40px !important; height: 40px !important; padding: 0 !important; overflow: hidden; opacity: 0.8; cursor: pointer; border-radius: 50%; background: #1890ff; box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } /* 按钮容器样式 */ .button-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; width: 100%; } /* 主按钮样式 */ .main-button { width: 100%; height: 38px; display: flex; align-items: center; justify-content: center; border-radius: 4px; font-size: 14px; font-weight: 500; box-shadow: 0 2px 0 rgba(0,0,0,0.05); transition: all 0.3s; } /* 按钮样式优化 */ .ant-btn-danger { background: #ff4d4f !important; border-color: #ff4d4f !important; color: white !important; } .ant-btn-danger:hover { background: #ff7875 !important; border-color: #ff7875 !important; } .ant-btn-primary { background: #1890ff !important; border-color: #1890ff !important; } .ant-btn-primary:hover { background: #40a9ff !important; border-color: #40a9ff !important; } .ant-btn-success { background: #52c41a !important; border-color: #52c41a !important; color: white !important; } .ant-btn-success:hover { background: #73d13d !important; border-color: #73d13d !important; } .ant-btn-warning { background: #faad14 !important; border-color: #faad14 !important; color: white !important; } .ant-btn-warning:hover { background: #ffc53d !important; border-color: #ffc53d !important; } /* 下拉菜单样式 */ .more-menu { position: absolute; top: 135px; right: 30px; background: white; border-radius: 4px; box-shadow: 0 3px 6px -4px rgba(0,0,0,0.12), 0 6px 16px 0 rgba(0,0,0,0.08), 0 9px 28px 8px rgba(0,0,0,0.05); z-index: 10000; min-width: 120px; display: none; } .more-menu.show { display: block; animation: fadeIn 0.2s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .menu-item { padding: 10px 16px; color: #333; cursor: pointer; transition: all 0.3s; display: flex; align-items: center; } .menu-item:hover { background: #f5f5f5; } .menu-item i { margin-right: 8px; font-size: 14px; } .menu-divider { height: 1px; background: #f0f0f0; margin: 4px 0; } /* 最小化状态下的卡片样式 */ .rlBox.minimized .ant-card { background: transparent; border: none; box-shadow: none; } /* 最小化状态下的标题隐藏 */ .rlBox.minimized .ant-card-head-title { display: none; } /* 最小化状态下的开按钮样式 */ .rlBox.minimized .ant-btn-circle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: transparent !important; border: 2px solid white !important; color: white !important; box-shadow: none; } .rlBox.minimized .ant-btn-circle:hover { background: rgba(255, 255, 255, 0.2) !important; } /* 最小化状态悬停效果 */ .rlBox.minimized:hover { opacity: 1; box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4); transform: translateY(-2px) !important; } /* 移动端适配 */ @media screen and (max-width: 768px) { .rlBox { width: 300px; } .rlBox.minimized { width: 36px !important; height: 36px !important; } } /* 表格容器样式 */ .rl-panel { height: auto; max-height: calc(100vh - 250px); overflow-y: auto; overflow-x: hidden; } /* 表格样式优化 */ .ant-table-wrapper { overflow: visible; } .ant-table { min-width: 100%; background: transparent; } /* 美化滚动条样式 */ .rl-panel::-webkit-scrollbar { width: 6px; height: 6px; } .rl-panel::-webkit-scrollbar-thumb { background: #d9d9d9; border-radius: 3px; } .rl-panel::-webkit-scrollbar-track { background: #f0f0f0; border-radius: 3px; } /* 表格单元格样式 */ .ant-table-tbody > tr > td { white-space: normal; word-break: break-word; padding: 8px 16px; line-height: 1.5; max-width: 0; } /* 表格头部样式 */ .ant-table-thead > tr > th { background: #f5f5f5; padding: 12px 16px; white-space: nowrap; position: sticky; top: 0; z-index: 2; } /* 保表格布局合理 */ #rlTable { table-layout: fixed; width: 100%; } /* 移动端适配 */ @media screen and (max-width: 768px) { .rl-panel { max-height: calc(100vh - 200px); } .ant-table-tbody > tr > td { padding: 6px 12px; } .ant-table-thead > tr > th { padding: 8px 12px; } } `; $("body").append($div); GM_addStyle(GM_getResourceText("cs1")); GM_addStyle(GM_getResourceText("cs2")); GM_addStyle(customStyle); // 创建更多菜单DOM const moreMenuHtml = ` <div id="moreMenu" class="more-menu"> <div class="menu-item" id="videoSkipBtn"> <i class="anticon anticon-forward"></i>秒过视频 </div> <div class="menu-item" id="exportExcelBtn"> <i class="anticon anticon-export"></i>导出题库 </div> <div class="menu-item" id="clearLogsBtn"> <i class="anticon anticon-delete"></i>清除日志 </div> <div class="menu-item" id="clearProgressBtn"> <i class="anticon anticon-delete"></i>清除进度 </div> <div class="menu-divider"></div> <div class="menu-item" id="settingsBtn"> <i class="anticon anticon-setting"></i>答题设置 </div> </div> `; $("body").append(moreMenuHtml); // 手动定义直接绑定的事件处理函数 window.videoSkipHandler = function() { if (window.vue) window.vue.videoSkip(); document.getElementById('moreMenu').classList.remove('show'); }; window.exportExcelHandler = function() { if (window.vue) window.vue.exportExcel(); document.getElementById('moreMenu').classList.remove('show'); }; window.clearLogsHandler = function() { if (window.vue) window.vue.clearLogs(); document.getElementById('moreMenu').classList.remove('show'); }; window.clearProgressHandler = function() { if (window.vue) window.vue.clearProgress(); document.getElementById('moreMenu').classList.remove('show'); }; window.openSettingsHandler = function() { if (window.vue) window.vue.openSettings(); document.getElementById('moreMenu').classList.remove('show'); }; // 直接绑定事件 document.getElementById('videoSkipBtn').onclick = window.videoSkipHandler; document.getElementById('exportExcelBtn').onclick = window.exportExcelHandler; document.getElementById('clearLogsBtn').onclick = window.clearLogsHandler; document.getElementById('clearProgressBtn').onclick = window.clearProgressHandler; document.getElementById('settingsBtn').onclick = window.openSettingsHandler; var vue = new Vue({ el: '.rlBox', data:{ logs: setting.logs, close: true, key: '1', columns:[ { title: '序号.选项', dataIndex: 'idAndOptions', key: 'idAndOptions', width: '80px', fixed: 'left', align: 'center' }, { title: '题目', dataIndex: 'question', key: 'question', width: '45%', ellipsis: true }, { title: '答案', dataIndex: 'answer', key: 'answer', width: '45%', customRender: (text) => { return text ? text.split('\n').join('<br/>') : ''; } } ], answerList: [], // 初化为空数组 isDragging: false, currentX: 0, currentY: 0, initialX: 0, initialY: 0, xOffset: 0, yOffset: 0, hasAnswer: false, // 添加答案获取状态标志 validUntil: localStorage.getItem('scriptValidUntil') || null, autoLearning: false, // 添加自动学习状态标志 }, mounted() { window.vue = this; this.initDragEvents(); // 修改初始化位置设置 const box = document.querySelector('.rlBox'); box.style.right = '0px'; box.style.left = 'auto'; box.setAttribute('data-expand-side', 'right'); // 添加窗口大小改变监听 window.addEventListener('resize', this.checkPosition); // 每分钟更新一次验证时间显示 setInterval(() => { this.validUntil = localStorage.getItem('scriptValidUntil'); }, 60000); // 检查是否需要恢复学习进度 this.checkLearningProgress(); // 添加页面URL变化监听 this.initUrlChangeListener(); }, computed:{ isShow(){ return this.close ? 0.8 : 1.0; }, buttonIcon(){ return this.close ? 'plus' : 'minus'; }, buttonColor(){ return this.close ? 'primary' : 'default'; }, answerTabTitle() { return this.hasAnswer ? '答案列表' : '答案列表 (等待获取...)'; }, validTimeRemaining() { const validTime = getValidTime(); if (!validTime) return '未验证'; const remaining = validTime - Date.now(); if (remaining <= 0) return '验证已过期'; const days = Math.floor(remaining / (24 * 60 * 60 * 1000)); const hours = Math.floor((remaining % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); return `验证剩余: ${days}天${hours}小时`; } }, methods: { callback(key) { if (key === '2' && !this.hasAnswer) { this.$message.warning('请等待答案获取完成后再查看答案列表'); this.key = '1'; // 保持在日志页面 return; } this.key = key; }, toClose() { this.close = !this.close; const box = document.querySelector('.rlBox'); const rect = box.getBoundingClientRect(); const windowWidth = window.innerWidth; if (this.close) { // 最小化时,判断靠近哪边 box.style.transition = 'all 0.3s ease'; const centerX = rect.left + rect.width / 2; if (centerX > windowWidth / 2) { // 靠右 box.style.right = '10px'; box.style.left = 'auto'; box.setAttribute('data-side', 'right'); } else { // 靠左 box.style.left = '10px'; box.style.right = 'auto'; box.setAttribute('data-side', 'left'); } box.classList.add('minimized'); } else { // 展开时 box.style.transition = 'all 0.3s ease'; box.classList.remove('minimized'); const side = box.getAttribute('data-side') || 'right'; if (side === 'right') { box.style.right = '10px'; box.style.left = 'auto'; } else { box.style.left = '10px'; box.style.right = 'auto'; } } }, startAutoLearning() { if(this.autoLearning) { this.autoLearning = false; localStorage.removeItem('learningProgress'); log("已停止自动学习"); this.$message.info("已停止自动学习"); return; } this.autoLearning = true; this.$message.success({ content: "自动学习已启动", duration: 3, description: "系统将自动完成视频观看、文档阅读和测验答题" }); log("开始自动学习..."); this.processCurrentContent(); }, processCurrentContent() { if(!this.autoLearning) { log("自动学习已停止"); return; } const pageType = checkPageType(); if(!pageType) { log("未识别到当前内容类型,等待加载..."); setTimeout(() => this.processCurrentContent(), 1000); return; } const currentTab = document.querySelector('.tab-active .tab-inner'); const itemName = currentTab.getAttribute('itemname'); const currentChapter = document.querySelector('.nav-text.current'); const chapterName = currentChapter ? currentChapter.querySelector('.node-text').textContent.trim() : '未知章节'; // 显示当前学习内容提示 this.$message.info({ content: `正在处理: ${itemName}`, duration: 3, description: `章节: ${chapterName}` }); log(`正在处理 ${chapterName} - ${itemName} (${pageType})`); // 根据页面类型执行不同操作 switch(pageType) { case 'video': this.handleVideo(); break; case 'document': this.handleDocument(); break; case 'quiz': // 使用全局函数而不是方法 handleQuiz(); break; } }, handleVideo() { let video = document.getElementsByTagName("video")[0]; if(!video) { log("当前页面不存在视频,请确保视频已加载"); setTimeout(() => this.handleVideo(), 1000); return; } // 显示视频处理提示 this.$message.info({ content: "正在处理视频", duration: 3, description: "系统将自动完成视频观看" }); video.addEventListener('ended', () => { if(this.autoLearning) { log("视频播放完成,准备切换下一个内容"); this.$message.success({ content: "视频学习完成", duration: 2 }); this.switchToNextContent(); } }); try { if (video.duration && !isNaN(video.duration)) { video.currentTime = video.duration - 0.1; video.playbackRate = 1; log("正在完成当前视频..."); } else { log("等待视频加载..."); video.addEventListener('loadedmetadata', () => { video.currentTime = video.duration - 0.1; video.playbackRate = 1; log("视频加载完成,正在处理..."); }); } } catch (error) { log("处理视频时出错:" + error.message); } }, handleDocument() { const totalPages = parseInt(document.querySelector('.flexpaper_lblTotalPages')?.textContent.replace('/ ', '')) || 0; if(totalPages === 0) { log("未检测到文档页数,请确保文档已加载"); setTimeout(() => this.handleDocument(), 1000); return; } // 显示文档阅读提示 this.$message.info({ content: "开始阅读文档", duration: 3, description: `共 ${totalPages} 页,系统将自动完成阅读` }); log(`正在阅读文档,共${totalPages}页`); let currentPage = 1; const readInterval = setInterval(() => { if(!this.autoLearning || currentPage >= totalPages) { clearInterval(readInterval); if(this.autoLearning) { log(`文档阅读完成,准备切换到下一个内容`); this.$message.success({ content: "文档阅读完成", duration: 2 }); this.switchToNextContent(); } return; } try { const nextButton = document.querySelector('.flexpaper_bttnPrevNext'); if(nextButton) { nextButton.click(); // 每5页显示一次进度 if(currentPage % 5 === 0) { this.$message.info({ content: `阅读进度: ${Math.round((currentPage/totalPages) * 100)}%`, duration: 1 }); } log(`正在阅读第${currentPage}页,共${totalPages}页`); currentPage++; } else { log("未找到翻页按钮,尝试重新加载"); setTimeout(() => this.handleDocument(), 1000); } } catch (error) { log(`翻页过程出错: ${error.message}`); clearInterval(readInterval); setTimeout(() => this.handleDocument(), 1000); } }, 2000); }, switchToNextContent() { if(!this.autoLearning) return; // 获取所有标签页 const tabs = document.querySelectorAll('.tab-inner'); let currentTabIndex = -1; // 找到当前激活的标签页 tabs.forEach((tab, index) => { if(tab.parentElement.classList.contains('tab-active')) { currentTabIndex = index; } }); // 检查是否还有下一个内容 if(currentTabIndex < tabs.length - 1) { // 还有下一个内容,继续在当前章节学习 const nextTab = tabs[currentTabIndex + 1]; const nextTabName = nextTab.getAttribute('itemname'); log(`切换到下一个内容: ${nextTabName}`); nextTab.click(); setTimeout(() => this.processCurrentContent(), 1500); } else { // 当前章节的所有内容已学习完成 const currentChapter = document.querySelector('.nav-text.current'); if (currentChapter) { const chapterName = currentChapter.querySelector('.node-text').textContent.trim(); log(`章节 ${chapterName} 的所有内容已学习完成,准备切换到下一章节...`); } // 检查是否是最后一个章节 const allChapters = document.querySelectorAll('.sidebar .nav-list .nav-item.sup-item'); let currentChapterIndex = -1; allChapters.forEach((chapter, index) => { if(chapter.querySelector('.nav-text.current')) { currentChapterIndex = index; } }); if(currentChapterIndex < allChapters.length - 1) { // 不是最后一个章节,切换到下一章节 log(`当前是第 ${currentChapterIndex + 1} 章,共 ${allChapters.length} 章`); this.switchToNextChapter(); } else { // 所有章节都已完成 this.autoLearning = false; log("所有章节学习完成!"); this.$notification.success({ message: '学习完成', description: '恭喜!已完成所有章节的学习', duration: 0 }); } } }, switchToNextChapter() { try { const allChapters = document.querySelectorAll('.sidebar .nav-list .nav-item.sup-item'); if (!allChapters.length) { this.autoLearning = false; log("未找到章节列表,自动学习已停止"); this.$message.warning("未找到章节列表"); return; } log(`共发现 ${allChapters.length} 个章节`); let currentChapterIndex = -1; allChapters.forEach((chapter, index) => { if(chapter.querySelector('.nav-text.current')) { currentChapterIndex = index; } }); if(currentChapterIndex < allChapters.length - 1) { const nextChapter = allChapters[currentChapterIndex + 1]; const nextChapterLink = nextChapter.querySelector('.nav-text'); const nextChapterName = nextChapter.querySelector('.node-text').textContent.trim(); if (!nextChapterLink) { throw new Error("无法找到章节链接"); } const unitId = nextChapterLink.getAttribute('unitid'); if (!unitId) { throw new Error("无法获取章节ID"); } // 显示章节切换提示 this.$notification.info({ message: '准备切换章节', description: `即将切换到: ${nextChapterName}`, duration: 3 }); log(`准备切换到下一章节: ${nextChapterName} (${currentChapterIndex + 2}/${allChapters.length})`); // 直接点击章节链接 nextChapterLink.click(); // 等待页面加载完成 setTimeout(() => { try { // 展开子章节列表 const subNav = nextChapter.querySelector('.sub-nav'); if (!subNav) { throw new Error("未找到子章节列表"); } subNav.style.display = 'block'; // 获取第一个子章节 const firstSubChapter = subNav.querySelector('.sub-nav-text'); if (!firstSubChapter) { throw new Error("未找到子章节"); } // 点击第一个子章节 log(`点击第一个子章节...`); firstSubChapter.click(); // 等待子章节内容加载 setTimeout(() => { try { // 获取并点击第一个内容标签 const tabList = document.querySelectorAll('.tab-inner'); if (!tabList || tabList.length === 0) { throw new Error("未找到可学习的内容"); } const firstTab = tabList[0]; log(`开始学习: ${firstTab.getAttribute('itemname')}`); firstTab.click(); // 确保自动学习状态保持开启 this.autoLearning = true; // 保存新的学习进度 const newProgress = { isLearning: true, currentChapter: unitId, currentContent: firstTab.getAttribute('itemid'), timestamp: Date.now() }; localStorage.setItem('learningProgress', JSON.stringify(newProgress)); // 等待内容加载完成后开始学习 setTimeout(() => { if (this.autoLearning) { log(`开始自动学习新章节内容...`); this.processCurrentContent(); } }, 1500); } catch (error) { throw new Error(`加载内容失败: ${error.message}`); } }, 1500); } catch (error) { throw new Error(`切换子章节失败: ${error.message}`); } }, 2000); } else { this.autoLearning = false; log("已完成所有章节的学习!"); this.$notification.success({ message: '学习完成', description: '恭喜!已完成所有章节的学习', duration: 0 }); } } catch (error) { this.autoLearning = false; log(`切换章节失败: ${error.message}`); this.$message.error("切换章节失败,请刷新页面重试"); } }, checkPosition() { const box = document.querySelector('.rlBox'); const rect = box.getBoundingClientRect(); const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; // 检查并修正水平位置 if (rect.right > windowWidth) { box.style.right = '0px'; box.style.left = 'auto'; } if (rect.left < 0) { box.style.left = '0px'; box.style.right = 'auto'; } // 检查并修正垂直位置 if (rect.bottom > windowHeight) { box.style.top = `${windowHeight - rect.height - 10}px`; } if (rect.top < 0) { box.style.top = '10px'; } }, autoAnswer() { log("开始自动答题..."); // 检查是否已获取答案 if (!setting.datas || setting.datas.length === 0) { log("未找到答案信息,请先获取答案"); return; } log(`共发现 ${setting.datas.length} 道题目的答案`); let answeredCount = 0; // 获取所有题目容器 const allQuestionContainers = document.querySelectorAll('.view-test'); if (!allQuestionContainers || allQuestionContainers.length === 0) { log("未找到题目容器,请确认是否在答题页面"); return; } log(`页面上共有 ${allQuestionContainers.length} 道题目`); // 遍历所有题目容器,确保每个题目都能找到对应的答案 allQuestionContainers.forEach((questionContainer, containerIndex) => { try { // 获取题目文本,用于匹配答案 const questionTextElement = questionContainer.querySelector('.test-text-tutami'); if (!questionTextElement) { log(`第 ${containerIndex + 1} 题未找到题目文本`); return; } const questionText = questionTextElement.textContent.trim(); // 在答案列表中查找匹配的题目 let matchedAnswerData = null; let matchIndex = -1; // 首先尝试按索引匹配 if (containerIndex < setting.datas.length) { matchedAnswerData = setting.datas[containerIndex]; matchIndex = containerIndex; } // 如果索引匹配失败或题目不匹配,则尝试通过题目内容匹配 if (!matchedAnswerData || !questionText.includes(cleanHtmlTags(matchedAnswerData.question))) { // 在答案列表中查找匹配的题目 for (let i = 0; i < setting.datas.length; i++) { const answerData = setting.datas[i]; const cleanQuestion = cleanHtmlTags(answerData.question); // 检查题目文本是否匹配 if (questionText.includes(cleanQuestion) || cleanQuestion.includes(questionText)) { matchedAnswerData = answerData; matchIndex = i; break; } } } if (!matchedAnswerData) { log(`第 ${containerIndex + 1} 题未找到匹配的答案`); return; } // 获取匹配到的答案 const answer = matchedAnswerData.answer; if (!answer) { log(`第 ${containerIndex + 1} 题答案为空`); return; } log(`第 ${containerIndex + 1} 题匹配到答案列表中的第 ${matchIndex + 1} 题`); // 判断题目类型 const isMultiChoice = questionContainer.querySelector('.input-c') !== null; const isSingleChoice = questionContainer.querySelector('.input-r') !== null; const isFillBlank = questionContainer.querySelector('.fillblank') !== null; if (isMultiChoice || isSingleChoice) { // 处理选择题 const answerLetters = answer.split('\n').map(a => a.trim().charAt(0)); const options = questionContainer.querySelectorAll('.t-option'); options.forEach((option, idx) => { const letter = String.fromCharCode(65 + idx); if (answerLetters.includes(letter)) { // 查找选项的点击元素 const clickTarget = option.querySelector('a[href="javascript:void(0)"]') || option.querySelector(isMultiChoice ? '.input-c' : '.input-r'); if (clickTarget) { // 检查是否已经选中 const isChecked = option.querySelector(isMultiChoice ? '.input-c.selected' : '.input-r.selected'); if (!isChecked) { clickTarget.click(); log(`第 ${containerIndex + 1} 题选择了选项 ${letter}`); } } } }); answeredCount++; } else if (isFillBlank) { // 处理填空题 const input = questionContainer.querySelector('.fillblank'); if (input) { input.value = answer.replace(/^.*?\./,'').trim(); // 移除可能的选项标记 // 触发必要的事件 input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); log(`第 ${containerIndex + 1} 题填写了答案: ${answer}`); answeredCount++; } } } catch (error) { log(`第 ${containerIndex + 1} 题自动答题出错: ${error.message}`); } }); // 显示完成提示 log(`自动答题完成!成功答题 ${answeredCount} 道题目`); this.$message.success(`自动答题完成!成功答题 ${answeredCount} 道题目,请检查后手动提交。`); }, exportExcel(){ // 检查是否有答案数据 if (!this.answerList || this.answerList.length === 0) { this.$message.error('没有可导出的答案数!请等待答案获取完成。'); log('导出失败:没有答案数据'); return; } // 准备数据 const data = this.answerList.map(item => ({ '序号.选项': item.idAndOptions, '题目': item.question, '答案': item.answer })); // 创建工作簿 const wb = XLSX.utils.book_new(); // 创建工作表 const ws = XLSX.utils.json_to_sheet(data); // 设置列宽 const colWidths = { '序号.选项': 10, '题目': 50, '答案': 30 }; ws['!cols'] = Object.keys(colWidths).map(key => ({ wch: colWidths[key] })); // 将工作表添加到工作簿 XLSX.utils.book_append_sheet(wb, ws, "题库"); // 生成并下载文件 XLSX.writeFile(wb, "题库.xlsx"); log('题库已导出为 Excel 文件'); }, clearLogs() { // 清空所有现有日志 this.logs = []; setting.logs = []; // 添加清除提示 const clearMessage = [ '日志已清除', '------------------------', ]; // 直接设置新的日志数组,而不是使用 log 函数 this.logs = clearMessage; setting.logs = [...clearMessage]; // 阻止其他日志添加 setTimeout(() => { // 确保清除状态保持 if (this.logs.length > clearMessage.length) { this.logs = [...clearMessage]; setting.logs = [...clearMessage]; } }, 200); }, // 视频快速完成 videoSkip() { const pageType = checkPageType(); if (pageType !== 'video') { this.$message.warning('当前页面不是视频页面,请在视频页面使用此功能'); log('秒过视频功能仅在视频页面有效'); return; } log('正在尝试秒过视频...'); try { // 尝试找到视频元素 const videoPlayer = document.querySelector('video'); if (!videoPlayer) { log('未找到视频元素,请确认页面已完全加载'); this.$message.error('未找到视频元素,请刷新页面重试'); return; } // 设置视频当前时间为接近结尾 const duration = videoPlayer.duration || 0; if (duration <= 0) { log('无法获取视频时长,请等待视频加载完成'); this.$message.warning('请等待视频加载完成后再试'); return; } // 设置为视频末尾前几秒 videoPlayer.currentTime = duration - 1; log(`已将视频进度设置为结束位置(${Math.floor(duration)}秒)`); // 模拟视频播放完成事件 setTimeout(() => { videoPlayer.play(); log('视频即将自动结束,请等待系统记录完成状态'); this.$message.success('视频已跳至结尾,即将自动完成'); }, 500); } catch (error) { log(`秒过视频时出错: ${error.message}`); this.$message.error('操作失败,请刷新页面重试'); } }, // 显示更多功能菜单 showMoreMenu() { const menu = document.getElementById('moreMenu'); if (menu) { // 切换显示状态 if (menu.classList.contains('show')) { menu.classList.remove('show'); } else { menu.classList.add('show'); // 点击其他区域关闭菜单 const closeMenu = (e) => { if (!menu.contains(e.target) && !e.target.closest('.main-button')) { menu.classList.remove('show'); document.removeEventListener('click', closeMenu); } }; // 延迟添加事件监听,避免立即触发 setTimeout(() => { document.addEventListener('click', closeMenu); }, 100); } } }, // 打开设置对话框 openSettings() { // 清除可能存在的旧模态框 const oldModal = document.getElementById('settingsModal'); if (oldModal) { document.body.removeChild(oldModal); } // 创建模态框容器 const modalDiv = document.createElement('div'); modalDiv.id = 'settingsModal'; modalDiv.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 10001; `; // 创建模态框内容 const modalContent = document.createElement('div'); modalContent.style.cssText = ` background: white; border-radius: 4px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); width: 400px; max-width: 90%; position: relative; `; // 标题 const modalHeader = document.createElement('div'); modalHeader.style.cssText = ` padding: 16px 24px; color: rgba(0, 0, 0, 0.85); background: #fff; border-bottom: 1px solid #f0f0f0; border-radius: 4px 4px 0 0; font-weight: 500; font-size: 16px; `; modalHeader.innerText = "答题设置"; modalContent.appendChild(modalHeader); // 内容区域 const modalBody = document.createElement('div'); modalBody.style.cssText = ` padding: 24px; font-size: 14px; line-height: 1.5; word-wrap: break-word; `; // 创建设置项 const autoSubmitDiv = document.createElement('div'); autoSubmitDiv.style.cssText = ` margin-bottom: 16px; display: flex; align-items: flex-start; `; const autoSubmitCheckbox = document.createElement('input'); autoSubmitCheckbox.type = 'checkbox'; autoSubmitCheckbox.id = 'autoSubmitSetting'; autoSubmitCheckbox.checked = setting.autoSubmit; autoSubmitCheckbox.style.cssText = ` margin-right: 8px; margin-top: 3px; `; const autoSubmitLabel = document.createElement('label'); autoSubmitLabel.htmlFor = 'autoSubmitSetting'; autoSubmitLabel.innerHTML = ` 自动提交答案 <div style="font-size: 12px; color: #999; margin-top: 4px;"> 自动答题完成后自动提交答卷 </div> `; autoSubmitDiv.appendChild(autoSubmitCheckbox); autoSubmitDiv.appendChild(autoSubmitLabel); modalBody.appendChild(autoSubmitDiv); // 自动进入考试设置 const autoEnterExamDiv = document.createElement('div'); autoEnterExamDiv.style.cssText = ` margin-bottom: 16px; display: flex; align-items: flex-start; `; const autoEnterExamCheckbox = document.createElement('input'); autoEnterExamCheckbox.type = 'checkbox'; autoEnterExamCheckbox.id = 'autoEnterExamSetting'; autoEnterExamCheckbox.checked = setting.autoEnterExam; autoEnterExamCheckbox.style.cssText = ` margin-right: 8px; margin-top: 3px; `; const autoEnterExamLabel = document.createElement('label'); autoEnterExamLabel.htmlFor = 'autoEnterExamSetting'; autoEnterExamLabel.innerHTML = ` 自动进入考试 <div style="font-size: 12px; color: #999; margin-top: 4px;"> 自动点击进入/继续考试按钮 </div> `; autoEnterExamDiv.appendChild(autoEnterExamCheckbox); autoEnterExamDiv.appendChild(autoEnterExamLabel); modalBody.appendChild(autoEnterExamDiv); modalContent.appendChild(modalBody); // 底部按钮 const modalFooter = document.createElement('div'); modalFooter.style.cssText = ` padding: 10px 16px; text-align: right; background: transparent; border-top: 1px solid #f0f0f0; border-radius: 0 0 4px 4px; `; // 取消按钮 const cancelButton = document.createElement('button'); cancelButton.className = 'ant-btn'; cancelButton.style.cssText = ` line-height: 1.5; display: inline-block; font-weight: 400; text-align: center; cursor: pointer; background-image: none; border: 1px solid #d9d9d9; white-space: nowrap; padding: 0 15px; font-size: 14px; border-radius: 4px; height: 32px; user-select: none; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); position: relative; color: rgba(0, 0, 0, 0.65); background-color: #fff; margin-right: 8px; `; cancelButton.innerText = "取消"; cancelButton.onclick = function() { document.body.removeChild(modalDiv); }; // 确定按钮 const okButton = document.createElement('button'); okButton.className = 'ant-btn ant-btn-primary'; okButton.style.cssText = ` line-height: 1.5; display: inline-block; font-weight: 400; text-align: center; cursor: pointer; background-image: none; border: 1px solid #1890ff; white-space: nowrap; padding: 0 15px; font-size: 14px; border-radius: 4px; height: 32px; user-select: none; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); position: relative; color: #fff; background-color: #1890ff; `; okButton.innerText = "保存"; okButton.onclick = () => { try { // 获取设置值 const autoSubmit = document.getElementById('autoSubmitSetting').checked; const autoEnterExam = document.getElementById('autoEnterExamSetting').checked; // 保存设置 setting.autoSubmit = autoSubmit; setting.autoEnterExam = autoEnterExam; // 保存到localStorage localStorage.setItem('examConfig', JSON.stringify({ autoSubmit, autoEnterExam })); // 成功提示 this.$message.success('设置已保存'); log('已更新答题设置'); // 关闭模态框 document.body.removeChild(modalDiv); } catch (error) { console.error('保存设置出错:', error); // 出错提示 this.$message.error('保存失败,请刷新页面重试'); } }; modalFooter.appendChild(cancelButton); modalFooter.appendChild(okButton); modalContent.appendChild(modalFooter); // 点击背景关闭模态框 modalDiv.onclick = function(event) { if (event.target === modalDiv) { document.body.removeChild(modalDiv); } }; // 添加到body modalDiv.appendChild(modalContent); document.body.appendChild(modalDiv); // 添加动画效果 modalContent.animate([ { opacity: 0, transform: 'translateY(-20px)' }, { opacity: 1, transform: 'translateY(0)' } ], { duration: 200, easing: 'ease-out' }); }, // 优化拖动处理 initDragEvents() { const box = document.querySelector('.rlBox'); const dragZone = document.querySelector('.ant-card-head'); let startX, startY, initialMouseX, initialMouseY; dragZone.addEventListener('mousedown', (e) => { if (this.close) return; // 小化时禁止拖动 e.preventDefault(); this.isDragging = true; const rect = box.getBoundingClientRect(); startX = rect.left; startY = rect.top; initialMouseX = e.clientX; initialMouseY = e.clientY; box.style.transition = 'none'; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', (e) => { if (!this.isDragging) return; const dx = e.clientX - initialMouseX; const dy = e.clientY - initialMouseY; let newX = startX + dx; let newY = startY + dy; box.style.left = `${newX}px`; box.style.top = `${newY}px`; box.style.right = 'auto'; // 实时检查位置 this.checkPosition(); }); document.addEventListener('mouseup', () => { if (this.isDragging) { this.isDragging = false; box.style.transition = 'all 0.2s'; document.body.style.userSelect = ''; } }); // 添加摸支持 dragZone.addEventListener('touchstart', (e) => { const touch = e.touches[0]; const rect = box.getBoundingClientRect(); startX = rect.left; startY = rect.top; initialMouseX = touch.clientX; initialMouseY = touch.clientY; this.isDragging = true; }); document.addEventListener('touchmove', (e) => { if (!this.isDragging) return; e.preventDefault(); const touch = e.touches[0]; const dx = touch.clientX - initialMouseX; const dy = touch.clientY - initialMouseY; let newX = startX + dx; let newY = startY + dy; const maxX = window.innerWidth - box.offsetWidth; const maxY = window.innerHeight - box.offsetHeight; newX = Math.min(Math.max(0, newX), maxX); newY = Math.min(Math.max(0, newY), maxY); box.style.left = `${newX}px`; box.style.top = `${newY}px`; }); document.addEventListener('touchend', () => { this.isDragging = false; }); }, // 添加新方法 saveLearningProgress() { try { const currentChapter = document.querySelector('.nav-text.current'); const currentContent = document.querySelector('.tab-active .tab-inner'); if (currentChapter && this.autoLearning) { const progress = { isLearning: this.autoLearning, currentChapter: currentChapter.getAttribute('unitid'), currentContent: currentContent ? currentContent.getAttribute('itemid') : null, timestamp: Date.now(), nextChapterUrl: null, // 添加下一章节URL isPageSwitching: false // 添加页面切换标记 }; localStorage.setItem('learningProgress', JSON.stringify(progress)); setting.learningProgress = progress; } } catch (error) { console.log('保存学习进度时出错:', error); } }, checkLearningProgress() { try { const savedProgress = localStorage.getItem('learningProgress'); if (savedProgress) { const progress = JSON.parse(savedProgress); const timeDiff = Date.now() - progress.timestamp; // 如果保存时间在30分钟内且正在学习 if (timeDiff < 30 * 60 * 1000 && progress.isLearning) { setting.learningProgress = progress; log('检测到未完成的学习进度,准备恢复...'); // 设置自动学习状态 this.autoLearning = true; // 等待页面完全加载 const waitForLoad = () => { // 检查必要的DOM元素是否存在 const currentChapter = document.querySelector('.nav-text.current'); const tabInner = document.querySelector('.tab-inner'); if (!currentChapter || !tabInner) { log('等待页面加载...'); setTimeout(waitForLoad, 1000); return; } // 获取当前章节信息 const chapterName = currentChapter.querySelector('.node-text')?.textContent.trim(); log(`恢复学习章节: ${chapterName || '未知章节'}`); // 确保子章节列表展开 const subNav = currentChapter.closest('.nav-item.sup-item').querySelector('.sub-nav'); if (subNav) { subNav.style.display = 'block'; } // 获取第一个内容标签 const firstTab = document.querySelector('.tab-inner'); if (firstTab) { log(`开始学习: ${firstTab.getAttribute('itemname')}`); firstTab.click(); // 延迟启动学习 setTimeout(() => { if (this.autoLearning) { log('开始自动学习...'); this.processCurrentContent(); } }, 1500); } else { log('未找到可学习的内容,请刷新页面重试'); } }; // 开始等待加载 setTimeout(waitForLoad, 2000); } else { localStorage.removeItem('learningProgress'); } } } catch (error) { console.error('检查学习进度时出错:', error); log('恢复进度失败,请刷新页面重试'); } }, restoreLearningProgress() { try { const progress = setting.learningProgress; if (!progress || !progress.isLearning) return; // 查找并点击对应章节 const chapterLink = document.querySelector(`.nav-text[unitid="${progress.currentChapter}"]`); if (chapterLink) { log('正在恢复上次的学习进度...'); chapterLink.click(); // 等待章节展开后点击对应内容 setTimeout(() => { const contentLink = document.querySelector(`.tab-inner[itemid="${progress.currentContent}"]`); if (contentLink) { contentLink.click(); // 恢复自动学习状态 setTimeout(() => { this.autoLearning = true; this.processCurrentContent(); log('学习进度已恢复,继续学习'); }, 1500); } }, 1000); } } catch (error) { console.log('恢复学习进度时出错:', error); } }, // 添加清除进度方法 clearProgress() { try { localStorage.removeItem('learningProgress'); this.autoLearning = false; log("已清除学习进度"); this.$message.success("学习进度已清除"); } catch (error) { log("清除进度失败: " + error.message); this.$message.error("清除进度失败"); } }, // 新增方法:开始学习新章节 startLearningNewChapter(chapter, chapterName) { const startLearning = () => { const subNav = chapter.querySelector('.sub-nav'); if (!subNav) { throw new Error("未找到子章节列表"); } subNav.style.display = 'block'; // 获取第一个子章节 const firstSubChapter = subNav.querySelector('.sub-nav-text'); if (!firstSubChapter) { throw new Error("未找到子章节"); } log(`点击第一个子章节...`); firstSubChapter.click(); // 等待子章节内容加载 setTimeout(() => { // 获取并点击第一个内容标签 const tabList = document.querySelectorAll('.tab-inner'); if (!tabList || tabList.length === 0) { throw new Error("未找到可学习的内容"); } const firstTab = tabList[0]; log(`开始学习: ${firstTab.getAttribute('itemname')}`); firstTab.click(); // 确保内容加载完成后开始学习 setTimeout(() => { if (this.autoLearning) { log(`开始自动学习新章节内容...`); this.processCurrentContent(); } }, 1500); }, 1000); }; // 重试机制 let retryCount = 0; const maxRetries = 3; const tryStartLearning = () => { try { startLearning(); } catch (error) { retryCount++; if (retryCount < maxRetries) { log(`启动学习失败,${maxRetries - retryCount}秒后重试...`); setTimeout(tryStartLearning, 1000); } else { this.autoLearning = false; log(`无法启动学习: ${error.message}`); this.$message.error("无法启动学习,请手动点击内容或刷新页面"); } } }; // 延迟启动,确保DOM已更新 setTimeout(tryStartLearning, 1000); }, // 添加URL变化监听器 initUrlChangeListener() { // 监听 popstate 事件 window.addEventListener('popstate', () => { this.handleUrlChange(); }); // 重写 pushState 和 replaceState 方法以捕获URL变化 const originalPushState = window.history.pushState; const originalReplaceState = window.history.replaceState; window.history.pushState = function() { originalPushState.apply(this, arguments); window.vue.handleUrlChange(); }; window.history.replaceState = function() { originalReplaceState.apply(this, arguments); window.vue.handleUrlChange(); }; }, // 处理URL变化 handleUrlChange() { // 检查是否是章节页面 if (location.pathname.includes('/study/unit/')) { // 获取当前章节ID const currentUnitId = location.pathname.match(/\/unit\/(\d+)/)?.[1]; if (currentUnitId) { // 检查是否有保存的进度 const savedProgress = localStorage.getItem('learningProgress'); if (savedProgress) { const progress = JSON.parse(savedProgress); // 如果当前章节与保存的进度章节匹配,且是自动学习状态 if (progress.isLearning && progress.currentChapter === currentUnitId) { log('检测到章节切换,准备继续学习...'); this.autoLearning = true; this.startLearningCurrentChapter(); } } } } }, // 开始学习当前章节 startLearningCurrentChapter() { try { // 获取当前章节 const currentChapter = document.querySelector('.nav-text.current')?.closest('.nav-item.sup-item'); if (!currentChapter) { throw new Error("未找到当前章节"); } const chapterName = currentChapter.querySelector('.node-text')?.textContent.trim(); log(`准备学习章节: ${chapterName || '未知章节'}`); // 展开子章节列表并开始学习 this.startLearningNewChapter(currentChapter, chapterName); } catch (error) { log(`启动学习失败: ${error.message}`); // 延迟重试 setTimeout(() => { if (this.autoLearning) { this.startLearningCurrentChapter(); } }, 2000); } }, } }); } // 自动阅读文档功能 function autoReadDocument() { // 获取当前文档总页数 const totalPages = parseInt(document.querySelector('.flexpaper_lblTotalPages').textContent.replace('/ ', '')) || 0; if(totalPages === 0) { log("未检测到文档页数,请确保文档已加载"); return; } log(`开始自动阅读文档,共${totalPages}页`); let currentPage = 1; const readInterval = setInterval(() => { if(currentPage > totalPages) { clearInterval(readInterval); log("文档阅读完成,准备切换到下一个内容"); switchToNextContent(); return; } // 模拟翻页 const nextButton = document.querySelector('.flexpaper_bttnPrevNext'); if(nextButton) { nextButton.click(); log(`正在阅读第${currentPage}页,共${totalPages}页`); currentPage++; } }, 2000); // 每2秒翻一页 } // 切换到下一个内容 function switchToNextContent() { // 获取所有标签页 const tabs = document.querySelectorAll('.tab-inner'); let currentTabIndex = -1; // 找到当前激活的标签页 tabs.forEach((tab, index) => { if(tab.parentElement.classList.contains('tab-active')) { currentTabIndex = index; } }); // 如果还有下一个标签页,则切换 if(currentTabIndex < tabs.length - 1) { const nextTab = tabs[currentTabIndex + 1]; log(`切换到下一个内容: ${nextTab.getAttribute('itemname')}`); // 根据内容类型执行不同操作 const itemType = nextTab.getAttribute('itemtype'); nextTab.click(); setTimeout(() => { if(itemType === '10') { // 视频类型 passVideo(); } else if(itemType === '20') { // 文档类型 autoReadDocument(); } }, 1500); } else { log("已完成所有内容学习"); } } // 初始化获取答案,延迟5秒防止流程崩溃 function initGetAnswer(settings){ var url = location.origin + settings.url; var data = settings.data.replace(/(testPaperId=).*?(&)/,'$1' + '1250' + '$2'); console.log("=====") console.log(url,'url') console.log(data) getAnswer(url,data); } // 添加答案检查函数 function checkAnswers() { return new Promise((resolve) => { let checkCount = 0; const maxChecks = 30; // 最多等待30秒 function check() { if (setting.datas && setting.datas.length > 0) { log("答案已获取完成,开始答题..."); resolve(true); } else if (checkCount >= maxChecks) { log("等待答案超时,请确保答案已正确获取"); resolve(false); } else { checkCount++; log(`等待答案获取中...(${checkCount}/${maxChecks})`); setTimeout(check, 1000); } } check(); }); } // 添加答案查找函数 function findAnswerForQuestion(questionText) { if (!setting.datas || setting.datas.length === 0) { return null; } // 清理题目文本 const cleanedQuestionText = cleanHtmlTags(questionText).trim(); // 在答案列表中查找匹配的题目 for (const answerData of setting.datas) { const cleanedAnswerQuestion = cleanHtmlTags(answerData.question).trim(); // 检查题目是否匹配 if (cleanedQuestionText.includes(cleanedAnswerQuestion) || cleanedAnswerQuestion.includes(cleanedQuestionText)) { return answerData.answer; } } return null; } // 修改自动答题函数 async function autoAnswerQuestions() { log("准备自动答题..."); // 获取所有题目 const questions = document.querySelectorAll('.view-test'); if(!questions || questions.length === 0) { log("未找到题目,请确认是否在答题页面"); return; } log(`共发现 ${questions.length} 道题目`); let answeredCount = 0; // 遍历每个题目 for (let index = 0; index < questions.length; index++) { const question = questions[index]; try { // 获取题目类型 const isMultiChoice = question.querySelector('.t-option .input-c') !== null; // 多选 const isSingleChoice = question.querySelector('.t-option .input-r') !== null; // 单选 const isFillBlank = question.querySelector('.fillblank') !== null; // 填空 // 获取题目文本 const questionText = question.querySelector('.test-text-tutami')?.textContent.trim(); if(!questionText) { log(`第 ${index + 1} 题未找到题目文本`); continue; } // 查找答案 const answer = findAnswerForQuestion(questionText); if(!answer) { log(`第 ${index + 1} 题未找到匹配的答案`); continue; } log(`正在答第 ${index + 1} 题...`); // 根据题型填写答案 if(isMultiChoice) { // 处理多选题 const answerLetters = answer.split('\n').map(a => a.trim().charAt(0)); const options = question.querySelectorAll('.t-option'); options.forEach((option, idx) => { const letter = String.fromCharCode(65 + idx); if(answerLetters.includes(letter)) { const checkbox = option.querySelector('.input-c'); if(checkbox && !checkbox.classList.contains('selected')) { checkbox.click(); log(` - 选择了选项 ${letter}`); } } }); answeredCount++; } else if(isSingleChoice) { // 处理单选题 const answerLetter = answer.trim().charAt(0); const options = question.querySelectorAll('.t-option'); options.forEach((option, idx) => { const letter = String.fromCharCode(65 + idx); if(letter === answerLetter) { const radio = option.querySelector('.input-r'); if(radio && !radio.classList.contains('selected')) { radio.click(); log(` - 选择了选项 ${letter}`); } } }); answeredCount++; } else if(isFillBlank) { // 处理填空题 const input = question.querySelector('.fillblank'); if(input) { const fillAnswer = answer.replace(/^.*?\./, '').trim(); input.value = fillAnswer; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); log(` - 填写答案: ${fillAnswer}`); answeredCount++; } } } catch(error) { log(`第 ${index + 1} 题答题出错: ${error.message}`); } } log(`自动答题完成!成功答题 ${answeredCount} 道题目`); // 如果启用了自动提交 if(setting.autoSubmit) { const submitBtn = document.querySelector('#submit_exam'); if(submitBtn) { log("准备自动提交答案..."); setTimeout(() => { submitBtn.click(); // 监听确认对话框并自动点击"坚持提交" log("等待确认对话框..."); const checkConfirmDialog = setInterval(() => { const confirmBtn = document.querySelector('.d-button.d-state-highlight[value="坚持提交"]'); if(confirmBtn) { clearInterval(checkConfirmDialog); log("检测到确认对话框,自动点击'坚持提交'"); setTimeout(() => { confirmBtn.click(); }, 500); } }, 500); // 5秒后清除定时器,避免无限循环 setTimeout(() => { clearInterval(checkConfirmDialog); }, 5000); }, 1000); } else { log("未找到提交按钮,请手动提交"); } } else { if(window.vue) { window.vue.$message.info('答题完成,请检查后手动提交'); } } } // 添加自动答题按钮 function addAutoAnswerButton() { const btnArea = document.querySelector('.practice-action'); if(!btnArea) return; const btn = document.createElement('a'); btn.innerText = '自动答题'; btn.className = 'btn-public btn-min'; btn.href = 'javascript:void(0)'; btn.onclick = autoAnswerQuestions; btnArea.appendChild(btn); log("已添加自动答题按钮"); } // 初始化 function initAutoAnswer() { if(location.href.includes('/examSubmit/')) { log("检测到答题页面"); setTimeout(addAutoAnswerButton, 2000); } } initAutoAnswer(); // 脚本入口 initView(); //监听跳过视频按钮 $('#rl_passVideo').click(function(){passVideo();}); //监听url访问,当访问了加载题目的url时,将获取答案 $(document).ready(function(){ $(document).ajaxComplete(function (evt, request, settings) { if(settings.url.search('getExamPaper') != -1){ setting.logs.unshift("您已打开作业界面,5秒后将为您获取答案") setTimeout(initGetAnswer,5000, settings); } }); }) // 获取验证时间的函数 function getValidTime() { try { const data = localStorage.getItem('scriptValidUntil'); if (!data) return null; const { time, hash } = JSON.parse(data); if (!time || !hash) return null; // 验证哈希值 if (hash !== CryptoJS.SHA256(time.toString()).toString()) { localStorage.removeItem('scriptValidUntil'); return null; } return time; } catch (e) { console.error('获取验证时间出错:', e); localStorage.removeItem('scriptValidUntil'); return null; } } // 在界面中添加自动学习按钮 function addAutoLearnButton() { const buttonGroup = document.querySelector('.ant-btn-group'); if(!buttonGroup) return; const autoLearnBtn = document.createElement('button'); autoLearnBtn.className = 'ant-btn ant-btn-primary'; autoLearnBtn.style.width = '25%'; autoLearnBtn.innerHTML = '<span>自动学习</span>'; autoLearnBtn.onclick = () => window.vue.startAutoLearning(); buttonGroup.appendChild(autoLearnBtn); } // 添加自动答题相关功能 function initAutoExam() { // 先检查页面类型 const pageType = checkPageType(); // 如果不是测验页面,直接返回 if(pageType !== 'quiz') { return; } log("检测到测验页面,初始化答题功能..."); // 添加配置按钮到界面 const configBtn = document.createElement('button'); configBtn.className = 'ant-btn ant-btn-primary'; configBtn.innerHTML = ` <span>答题设置</span> `; configBtn.onclick = showExamConfig; // 找到按钮区域并添加配置按钮 const btnArea = document.querySelector('.ant-btn-group'); if(btnArea) { btnArea.appendChild(configBtn); log("已添加答题设置按钮"); } // 自动处理测验 handleQuiz(); } // 显示答题配置对话框 function showExamConfig() { const configHtml = ` <div class="exam-config" style="padding: 20px;"> <h3>答题设置</h3> <div style="margin: 10px 0;"> <label> <input type="checkbox" ${setting.autoSubmit ? 'checked' : ''} id="autoSubmit"> 自动提交答案 </label> </div> <div style="margin: 10px 0;"> <label> <input type="checkbox" ${setting.autoEnterExam ? 'checked' : ''} id="autoEnterExam"> 自动进入考试 </label> </div> <div style="margin-top: 20px;"> <button class="ant-btn ant-btn-primary" onclick="saveExamConfig()">保存设置</button> </div> </div> `; // 使用 ant-design-vue 的对话框显示配置 if(window.vue) { window.vue.$info({ title: '答题设置', content: configHtml, width: 400 }); } } // 保存答题配置 window.saveExamConfig = function() { setting.autoSubmit = document.getElementById('autoSubmit').checked; setting.autoEnterExam = document.getElementById('autoEnterExam').checked; // 保存到localStorage localStorage.setItem('examConfig', JSON.stringify({ autoSubmit: setting.autoSubmit, autoEnterExam: setting.autoEnterExam })); if(window.vue) { window.vue.$message.success('设置已保存'); } } // 加载保存的配置 function loadExamConfig() { try { const savedConfig = localStorage.getItem('examConfig'); if(savedConfig) { const config = JSON.parse(savedConfig); // 使用默认值作为后备 setting.autoSubmit = config.autoSubmit !== undefined ? config.autoSubmit : false; setting.autoEnterExam = config.autoEnterExam !== undefined ? config.autoEnterExam : true; log("已加载答题设置"); } else { // 如果没有保存的配置,设置默认值 setting.autoSubmit = false; setting.autoEnterExam = true; // 保存默认设置 localStorage.setItem('examConfig', JSON.stringify({ autoSubmit: false, autoEnterExam: true })); log("已设置默认答题选项"); } } catch(e) { console.error('加载答题配置失败:', e); // 出错时使用默认值 setting.autoSubmit = true; setting.autoEnterExam = true; } } // 修改自动答题函数 function autoAnswerQuestions() { log("开始自动答题..."); // 获取所有题目 const questions = document.querySelectorAll('.view-test'); if(!questions || questions.length === 0) { log("未找到题目,请确认是否在答题页面"); return; } log(`共发现 ${questions.length} 道题目`); let answeredCount = 0; questions.forEach((question, index) => { try { // 获取题目类型 const isMultiChoice = question.querySelector('.t-option .input-c') !== null; // 多选 const isSingleChoice = question.querySelector('.t-option .input-r') !== null; // 单选 const isFillBlank = question.querySelector('.fillblank') !== null; // 填空 // 获取题目文本用于匹配答案 const questionText = question.querySelector('.test-text-tutami')?.textContent.trim(); if(!questionText) { log(`第 ${index + 1} 题未找到题目文本`); return; } // 在答案列表中查找匹配的答案 const answer = findAnswerForQuestion(questionText); if(!answer) { log(`第 ${index + 1} 题未找到匹配的答案`); return; } // 根据题型填写答案 if(isMultiChoice) { // 处理多选题 const answerLetters = answer.split('\n').map(a => a.trim().charAt(0)); const options = question.querySelectorAll('.t-option'); options.forEach((option, idx) => { const letter = String.fromCharCode(65 + idx); if(answerLetters.includes(letter)) { const checkbox = option.querySelector('.input-c'); if(checkbox && !checkbox.classList.contains('selected')) { checkbox.click(); } } }); answeredCount++; } else if(isSingleChoice) { // 处理单选题 const answerLetter = answer.trim().charAt(0); const options = question.querySelectorAll('.t-option'); options.forEach((option, idx) => { const letter = String.fromCharCode(65 + idx); if(letter === answerLetter) { const radio = option.querySelector('.input-r'); if(radio && !radio.classList.contains('selected')) { radio.click(); } } }); answeredCount++; } else if(isFillBlank) { // 处理填空题 const input = question.querySelector('.fillblank'); if(input) { input.value = answer.replace(/^.*?\./, '').trim(); // 触发input事件 input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); answeredCount++; } } } catch(error) { log(`第 ${index + 1} 题答题出错: ${error.message}`); } }); log(`自动答题完成!成功答题 ${answeredCount} 道题目`); // 如果启用了自动提交 if(setting.autoSubmit) { const submitBtn = document.querySelector('#submit_exam'); if(submitBtn) { log("准备自动提交答案..."); setTimeout(() => { submitBtn.click(); // 监听确认对话框并自动点击"坚持提交" log("等待确认对话框..."); const checkConfirmDialog = setInterval(() => { const confirmBtn = document.querySelector('.d-button.d-state-highlight[value="坚持提交"]'); if(confirmBtn) { clearInterval(checkConfirmDialog); log("检测到确认对话框,自动点击'坚持提交'"); setTimeout(() => { confirmBtn.click(); }, 500); } }, 500); // 5秒后清除定时器,避免无限循环 setTimeout(() => { clearInterval(checkConfirmDialog); }, 5000); }, 1000); } else { log("未找到提交按钮,请手动提交"); } } else { if(window.vue) { window.vue.$message.info('答题完成,请检查后手动提交'); } } } // 在页面加载完成后初始化 document.addEventListener('DOMContentLoaded', () => { loadExamConfig(); initAutoExam(); initPageChangeListener(); }); // 修改页面类型识别逻辑 function checkPageType() { // 获取当前打开的标签页信息 const activeTab = document.querySelector('.tab-active .tab-inner'); if (!activeTab) { return null; } // 获取页面信息 const itemType = activeTab.getAttribute('itemtype'); const itemName = activeTab.getAttribute('itemname') || ''; // 测验页面的多重判断条件 const isQuiz = ( // 1. 通过标题关键词判断 itemName.includes('测试题') || itemName.includes('测验题') || itemName.includes('练习题') || // 2. 通过页面元素判断 document.querySelector('.h-commit-tip') !== null || document.querySelector('.exam_submit_score') !== null || document.querySelector('.enter_exam') !== null || document.querySelector('.doObjExam') !== null || // 3. 通过URL判断 location.href.includes('/examSubmit/') || // 4. 通过图标判断 activeTab.querySelector('.icon-edit02') !== null || // 5. 通过itemType判断 itemType === '50' || // 6. 通过页面结构判断 document.querySelector('.h-result-table') !== null || // 7. 通过提交次数信息判断 document.querySelector('.h-commit-info') !== null ); if (isQuiz) { // 输出详细的识别信息 log(`识别到测验页面:`); log(`- 标题: ${itemName}`); log(`- 类型: ${itemType}`); if (document.querySelector('.h-commit-tip')) { const submitInfo = document.querySelector('.h-commit-tip').textContent; log(`- 提交信息: ${submitInfo.trim()}`); } return 'quiz'; } // 其他页面类型判断 if (itemType === '10') { return 'video'; } else if (itemType === '20') { return 'document'; } return null; } // 修改handleQuiz函数中的答题逻辑 function handleQuiz() { log("检测到测验页面,准备处理..."); // 获取测验信息 const quizInfo = { title: document.querySelector('.tab-active .tab-inner')?.getAttribute('itemname') || '未知测验', submitLimit: document.querySelector('.h-commit-info')?.textContent.match(/可提交次数:(\d+)次/) || ['', '未知'], timeLimit: document.querySelector('.h-commit-info')?.textContent.includes('不限时') ? '不限时' : document.querySelector('.h-commit-info')?.textContent.match(/限时:(\d+)分钟/) || ['', '未知'] }; log(`测验信息:`); log(`- 标题: ${quizInfo.title}`); log(`- 提交次数限制: ${quizInfo.submitLimit[1]}次`); log(`- 时间限制: ${quizInfo.timeLimit === '不限时' ? '不限时' : quizInfo.timeLimit[1] + '分钟'}`); // 检查是否有"继续"按钮(未完成的测验) const enterBtn = document.querySelector('a.link-action.enter_exam'); if(enterBtn) { const examId = enterBtn.id || ''; log(`发现未完成的测验 (ID: ${examId}),准备继续答题...`); // 确保自动进入答题功能已启用 if(setting.autoEnterExam) { log("自动进入答题已启用,即将进入答题页面..."); // 模拟点击事件 try { // 先尝试直接触发点击事件 enterBtn.click(); log("已触发进入答题页面"); // 等待页面加载和答案获取 waitForAnswersAndQuestions(); } catch(error) { log(`进入答题页面时出错: ${error.message}`); log("请尝试手动点击'继续'按钮"); } } else { log("自动进入答题已禁用,请手动点击'继续'按钮"); } return; } // 检查是否已在答题页面 const questions = document.querySelectorAll('.view-test'); if(questions && questions.length > 0) { log(`已在答题页面,等待答案获取...`); waitForAnswersAndQuestions(); return; } // 检查是否有开始答题按钮 const startBtn = document.querySelector('.doObjExam'); if(startBtn) { log("发现新测验,准备开始答题..."); if(setting.autoEnterExam) { setTimeout(() => { startBtn.click(); log("已自动开始答题"); // 等待页面加载和答案获取 waitForAnswersAndQuestions(); }, 1000); } else { log("自动进入答题已禁用,请手动点击开始按钮"); } return; } log("等待页面加载完成..."); } // 修改等待答案和题目加载的函数 function waitForAnswersAndQuestions() { let checkCount = 0; const maxChecks = 30; // 最多等待30秒 function check() { // 检查题目是否加载 const questions = document.querySelectorAll('.view-test'); if (!questions || questions.length === 0) { if (checkCount >= maxChecks) { log("等待题目加载超时,请刷新页面重试"); return; } checkCount++; setTimeout(check, 1000); return; } // 检查答案是否已获取 if (!setting.datas || setting.datas.length === 0) { if (checkCount >= maxChecks) { log("等待答案获取超时,请确保答案已正确获取"); return; } checkCount++; setTimeout(check, 1000); return; } // 检查答案匹配情况 checkAnswerMatching(questions).then(matchResult => { if (matchResult.success) { log("答案匹配检查完成:"); log(`- 题目总数:${matchResult.totalQuestions}道`); log(`- 成功匹配:${matchResult.matchedCount}道`); if (matchResult.unmatchedQuestions.length > 0) { log(`- 未匹配题目:${matchResult.unmatchedQuestions.join(', ')}题`); } if (matchResult.matchedCount > 0) { // 开始自动答题 setTimeout(() => { autoAnswerQuestions(); }, 1000); } else { log("没有找到任何可匹配的答案,请检查答案是否正确"); } } else { log("答案匹配检查失败,请刷新页面重试"); } }); } // 开始检查 check(); } // 添加答案匹配检查函数 async function checkAnswerMatching(questions) { try { const result = { success: true, totalQuestions: questions.length, matchedCount: 0, unmatchedQuestions: [] }; // 遍历所有题目进行检查 for (let i = 0; i < questions.length; i++) { const question = questions[i]; const questionText = question.querySelector('.test-text-tutami')?.textContent.trim(); if (!questionText) { result.unmatchedQuestions.push(i + 1); continue; } // 清理题目文本 const cleanedQuestionText = cleanHtmlTags(questionText).trim(); let matched = false; // 在答案列表中查找匹配的题目 for (const answerData of setting.datas) { const cleanedAnswerQuestion = cleanHtmlTags(answerData.question).trim(); // 检查题目是否匹配 if (cleanedQuestionText.includes(cleanedAnswerQuestion) || cleanedAnswerQuestion.includes(cleanedQuestionText)) { result.matchedCount++; matched = true; break; } } if (!matched) { result.unmatchedQuestions.push(i + 1); } } return result; } catch (error) { return { success: false, error: error.message }; } } // 修改handleQuiz函数中的日志部分 function handleQuiz() { log("检测到测验页面,准备处理..."); // 获取测验信息 const quizInfo = { title: document.querySelector('.tab-active .tab-inner')?.getAttribute('itemname') || '未知测验', submitLimit: document.querySelector('.h-commit-info')?.textContent.match(/可提交次数:(\d+)次/) || ['', '未知'], timeLimit: document.querySelector('.h-commit-info')?.textContent.includes('不限时') ? '不限时' : document.querySelector('.h-commit-info')?.textContent.match(/限时:(\d+)分钟/) || ['', '未知'] }; // 检查是否有"继续"按钮(未完成的测验) const enterBtn = document.querySelector('a.link-action.enter_exam'); if(enterBtn) { const examId = enterBtn.id || ''; log(`准备继续答题 (ID: ${examId})`); if(setting.autoEnterExam) { try { enterBtn.click(); waitForAnswersAndQuestions(); } catch(error) { log(`进入答题页面时出错: ${error.message}`); log("请尝试手动点击'继续'按钮"); } } else { log("自动进入答题已禁用,请手动点击'继续'按钮"); } return; } // 检查是否已在答题页面 const questions = document.querySelectorAll('.view-test'); if(questions && questions.length > 0) { waitForAnswersAndQuestions(); return; } // 检查是否有开始答题按钮 const startBtn = document.querySelector('.doObjExam'); if(startBtn) { if(setting.autoEnterExam) { setTimeout(() => { startBtn.click(); waitForAnswersAndQuestions(); }, 1000); } else { log("自动进入答题已禁用,请手动点击开始按钮"); } return; } } // 添加页面变化监听 function initPageChangeListener() { // 监听标签页切换 const tabContainer = document.querySelector('.tabs'); if(tabContainer) { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if(mutation.type === 'attributes' && mutation.attributeName === 'class') { const pageType = checkPageType(); if(pageType === 'quiz') { log("检测到切换到测验页面"); handleQuiz(); } } }); }); observer.observe(tabContainer, { attributes: true, subtree: true, attributeFilter: ['class'] }); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址