Greasy Fork

Greasy Fork is available in English.

Mutx163学习通自动评教

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

// ==UserScript==
// @name         Mutx163学习通自动评教
// @namespace    http://tampermonkey.net/
// @version      v1.28
// @description  学习通自动评价,支持批量评教,默认满分
// @author       Mutx163
// @match        https://newes.chaoxing.com/pj/newesReception/questionnaireInfo*
// @match        http://newes.chaoxing.com/pj/newesReception/questionnaireInfo*
// @match        https://newes.chaoxing.com/pj/frontv2/evaluateList/whatIEvaluated*
// @match        http://newes.chaoxing.com/pj/frontv2/evaluateList/whatIEvaluated*
// @match        https://newes.chaoxing.com/pj/frontv2/whatIEvaluatedDetails*
// @match        http://newes.chaoxing.com/pj/frontv2/whatIEvaluatedDetails*
// @icon
// @license MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_log
// ==/UserScript==

// ��������������
function log(message, type = 'info') {
    const timestamp = new Date().toLocaleTimeString();
    const logMessage = `[${timestamp}] ${message}`;

    // 更新进度显示
    if (window.progressDiv) {
        window.progressDiv.innerHTML += `<div class="${type}">${logMessage}</div>`;
        // 保持最新消息可见
        window.progressDiv.scrollTop = window.progressDiv.scrollHeight;
    }

    // 控制台输出
    switch(type) {
        case 'error':
            console.error(logMessage);
            GM_log('[ERROR] ' + message);
            break;
        case 'warning':
            console.warn(logMessage);
            GM_log('[WARNING] ' + message);
            break;
        default:
            console.log(logMessage);
            GM_log('[INFO] ' + message);
    }
}

window.onload = function() {
    'use strict';

    try {
        log('脚本开始初始化...');

        // 创建进度显示元素
        const progressDiv = document.createElement('div');
        progressDiv.style.position = 'fixed';
        progressDiv.style.bottom = '60px';
        progressDiv.style.left = '10px';
        progressDiv.style.zIndex = '1000';
        progressDiv.style.padding = '10px';
        progressDiv.style.backgroundColor = '#f0f0f0';
        progressDiv.style.border = '1px solid #ccc';
        progressDiv.style.borderRadius = '5px';
        progressDiv.style.maxHeight = '400px';
        progressDiv.style.overflowY = 'auto';
        progressDiv.style.width = '300px';
        progressDiv.style.fontSize = '12px';
        progressDiv.style.lineHeight = '1.5';
        document.body.appendChild(progressDiv);
        window.progressDiv = progressDiv;

        // 添加样式
        const style = document.createElement('style');
        style.textContent = `
            .info { color: black; }
            .warning { color: orange; }
            .error { color: red; }
            .success { color: green; }
        `;
        document.head.appendChild(style);

        // 创建控制按钮
        const autoEvaluateButton = document.createElement('button');
        autoEvaluateButton.textContent = '启动自动评教';
        autoEvaluateButton.style.position = 'fixed';
        autoEvaluateButton.style.bottom = '10px';
        autoEvaluateButton.style.left = '10px';
        autoEvaluateButton.style.zIndex = '1000';
        autoEvaluateButton.style.padding = '10px';
        autoEvaluateButton.style.backgroundColor = '#4CAF50';
        autoEvaluateButton.style.color = 'white';
        autoEvaluateButton.style.border = 'none';
        autoEvaluateButton.style.borderRadius = '5px';
        autoEvaluateButton.style.cursor = 'pointer';
        document.body.appendChild(autoEvaluateButton);

        // 检查localStorage中的状态
        const autoEvaluateEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
        log(`当前自动评教状态: ${autoEvaluateEnabled ? '已启用' : '未启用'}`);

        // 根据存储的状态更新按钮文本
        autoEvaluateButton.textContent = autoEvaluateEnabled ? '禁用自动评教' : '启动自动评教';

        // 根据当前页面类型执行不同的操作
        const currentURL = window.location.href;
        log(`当前页面URL: ${currentURL}`);

        // 使用 URL 对象解析当前页面 URL
        const urlPath = new URL(currentURL).pathname;

        if (urlPath.includes('evaluateList/whatIEvaluated')) {
            log('检测到评教列表页面');
            handleEvaluationList(autoEvaluateButton);
        } else if (urlPath.includes('questionnaireInfo')) {
            log('检测到具体评教页面');
            handleEvaluationPage(autoEvaluateButton);
        } else if (urlPath.includes('whatIEvaluatedDetails')) {
            log('检测到评教详情页面');
            // 如果自动评教已启用,直接执行评教流程
            if (autoEvaluateEnabled) {
                executeEvaluationDetails();
            }
            handleEvaluationDetails(autoEvaluateButton);
        }

        log('脚本初始化完成', 'success');
    } catch (error) {
        log(`脚本初始化失败: ${error.message}`, 'error');
        console.error(error);
    }
};

// 处理评教列表
function handleEvaluationList(button) {
    // 添加自动执行逻辑
    if (localStorage.getItem('autoEvaluateEnabled') === 'true') {
        log('检测到自动评教已启用,开始执行...');
        startEvaluationProcess();
    }

    button.addEventListener('click', async function() {
        try {
            const isEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
            localStorage.setItem('autoEvaluateEnabled', !isEnabled);
            button.textContent = !isEnabled ? '禁用自动评教' : '启动自动评教';

            if (!isEnabled) {
                startEvaluationProcess();
            }
        } catch (error) {
            log(`评教列表处理出错: ${error.message}`, 'error');
            console.error(error);
        }
    });
}

// 新增函数:等待元素加载
function waitForElement(selector, maxWaitTime = 10000) {
    return new Promise((resolve, reject) => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(() => {
            if (document.querySelector(selector)) {
                observer.disconnect();
                resolve(document.querySelector(selector));
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        setTimeout(() => {
            observer.disconnect();
            reject(new Error(`等待元素 ${selector} 超时`));
        }, maxWaitTime);
    });
}

// 修改开始评教流程函数
async function startEvaluationProcess() {
    try {
        log('开始检查未完成的评教任务...');

        // 等待表格加载
        log('等待评教任务表格加载...');
        await waitForElement('.el-table__body-wrapper');

        // 确保表格内容完全加载
        await new Promise(resolve => setTimeout(resolve, 2000));

        // 获取所有行
        const rows = document.querySelectorAll('.el-table__row');
        log(`找到 ${rows.length} 个评教任务行`);

        if (rows.length === 0) {
            log('尝试使用备用选择器查找任务行...');
            const tableBody = document.querySelector('.el-table__body');
            if (tableBody) {
                const alternativeRows = tableBody.querySelectorAll('tr');
                log(`使用备用选择器找到 ${alternativeRows.length} 个任务行`);
                if (alternativeRows.length > 0) {
                    processRows(Array.from(alternativeRows));
                    return;
                }
            }
            log('无法找到评教任务行,请检查页面是否正确加载', 'error');
            return;
        }

        processRows(Array.from(rows));
    } catch (error) {
        log(`评教流程出错: ${error.message}`, 'error');
        console.error(error);
    }
}

// 新增函数:处理任务行
function processRows(rows) {
    // 过滤出未完成的任务行
    const unfinishedRows = rows.filter(row => {
        const statusTag = row.querySelector('.d_submit_tag');
        return statusTag && !statusTag.classList.contains('green');
    });

    const totalTasks = unfinishedRows.length;
    log(`其中有 ${totalTasks} 个未完成的评教任务`);

    if (totalTasks === 0) {
        log('没有找到未完成的评教任务', 'warning');
        return;
    }

    // 保存任务信息到GM存储
    GM_setValue('totalTasks', totalTasks);
    GM_setValue('currentTask', 0);
    log('已保存任务信息到存储');

    // 获取第一个未完成任务的"查看详情"按钮并点击
    const firstUnfinishedRow = unfinishedRows[0];

    // 查找查看详情按钮
    const buttons = firstUnfinishedRow.querySelectorAll('a.d_button_text');
    const detailButton = Array.from(buttons).find(btn => btn.textContent.trim() === '查看详情');

    if (detailButton) {
        log('找到未完成任务的查看详情按钮,准备点击...');
        setTimeout(() => {
            detailButton.click();
            log('已点击查看详情按钮');
        }, 1000);
    } else {
        log(`在未完成任务行中找到 ${buttons.length} 个按钮,但没有找到查看详情按钮`, 'error');
        // 输出所有按钮的文本内容以供调试
        buttons.forEach((btn, index) => {
            log(`按钮 ${index + 1} 的文本内容: "${btn.textContent.trim()}"`, 'info');
        });
    }
}

// 新增函数:执行评教详情页面的评教流程
function executeEvaluationDetails() {
    log('检测到自动评教已启用,准备查找评价按钮...');

    // 等待页面加载完成
    waitForElement('.d_table_btns').then(() => {
        try {
            // 查找所有按钮容器
            const btnContainers = document.querySelectorAll('.d_table_btns');
            log(`找到 ${btnContainers.length} 个按钮容器`);

            // 遍历容器查找评价按钮
            let evaluateButton = null;
            btnContainers.forEach((container, index) => {
                const btn = container.querySelector('a.d_button_text');
                if (btn && btn.textContent.trim() === '评价') {
                    evaluateButton = btn;
                    log(`在第 ${index + 1} 个容器中找到评价按钮`);
                }
            });

            if (evaluateButton) {
                log('找到评价按钮,准备点击...');
                setTimeout(() => {
                    evaluateButton.click();
                    log('已点击评价按钮');
                }, 1000);
            } else {
                // 如果没有找到评价按钮,说明可能已经评价完成,尝试返回
                const backButton = document.querySelector('a.d_back');
                if (backButton) {
                    log('找到返回按钮,准备返回上一页...');
                    setTimeout(() => {
                        backButton.click();
                        log('已点击返回按钮');
                    }, 1000);
                } else {
                    log('未找到返回按钮', 'error');
                    // 输出所有按钮的文本内容以供调试
                    btnContainers.forEach((container, index) => {
                        const btn = container.querySelector('a.d_button_text');
                        if (btn) {
                            log(`容器 ${index + 1} 中的按钮文本: "${btn.textContent.trim()}"`, 'info');
                        }
                    });
                }
            }
        } catch (error) {
            log(`查找评价按钮出错: ${error.message}`, 'error');
        }
    }).catch(error => {
        log(`等待按钮容器加载失败: ${error.message}`, 'error');
        // 尝试备用方案
        const allButtons = document.querySelectorAll('a.d_button_text');
        log(`使用备用选择器找到 ${allButtons.length} 个按钮`);
        allButtons.forEach((btn, index) => {
            log(`按钮 ${index + 1} 的文本内容: "${btn.textContent.trim()}"`, 'info');
            if (btn.textContent.trim() === '评价') {
                log('找到评价按钮,准备点击...');
                setTimeout(() => {
                    btn.click();
                    log('已点击评价按钮');
                }, 1000);
            }
        });
    });
}

// 处理评教详情页面
function handleEvaluationDetails(button) {
    try {
        log('开始处理评教详情页面...');
        // 如果自动评教已启用,自动执行评教流程
        if (localStorage.getItem('autoEvaluateEnabled') === 'true') {
            log('检测到自动评教已启用,准备执行评教流程...');
            // 确保页面完全加载后再执行
            setTimeout(executeEvaluationDetails, 1000);
        }

        // 添加按钮点击事件
        button.addEventListener('click', function() {
            const isEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
            localStorage.setItem('autoEvaluateEnabled', !isEnabled);
            button.textContent = !isEnabled ? '禁用自动评教' : '启动自动评教';

            if (!isEnabled) {
                executeEvaluationDetails();
            }
        });
    } catch (error) {
        log(`评教详情页面处理出错: ${error.message}`, 'error');
        console.error(error);
    }
}

// 处理具体评教页面
function handleEvaluationPage(button) {
    try {
        const totalTasks = GM_getValue('totalTasks', 0);
        const currentTask = GM_getValue('currentTask', 0);

        if (totalTasks > 0) {
            log(`正在处理第 ${currentTask + 1}/${totalTasks} 个任务的评教`);
        }

        button.addEventListener('click', function() {
            const isEnabled = localStorage.getItem('autoEvaluateEnabled') === 'true';
            localStorage.setItem('autoEvaluateEnabled', !isEnabled);
            button.textContent = !isEnabled ? '禁用自动评教' : '启动自动评教';

            if (!isEnabled) {
                startAutoEvaluation();
            }
        });

        // 果已启用,自动开始评教
        if (localStorage.getItem('autoEvaluateEnabled') === 'true') {
            startAutoEvaluation();
        }
    } catch (error) {
        log(`评教页面处理出错: ${error.message}`, 'error');
        console.error(error);
    }
}

function triggerEvent(element, eventType) {
    try {
        const event = new Event(eventType, {
            bubbles: true,
            cancelable: true
        });
        element.dispatchEvent(event);
        log(`触发事件 ${eventType} 成功`);
    } catch (error) {
        log(`触发事件 ${eventType} 失败: ${error.message}`, 'error');
        console.error(error);
    }
}

function startAutoEvaluation() {
    setTimeout(() => {
        try {
            log('开始自动评教...');

            // 查找所有打分题输入框并填写满分
            const scoreInputs = document.querySelectorAll('input.dafen');
            log(`找到 ${scoreInputs.length} 个打分输入框`);

            if (scoreInputs.length > 0) {
                scoreInputs.forEach((input, index) => {
                    try {
                        const maxScore = input.getAttribute('maxscore');
                        input.value = maxScore;
                        log(`设置第 ${index + 1} 个评分为满分: ${maxScore}`);

                        // 触发所有必要的事件
                        triggerEvent(input, 'input');
                        triggerEvent(input, 'propertychange');
                        triggerEvent(input, 'keyup');
                        triggerEvent(input, 'blur');

                        if (typeof window.allScore === 'function') {
                            window.allScore();
                            log('调用计分函数成功');
                        }
                    } catch (error) {
                        log(`处理第 ${index + 1} 个评分出错: ${error.message}`, 'error');
                    }
                });
            }

            // 查找所有文本框并填写"无"
            const textareas = document.querySelectorAll('textarea.blueTextarea');
            log(`找到 ${textareas.length} 个文本框`);

            if (textareas.length > 0) {
                textareas.forEach((textarea, index) => {
                    try {
                        textarea.value = '无';
                        log(`设置第 ${index + 1} 个文本框内容为"无"`);
                        triggerEvent(textarea, 'input');
                        triggerEvent(textarea, 'propertychange');
                        triggerEvent(textarea, 'keyup');
                    } catch (error) {
                        log(`处理第 ${index + 1} 个文本框出错: ${error.message}`, 'error');
                    }
                });
            }

            // 延迟提交
            setTimeout(() => {
                log('准备提交评教...');
                // 查找提交按钮
                const submitButton = document.querySelector('a[onclick*="save(2)"]');

                if (submitButton) {
                    log('找到提交按钮,准备点击...');
                    // 直接调用save函数
                    if (typeof window.save === 'function') {
                        window.save(2);
                        log('已调用提交函数');
                    } else {
                        // 如果找不到save函数,尝试点击按钮
                        submitButton.click();
                        log('已点击提交按钮');
                    }

                    // 处理确认弹窗
                    setTimeout(() => {
                        try {
                            const confirmButtons = document.querySelectorAll('.layui-layer-btn0');
                            log(`找到 ${confirmButtons.length} 个确认按钮`);

                            if (confirmButtons.length > 0) {
                                confirmButtons.forEach(button => button.click());
                                log('已点击确认按钮');

                                // 更新任务计数并返回列表页
                                const currentTask = GM_getValue('currentTask', 0);
                                const totalTasks = GM_getValue('totalTasks', 0);

                                if (currentTask < totalTasks - 1) {
                                    GM_setValue('currentTask', currentTask + 1);
                                    log(`更新任务进度: ${currentTask + 1}/${totalTasks}`);
                                    setTimeout(() => {
                                        log('返回上一页...');
                                        window.location.href = document.referrer;
                                    }, 1000);
                                } else {
                                    // 所有任务完成,清除存储的任务信息
                                    GM_deleteValue('totalTasks');
                                    GM_deleteValue('currentTask');
                                    log('所有评教任务已完成!', 'success');
                                    setTimeout(() => {
                                        log('返回列表页...');
                                        window.location.href = document.referrer;
                                    }, 1000);
                                }
                            } else {
                                log('未找到确认按钮', 'error');
                            }
                        } catch (error) {
                            log(`处理确认弹窗出错: ${error.message}`, 'error');
                        }
                    }, 1000);
                } else {
                    log('未找到提交按钮', 'error');
                }
            }, 1000);
        } catch (error) {
            log(`自动评教过程出错: ${error.message}`, 'error');
            console.error(error);
        }
    }, 1000);
}