可以试试这个?
// ==UserScript==
// @name 兰大改
// @namespace https://github.com/DonnyRe/LZUAutoEvaluation
// @version 1.0.0
// @description 实现自动评教、可选的自动提交的自动化操作,省时省力,老师们也辛苦了默认最高好评。
// @author Donny
// @license GPL-3.0-or-later
// @match *://jwqe.lzu.edu.cn:8080/*
// @match *://my.lzu.edu.cn/*
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-end
// ==/UserScript==
(function() { 'use strict';
// ========== 配置区域 ========== const CONFIG = { AUTO\_SUBMIT: true, LOG\_DETAIL: true, DEFAULT\_SCORE\_LEVEL: 100 // 默认评分等级:100, 95, 90 };
// ---------- 工具函数 ---------- 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('LZU\_AUTO\_SUBMIT\_FINAL'); 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('LZU\_AUTO\_SUBMIT\_FINAL', JSON.stringify(state)); } updateToggleButton(state); } function getScoreLevel() { if (typeof GM\_getValue !== 'undefined') { return GM\_getValue('SCORE\_LEVEL', CONFIG.DEFAULT\_SCORE\_LEVEL); } const saved = localStorage.getItem('LZU\_SCORE\_LEVEL'); return saved !== null ? parseInt(saved) : CONFIG.DEFAULT\_SCORE\_LEVEL; } function setScoreLevel(level) { if (typeof GM\_setValue !== 'undefined') { GM\_setValue('SCORE\_LEVEL', level); } else { localStorage.setItem('LZU\_SCORE\_LEVEL', level.toString()); } updateScoreLevelButton(level); }
// ---------- 1. 创建控制按钮 (防重复) ---------- 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 updateScoreLevelButton(level) { const scoreBtn = document.getElementById('scoreLevelBtn'); if (scoreBtn) { scoreBtn.textContent = \`📊 评分: ${level}分\`; } }
function createControlButtons() { if \(buttonsCreated \|\| document\.getElementById\('lzu\-eval\-controls'\)\) \{ log('按钮已存在,跳过创建。'); return; } buttonsCreated = true; logImportant('正在创建控制按钮...');
const container = document.createElement('div'); container.id = 'lzu-eval-controls'; container.style.cssText = \` position: fixed !important; top: 20px !important; right: 20px !important; z-index: 2147483647 !important; display: flex !important; flex-direction: column !important; gap: 12px !important; align-items: flex-end !important; font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif !important; \`;
// 主按钮 const mainBtn = document.createElement('button'); mainBtn.id = 'mainEvaluateBtn'; mainBtn.textContent = '🚀 开始自动评教'; mainBtn.style.cssText = \` padding: 14px 28px !important; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; border: none !important; border-radius: 30px !important; font-size: 16px !important; font-weight: bold !important; cursor: pointer !important; box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4) !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; min-width: 200px !important; font-family: 'Microsoft YaHei', sans-serif !important; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important; \`;
// 切换按钮 const toggleBtn = document.createElement('button'); toggleBtn.id = 'toggleAutoSubmitBtn'; const autoSubmitState = getAutoSubmitState(); toggleBtn.textContent = autoSubmitState ? '✅ 自动提交:开' : '⏸️ 自动提交:关'; toggleBtn.style.cssText = \` padding: 10px 20px !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: 25px !important; font-size: 14px !important; font-weight: 600 !important; cursor: pointer !important; box-shadow: 0 4px 15px ${autoSubmitState ? 'rgba(17, 153, 142, 0.3)' : 'rgba(0, 0, 0, 0.1)'} !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; min-width: 160px !important; font-family: 'Microsoft YaHei', sans-serif !important; \`;
// 评分等级面板 const scorePanel = document.createElement('div'); scorePanel.id = 'scoreLevelPanel'; scorePanel.style.cssText = \` background: rgba(255, 255, 255, 0.98) !important; backdrop-filter: blur(10px) !important; border-radius: 16px !important; padding: 16px !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12) !important; border: 1px solid rgba(255, 255, 255, 0.8) !important; min-width: 200px !important; \`;
const panelTitle = document.createElement('div'); panelTitle.textContent = '📊 评分等级'; panelTitle.style.cssText = \` font-size: 15px !important; font-weight: bold !important; color: #333 !important; margin-bottom: 12px !important; padding-bottom: 8px !important; border-bottom: 2px solid #f0f0f0 !important; \`; scorePanel.appendChild(panelTitle);
const currentScore = getScoreLevel(); const scoreOptions = [ { value: 100, label: '100分', desc: '全部最高分', color: '#4CAF50' }, { value: 95, label: '95分', desc: '90%最高 + 10%次高', color: '#FF9800' }, { value: 90, label: '90分', desc: '80%最高 + 20%次高', color: '#2196F3' } ];
scoreOptions.forEach(option => { const optionDiv = document.createElement('div'); optionDiv.style.cssText = \` display: flex !important; align-items: center !important; padding: 10px !important; margin: 6px 0 !important; border-radius: 10px !important; cursor: pointer !important; transition: all 0.2s ease !important; background: ${currentScore === option.value ? 'rgba(33, 150, 243, 0.08)' : 'transparent'} !important; border: 2px solid ${currentScore === option.value ? option.color : 'transparent'} !important; \`;
const radio = document.createElement('input'); radio.type = 'radio'; radio.name = 'scoreLevel'; radio.value = option.value; radio.checked = currentScore === option.value; radio.style.cssText = \` width: 18px !important; height: 18px !important; margin-right: 10px !important; cursor: pointer !important; accent-color: ${option.color} !important; \`;
const labelDiv = document.createElement('div'); labelDiv.style.cssText = \`flex: 1 !important;\`;
const labelText = document.createElement('div'); labelText.textContent = option.label; labelText.style.cssText = \` font-size: 14px !important; font-weight: 600 !important; color: #333 !important; margin-bottom: 2px !important; \`;
const descText = document.createElement('div'); descText.textContent = option.desc; descText.style.cssText = \` font-size: 12px !important; color: #666 !important; \`;
labelDiv.appendChild(labelText); labelDiv.appendChild(descText); optionDiv.appendChild(radio); optionDiv.appendChild(labelDiv);
// 悬停效果 optionDiv.onmouseover = () => { if (currentScore !== option.value) { optionDiv.style.background = 'rgba(0, 0, 0, 0.03)'; } }; optionDiv.onmouseout = () => { if (currentScore !== option.value) { optionDiv.style.background = 'transparent'; } };
// 点击事件 optionDiv.onclick = () => { setScoreLevel(option.value); logImportant(\`评分等级已切换为: ${option.value}分\`); // 重新创建面板以更新选中状态 container.innerHTML = ''; buttonsCreated = false; createControlButtons(); };
scorePanel.appendChild(optionDiv); });
// 按钮交互 [mainBtn, toggleBtn].forEach(btn => { btn.onmouseover = () => btn.style.transform = 'scale(1.05)'; btn.onmouseout = () => btn.style.transform = 'scale(1)'; });
// 主按钮点击事件 mainBtn.addEventListener('click', () => { mainBtn.disabled = true; const originalText = mainBtn.textContent; mainBtn.textContent = '⏳ 执行中...'; autoEvaluateForNewPage().finally(() => { setTimeout(() => { mainBtn.disabled = false; mainBtn.textContent = originalText; }, 3000); }); });
// 切换按钮点击事件 toggleBtn.addEventListener('click', () => { const currentState = getAutoSubmitState(); const newState = !currentState; setAutoSubmitState(newState); logImportant(\`自动提交已${newState ? '开启' : '关闭'}\`); });
container.appendChild(mainBtn); container.appendChild(scorePanel); container.appendChild(toggleBtn); document.body.appendChild(container); logImportant('控制按钮创建完成。'); }
// ---------- 2. 核心评教功能 ---------- async function fillTextEvaluations() { logImportant('开始处理文字评价...'); const textAreas = document.querySelectorAll('textarea.uni-textarea-textarea'); if (textAreas.length === 0) { log('未找到文字评价输入框。'); return; } logImportant(\`找到 ${textAreas.length} 个输入框。\`);
const positiveComments = [ "老师授课准备充分,讲解清晰生动,重点突出,课堂节奏把握得很好。", "课程内容充实,理论与实践结合紧密,对启发思维和掌握知识很有帮助。", "课堂氛围融洽,师生互动积极,能鼓励学生提出问题并耐心解答。", "教学态度认真负责,对课程内容掌握深入,能感受到老师对教学的热情。", "本学期收获很大,感谢老师的辛勤付出,希望后续课程能继续保持。", "课程组织有序,作业和考核方式合理,能有效检验学习成果。", ];
const usedIndices = new Set(); for (let i = 0; i < textAreas.length; i++) { const textArea = textAreas[i]; textArea.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(400 + Math.random() \* 600); textArea.focus(); await sleep(200);
let randomIndex; do { randomIndex = Math.floor(Math.random() \* positiveComments.length); if (usedIndices.size >= positiveComments.length \* 0.7) usedIndices.clear(); } while (usedIndices.has(randomIndex));
usedIndices.add(randomIndex); const comment = positiveComments[randomIndex]; textArea.value = comment; textArea.dispatchEvent(new Event('input', { bubbles: true })); textArea.dispatchEvent(new Event('change', { bubbles: true })); log(\`第 ${i + 1} 个输入框: ${comment.substring(0, 20)}...\`); textArea.blur(); await sleep(300); } logImportant('文字评价填写完成。'); }
// ---------- 3. 【关键修改】精准提交函数 ---------- async function submitEvaluation() { const autoSubmit = getAutoSubmitState(); if (!autoSubmit) { logImportant('自动提交已关闭,请手动提交。'); return false; }
logImportant('正在查找提交按钮...'); await sleep(1000); // 等待页面稳定
// 首要策略:精准查找您提供的 uni-view.box3-1 元素 let submitButton = document.querySelector('uni-view.box3-1');
// 备用策略:如果精准查找失败,尝试其他常见选择器 if (!submitButton) { log('精准查找失败,尝试备用选择器...'); const backupSelectors = [ 'uni-view:contains("提交")', '.box3-1', '[class\*="submit"]', 'button:contains("提交评价")', 'button:contains("提交")', 'button:contains("确定")' ]; for (const selector of backupSelectors) { submitButton = document.querySelector(selector); if (submitButton) { logImportant(\`通过备用选择器找到: ${selector}\`); break; } } }
if (submitButton) { logImportant(\`✅ 找到提交按钮,开始提交... (元素: ${submitButton.tagName}.${submitButton.className})\`); submitButton.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(800); submitButton.focus(); submitButton.click(); logImportant('已点击提交按钮。');
// 处理可能的二次确认弹窗 await sleep(1500); const confirmSubmitted = await handleConfirmationDialog(); if (confirmSubmitted) { logImportant('✅ 自动提交流程完成。'); } return true; } else { logImportant('⚠️ 未找到提交按钮,请手动提交。'); // 在控制台输出提示,便于用户手动操作 console.warn('请手动查找并点击页面上的提交按钮。'); return false; } }
// 处理二次确认对话框 async function handleConfirmationDialog() { log('检查二次确认对话框...'); await sleep(1000); // 常见的确认按钮选择器 const confirmSelectors = [ '.confirm', '.btn-confirm', 'uni-view:contains("确定")', 'button:contains("确定")', '.el-button--primary' ]; for (const selector of confirmSelectors) { const confirmBtn = document.querySelector(selector); if (confirmBtn) { const style = window.getComputedStyle(confirmBtn); if (style.display !== 'none' && style.visibility !== 'hidden') { confirmBtn.click(); log('✅ 已点击确认按钮。'); await sleep(500); return true; } } } log('未检测到二次确认对话框。'); return true; // 没有确认对话框也视为成功 }
// ---------- 4. 自动评教主流程 ---------- async function autoEvaluateForNewPage() { const autoSubmit = getAutoSubmitState(); const scoreLevel = getScoreLevel(); logImportant('🚀 开始自动评教...'); logImportant(\`模式: 自动提交 ${autoSubmit ? '开启' : '关闭'}, 评分等级: ${scoreLevel}分\`);
// 根据评分等级选择目标选项 const optionSets = { 100: [['完全符合', '优秀', 'A']], 95: [['完全符合', '优秀', 'A'], ['符合', '良好', 'B']], 90: [['完全符合', '优秀', 'A'], ['符合', '良好', 'B']] }; const availableOptions = optionSets\[scoreLevel\] \|\| optionSets\[100\];
// 计算选项分配比例 const getTargetOptions = (questionIndex) => { if (scoreLevel === 100) { return availableOptions[0]; // 全部最高分 } else if (scoreLevel === 95) { // 90%最高分,10%次高分 return Math.random() < 0.9 ? availableOptions[0] : availableOptions[1]; } else if (scoreLevel === 90) { // 80%最高分,20%次高分 return Math.random() < 0.8 ? availableOptions[0] : availableOptions[1]; } return availableOptions[0]; };
// 定位题目容器 let questionContainers = document.querySelectorAll('[class\*="question"], [class\*="item"], .el-form-item, .uni-list-cell'); if (questionContainers.length === 0) { log('尝试智能回溯寻找题目容器...'); const allOptionElements = document.querySelectorAll('uni-view'); const parentSet = new Set(); allOptionElements.forEach(el => { let parent = el.parentElement; for (let i = 0; i < 4 && parent; i++) { if (parent.classList && parent.classList.length > 0) { parentSet.add(parent); break; } parent = parent.parentElement; } }); questionContainers = Array.from(parentSet); } logImportant(\`识别出 ${questionContainers.length} 个题目区域。\`);
// 处理选择题 let clickedCount = 0; for (const container of questionContainers) { let clicked = false; const targetOptions = getTargetOptions(clickedCount); for (const targetText of targetOptions) { const elements = container.querySelectorAll('uni-view, span, div, label'); for (let el of elements) { if (el.textContent && el.textContent.trim() === targetText) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }); await sleep(300 + Math.random() \* 400); el.click(); log(\`题目${clickedCount + 1}: ✅ "${targetText}"\`); clickedCount++; clicked = true; await sleep(150); break; } } if (clicked) break; } } logImportant(\`选择题完成 ${clickedCount} 题。\`);
// 处理文字评价 await fillTextEvaluations();
// 执行提交 const submitted = await submitEvaluation(); if (submitted) { logImportant('✨ 自动评教流程执行完毕!'); } else { logImportant('✨ 内容已填写完成,请根据控制台提示操作。'); } }
// ---------- 5. 页面初始化 ---------- function initialize() { logImportant('脚本初始化...'); // 简单检测:页面是否有 uni-view 元素 if (document.querySelector('uni-view')) { setTimeout(createControlButtons, 1000); } else { // 如果没有,等待一下再尝试(应对动态加载) const observer = new MutationObserver((mutations, obs) => { if (document.querySelector('uni-view')) { obs.disconnect(); setTimeout(createControlButtons, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); // 5秒后超时停止观察 setTimeout(() => observer.disconnect(), 5000); } }
// 启动 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); }
})();
这个就是我发布的脚本啊
我改了
是我的脚本实效了吗?
Login to comment