// ==UserScript==
// @name 兰州博文科技学院教学评教
// @namespace https://github.com/bowenedu-auto-evaluation
// @version 1.0.0
// @description 兰博文教务系统自动评教脚本,一键完成教学评价,默认最高好评
// @author Assistant
// @license GPL-3.0-or-later
// @match *://jwxt.bowenedu.cn:8080/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
// ========== 验证码验证模块 ==========
window.BOWEN_VERIFIED = false;
(function() {
'use strict';
const VERIFY_API = 'https://qsy.iano.cn/index.php?s=/api/code/verify';
const STORAGE_KEY = 'bowen_eval_valid_until';
const VERIFY_DATE_KEY = 'bowen_eval_verify_date';
const SESSION_KEY = 'bowen_eval_session';
const QRCODE_IMG = 'https://qsy.iano.cn/yzm.png';
function getTodayStr() {
return new Date().toISOString().slice(0, 10);
}
function isVerified() {
const validUntil = GM_getValue(STORAGE_KEY, 0);
if (validUntil <= Date.now() / 1000) return false;
if (sessionStorage.getItem(SESSION_KEY)) return true;
const verifyDate = GM_getValue(VERIFY_DATE_KEY, '');
const today = getTodayStr();
if (verifyDate !== today) return false;
sessionStorage.setItem(SESSION_KEY, '1');
return true;
}
function showVerifyDialog() {
if (document.getElementById('_verify_overlay')) return;
const overlay = document.createElement('div');
overlay.id = '_verify_overlay';
overlay.innerHTML = `
🔐 验证码验证
扫码观看广告获取验证码
验证后免费使用24小时
`;
(document.body || document.documentElement).appendChild(overlay);
const input = document.getElementById('_verify_code');
const btn = document.getElementById('_verify_submit');
const errorEl = document.getElementById('_verify_error');
btn.onclick = function() {
const code = input.value.trim();
if (!/^\d{4}$/.test(code)) {
errorEl.textContent = '请输入4位验证码';
errorEl.style.display = 'block';
errorEl.classList.remove('shake');
void errorEl.offsetWidth;
errorEl.classList.add('shake');
return;
}
btn.disabled = true;
btn.textContent = '验证中...';
errorEl.style.display = 'none';
GM_xmlhttpRequest({
method: 'POST',
url: VERIFY_API,
timeout: 10000,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: 'code=' + encodeURIComponent(code),
onload: function(res) {
try {
const data = JSON.parse(res.responseText);
if (data.code === 1 && data.data.valid) {
GM_setValue(STORAGE_KEY, data.data.valid_until);
GM_setValue(VERIFY_DATE_KEY, getTodayStr());
sessionStorage.setItem(SESSION_KEY, '1');
overlay.remove();
alert('验证成功!24小时内免费使用');
location.reload();
} else {
errorEl.textContent = data.msg || '验证码无效或已过期';
errorEl.style.display = 'block';
errorEl.classList.remove('shake');
void errorEl.offsetWidth;
errorEl.classList.add('shake');
btn.disabled = false;
btn.textContent = '立即验证';
}
} catch(e) {
errorEl.textContent = '验证失败,请重试';
errorEl.style.display = 'block';
btn.disabled = false;
btn.textContent = '立即验证';
}
},
onerror: function() {
errorEl.textContent = '网络错误,请重试';
errorEl.style.display = 'block';
btn.disabled = false;
btn.textContent = '立即验证';
}
});
};
input.onkeypress = function(e) { if (e.key === 'Enter') btn.click(); };
}
if (!isVerified()) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', showVerifyDialog);
} else {
showVerifyDialog();
}
return;
}
window.BOWEN_VERIFIED = true;
})();
// ========== 验证码验证模块结束 ==========
(function() {
'use strict';
if (!window.BOWEN_VERIFIED) {
console.log('[博文评教] 未通过验证,脚本已停止');
return;
}
const CONFIG = {
AUTO_SUBMIT: true,
LOG_DETAIL: true
};
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const log = (...args) => CONFIG.LOG_DETAIL && console.log('[博文评教]', ...args);
const logImportant = (...args) => console.log('[博文评教]', ...args);
function getAutoSubmitState() {
if (typeof GM_getValue !== 'undefined') {
return GM_getValue('AUTO_SUBMIT', CONFIG.AUTO_SUBMIT);
}
const saved = localStorage.getItem('BOWEN_AUTO_SUBMIT');
return saved !== null ? JSON.parse(saved) : CONFIG.AUTO_SUBMIT;
}
function setAutoSubmitState(state) {
if (typeof GM_setValue !== 'undefined') {
GM_setValue('AUTO_SUBMIT', state);
} else {
localStorage.setItem('BOWEN_AUTO_SUBMIT', JSON.stringify(state));
}
updateToggleButton(state);
}
let buttonsCreated = false;
function updateToggleButton(autoSubmitState) {
const toggleBtn = document.getElementById('toggleAutoSubmitBtn');
if (toggleBtn) {
toggleBtn.textContent = autoSubmitState ? '✅ 自动提交:开' : '⏸️ 自动提交:关';
toggleBtn.style.background = autoSubmitState ?
'linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%)' :
'linear-gradient(135deg, #757575 0%, #424242 100%)';
}
}
function createControlButtons() {
// 检查面板是否真实存在于DOM中
const existingPanel = document.getElementById('bowen-eval-controls');
if (existingPanel) {
logImportant('控制面板已存在于DOM,跳过创建');
return;
}
// 检查是否已经在创建过程中
if (buttonsCreated) {
logImportant('控制面板正在创建中,跳过');
return;
}
buttonsCreated = true;
logImportant('创建控制面板...');
const panel = document.createElement('div');
panel.id = 'bowen-eval-controls';
panel.style.cssText = `
position: fixed !important;
top: 20px !important;
right: 20px !important;
z-index: 2147483647 !important;
background: linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(250,250,255,0.98) 100%) !important;
backdrop-filter: blur(15px) !important;
border-radius: 20px !important;
padding: 24px !important;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15) !important;
border: 1px solid rgba(255, 255, 255, 0.9) !important;
min-width: 280px !important;
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif !important;
`;
const title = document.createElement('div');
title.style.cssText = `
font-size: 18px !important;
font-weight: bold !important;
color: #333 !important;
margin-bottom: 16px !important;
text-align: center !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
background-clip: text !important;
`;
title.textContent = '博文自动评教';
const mainBtn = document.createElement('button');
mainBtn.id = 'mainEvaluateBtn';
mainBtn.textContent = '开始自动评教';
mainBtn.style.cssText = `
width: 100% !important;
padding: 14px !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
border: none !important;
border-radius: 12px !important;
font-size: 15px !important;
font-weight: bold !important;
cursor: pointer !important;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
transition: all 0.3s !important;
margin-bottom: 12px !important;
`;
const toggleBtn = document.createElement('button');
toggleBtn.id = 'toggleAutoSubmitBtn';
const autoSubmitState = getAutoSubmitState();
toggleBtn.textContent = autoSubmitState ? '自动提交: 开启' : '自动提交: 关闭';
toggleBtn.style.cssText = `
width: 100% !important;
padding: 10px !important;
background: ${autoSubmitState ?
'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)' :
'linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%)'} !important;
color: white !important;
border: none !important;
border-radius: 10px !important;
font-size: 13px !important;
font-weight: 600 !important;
cursor: pointer !important;
transition: all 0.3s !important;
margin-bottom: 16px !important;
`;
const divider = document.createElement('div');
divider.style.cssText = `
height: 1px !important;
background: linear-gradient(90deg, transparent, #e0e0e0, transparent) !important;
margin: 16px 0 !important;
`;
const infoText = document.createElement('div');
infoText.style.cssText = `
font-size: 12px !important;
color: #666 !important;
line-height: 1.8 !important;
`;
infoText.innerHTML = `
✓ 自动选择最高分
✓ 自动填写好评
✓ 可选自动提交
`;
mainBtn.onmouseover = () => {
mainBtn.style.transform = 'translateY(-2px)';
mainBtn.style.boxShadow = '0 6px 20px rgba(102, 126, 234, 0.5)';
};
mainBtn.onmouseout = () => {
mainBtn.style.transform = 'translateY(0)';
mainBtn.style.boxShadow = '0 4px 15px rgba(102, 126, 234, 0.4)';
};
toggleBtn.onmouseover = () => toggleBtn.style.transform = 'translateY(-1px)';
toggleBtn.onmouseout = () => toggleBtn.style.transform = 'translateY(0)';
mainBtn.addEventListener('click', async () => {
logImportant('按钮被点击');
mainBtn.disabled = true;
mainBtn.style.opacity = '0.6';
mainBtn.textContent = '执行中...';
try {
const evalLink = document.querySelector('li[data-link*="teacherstudentemenu"]') ||
document.querySelector('li.menu-item[title*="教学评价"]') ||
Array.from(document.querySelectorAll('li, a')).find(el =>
el.textContent?.trim() === '教学评价' || el.getAttribute('title')?.includes('教学评价'));
if (evalLink && !document.querySelector('textarea, input[type="radio"]')) {
logImportant('检测到教学评价链接,正在跳转...', evalLink);
evalLink.click();
await sleep(3000);
const iframe = document.querySelector('iframe');
if (iframe) {
logImportant('检测到iframe,等待加载...');
await sleep(2000);
}
} else if (!evalLink) {
logImportant('未找到教学评价链接,直接执行评教');
} else {
logImportant('已在评教页面,直接执行');
}
await autoEvaluate();
} catch (error) {
logImportant('执行出错:', error);
} finally {
setTimeout(() => {
mainBtn.disabled = false;
mainBtn.style.opacity = '1';
mainBtn.textContent = '开始自动评教';
}, 3000);
}
});
toggleBtn.addEventListener('click', () => {
const newState = !getAutoSubmitState();
setAutoSubmitState(newState);
toggleBtn.textContent = newState ? '自动提交: 开启' : '自动提交: 关闭';
toggleBtn.style.background = newState ?
'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)' :
'linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%)';
logImportant(`自动提交已${newState ? '开启' : '关闭'}`);
});
panel.appendChild(title);
panel.appendChild(mainBtn);
panel.appendChild(toggleBtn);
panel.appendChild(divider);
panel.appendChild(infoText);
document.body.appendChild(panel);
}
async function fillTextEvaluations() {
const textAreas = document.querySelectorAll('textarea');
if (textAreas.length === 0) return;
logImportant(`找到 ${textAreas.length} 个文本框`);
const comments = [
"老师授课准备充分,讲解清晰生动,重点突出,课堂节奏把握得很好。",
"课程内容充实,理论与实践结合紧密,对启发思维和掌握知识很有帮助。",
"教学态度认真负责,对课程内容掌握深入,能感受到老师对教学的热情。"
];
for (let i = 0; i < textAreas.length; i++) {
const textArea = textAreas[i];
const comment = comments[i % comments.length];
textArea.value = comment;
textArea.dispatchEvent(new Event('input', { bubbles: true }));
textArea.dispatchEvent(new Event('change', { bubbles: true }));
await sleep(300);
}
logImportant('文本评价完成');
}
async function submitEvaluation() {
if (!getAutoSubmitState()) {
logImportant('自动提交已关闭,请手动提交');
return false;
}
await sleep(1000);
const submitBtn = document.querySelector('input[type="submit"], button[type="submit"], input[value*="提交"], button:contains("提交")') ||
Array.from(document.querySelectorAll('input, button')).find(el =>
el.value?.includes('提交') || el.textContent?.includes('提交'));
if (submitBtn) {
logImportant('找到提交按钮,准备提交...');
submitBtn.click();
await sleep(1500);
const confirmBtn = document.querySelector('.confirm, button:contains("确定")') ||
Array.from(document.querySelectorAll('button')).find(el => el.textContent?.includes('确定'));
if (confirmBtn) confirmBtn.click();
logImportant('✅ 提交完成');
return true;
}
logImportant('⚠️ 未找到提交按钮');
return false;
}
async function autoEvaluate() {
logImportant('🚀 开始自动评教...');
// 选择所有单选按钮的最高分
const radioGroups = {};
document.querySelectorAll('input[type="radio"]').forEach(radio => {
if (!radioGroups[radio.name]) radioGroups[radio.name] = [];
radioGroups[radio.name].push(radio);
});
let clickedCount = 0;
for (const [name, radios] of Object.entries(radioGroups)) {
if (radios.length > 0) {
radios[0].click();
clickedCount++;
await sleep(200);
}
}
logImportant(`选择题完成 ${clickedCount} 题`);
// 选择所有复选框
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(cb => !cb.checked && cb.click());
if (checkboxes.length > 0) logImportant(`复选框完成 ${checkboxes.length} 个`);
await fillTextEvaluations();
await submitEvaluation();
logImportant('✨ 评教流程完成!');
}
function initialize() {
if (!window.location.href.includes('jwxt.bowenedu.cn')) return;
// 防止重复创建
if (document.getElementById('bowen-eval-controls')) {
logImportant('控制面板已存在,跳过创建');
return;
}
// 延迟创建,确保页面加载完成
const createPanel = () => {
if (document.getElementById('bowen-eval-controls')) return;
createControlButtons();
};
if (document.readyState === 'complete') {
createPanel();
} else {
window.addEventListener('load', createPanel, { once: true });
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();