// ==UserScript== // @name 半自动量化评教器(天津农学院教务系统专用-标题拖动) // @namespace http://tampermonkey.net/ // @version 1.1 // @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, buttonOpacity: 0.5, submitButtonId: 'sub' }; // ==== 评分等级按钮颜色 ==== function getButtonColor(value) { const colors = { 0: '#4CAF50', 1: '#8BC34A', 2: '#FFC107', 3: '#FF9800', 4: '#F44336' }; return colors[value] || '#607D8B'; } // ==== 十六进制颜色转RGBA ==== function hexToRgba(hex, opacity = 1) { hex = hex.replace('#', ''); const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); return `rgba(${r}, ${g}, ${b}, ${opacity})`; } // ==== 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); cursor: move; user-select: none; `, 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: ${hexToRgba(getButtonColor(value), VALUE_CONFIG.buttonOpacity)}; 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: rgba(96, 125, 139, ${VALUE_CONFIG.buttonOpacity}); 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: rgba(156, 39, 176, ${VALUE_CONFIG.buttonOpacity}); 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'; // ==== 拖动功能变量 ==== let isDragging = false; let offsetX = 0; let offsetY = 0; let currentPanel = null; let dragHandle = null; // ==== 面板拖动处理函数 ==== function handleMouseDown(e) { if (e.target === dragHandle) { isDragging = true; offsetX = e.clientX - currentPanel.getBoundingClientRect().left; offsetY = e.clientY - currentPanel.getBoundingClientRect().top; dragHandle.style.cursor = 'grabbing'; currentPanel.style.userSelect = 'none'; } } function handleMouseMove(e) { if (!isDragging) return; const x = e.clientX - offsetX; const y = e.clientY - offsetY; currentPanel.style.left = `${x}px`; currentPanel.style.top = `${y}px`; currentPanel.style.right = 'auto'; currentPanel.style.bottom = 'auto'; } function handleMouseUp() { isDragging = false; if (dragHandle) { dragHandle.style.cursor = 'move'; } if (currentPanel) { currentPanel.style.userSelect = 'auto'; } } // ==== 主功能:自动选择指定评分的选项 ==== function autoSelectOptions(targetValue = VALUE_CONFIG.defaultValue) { const displayScore = mappingIdValueToScore(targetValue); console.log(`开始自动选择评分等级=${targetValue}(显示为${displayScore}分)的选项...`); const iframe = findTargetIframe(); if (!iframe) { console.log('未找到评教iframe'); showNotification('未找到评教页面,请检查是否在评教界面'); return; } try { 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 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 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.notificationCss; 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; currentPanel = panel; // 创建标题区域(拖动句柄) const title = document.createElement('div'); title.textContent = VALUE_CONFIG.evaluationTitle; title.style.cssText = CSS_CONFIG.titleCss; dragHandle = title; panel.appendChild(title); // 添加拖动事件监听(只在标题上) dragHandle.addEventListener('mousedown', handleMouseDown); document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); 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.style.cssText = CSS_CONFIG.elementTitleCss; elementSection.appendChild(elementTitle); 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(); })();