// ==UserScript==
// @name 半自动量化评教器(天津农学院教务系统专用)
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description 天津农学院教务系统量化评教半自动选择工具,支持评分选择、文本输入和元素点击
// @author NephrenRuqInsania-1582160745@qq.com;
// @match *://jwxt.tjau.edu.cn/eams/homeExt.action*
// @grant none
//
// @tag 天农
// @tag 天津农学院
// @tag 教务系统
// @tag 量化评教
// ==/UserScript==
// ==== 量化评教配置 ====
const VALUE_CONFIG = {
targetValues: [0, 1, 2, 3, 4], // 支持的评分等级
defaultValue: 0, // 默认选择的评分等级
selector: 'input.option-radio', // 基础选择器
evaluationTitle: '天津农学院量化评教系统', // 评教系统标题
evaluationDescription: '请根据教学质量选择相应评分等级', // 评教说明
defaultText: '无', // 默认文本内容
panelOpacity: 0.5, // 面板透明度
submitButtonId: 'sub' // 评教页内的提交按钮`input`元素的ID
};
// ==== 评分等级按钮颜色 ====
function getButtonColor(value) {
const colors = {
0: '#4CAF50', // 5分 - 绿色
1: '#8BC34A', // 4分 - 浅绿
2: '#FFC107', // 3分 - 黄色
3: '#FF9800', // 2分 - 橙色
4: '#F44336' // 1分 - 红色
};
return colors[value] || '#607D8B';
}
// ==== CSS样式配置 ====
const CSS_CONFIG = {
notificationCss: `
position: fixed;
top: 20px;
right: 20px;
background: #4CAF50;
color: white;
padding: 15px 20px;
border-radius: 5px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
max-width: 350px;
opacity: 0;
transform: translateY(-10px);
transition: opacity 0.3s, transform 0.3s;
pointer-events: none;
`,
panelCss: `
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(51, 122, 183, ${VALUE_CONFIG.panelOpacity});
border-radius: 12px;
padding: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
z-index: 10000;
min-width: 200px;
font-family: Arial, sans-serif;
backdrop-filter: blur(5px);
`,
titleCss: `
color: white;
font-weight: bold;
text-align: center;
margin-bottom: 10px;
font-size: 16px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255,255,255,0.3);
`,
scoreTitleCss: `
color: rgba(255,255,255,0.8);
font-size: 12px;
margin-bottom: 8px;
text-align: center;
`,
bottonsContainerCss: `
display: flex;
flex-direction: column;
gap: 8px;
`,
textTitleCss: `
color: rgba(255,255,255,0.8);
font-size: 12px;
margin-bottom: 8px;
text-align: center;
`,
textInputCss: `
width: 100%;
height: 50px;
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
margin-bottom: 8px;
resize: vertical;
font-family: Arial, sans-serif;
font-size: 12px;
`,
buttonCss(value) { return `
background: ${getButtonColor(value)};
color: white;
border: none;
border-radius: 6px;
padding: 8px 12px;
font-size: 12px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
width: 100%;
text-align: center;
`},
fillButtonCss: `
background: #607D8B;
color: white;
border: none;
border-radius: 6px;
padding: 8px 12px;
font-size: 12px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
width: 100%;
text-align: center;
`,
elementTitleCss: `
color: rgba(255,255,255,0.8);
font-size: 12px;
margin-bottom: 8px;
text-align: center;
`,
clickInputButtonCss: `
background: #9C27B0;
color: white;
border: none;
border-radius: 6px;
padding: 8px 12px;
font-size: 12px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
width: 100%;
text-align: center;
`,
};
// ==== 反向评分映射 ====
function mappingIdValueToScore(idValue) {
const scoreMap = {
0: 5,
1: 4,
2: 3,
3: 2,
4: 1
};
return scoreMap[idValue] || 0;
}
(function() {
'use strict';
// ==== 评教页面元素查找 ====
function findTargetIframe() {
const iframePaths = [
'iframe#iframeMain',
'iframe.evaluation-frame',
'div.evaluation-container iframe',
'div#main-content iframe'
];
for (let path of iframePaths) {
const iframe = document.querySelector(path);
if (iframe) return iframe;
}
return document.querySelectorAll('iframe')[0] || null;
}
// ==== 主功能:自动选择指定评分的选项 ====
function autoSelectOptions(targetValue = VALUE_CONFIG.defaultValue) {
const displayScore = mappingIdValueToScore(targetValue);
console.log(`开始自动选择评分等级=${targetValue}(显示为${displayScore}分)的选项...`);
// 查找iframe元素
const iframe = findTargetIframe();
if (!iframe) {
console.log('未找到评教iframe');
showNotification('未找到评教页面,请检查是否在评教界面');
return;
}
try {
// 获取iframe内的评教文档
const iframeDoc = getIframeDocument(iframe);
if (!iframeDoc) {
console.log('无法访问评教内容');
showNotification('无法访问评教内容,可能是跨域限制');
return;
}
// 在评教页面内部查找目标选项
const selector = `${VALUE_CONFIG.selector}[value="${targetValue}"]`;
const targetOptions = iframeDoc.querySelectorAll(selector);
if (targetOptions.length === 0) {
console.log(`评教页面内未找到评分等级为${targetValue}的选项`);
showNotification(`未找到评分等级${displayScore}分的选项`);
return;
}
// 选择所有找到的评分选项
let selectedCount = 0;
targetOptions.forEach(option => {
if (option.offsetParent !== null && !option.disabled) {
option.checked = true;
option.dispatchEvent(new Event('change', { bubbles: true }));
option.dispatchEvent(new Event('click', { bubbles: true }));
selectedCount++;
}
});
console.log(`已自动选择 ${selectedCount} 个评分等级=${targetValue}(显示为${displayScore}分)的选项`);
showNotification(`已选择 ${selectedCount} 个${displayScore}分选项`);
} catch (error) {
console.error('处理评教内容时出错:', error);
showNotification('评教处理出错: ' + error.message);
}
}
// ==== 文本输入功能 ====
function fillTextAreas() {
const customText = document.getElementById('customTextInput').value;
if (!customText) {
showNotification('请输入要填充的文本内容');
return;
}
const iframe = findTargetIframe();
if (!iframe) {
showNotification('未找到评教页面');
return;
}
try {
const iframeDoc = getIframeDocument(iframe);
if (!iframeDoc) {
showNotification('无法访问评教内容');
return;
}
const textAreas = iframeDoc.querySelectorAll('textarea.answer.answer-textarea');
if (textAreas.length === 0) {
showNotification('未找到需要填写的文本框');
return;
}
let filledCount = 0;
textAreas.forEach(textarea => {
if (textarea.offsetParent !== null && !textarea.disabled) {
textarea.value = customText;
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
filledCount++;
}
});
showNotification(`已填充 ${filledCount} 个文本框`);
} catch (error) {
console.error('填充文本框时出错:', error);
showNotification('填充文本框出错: ' + error.message);
}
}
// ==== 获取并点击指定提交按钮 ====
function clickTargetInput() {
const iframe = findTargetIframe();
if (!iframe) {
showNotification('未找到评教页面');
return;
}
try {
const iframeDoc = getIframeDocument(iframe);
if (!iframeDoc) {
showNotification('无法访问评教内容');
return;
}
const targetInput = iframeDoc.getElementById(VALUE_CONFIG.submitButtonId);
if (!targetInput) {
showNotification(`未找到ID为${VALUE_CONFIG.submitButtonId}的input元素`);
return;
}
console.log('找到目标input元素:', targetInput);
// 检查元素是否可见和可点击
if (targetInput.offsetParent === null || targetInput.disabled) {
showNotification(`找到的input元素不可点击`);
return;
}
// 创建并触发鼠标点击事件
const clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
targetInput.dispatchEvent(clickEvent);
showNotification(`已触发ID为${VALUE_CONFIG.submitButtonId}的input点击事件`);
} catch (error) {
console.error('处理input元素时出错:', error);
showNotification('处理input元素出错: ' + error.message);
}
}
// ==== 安全获取评教内容 ====
function getIframeDocument(iframe) {
try {
return iframe.contentDocument ||
iframe.contentWindow?.document ||
iframe.document;
} catch (error) {
console.error('无法访问评教iframe文档:', error);
return null;
}
}
// ==== 评教操作通知 ====
function showNotification(message) {
const existingNotification = document.getElementById('evaluationNotification');
if (existingNotification) {
existingNotification.remove();
}
const notification = document.createElement('div');
notification.id = 'evaluationNotification';
notification.style.cssText = CSS_CONFIG.textInputCss;
notification.innerHTML = `
${VALUE_CONFIG.evaluationTitle}
${message}
`;
document.body.appendChild(notification);
// 显示通知
setTimeout(() => {
notification.style.opacity = '1';
notification.style.transform = 'translateY(0)';
}, 10);
// 自动淡出并移除
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateY(-10px)';
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 300);
}, 3000);
}
// ==== 创建评教控制面板 ====
function createEvaluationPanel() {
const existingPanel = document.getElementById('evaluationControlPanel');
if (existingPanel) {
existingPanel.remove();
}
const panel = document.createElement('div');
panel.id = 'evaluationControlPanel';
panel.style.cssText = CSS_CONFIG.panelCss;
// 评教面板标题
const title = document.createElement('div');
title.textContent = VALUE_CONFIG.evaluationTitle;
title.style.cssText = CSS_CONFIG.titleCss;
panel.appendChild(title);
// 评分选择部分
const scoreSection = document.createElement('div');
scoreSection.style.marginBottom = '15px';
const scoreTitle = document.createElement('div');
scoreTitle.textContent = '评分选择 (5分最高)';
scoreTitle.style.cssText = CSS_CONFIG.scoreTitleCss;
scoreSection.appendChild(scoreTitle);
// 评分等级按钮容器
const buttonsContainer = document.createElement('div');
buttonsContainer.style.cssText = CSS_CONFIG.bottonsContainerCss;
// 创建每个评分等级的按钮
VALUE_CONFIG.targetValues.forEach(value => {
const displayScore = mappingIdValueToScore(value);
const button = document.createElement('button');
button.textContent = `选择 ${displayScore} 分`;
button.title = `选择所有评分等级为 ${displayScore} 分的选项`;
button.style.cssText = CSS_CONFIG.buttonCss(value);
// 按钮交互效果
button.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-1px)';
this.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
this.style.filter = 'brightness(1.1)';
});
button.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
this.style.boxShadow = 'none';
this.style.filter = 'brightness(1)';
});
button.addEventListener('click', () => autoSelectOptions(value));
buttonsContainer.appendChild(button);
});
scoreSection.appendChild(buttonsContainer);
panel.appendChild(scoreSection);
// 文本输入部分
const textSection = document.createElement('div');
const textTitle = document.createElement('div');
textTitle.textContent = '文本输入';
textTitle.style.cssText = CSS_CONFIG.textTitleCss;
textSection.appendChild(textTitle);
// 创建文本输入框
const textInput = document.createElement('textarea');
textInput.id = 'customTextInput';
textInput.value = VALUE_CONFIG.defaultText;
textInput.placeholder = '输入要填充的文本内容...';
textInput.style.cssText = CSS_CONFIG.textInputCss;
textSection.appendChild(textInput);
// 创建填充按钮
const fillButton = document.createElement('button');
fillButton.textContent = '填充所有文本框';
fillButton.style.cssText = CSS_CONFIG.fillButtonCss;
fillButton.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-1px)';
this.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
this.style.filter = 'brightness(1.1)';
});
fillButton.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
this.style.boxShadow = 'none';
this.style.filter = 'brightness(1)';
});
fillButton.addEventListener('click', fillTextAreas);
textSection.appendChild(fillButton);
panel.appendChild(textSection);
// 元素获取部分
const elementSection = document.createElement('div');
elementSection.style.marginTop = '15px';
const elementTitle = document.createElement('div');
elementTitle.textContent = '元素操作';
elementTitle.style.cssText = CSS_CONFIG.elementTitleCss;
elementSection.appendChild(elementTitle);
// 创建点击input按钮
const clickInputButton = document.createElement('button');
clickInputButton.textContent = `点击提交`;
clickInputButton.style.cssText = CSS_CONFIG.clickInputButtonCss;
clickInputButton.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-1px)';
this.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
this.style.filter = 'brightness(1.1)';
});
clickInputButton.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
this.style.boxShadow = 'none';
this.style.filter = 'brightness(1)';
});
clickInputButton.addEventListener('click', clickTargetInput);
elementSection.appendChild(clickInputButton);
panel.appendChild(elementSection);
document.body.appendChild(panel);
}
// ==== 初始化评教工具 ====
function initEvaluationTool() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
setTimeout(createEvaluationPanel, 1000);
});
} else {
setTimeout(createEvaluationPanel, 1000);
}
}
// ==== 启动量化评教工具 ====
initEvaluationTool();
})();