您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动完成宜宾学院教学评价,支持自定义评分(90-95分),自动填写评语,一键完成评教
// ==UserScript== // @name 宜宾学院评教助手 // @namespace 宜宾学院教务系统评教自动化工具,帮助学生快速完成教学评价===来自计算机科学与技术学院 // @version 2.0 // @description 自动完成宜宾学院教学评价,支持自定义评分(90-95分),自动填写评语,一键完成评教 // @author 计算机科学与技术学院---软工 // @match https://ehall.yibinu.edu.cn/jwapp/sys/jwwspj/* // @icon https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png // @grant none // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // 添加样式 const style = document.createElement('style'); style.textContent = ` .auto-eval-btn { padding: 8px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; } .auto-eval-btn:hover { background-color: #45a049; } .score-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); z-index: 10000; max-height: 80vh; overflow-y: auto; min-width: 300px; } .eval-status { position: fixed; top: 50px; right: 10px; z-index: 9999; padding: 10px; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; max-width: 300px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); } .eval-complete-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); text-align: center; z-index: 10001; min-width: 300px; } .eval-complete-dialog h2 { color: #4CAF50; margin: 0 0 20px 0; } .eval-complete-dialog .icon { font-size: 48px; margin-bottom: 20px; color: #4CAF50; } .eval-complete-dialog .message { color: #666; margin-bottom: 20px; } .eval-complete-dialog button { padding: 8px 20px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; } .eval-complete-dialog button:hover { background: #45a049; } .eval-complete-dialog .author-info { color: #888; font-size: 12px; margin-top: 15px; padding-top: 15px; border-top: 1px solid #eee; } .eval-complete-dialog .author-info span { color: #4CAF50; font-weight: bold; } `; document.head.appendChild(style); // 状态显示函数 let statusDiv = null; function showStatus(message, duration = 3000) { if (!statusDiv) { statusDiv = document.createElement('div'); statusDiv.className = 'eval-status'; document.body.appendChild(statusDiv); } statusDiv.textContent = message; statusDiv.style.display = 'block'; console.log(message); // 添加淡出动画样式 statusDiv.style.transition = 'opacity 0.5s ease-in-out'; // 设置定时使提示消失 setTimeout(() => { statusDiv.style.opacity = '0'; setTimeout(() => { statusDiv.style.display = 'none'; statusDiv.style.opacity = '1'; }, 500); }, duration); } // 等待函数 function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 创建分数设置对话框 function createScoreDialog() { const dialog = document.createElement('div'); dialog.className = 'score-dialog'; // 获取所有教师 const teachers = []; const teacherElements = document.querySelectorAll('.sc-panel-js'); teacherElements.forEach(el => { const name = el.textContent.trim(); const course = el.closest('.kc-js-array').querySelector('.sc-panel-kc').textContent.trim(); // 使用教师名+课程名作为唯一标识 const teacherId = `${name}_${course}`; if (!teachers.some(t => t.id === teacherId)) { teachers.push({ id: teacherId, name, course }); } }); // 创建对话框内容 let html = '<h3>教师评分设置(建议90-95分)</h3>'; // 批量设置 html += ` <div style="margin-bottom: 15px; padding: 10px; border: 1px solid #eee; border-radius: 4px;"> <div>批量设置: <input type="number" id="batchScore" value="92" min="0" max="95" style="width: 60px; padding: 4px;"> <button id="applyBatchBtn" style="padding: 4px 8px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">应用到全部</button> </div> </div> `; // 教师列表 teachers.forEach(teacher => { html += ` <div style="margin-bottom: 15px; padding: 10px; border: 1px solid #eee; border-radius: 4px;"> <div style="font-weight: bold; margin-bottom: 5px;">${teacher.name}</div> <div style="font-size: 12px; color: #666; margin-bottom: 8px;">${teacher.course}</div> <div style="display: flex; align-items: center; gap: 10px;"> <input type="number" class="teacher-score" data-teacher-id="${teacher.id}" value="${window.teacherScores[teacher.id] || 92}" min="0" max="95" style="width: 80px; padding: 6px; border: 1px solid #ddd; border-radius: 4px;"> <span style="color: #666; font-size: 12px;">分 (建议90-95分)</span> </div> </div> `; }); // 按钮 html += ` <div style="margin-top: 15px; text-align: right;"> <button id="cancelBtn" style="padding: 6px 12px; margin-right: 10px; background: #f5f5f5; border: none; border-radius: 4px; cursor: pointer;">取消</button> <button id="confirmBtn" style="padding: 6px 12px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">确定</button> </div> `; dialog.innerHTML = html; document.body.appendChild(dialog); // 绑定事件 dialog.querySelector('#applyBatchBtn').addEventListener('click', window.applyBatchScore); dialog.querySelector('#cancelBtn').addEventListener('click', window.closeScoreDialog); dialog.querySelector('#confirmBtn').addEventListener('click', window.saveScores); // 添加输入验证 dialog.querySelectorAll('input[type="number"]').forEach(input => { input.addEventListener('input', (e) => { let value = parseInt(e.target.value) || 0; value = Math.min(95, Math.max(0, value)); e.target.value = value; }); }); } // 在评教函数前添加评语数组 const teacherComments = [ '老师授课的方式非常适合我们,他根据本课程知识结构的特点,重点突出,层次分明。和实际相结合,通过例题使知识更条理化。', '老师授课有条理,有重点,对同学既热情又严格,是各位老师学习的榜样。', '老师上课有时非常幽默,有时非常严格,不过还是非常有教授风度的。课堂氛围非常好!', '教师在课堂上通过丰富的教学方法和生动的案例,有效提高了学生的学习兴趣,增强了知识的吸收与理解能力。', '这位老师耐心细致,善于引导学生思考,课堂氛围活跃,学生积极参与,真正做到了寓教于乐。', '课程内容丰富深刻,老师的讲解条理清晰,能够将复杂的概念简单化,使学生在轻松愉快的环境中掌握知识。', '老师非常关心学生的学习进度,针对不同学生的需求,提供个性化的指导,帮助我们克服学习中的困难。', '教师善于与学生沟通,倾听我们的意见与建议,鼓励我们提出问题,让课堂成为一个充满互动和创造力的学习场所。', '课堂上教师运用多媒体辅助教学,生动形象地展示知识,激发了我们的学习热情,使我们对课程内容产生了浓厚的兴趣。', '这位老师不仅学识渊博,而且为人师表,言传身教,让我们不仅仅学到知识,更领悟了学习的态度。', '老师重视学术诚信,始终鼓励我们独立思考,抵制抄袭和依赖,使我们懂得诚信的重要性,塑造了良好学风。', '教师对学生的问题总是给予积极反馈,鼓励我们提出更多的疑问,这对促进我们的思考能力非常有帮助。', '这位老师热情洋溢,上课时能有效调动学生的积极性,使得课堂不再枯燥,激发了我们的学习热情。', '老师的教案准备得非常充分,知识点安排合理,有助于我们进行有效的复习,帮助我们提高学习效率。', '课堂氛围轻松愉快,教师善于使用幽默的方式讲解知识,减轻了我们的学习压力,让我们能更好地吸收。', '这位老师非常注重学生的反馈,经常根据我们的意见进行调整,使得课程的灵活性和适应性越来越强。', '教师的教学理念先进,强调批判性思维的培养,让我们在学习中不断提出问题,探索新的知识和解决方案。', '老师非常关心学生的全面发展,除了学业追求,还鼓励我们在课外参加丰富的活动,提升综合素质。', '教师非常关注学生的心理健康,善于倾听我们的问题,并给予恰当的建议,让我们在学习中更加自信。', '这位老师把实际案例与理论教学结合得很好,让我们在理解知识的同时,也能看到其实际应用的价值。' ]; // 评教函数 async function startEvaluation() { try { showStatus('开始评教流程...'); // 等待页面加载 await wait(2000); // 选择分数 const scoreContainers = document.querySelectorAll('.sc-panel-content.bh-clearfix.bh-mv-8.wjzb-card-jskc'); if (!scoreContainers || scoreContainers.length === 0) { throw new Error('未找到评分项'); } // 按教师分组处理评分项 const teacherQuestions = {}; scoreContainers.forEach(container => { const teacherElement = container.querySelector('.sc-panel-js'); if (!teacherElement) return; // 跳过没有教师名的容器 const teacherName = teacherElement.textContent.trim(); const courseElement = teacherElement.closest('.kc-js-array').querySelector('.sc-panel-kc'); if (!courseElement) return; const courseName = courseElement.textContent.trim(); // 使用教师名+课程名作为唯一标识 const teacherId = `${teacherName}_${courseName}`; if (!teacherQuestions[teacherId]) { teacherQuestions[teacherId] = []; } teacherQuestions[teacherId].push(container); }); // 处理每个教师的评分 for (let teacherId in teacherQuestions) { const targetScore = window.teacherScores[teacherId] || 92; const questions = teacherQuestions[teacherId]; // 计算需要减去的总分数 const pointsToDeduct = 100 - targetScore; let remainingPoints = pointsToDeduct; // 将题目按分值分组 const questionGroups = { high: [], // 10分以上 medium: [], // 5-9分 low: [] // 2-4分 }; questions.forEach(container => { const buttons = container.querySelectorAll('.fzItem.bh-pull-left'); if (!buttons || buttons.length <= 2) return; const maxScore = buttons.length; if (maxScore >= 10) { questionGroups.high.push(container); } else if (maxScore >= 5) { questionGroups.medium.push(container); } else { questionGroups.low.push(container); } }); // 随机决定是否在高分题目中扣分 const shouldDeductFromHigh = Math.random() < 0.3; // 30%概率从高分题扣 // 处理每个分值组的题目 for (let container of questions) { const scoreButtons = container.querySelectorAll('.fzItem.bh-pull-left'); if (!scoreButtons || scoreButtons.length === 0) continue; const maxScore = scoreButtons.length; let targetIndex = maxScore - 1; // 默认满分 if (remainingPoints > 0) { // 根据题目分值和随机性决定是否扣分 let shouldDeduct = false; let maxDeduct = 0; if (maxScore >= 10) { // 高分题目 (10分及以上) shouldDeduct = Math.random() < 0.8; // 80%概率扣分 maxDeduct = shouldDeduct ? 2 : 0; } else if (maxScore >= 7) { // 较高分题目 (7-9分) shouldDeduct = Math.random() < 0.6; // 60%概率扣分 maxDeduct = 2; } else if (maxScore >= 5) { // 中分题目 (5-6分) shouldDeduct = Math.random() < 0.4; // 40%概率扣分 maxDeduct = 1; } else if (maxScore > 2) { // 低分题目 (3-4分) shouldDeduct = Math.random() < 0.2; // 20%概率扣分 maxDeduct = 1; } if (shouldDeduct) { // 根据剩余需要扣的分数调整实际扣分 let actualDeduct = Math.min(maxDeduct, remainingPoints); // 对于高分值题目,确保至少扣1分 if (maxScore >= 7 && actualDeduct > 0) { actualDeduct = Math.max(1, actualDeduct); } if (actualDeduct > 0) { targetIndex = maxScore - 1 - actualDeduct; remainingPoints -= actualDeduct; } } } // 确保索引在有效范围内并点击 targetIndex = Math.max(0, Math.min(targetIndex, maxScore - 1)); if (scoreButtons[targetIndex] && !scoreButtons[targetIndex].classList.contains('active')) { scoreButtons[targetIndex].click(); await wait(100); } } } // 创建评语副本用于随机选择 let availableComments = [...teacherComments]; // 填写评语 const textareas = document.querySelectorAll('.bh-txt-input__txtarea'); for (let textarea of textareas) { // 如果评语用完了,重新填充 if (availableComments.length === 0) { availableComments = [...teacherComments]; } // 随机选择一条评语并从可用列表中移除 const randomIndex = Math.floor(Math.random() * availableComments.length); const comment = availableComments.splice(randomIndex, 1)[0]; textarea.value = comment; textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); await wait(100); } // 修改提交后的提示 const submitBtn = document.querySelector('a.bh-btn.bh-btn-success.bh-btn-large[data-action="提交"]'); if (submitBtn) { submitBtn.click(); showStatus('正在提交评教...', 2000); await wait(2000); showCompleteDialog(); } } catch (error) { console.error('评教过程出错:', error); showStatus('评教出错:' + error.message); alert('评教出错:' + error.message + '\n请刷新页面后重试'); } } // 初始化函数 async function initialize() { try { if (document.readyState !== 'complete') { await new Promise(resolve => window.addEventListener('load', resolve)); } await wait(3000); showStatus('页面加载完成,正在初始化评教脚本...'); addButton(); } catch (error) { console.error('初始化失败:', error); showStatus('初始化失败:' + error.message); } } // 启动脚本 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } // 添加按钮函数 function addButton() { const buttonContainer = document.createElement('div'); buttonContainer.style.position = 'fixed'; buttonContainer.style.top = '10px'; buttonContainer.style.right = '10px'; buttonContainer.style.zIndex = '9999'; // 设置分数按钮 const scoreButton = document.createElement('button'); scoreButton.className = 'auto-eval-btn'; scoreButton.style.marginRight = '10px'; scoreButton.textContent = '设置分数'; scoreButton.addEventListener('click', createScoreDialog); // 一键评教按钮 const evalButton = document.createElement('button'); evalButton.className = 'auto-eval-btn'; evalButton.textContent = '一键评教'; evalButton.addEventListener('click', startEvaluation); buttonContainer.appendChild(scoreButton); buttonContainer.appendChild(evalButton); document.body.appendChild(buttonContainer); showStatus('评教脚本已加载,请点击"设置分数"设置教师分数,然后点击"一键评教"开始评教', 5000); } // 添加全局变量 window.teacherScores = {}; // 添加全局函数 window.applyBatchScore = function() { const batchScore = parseInt(document.getElementById('batchScore').value) || 92; const validScore = Math.min(95, Math.max(0, batchScore)); document.querySelectorAll('.teacher-score').forEach(input => { input.value = validScore; }); }; window.saveScores = function() { document.querySelectorAll('.teacher-score').forEach(input => { const teacherId = input.dataset.teacherId; const score = parseInt(input.value) || 92; window.teacherScores[teacherId] = score; }); window.closeScoreDialog(); showStatus('教师评分已保存', 2000); }; window.closeScoreDialog = function() { const dialog = document.querySelector('.score-dialog'); if (dialog) { dialog.remove(); } }; // 修改评教完成提示函数 function showCompleteDialog() { const dialog = document.createElement('div'); dialog.className = 'eval-complete-dialog'; dialog.innerHTML = ` <div class="icon">✅</div> <h2>评教完成</h2> <div class="message">所有教师评分已完成,感谢您的参与!</div> <button onclick="this.parentElement.remove()">确定</button> <div class="author-info">作者:<span>软工</span>,谢谢支持!</div> `; document.body.appendChild(dialog); } })();