// ==UserScript== // @name 曲阜师范大学全自动评教 // @namespace github.com/cat-Logan // @version 1.0 // @description 全自动完成所有课程的教学评价:自动选择、自动填写建议、自动保存、自动切换课程 // @license GPL-3.0-only // @author cat-logan // @match http://zhjw.qfnu.edu.cn/* // @match https://zhjw.qfnu.edu.cn/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_addStyle // @grant unsafeWindow // @run-at document-end // ==/UserScript== // 致谢:感谢卷卷的帮助 ❤️ (function () { 'use strict'; function sandboxHiddenIframe() { var hidfrm = document.getElementById('hidfrm'); if (hidfrm) { hidfrm.setAttribute('sandbox', 'allow-forms'); log('已锁定 hidfrm iframe (仅允许表单提交)', 'success'); } } sandboxHiddenIframe(); setTimeout(sandboxHiddenIframe, 500); var styleEl = document.createElement('style'); styleEl.id = 'qfsd-dialog-blocker'; styleEl.textContent = '.ui_dialog, .ui_overlay, .ui_dialog_wrap, [id*="artDialog"], [class*="artdialog"] ' + '{ display: none !important; visibility: hidden !important; }'; document.documentElement.appendChild(styleEl); const CONFIG = { // 建议文本(jynr 字段的内容) suggestionText: '老师教学态度认真负责,课堂内容丰富充实,教学方法得当,能够很好地引导学生思考和学习。', // 延时设置(毫秒) delayBeforeFill: 1500, // 评价页面加载后等待时间 delayAfterSubmit: 2000, // 点击提交后等待时间 delayListPageCheck: 1500, // 列表页检查延时 // 评分策略 goodOptionSuffix: '_2', // "良" 的选项 ID 后缀 excellentOptionSuffix: '_1', // "优" 的选项 ID 后缀 randomExcellentCount: 1, // 每门课随机选几个"优" }; // ============ GM 存储键名 ============ const STORAGE_KEYS = { autoMode: 'qfsd_auto_eval_active', courseList: 'qfsd_course_list', currentIndex: 'qfsd_current_index', totalCourses: 'qfsd_total_courses', completedCourses: 'qfsd_completed_courses', saveInProgress: 'qfsd_save_in_progress', }; // ============ 工具函数 ============ function getPageType() { const url = window.location.href; if (url.includes('xspj_edit.do')) return 'eval'; if (url.includes('xspj_list.do') || url.includes('xspj_find.do')) return 'list'; return 'other'; } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function log(msg, type) { type = type || 'info'; var prefix = '[全自动评教]'; var styles = { info: 'color: #178fe6; font-weight: bold;', success: 'color: #67c23a; font-weight: bold;', warn: 'color: #e6a23c; font-weight: bold;', error: 'color: #f56c6c; font-weight: bold;', }; console.log('%c' + prefix + '%c ' + msg, styles[type] || styles.info, ''); } // ============ alert / confirm / artDialog 拦截 ============ var _dialogObserver = null; /** 用 MutationObserver 监控并自动关闭所有弹窗 (artDialog / layer / 原生 alert) */ function startDialogObserver() { if (_dialogObserver) return; _dialogObserver = new MutationObserver(function (mutations) { for (var i = 0; i < mutations.length; i++) { var nodes = mutations[i].addedNodes; for (var j = 0; j < nodes.length; j++) { var node = nodes[j]; if (node.nodeType !== 1) continue; // 只处理元素节点 var el = node; // artDialog 风格的弹窗 if (el.classList && ( el.classList.contains('ui_dialog') || el.classList.contains('ui_overlay') || el.classList.contains('ui_dialog_wrap') || el.classList.contains('artdialog') || el.id === 'artDialog' )) { log('🤖 已自动关闭弹窗: ' + (el.className || el.id), 'info'); el.style.display = 'none'; el.remove(); } // 也查看看它里面有没有确认按钮,自动点 var closes = el.querySelectorAll ? el.querySelectorAll('.ui_close, .ui_btns button, .ui_btns .ui_close, button:contains("确定"), input[value="确定"]') : []; for (var k = 0; k < closes.length; k++) { setTimeout(function (btn) { btn.click(); }, 50); } } } }); _dialogObserver.observe(document.body, { childList: true, subtree: true }); log('已启动 DOM 弹窗监控', 'success'); } /** 停止弹窗监控 */ function stopDialogObserver() { if (_dialogObserver) { _dialogObserver.disconnect(); _dialogObserver = null; } } /** 拦截原生 alert / confirm — 用 unsafeWindow 因为 @grant 导致沙箱隔离 */ function overrideDialogs() { var win = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window; if (win.alert._qfsdOverridden) return; win.alert = function (msg) { log('🤖 alert已拦截: ' + String(msg), 'info'); }; win.alert._qfsdOverridden = true; win.confirm = function (msg) { log('🤖 confirm已拦截: ' + String(msg), 'info'); return true; }; // 同样拦截 iframe 内的 alert/confirm(页面可能在 iframe 中运行) try { var fw = window.frames; for (var i = 0; i < fw.length; i++) { try { fw[i].alert = win.alert; fw[i].confirm = win.confirm; } catch (e) {} } } catch (e) {} startDialogObserver(); startDialogNuker(); log('已拦截所有弹窗 (unsafeWindow.alert/confirm + artDialog)', 'success'); } // 兜底方案:每 500ms 暴力清理弹窗 DOM var _nukeInterval = null; function startDialogNuker() { if (_nukeInterval) return; _nukeInterval = setInterval(function () { var dialogs = document.querySelectorAll('.ui_dialog, .ui_overlay, .ui_dialog_wrap, ' + '[id*="artDialog"], [class*="artdialog"], .aui_dialog, .layui-layer-dialog'); for (var i = 0; i < dialogs.length; i++) { dialogs[i].remove(); } // 强制恢复页面滚动(弹窗可能会锁 body) if (document.body.style.overflow === 'hidden') { document.body.style.overflow = ''; } }, 500); } function stopDialogNuker() { if (_nukeInterval) { clearInterval(_nukeInterval); _nukeInterval = null; } } function restoreDialogs() { stopDialogObserver(); stopDialogNuker(); } // ============ 状态面板 ============ function createStatusPanel() { var existing = document.getElementById('qfsd-auto-eval-panel'); if (existing) existing.remove(); var panel = document.createElement('div'); panel.id = 'qfsd-auto-eval-panel'; panel.innerHTML = '
🤖 全自动评教进行中author by cat-logan
' + '
' + '
初始化...
' + '
' + '
' + '
' + '
' + '
' + '
' + ''; document.body.appendChild(panel); return panel; } function updateStatus(status, courseInfo, logMsg) { var statusEl = document.getElementById('qfsd-status-text'); var courseEl = document.getElementById('qfsd-course-info'); var progressEl = document.getElementById('qfsd-progress-bar'); var logEl = document.getElementById('qfsd-log'); if (statusEl) statusEl.textContent = status; if (courseEl) courseEl.textContent = courseInfo || ''; if (logEl && logMsg) { var time = new Date().toLocaleTimeString(); logEl.innerHTML += '
[' + time + '] ' + logMsg + '
'; logEl.scrollTop = logEl.scrollHeight; } var total = parseInt(GM_getValue(STORAGE_KEYS.totalCourses, '0')); var current = parseInt(GM_getValue(STORAGE_KEYS.completedCourses, '0')); if (progressEl && total > 0) { var pct = Math.round((current / total) * 100); progressEl.style.width = pct + '%'; progressEl.textContent = current + '/' + total + ' (' + pct + '%)'; } } function showToast(msg) { var existing = document.getElementById('qfsd-toast'); if (existing) existing.remove(); var toast = document.createElement('div'); toast.id = 'qfsd-toast'; toast.textContent = '✅ ' + msg; document.body.appendChild(toast); setTimeout(function () { toast.remove(); }, 3000); } // ============ 列表页逻辑 ============ function collectCourseList() { var courses = []; var table = document.getElementById('dataList'); if (!table) { log('未找到课程列表表格', 'error'); return courses; } var rows = table.querySelectorAll('tbody tr'); log('找到 ' + rows.length + ' 行数据'); for (var r = 0; r < rows.length; r++) { var row = rows[r]; var cells = row.querySelectorAll('td'); if (cells.length < 8) continue; var courseNumEl = cells[1]; // 跳过表头行或无效行(课程编号应该是数字) var cnText = (courseNumEl && courseNumEl.textContent) ? courseNumEl.textContent.trim() : ''; if (!cnText || !/^\d+$/.test(cnText)) continue; var courseName = cells[2] ? cells[2].textContent.trim() : ''; var teacherName = cells[3] ? cells[3].textContent.trim() : ''; // "是否提交"列 - 在已评列(6)之后 var submitted = cells[7] ? cells[7].textContent.trim() : ''; // "已评"列 var alreadyRated = cells[6] ? cells[6].textContent.trim() : ''; var evalLink = row.querySelector('a[href*="xspj_edit.do"]'); if (!evalLink) continue; var href = evalLink.getAttribute('href'); var fullUrl = (href.indexOf('http') === 0) ? href : 'http://zhjw.qfnu.edu.cn' + href; if (submitted === '否') { courses.push({ index: courses.length + 1, courseName: courseName || '未知课程', teacherName: teacherName || '未知教师', url: fullUrl, submitted: false, }); log(' 📋 ' + courseName + ' - ' + teacherName + ' [未提交] → 加入队列'); } else { log(' ✓ ' + courseName + ' - ' + teacherName + ' [已提交] → 跳过'); } } return courses; } function createStartButton() { var existing = document.getElementById('qfsd-auto-start-btn'); if (existing) existing.remove(); var btnContainer = document.createElement('div'); btnContainer.id = 'qfsd-auto-start-container'; btnContainer.innerHTML = '' + '' + '自动遍历所有未提交课程,填写评价并保存(已处理弹窗拦截)' + ''; var table = document.getElementById('dataList'); if (table && table.parentElement) { table.parentElement.insertBefore(btnContainer, table); } else { document.body.insertBefore(btnContainer, document.body.firstChild); } document.getElementById('qfsd-auto-start-btn').addEventListener('click', startAutoEval); } function startAutoEval() { // 先拦截弹窗 overrideDialogs(); var courses = collectCourseList(); if (courses.length === 0) { alert('🎉 所有课程都已提交,无需评教!'); restoreDialogs(); return; } log('共找到 ' + courses.length + ' 门未提交课程,开始全自动评教...', 'success'); GM_setValue(STORAGE_KEYS.autoMode, 'true'); GM_setValue(STORAGE_KEYS.courseList, JSON.stringify(courses)); GM_setValue(STORAGE_KEYS.currentIndex, '0'); GM_setValue(STORAGE_KEYS.totalCourses, courses.length.toString()); GM_setValue(STORAGE_KEYS.completedCourses, '0'); GM_setValue(STORAGE_KEYS.saveInProgress, 'false'); createStatusPanel(); document.getElementById('qfsd-stop-btn').addEventListener('click', stopAutoEval); updateStatus('开始评教...', '', '开始处理 ' + courses.length + ' 门课程'); var firstUrl = courses[0].url; log('跳转到第一门: ' + courses[0].courseName + ' - ' + courses[0].teacherName); updateStatus('正在跳转...', courses[0].courseName + ' - ' + courses[0].teacherName); window.location.href = firstUrl; } function stopAutoEval() { restoreDialogs(); GM_deleteValue(STORAGE_KEYS.autoMode); GM_deleteValue(STORAGE_KEYS.courseList); GM_deleteValue(STORAGE_KEYS.currentIndex); GM_deleteValue(STORAGE_KEYS.totalCourses); GM_deleteValue(STORAGE_KEYS.completedCourses); GM_deleteValue(STORAGE_KEYS.saveInProgress); var panel = document.getElementById('qfsd-auto-eval-panel'); if (panel) panel.remove(); log('自动评教已停止', 'warn'); showToast('自动评教已停止'); } // ============ 评价页逻辑 ============ function fillEvaluationOptions() { var goodOptions = document.querySelectorAll('input[id$="' + CONFIG.goodOptionSuffix + '"]'); log('找到 ' + goodOptions.length + ' 个"良"选项'); var clickedCount = 0; for (var i = 0; i < goodOptions.length; i++) { var opt = goodOptions[i]; if (opt.type === 'radio' && !opt.disabled) { opt.click(); clickedCount++; } } var excellentOptions = document.querySelectorAll('input[id$="' + CONFIG.excellentOptionSuffix + '"]'); var availableExcellent = []; for (var j = 0; j < excellentOptions.length; j++) { if (excellentOptions[j].type === 'radio' && !excellentOptions[j].disabled) { availableExcellent.push(excellentOptions[j]); } } if (availableExcellent.length > 0) { var count = Math.min(CONFIG.randomExcellentCount, availableExcellent.length); // Fisher-Yates 洗牌 for (var k = availableExcellent.length - 1; k > 0; k--) { var r = Math.floor(Math.random() * (k + 1)); var tmp = availableExcellent[k]; availableExcellent[k] = availableExcellent[r]; availableExcellent[r] = tmp; } for (var m = 0; m < count; m++) { availableExcellent[m].click(); clickedCount++; } log('随机选了 ' + count + ' 个"优"'); } return clickedCount; } function fillSuggestions() { var fields = document.querySelectorAll('textarea[name="jynr"], input[name="jynr"]'); log('找到 ' + fields.length + ' 个建议输入框'); var filledCount = 0; for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (!field.disabled && !field.readOnly) { // 直接设置值和属性,兼容各种框架 field.value = CONFIG.suggestionText; field.setAttribute('value', CONFIG.suggestionText); // 触发事件 field.dispatchEvent(new Event('input', { bubbles: true })); field.dispatchEvent(new Event('change', { bubbles: true })); field.dispatchEvent(new Event('blur', { bubbles: true })); filledCount++; } } return filledCount; } function clickSubmitButton() { var btn = document.getElementById('bc'); var win = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window; if (btn && !btn.disabled) { // 按钮 onclick 是 saveData(this,'0') → 仅保存 // 必须调 saveData(btn,'1') → 真正提交 if (typeof win.saveData === 'function') { log('调用 unsafeWindow.saveData(btn, "1") 执行提交', 'success'); win.saveData(btn, '1'); } else { log('saveData 不可用,降级点击按钮', 'warn'); btn.click(); } return true; } log('未找到提交按钮!', 'error'); return false; } /** 提交后,等待并导航到下一门课程 */ async function waitForSaveAndContinue(currentIndex, courses) { var currentCourse = courses[currentIndex]; var totalCourses = courses.length; var completedCount = parseInt(GM_getValue(STORAGE_KEYS.completedCourses, '0')) + 1; // 更新进度(在跳转前保存) GM_setValue(STORAGE_KEYS.completedCourses, completedCount.toString()); GM_setValue(STORAGE_KEYS.currentIndex, (currentIndex + 1).toString()); GM_setValue(STORAGE_KEYS.saveInProgress, 'false'); updateStatus( '✅ 保存成功 (' + completedCount + '/' + totalCourses + ')', currentCourse.courseName + ' - ' + currentCourse.teacherName, '✅ ' + currentCourse.courseName + ' 评价已保存' ); log(currentCourse.courseName + ' 评价保存完成!(' + completedCount + '/' + totalCourses + ')', 'success'); // 短暂等待,如果服务器会自动跳转,就让它跳 // 如果不跳转,我们手动导航 await sleep(1500); if (currentIndex + 1 < courses.length) { var nextCourse = courses[currentIndex + 1]; log('➡ 跳转到: ' + nextCourse.courseName + ' - ' + nextCourse.teacherName); window.location.href = nextCourse.url; } else { finishAutoEval(); } } async function performAutoEval() { log('========================================', 'info'); log('检测到评价页面,开始自动评价...', 'success'); // 确保自动模式开启 var isAutoMode = GM_getValue(STORAGE_KEYS.autoMode, 'false'); if (isAutoMode !== 'true') { log('自动模式未开启,跳过', 'warn'); return; } // 防止重复提交 var saveInProgress = GM_getValue(STORAGE_KEYS.saveInProgress, 'false'); if (saveInProgress === 'true') { log('⚠ 检测到重复进入评价页(可能是服务器跳回),跳过本次自动填写', 'warn'); return; } var coursesJson = GM_getValue(STORAGE_KEYS.courseList, '[]'); var courses = JSON.parse(coursesJson); var currentIndex = parseInt(GM_getValue(STORAGE_KEYS.currentIndex, '0')); var totalCourses = parseInt(GM_getValue(STORAGE_KEYS.totalCourses, '0')); if (currentIndex >= courses.length) { log('所有课程已处理完毕', 'success'); finishAutoEval(); return; } var currentCourse = courses[currentIndex]; if (!currentCourse) { log('无法获取当前课程信息', 'error'); return; } // 拦截弹窗 overrideDialogs(); // 标记保存进行中 GM_setValue(STORAGE_KEYS.saveInProgress, 'true'); // 创建面板 createStatusPanel(); document.getElementById('qfsd-stop-btn').addEventListener('click', stopAutoEval); updateStatus( '正在评价 (' + (currentIndex + 1) + '/' + totalCourses + ')', currentCourse.courseName + ' - ' + currentCourse.teacherName, '开始评价: ' + currentCourse.courseName ); // 等待页面加载 log('等待 ' + CONFIG.delayBeforeFill + 'ms...'); await sleep(CONFIG.delayBeforeFill); // 1. 填写评分 updateStatus('正在填写评分...', currentCourse.courseName + ' - ' + currentCourse.teacherName); var ratingCount = fillEvaluationOptions(); log('已填写 ' + ratingCount + ' 个评分选项', 'success'); // 2. 填写建议 updateStatus('正在填写建议...', currentCourse.courseName + ' - ' + currentCourse.teacherName); var suggestionCount = fillSuggestions(); log('已填写 ' + suggestionCount + ' 个建议字段', 'success'); await sleep(800); // 3. 点击提交按钮 updateStatus( '正在提交...', currentCourse.courseName + ' - ' + currentCourse.teacherName, '点击提交按钮' ); var submitted = clickSubmitButton(); if (!submitted) { log('提交失败!', 'error'); updateStatus('❌ 提交失败', currentCourse.courseName, '未找到提交按钮'); GM_setValue(STORAGE_KEYS.saveInProgress, 'false'); return; } log('已点击提交,等待服务器响应...', 'info'); // 4. 等待服务器处理 await sleep(CONFIG.delayAfterSubmit); // 5. 继续下一门 await waitForSaveAndContinue(currentIndex, courses); } function finishAutoEval() { restoreDialogs(); var completedCount = GM_getValue(STORAGE_KEYS.completedCourses, '0'); var totalCourses = GM_getValue(STORAGE_KEYS.totalCourses, '0'); log('========================================', 'success'); log('🎉 全部评教完成!共 ' + completedCount + '/' + totalCourses + ' 门', 'success'); updateStatus( '🎉 全部完成!', '已完成 ' + completedCount + '/' + totalCourses + ' 门', '全部课程评教完成!即将返回列表页...' ); GM_deleteValue(STORAGE_KEYS.autoMode); GM_deleteValue(STORAGE_KEYS.courseList); GM_deleteValue(STORAGE_KEYS.currentIndex); GM_deleteValue(STORAGE_KEYS.totalCourses); GM_deleteValue(STORAGE_KEYS.completedCourses); GM_deleteValue(STORAGE_KEYS.saveInProgress); showToast('全部评教完成!共处理 ' + completedCount + ' 门课程'); setTimeout(function () { var returnBtn = document.getElementById('btnShenshen'); if (returnBtn) { returnBtn.click(); } else { window.location.href = '/jsxsd/xspj/xspj_find.do'; } }, 3000); } // ============ CSS ============ GM_addStyle( '#qfsd-auto-eval-panel { position:fixed; top:20px; right:20px; width:380px; background:#fff; ' + 'border:2px solid #178fe6; border-radius:12px; box-shadow:0 8px 32px rgba(0,0,0,0.2); ' + 'z-index:99999; font-family:"Microsoft YaHei","PingFang SC",sans-serif; overflow:hidden; ' + 'animation:qfsd-slide-in 0.3s ease-out; } ' + '@keyframes qfsd-slide-in { from { transform:translateX(400px); opacity:0; } ' + 'to { transform:translateX(0); opacity:1; } } ' + '.qfsd-panel-header { background:linear-gradient(135deg,#178fe6,#206ca2); color:#fff; ' + 'padding:14px 18px; font-size:16px; font-weight:bold; } ' + '.qfsd-panel-body { padding:16px 18px; } ' + '.qfsd-panel-status { font-size:15px; color:#303133; margin-bottom:12px; font-weight:bold; } ' + '.qfsd-panel-progress { height:22px; background:#f0f0f0; border-radius:11px; ' + 'overflow:hidden; margin-bottom:10px; } ' + '.qfsd-progress-bar { height:100%; background:linear-gradient(90deg,#67c23a,#85ce61); ' + 'border-radius:11px; transition:width 0.5s ease; font-size:11px; color:#fff; ' + 'text-align:center; line-height:22px; min-width:40px; } ' + '.qfsd-panel-info { font-size:13px; color:#606266; margin-bottom:8px; } ' + '.qfsd-panel-log { max-height:120px; overflow-y:auto; font-size:11px; color:#909399; ' + 'background:#fafafa; border-radius:6px; padding:8px; line-height:1.6; } ' + '.qfsd-log-entry { border-bottom:1px solid #f0f0f0; padding:2px 0; } ' + '.qfsd-panel-footer { padding:10px 18px; border-top:1px solid #eee; text-align:right; } ' + '.qfsd-btn { display:inline-block; padding:8px 20px; border:none; border-radius:6px; ' + 'cursor:pointer; font-size:13px; font-family:inherit; transition:all 0.2s; } ' + '.qfsd-btn-primary { background:linear-gradient(135deg,#178fe6,#36bafd); color:#fff; ' + 'font-size:15px; padding:10px 24px; box-shadow:0 2px 8px rgba(23,143,230,0.3); } ' + '.qfsd-btn-primary:hover { transform:translateY(-1px); box-shadow:0 4px 12px rgba(23,143,230,0.4); } ' + '.qfsd-btn-danger { background:#f56c6c; color:#fff; } ' + '.qfsd-btn-danger:hover { background:#f78989; } ' + '#qfsd-auto-start-container { margin:16px 0; padding:16px; background:#f0f9ff; ' + 'border:1px dashed #178fe6; border-radius:8px; text-align:center; } ' + '#qfsd-toast { position:fixed; top:20px; left:50%; transform:translateX(-50%); ' + 'background:#67c23a; color:#fff; padding:12px 24px; border-radius:8px; font-size:15px; ' + 'z-index:100000; box-shadow:0 4px 16px rgba(0,0,0,0.2); ' + 'animation:qfsd-fade-in 0.3s ease-out; } ' + '@keyframes qfsd-fade-in { from { opacity:0; transform:translateX(-50%) translateY(-20px); } ' + 'to { opacity:1; transform:translateX(-50%) translateY(0); } } ' + '#qfsd-manual-trigger { position:fixed; bottom:30px; right:30px; z-index:99999; ' + 'box-shadow:0 4px 16px rgba(0,0,0,0.2); }' ); // ============ 主入口 ============ (function main() { var pageType = getPageType(); log('当前页面: ' + pageType + ' [' + window.location.href + ']'); var isAutoMode = GM_getValue(STORAGE_KEYS.autoMode, 'false'); if (pageType === 'list') { log('已进入课程列表页'); setTimeout(function () { // 首先要确保弹窗拦截已启用(从 eval 页跳回时需要) if (isAutoMode === 'true') { overrideDialogs(); } createStartButton(); // 自动模式:列表页检测到后继续处理下一门 if (isAutoMode === 'true') { var coursesJson = GM_getValue(STORAGE_KEYS.courseList, '[]'); var courses = JSON.parse(coursesJson); var currentIndex = parseInt(GM_getValue(STORAGE_KEYS.currentIndex, '0')); log('自动模式: index=' + currentIndex + ', totalCourses=' + courses.length); if (currentIndex < courses.length && courses[currentIndex]) { var next = courses[currentIndex]; log('自动继续: ' + next.courseName + ' - ' + next.teacherName, 'success'); createStatusPanel(); document.getElementById('qfsd-stop-btn').addEventListener('click', stopAutoEval); updateStatus( '下一步 (' + (currentIndex + 1) + '/' + courses.length + ')', next.courseName + ' - ' + next.teacherName, '自动跳转到: ' + next.courseName ); setTimeout(function () { window.location.href = next.url; }, 1000); } else { // 所有课程完成 log('列表页检测: 所有课程已完成', 'success'); finishAutoEval(); } } }, CONFIG.delayListPageCheck); } else if (pageType === 'eval') { if (isAutoMode === 'true') { log('自动模式: 开始评价流程', 'success'); overrideDialogs(); // 延迟执行,确保页面完全初始化 setTimeout(function () { performAutoEval(); }, 500); } else { log('非自动模式,显示手动触发按钮'); setTimeout(function () { if (document.getElementById('qfsd-manual-trigger')) return; var btn = document.createElement('button'); btn.id = 'qfsd-manual-trigger'; btn.className = 'qfsd-btn qfsd-btn-primary'; btn.textContent = '⚡ 一键自动评价'; btn.onclick = async function () { btn.disabled = true; btn.textContent = '⏳ 评价中...'; overrideDialogs(); GM_setValue(STORAGE_KEYS.autoMode, 'true'); GM_setValue(STORAGE_KEYS.courseList, JSON.stringify([{ index: 0, courseName: '当前课程', teacherName: '', url: window.location.href, }])); GM_setValue(STORAGE_KEYS.currentIndex, '0'); GM_setValue(STORAGE_KEYS.totalCourses, '1'); GM_setValue(STORAGE_KEYS.completedCourses, '0'); GM_setValue(STORAGE_KEYS.saveInProgress, 'false'); await performAutoEval(); }; document.body.appendChild(btn); }, 1000); } } })(); })();