// ==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 =
'
' +
'' +
'
初始化...
' +
'
' +
'
' +
'
' +
'
' +
'';
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);
}
}
})();
})();