Mutx163学习通自动评教

学习通自动评价,支持批量评教,默认满分

  1. // ==UserScript==
  2. // @name Mutx163学习通自动评教
  3. // @namespace http://tampermonkey.net/
  4. // @version v1.28
  5. // @description 学习通自动评价,支持批量评教,默认满分
  6. // @author Mutx163
  7. // @match https://newes.chaoxing.com/pj/newesReception/questionnaireInfo*
  8. // @match http://newes.chaoxing.com/pj/newesReception/questionnaireInfo*
  9. // @match https://newes.chaoxing.com/pj/frontv2/evaluateList/whatIEvaluated*
  10. // @match http://newes.chaoxing.com/pj/frontv2/evaluateList/whatIEvaluated*
  11. // @match https://newes.chaoxing.com/pj/frontv2/whatIEvaluatedDetails*
  12. // @match http://newes.chaoxing.com/pj/frontv2/whatIEvaluatedDetails*
  13. // @icon
  14. // @license MIT
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @grant GM_deleteValue
  18. // @grant GM_log
  19. // ==/UserScript==
  20.  
  21. // ��������������
  22. function log(message, type = 'info') {
  23. const timestamp = new Date().toLocaleTimeString();
  24. const logMessage = `[${timestamp}] ${message}`;
  25.  
  26. // 更新进度显示
  27. if (window.progressDiv) {
  28. window.progressDiv.innerHTML += `<div class="${type}">${logMessage}</div>`;
  29. // 保持最新消息可见
  30. window.progressDiv.scrollTop = window.progressDiv.scrollHeight;
  31. }
  32.  
  33. // 控制台输出
  34. switch(type) {
  35. case 'error':
  36. console.error(logMessage);
  37. GM_log('[ERROR] ' + message);
  38. break;
  39. case 'warning':
  40. console.warn(logMessage);
  41. GM_log('[WARNING] ' + message);
  42. break;
  43. default:
  44. console.log(logMessage);
  45. GM_log('[INFO] ' + message);
  46. }
  47. }
  48.  
  49. window.onload = function() {
  50. 'use strict';
  51.  
  52. try {
  53. log('脚本开始初始化...');
  54.  
  55. // 创建进度显示元素
  56. const progressDiv = document.createElement('div');
  57. progressDiv.style.position = 'fixed';
  58. progressDiv.style.bottom = '60px';
  59. progressDiv.style.left = '10px';
  60. progressDiv.style.zIndex = '1000';
  61. progressDiv.style.padding = '10px';
  62. progressDiv.style.backgroundColor = '#f0f0f0';
  63. progressDiv.style.border = '1px solid #ccc';
  64. progressDiv.style.borderRadius = '5px';
  65. progressDiv.style.maxHeight = '400px';
  66. progressDiv.style.overflowY = 'auto';
  67. progressDiv.style.width = '300px';
  68. progressDiv.style.fontSize = '12px';
  69. progressDiv.style.lineHeight = '1.5';
  70. document.body.appendChild(progressDiv);
  71. window.progressDiv = progressDiv;
  72.  
  73. // 添加样式
  74. const style = document.createElement('style');
  75. style.textContent = `
  76. .info { color: black; }
  77. .warning { color: orange; }
  78. .error { color: red; }
  79. .success { color: green; }
  80. `;
  81. document.head.appendChild(style);
  82.  
  83. // 创建控制按钮
  84. const autoEvaluateButton = document.createElement('button');
  85. autoEvaluateButton.textContent = '启动自动评教';
  86. autoEvaluateButton.style.position = 'fixed';
  87. autoEvaluateButton.style.bottom = '10px';
  88. autoEvaluateButton.style.left = '10px';
  89. autoEvaluateButton.style.zIndex = '1000';
  90. autoEvaluateButton.style.padding = '10px';
  91. autoEvaluateButton.style.backgroundColor = '#4CAF50';
  92. autoEvaluateButton.style.color = 'white';
  93. autoEvaluateButton.style.border = 'none';
  94. autoEvaluateButton.style.borderRadius = '5px';
  95. autoEvaluateButton.style.cursor = 'pointer';
  96. document.body.appendChild(autoEvaluateButton);
  97.  
  98. // 检查localStorage中的状态
  99. const autoEvaluateEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
  100. log(`当前自动评教状态: ${autoEvaluateEnabled ? '已启用' : '未启用'}`);
  101.  
  102. // 根据存储的状态更新按钮文本
  103. autoEvaluateButton.textContent = autoEvaluateEnabled ? '禁用自动评教' : '启动自动评教';
  104.  
  105. // 根据当前页面类型执行不同的操作
  106. const currentURL = window.location.href;
  107. log(`当前页面URL: ${currentURL}`);
  108.  
  109. // 使用 URL 对象解析当前页面 URL
  110. const urlPath = new URL(currentURL).pathname;
  111.  
  112. if (urlPath.includes('evaluateList/whatIEvaluated')) {
  113. log('检测到评教列表页面');
  114. handleEvaluationList(autoEvaluateButton);
  115. } else if (urlPath.includes('questionnaireInfo')) {
  116. log('检测到具体评教页面');
  117. handleEvaluationPage(autoEvaluateButton);
  118. } else if (urlPath.includes('whatIEvaluatedDetails')) {
  119. log('检测到评教详情页面');
  120. // 如果自动评教已启用,直接执行评教流程
  121. if (autoEvaluateEnabled) {
  122. executeEvaluationDetails();
  123. }
  124. handleEvaluationDetails(autoEvaluateButton);
  125. }
  126.  
  127. log('脚本初始化完成', 'success');
  128. } catch (error) {
  129. log(`脚本初始化失败: ${error.message}`, 'error');
  130. console.error(error);
  131. }
  132. };
  133.  
  134. // 处理评教列表
  135. function handleEvaluationList(button) {
  136. // 添加自动执行逻辑
  137. if (localStorage.getItem('autoEvaluateEnabled') === 'true') {
  138. log('检测到自动评教已启用,开始执行...');
  139. startEvaluationProcess();
  140. }
  141.  
  142. button.addEventListener('click', async function() {
  143. try {
  144. const isEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
  145. localStorage.setItem('autoEvaluateEnabled', !isEnabled);
  146. button.textContent = !isEnabled ? '禁用自动评教' : '启动自动评教';
  147.  
  148. if (!isEnabled) {
  149. startEvaluationProcess();
  150. }
  151. } catch (error) {
  152. log(`评教列表处理出错: ${error.message}`, 'error');
  153. console.error(error);
  154. }
  155. });
  156. }
  157.  
  158. // 新增函数:等待元素加载
  159. function waitForElement(selector, maxWaitTime = 10000) {
  160. return new Promise((resolve, reject) => {
  161. if (document.querySelector(selector)) {
  162. return resolve(document.querySelector(selector));
  163. }
  164.  
  165. const observer = new MutationObserver(() => {
  166. if (document.querySelector(selector)) {
  167. observer.disconnect();
  168. resolve(document.querySelector(selector));
  169. }
  170. });
  171.  
  172. observer.observe(document.body, {
  173. childList: true,
  174. subtree: true
  175. });
  176.  
  177. setTimeout(() => {
  178. observer.disconnect();
  179. reject(new Error(`等待元素 ${selector} 超时`));
  180. }, maxWaitTime);
  181. });
  182. }
  183.  
  184. // 修改开始评教流程函数
  185. async function startEvaluationProcess() {
  186. try {
  187. log('开始检查未完成的评教任务...');
  188.  
  189. // 等待表格加载
  190. log('等待评教任务表格加载...');
  191. await waitForElement('.el-table__body-wrapper');
  192.  
  193. // 确保表格内容完全加载
  194. await new Promise(resolve => setTimeout(resolve, 2000));
  195.  
  196. // 获取所有行
  197. const rows = document.querySelectorAll('.el-table__row');
  198. log(`找到 ${rows.length} 个评教任务行`);
  199.  
  200. if (rows.length === 0) {
  201. log('尝试使用备用选择器查找任务行...');
  202. const tableBody = document.querySelector('.el-table__body');
  203. if (tableBody) {
  204. const alternativeRows = tableBody.querySelectorAll('tr');
  205. log(`使用备用选择器找到 ${alternativeRows.length} 个任务行`);
  206. if (alternativeRows.length > 0) {
  207. processRows(Array.from(alternativeRows));
  208. return;
  209. }
  210. }
  211. log('无法找到评教任务行,请检查页面是否正确加载', 'error');
  212. return;
  213. }
  214.  
  215. processRows(Array.from(rows));
  216. } catch (error) {
  217. log(`评教流程出错: ${error.message}`, 'error');
  218. console.error(error);
  219. }
  220. }
  221.  
  222. // 新增函数:处理任务行
  223. function processRows(rows) {
  224. // 过滤出未完成的任务行
  225. const unfinishedRows = rows.filter(row => {
  226. const statusTag = row.querySelector('.d_submit_tag');
  227. return statusTag && !statusTag.classList.contains('green');
  228. });
  229.  
  230. const totalTasks = unfinishedRows.length;
  231. log(`其中有 ${totalTasks} 个未完成的评教任务`);
  232.  
  233. if (totalTasks === 0) {
  234. log('没有找到未完成的评教任务', 'warning');
  235. return;
  236. }
  237.  
  238. // 保存任务信息到GM存储
  239. GM_setValue('totalTasks', totalTasks);
  240. GM_setValue('currentTask', 0);
  241. log('已保存任务信息到存储');
  242.  
  243. // 获取第一个未完成任务的"查看详情"按钮并点击
  244. const firstUnfinishedRow = unfinishedRows[0];
  245.  
  246. // 查找查看详情按钮
  247. const buttons = firstUnfinishedRow.querySelectorAll('a.d_button_text');
  248. const detailButton = Array.from(buttons).find(btn => btn.textContent.trim() === '查看详情');
  249.  
  250. if (detailButton) {
  251. log('找到未完成任务的查看详情按钮,准备点击...');
  252. setTimeout(() => {
  253. detailButton.click();
  254. log('已点击查看详情按钮');
  255. }, 1000);
  256. } else {
  257. log(`在未完成任务行中找到 ${buttons.length} 个按钮,但没有找到查看详情按钮`, 'error');
  258. // 输出所有按钮的文本内容以供调试
  259. buttons.forEach((btn, index) => {
  260. log(`按钮 ${index + 1} 的文本内容: "${btn.textContent.trim()}"`, 'info');
  261. });
  262. }
  263. }
  264.  
  265. // 新增函数:执行评教详情页面的评教流程
  266. function executeEvaluationDetails() {
  267. log('检测到自动评教已启用,准备查找评价按钮...');
  268.  
  269. // 等待页面加载完成
  270. waitForElement('.d_table_btns').then(() => {
  271. try {
  272. // 查找所有按钮容器
  273. const btnContainers = document.querySelectorAll('.d_table_btns');
  274. log(`找到 ${btnContainers.length} 个按钮容器`);
  275.  
  276. // 遍历容器查找评价按钮
  277. let evaluateButton = null;
  278. btnContainers.forEach((container, index) => {
  279. const btn = container.querySelector('a.d_button_text');
  280. if (btn && btn.textContent.trim() === '评价') {
  281. evaluateButton = btn;
  282. log(`在第 ${index + 1} 个容器中找到评价按钮`);
  283. }
  284. });
  285.  
  286. if (evaluateButton) {
  287. log('找到评价按钮,准备点击...');
  288. setTimeout(() => {
  289. evaluateButton.click();
  290. log('已点击评价按钮');
  291. }, 1000);
  292. } else {
  293. // 如果没有找到评价按钮,说明可能已经评价完成,尝试返回
  294. const backButton = document.querySelector('a.d_back');
  295. if (backButton) {
  296. log('找到返回按钮,准备返回上一页...');
  297. setTimeout(() => {
  298. backButton.click();
  299. log('已点击返回按钮');
  300. }, 1000);
  301. } else {
  302. log('未找到返回按钮', 'error');
  303. // 输出所有按钮的文本内容以供调试
  304. btnContainers.forEach((container, index) => {
  305. const btn = container.querySelector('a.d_button_text');
  306. if (btn) {
  307. log(`容器 ${index + 1} 中的按钮文本: "${btn.textContent.trim()}"`, 'info');
  308. }
  309. });
  310. }
  311. }
  312. } catch (error) {
  313. log(`查找评价按钮出错: ${error.message}`, 'error');
  314. }
  315. }).catch(error => {
  316. log(`等待按钮容器加载失败: ${error.message}`, 'error');
  317. // 尝试备用方案
  318. const allButtons = document.querySelectorAll('a.d_button_text');
  319. log(`使用备用选择器找到 ${allButtons.length} 个按钮`);
  320. allButtons.forEach((btn, index) => {
  321. log(`按钮 ${index + 1} 的文本内容: "${btn.textContent.trim()}"`, 'info');
  322. if (btn.textContent.trim() === '评价') {
  323. log('找到评价按钮,准备点击...');
  324. setTimeout(() => {
  325. btn.click();
  326. log('已点击评价按钮');
  327. }, 1000);
  328. }
  329. });
  330. });
  331. }
  332.  
  333. // 处理评教详情页面
  334. function handleEvaluationDetails(button) {
  335. try {
  336. log('开始处理评教详情页面...');
  337. // 如果自动评教已启用,自动执行评教流程
  338. if (localStorage.getItem('autoEvaluateEnabled') === 'true') {
  339. log('检测到自动评教已启用,准备执行评教流程...');
  340. // 确保页面完全加载后再执行
  341. setTimeout(executeEvaluationDetails, 1000);
  342. }
  343.  
  344. // 添加按钮点击事件
  345. button.addEventListener('click', function() {
  346. const isEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
  347. localStorage.setItem('autoEvaluateEnabled', !isEnabled);
  348. button.textContent = !isEnabled ? '禁用自动评教' : '启动自动评教';
  349.  
  350. if (!isEnabled) {
  351. executeEvaluationDetails();
  352. }
  353. });
  354. } catch (error) {
  355. log(`评教详情页面处理出错: ${error.message}`, 'error');
  356. console.error(error);
  357. }
  358. }
  359.  
  360. // 处理具体评教页面
  361. function handleEvaluationPage(button) {
  362. try {
  363. const totalTasks = GM_getValue('totalTasks', 0);
  364. const currentTask = GM_getValue('currentTask', 0);
  365.  
  366. if (totalTasks > 0) {
  367. log(`正在处理第 ${currentTask + 1}/${totalTasks} 个任务的评教`);
  368. }
  369.  
  370. button.addEventListener('click', function() {
  371. const isEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
  372. localStorage.setItem('autoEvaluateEnabled', !isEnabled);
  373. button.textContent = !isEnabled ? '禁用自动评教' : '启动自动评教';
  374.  
  375. if (!isEnabled) {
  376. startAutoEvaluation();
  377. }
  378. });
  379.  
  380. // 果已启用,自动开始评教
  381. if (localStorage.getItem('autoEvaluateEnabled') === 'true') {
  382. startAutoEvaluation();
  383. }
  384. } catch (error) {
  385. log(`评教页面处理出错: ${error.message}`, 'error');
  386. console.error(error);
  387. }
  388. }
  389.  
  390. function triggerEvent(element, eventType) {
  391. try {
  392. const event = new Event(eventType, {
  393. bubbles: true,
  394. cancelable: true
  395. });
  396. element.dispatchEvent(event);
  397. log(`触发事件 ${eventType} 成功`);
  398. } catch (error) {
  399. log(`触发事件 ${eventType} 失败: ${error.message}`, 'error');
  400. console.error(error);
  401. }
  402. }
  403.  
  404. function startAutoEvaluation() {
  405. setTimeout(() => {
  406. try {
  407. log('开始自动评教...');
  408.  
  409. // 查找所有打分题输入框并填写满分
  410. const scoreInputs = document.querySelectorAll('input.dafen');
  411. log(`找到 ${scoreInputs.length} 个打分输入框`);
  412.  
  413. if (scoreInputs.length > 0) {
  414. scoreInputs.forEach((input, index) => {
  415. try {
  416. const maxScore = input.getAttribute('maxscore');
  417. input.value = maxScore;
  418. log(`设置第 ${index + 1} 个评分为满分: ${maxScore}`);
  419.  
  420. // 触发所有必要的事件
  421. triggerEvent(input, 'input');
  422. triggerEvent(input, 'propertychange');
  423. triggerEvent(input, 'keyup');
  424. triggerEvent(input, 'blur');
  425.  
  426. if (typeof window.allScore === 'function') {
  427. window.allScore();
  428. log('调用计分函数成功');
  429. }
  430. } catch (error) {
  431. log(`处理第 ${index + 1} 个评分出错: ${error.message}`, 'error');
  432. }
  433. });
  434. }
  435.  
  436. // 查找所有文本框并填写"无"
  437. const textareas = document.querySelectorAll('textarea.blueTextarea');
  438. log(`找到 ${textareas.length} 个文本框`);
  439.  
  440. if (textareas.length > 0) {
  441. textareas.forEach((textarea, index) => {
  442. try {
  443. textarea.value = '无';
  444. log(`设置第 ${index + 1} 个文本框内容为"无"`);
  445. triggerEvent(textarea, 'input');
  446. triggerEvent(textarea, 'propertychange');
  447. triggerEvent(textarea, 'keyup');
  448. } catch (error) {
  449. log(`处理第 ${index + 1} 个文本框出错: ${error.message}`, 'error');
  450. }
  451. });
  452. }
  453.  
  454. // 延迟提交
  455. setTimeout(() => {
  456. log('准备提交评教...');
  457. // 查找提交按钮
  458. const submitButton = document.querySelector('a[onclick*="save(2)"]');
  459.  
  460. if (submitButton) {
  461. log('找到提交按钮,准备点击...');
  462. // 直接调用save函数
  463. if (typeof window.save === 'function') {
  464. window.save(2);
  465. log('已调用提交函数');
  466. } else {
  467. // 如果找不到save函数,尝试点击按钮
  468. submitButton.click();
  469. log('已点击提交按钮');
  470. }
  471.  
  472. // 处理确认弹窗
  473. setTimeout(() => {
  474. try {
  475. const confirmButtons = document.querySelectorAll('.layui-layer-btn0');
  476. log(`找到 ${confirmButtons.length} 个确认按钮`);
  477.  
  478. if (confirmButtons.length > 0) {
  479. confirmButtons.forEach(button => button.click());
  480. log('已点击确认按钮');
  481.  
  482. // 更新任务计数并返回列表页
  483. const currentTask = GM_getValue('currentTask', 0);
  484. const totalTasks = GM_getValue('totalTasks', 0);
  485.  
  486. if (currentTask < totalTasks - 1) {
  487. GM_setValue('currentTask', currentTask + 1);
  488. log(`更新任务进度: ${currentTask + 1}/${totalTasks}`);
  489. setTimeout(() => {
  490. log('返回上一页...');
  491. window.location.href = document.referrer;
  492. }, 1000);
  493. } else {
  494. // 所有任务完成,清除存储的任务信息
  495. GM_deleteValue('totalTasks');
  496. GM_deleteValue('currentTask');
  497. log('所有评教任务已完成!', 'success');
  498. setTimeout(() => {
  499. log('返回列表页...');
  500. window.location.href = document.referrer;
  501. }, 1000);
  502. }
  503. } else {
  504. log('未找到确认按钮', 'error');
  505. }
  506. } catch (error) {
  507. log(`处理确认弹窗出错: ${error.message}`, 'error');
  508. }
  509. }, 1000);
  510. } else {
  511. log('未找到提交按钮', 'error');
  512. }
  513. }, 1000);
  514. } catch (error) {
  515. log(`自动评教过程出错: ${error.message}`, 'error');
  516. console.error(error);
  517. }
  518. }, 1000);
  519. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址