UCAS课程自动评估脚本

自动化填写课程和教师评估问卷 - 智能识别文本框 + 验证码自动识别

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         UCAS课程自动评估脚本
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  自动化填写课程和教师评估问卷 - 智能识别文本框 + 验证码自动识别
// @author       LilanChen
// @icon         https://www.urongda.com/_next/image?url=%2Flogos%2Fnormal%2Fmedium%2Funiversity-of-chinese-academy-of-sciences-logo-1024px.png&w=640&q=75
// @match        *://xkcts.ucas.ac.cn:*/evaluate/*
// @require      https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js
// @grant        none
// @license      MIT
// ==/UserScript==

//有条件的同学麻烦在github上为我点个star!谢谢大家~~~

(function() {
    'use strict';

    // 评价内容配置 - 按顺序匹配
    let evaluationTexts = {
        course: [
            '这门课程我最喜欢它深入浅出的讲解方式,理论与实践紧密结合,让我在轻松氛围中扎实掌握核心知识,收获满满。',
            '建议增加更多互动式教学环节,丰富案例分析,并优化课程节奏,以进一步提升学习参与度和知识吸收效率。',
            '我平均每周在这门课程上花费约6小时,包括听课、完成作业和复习预习,时间投入合理,学习节奏适中。',
            '在参与这门课之前,我对这个学科领域了解较少,兴趣一般,但课程生动的内容和实用的应用激发了我深入学习的热情。',
            '我始终保持全勤,积极参与课堂讨论,主动回答问题,与老师同学互动频繁,展现出高度的投入和良好的学习态度。'
        ],
        teacher: [
            '我最喜欢老师清晰的逻辑讲解和生动的案例分析,这种富有情感的授课方式让复杂知识变得易于理解,启发思考。',
            '老师专业素养高,讲解清晰,建议继续保持互动式教学,并适当拓展前沿知识,进一步提升课程的深度与广度。'
        ]
    };

    // 单选题和多选题配置(通过关键词匹配)
    let radioCheckboxConfig = {
        courseRadios: [
            {
                keywords: ['教室', '舒适度', '大小'],
                selectOptions: ['教室大小合适', '教室电脑和投影效果好']
            }
        ],
        courseCheckboxes: [
            {
                keywords: ['修读原因', '原因'],
                selectOptions: ['自己需求和兴趣', '口碑好', '时间适宜']
            }
        ]
    };

    // ==================== 验证码识别相关函数 ====================
    
    // 查找验证码图片
    function findCaptchaImage() {
        // 常见的验证码图片选择器
        const selectors = [
            'img[src*="captcha"]',
            'img[src*="verif"]',
            'img[src*="code"]',
            'img[alt*="验证码"]',
            'img[title*="验证码"]',
            '#captchaImg',
            '#verifyCode',
            '.captcha-img',
            '.verify-img'
        ];
        
        for (let selector of selectors) {
            const img = document.querySelector(selector);
            if (img) return img;
        }
        
        // 如果没找到,尝试查找所有图片,看是否有验证码特征
        const allImages = document.querySelectorAll('img');
        for (let img of allImages) {
            const src = img.src.toLowerCase();
            if (src.includes('captcha') || src.includes('verif') || src.includes('code')) {
                return img;
            }
        }
        
        return null;
    }

    // 查找验证码输入框(更精确的匹配)
    function findCaptchaInput() {
        // 优先级1: 最常见的验证码输入框选择器
        const prioritySelectors = [
            'input[name="captcha"]',
            'input[name="verifyCode"]',
            'input[name="validateCode"]',
            'input[id="captcha"]',
            'input[id="verifyCode"]',
            'input[id="validateCode"]',
            'input[placeholder*="验证码"]',
            'input[placeholder*="Captcha"]',
            'input[type="text"][maxlength="4"]',
        ];
        
        for (let selector of prioritySelectors) {
            const input = document.querySelector(selector);
            if (input && input.type === 'text') {
                console.log(`🎯 优先匹配到验证码输入框: ${selector}`);
                return input;
            }
        }
        
        // 优先级2: 通过name或id模糊匹配
        const allInputs = document.querySelectorAll('input[type="text"]');
        for (let input of allInputs) {
            const name = (input.name || '').toLowerCase();
            const id = (input.id || '').toLowerCase();
            const placeholder = (input.placeholder || '').toLowerCase();
            
            // 排除明确不是验证码的字段
            if (name === 'captchatype' || id === 'captchatype') {
                continue;
            }
            
            if (name.includes('captcha') || name.includes('verif') || name.includes('code') ||
                id.includes('captcha') || id.includes('verif') || id.includes('code') ||
                placeholder.includes('验证') || placeholder.includes('captcha')) {
                console.log(`🎯 模糊匹配到验证码输入框: name="${input.name}", id="${input.id}"`);
                return input;
            }
        }
        
        // 优先级3: 查找验证码图片附近的输入框
        const captchaImg = findCaptchaImage();
        if (captchaImg) {
            const parent = captchaImg.closest('tr, div, form, td');
            if (parent) {
                const nearbyInput = parent.querySelector('input[type="text"]');
                if (nearbyInput && nearbyInput.id !== 'captchaType') {
                    console.log(`🎯 在验证码图片附近找到输入框: id="${nearbyInput.id}"`);
                    return nearbyInput;
                }
            }
        }
        
        console.log('❌ 未找到验证码输入框');
        return null;
    }

    // 图片预处理函数(增强版 - 提高识别率)
    function preprocessImage(img) {
        return new Promise((resolve) => {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            
            // 放大3倍以提高识别率
            canvas.width = img.width * 3;
            canvas.height = img.height * 3;
            
            // 绘制原图
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            
            // 获取图像数据
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const data = imageData.data;
            
            // 增强对比度和二值化
            for (let i = 0; i < data.length; i += 4) {
                // 转灰度
                const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
                
                // 更激进的二值化阈值(增强对比度)
                const threshold = 140;
                const binary = gray > threshold ? 255 : 0;
                
                data[i] = data[i + 1] = data[i + 2] = binary;
            }
            
            // 降噪处理(简单的中值滤波)
            const processedData = new Uint8ClampedArray(data);
            for (let y = 1; y < canvas.height - 1; y++) {
                for (let x = 1; x < canvas.width - 1; x++) {
                    const idx = (y * canvas.width + x) * 4;
                    const neighbors = [];
                    
                    // 获取周围8个像素
                    for (let dy = -1; dy <= 1; dy++) {
                        for (let dx = -1; dx <= 1; dx++) {
                            const nIdx = ((y + dy) * canvas.width + (x + dx)) * 4;
                            neighbors.push(data[nIdx]);
                        }
                    }
                    
                    // 使用中值
                    neighbors.sort((a, b) => a - b);
                    const median = neighbors[Math.floor(neighbors.length / 2)];
                    processedData[idx] = processedData[idx + 1] = processedData[idx + 2] = median;
                }
            }
            
            // 应用处理后的数据
            for (let i = 0; i < processedData.length; i++) {
                imageData.data[i] = processedData[i];
            }
            
            ctx.putImageData(imageData, 0, 0);
            
            // 可选:反色处理(如果验证码是深色背景)
            // 检查图片主要颜色
            let darkPixels = 0;
            for (let i = 0; i < data.length; i += 4) {
                if (data[i] < 128) darkPixels++;
            }
            
            // 如果深色像素多于一半,则反色
            if (darkPixels > data.length / 8) {
                const invertedData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                for (let i = 0; i < invertedData.data.length; i += 4) {
                    invertedData.data[i] = 255 - invertedData.data[i];
                    invertedData.data[i + 1] = 255 - invertedData.data[i + 1];
                    invertedData.data[i + 2] = 255 - invertedData.data[i + 2];
                }
                ctx.putImageData(invertedData, 0, 0);
            }
            
            resolve(canvas.toDataURL());
        });
    }

    // 清理识别结果(只保留数字和字母,长度为4)
    function cleanOCRResult(text) {
        // 移除所有非字母数字字符
        let cleaned = text.replace(/[^a-zA-Z0-9]/g, '');
        
        // 常见的OCR错误修正(只修正明显错误,保持原有大小写)
        cleaned = cleaned
            .replace(/O/g, '0')  // 大写O -> 0
            .replace(/o/g, '0')  // 小写o -> 0
            .replace(/I/g, '1')  // 大写I -> 1
            .replace(/l/g, '1')  // 小写l -> 1
            .replace(/S/g, '5')  // 大写S -> 5
            .replace(/Z/g, '2')  // 大写Z -> 2
            .replace(/z/g, '2'); // 小写z -> 2
        
        // 如果长度不是4,尝试截取
        if (cleaned.length > 4) {
            cleaned = cleaned.substring(0, 4);
        }
        
        // 转换为小写(因为验证码字母都是小写)
        return cleaned.toLowerCase();
    }

    // 验证码识别主函数(增强版)
    async function recognizeCaptcha() {
        try {
            console.log('🔍 开始识别验证码...');
            
            const captchaImg = findCaptchaImage();
            if (!captchaImg) {
                console.log('❌ 未找到验证码图片');
                return null;
            }
            
            console.log('✓ 找到验证码图片:', captchaImg.src);
            
            // 预处理图片
            console.log('⏳ 预处理图片中...');
            const processedImage = await preprocessImage(captchaImg);
            
            // 使用 Tesseract.js 识别(尝试多次以提高准确率)
            console.log('⏳ 正在识别中(这可能需要几秒钟)...');
            
            const result = await Tesseract.recognize(
                processedImage,
                'eng',
                {
                    logger: m => {
                        if (m.status === 'recognizing text') {
                            console.log(`识别进度: ${Math.round(m.progress * 100)}%`);
                        }
                    },
                    tessedit_char_whitelist: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
                    tessedit_pageseg_mode: Tesseract.PSM.SINGLE_LINE,  // 单行文本模式
                }
            );
            
            const rawText = result.data.text.trim();
            console.log('📄 原始识别结果:', rawText, '(置信度:', result.data.confidence + ')');
            
            // 如果置信度太低,尝试不同的预处理
            if (result.data.confidence < 60) {
                console.log('⚠️ 识别置信度较低,尝试原图识别...');
                const result2 = await Tesseract.recognize(
                    captchaImg,
                    'eng',
                    {
                        tessedit_char_whitelist: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
                        tessedit_pageseg_mode: Tesseract.PSM.SINGLE_WORD,
                    }
                );
                
                const rawText2 = result2.data.text.trim();
                console.log('📄 原图识别结果:', rawText2, '(置信度:', result2.data.confidence + ')');
                
                // 使用置信度更高的结果
                const bestResult = result2.data.confidence > result.data.confidence ? rawText2 : rawText;
                const cleanedText = cleanOCRResult(bestResult);
                
                if (cleanedText.length === 4) {
                    console.log('✅ 验证码识别成功:', cleanedText);
                    return cleanedText;
                }
            }
            
            const cleanedText = cleanOCRResult(rawText);
            
            if (cleanedText.length === 4) {
                console.log('✅ 验证码识别成功:', cleanedText);
                return cleanedText;
            } else {
                console.log('⚠️ 识别结果长度不正确:', cleanedText, '(长度:', cleanedText.length + ')');
                console.log('💡 建议:点击刷新验证码或手动输入');
                return null;
            }
            
        } catch (error) {
            console.error('❌ 验证码识别失败:', error);
            return null;
        }
    }

    // 增强的输入框填充函数(支持 input 和 textarea)
    function fillInput(element, text) {
        if (!element || !text) return false;
        
        try {
            // 方法1: 直接设置值
            element.value = text;
            
            // 方法2: 使用原生setter
            const elementType = element.tagName.toLowerCase();
            let nativeSetter;
            
            if (elementType === 'textarea') {
                nativeSetter = Object.getOwnPropertyDescriptor(
                    window.HTMLTextAreaElement.prototype, "value"
                ).set;
            } else if (elementType === 'input') {
                nativeSetter = Object.getOwnPropertyDescriptor(
                    window.HTMLInputElement.prototype, "value"
                ).set;
            }
            
            if (nativeSetter) {
                nativeSetter.call(element, text);
            }
            
            // 方法3: 触发多种事件
            const events = ['input', 'change', 'blur', 'keyup', 'keydown', 'focus'];
            events.forEach(eventType => {
                const event = new Event(eventType, { bubbles: true, cancelable: true });
                element.dispatchEvent(event);
            });
            
            // 方法4: 触发InputEvent
            const inputEvent = new InputEvent('input', {
                bubbles: true,
                cancelable: true,
                inputType: 'insertText',
                data: text
            });
            element.dispatchEvent(inputEvent);
            
            // 聚焦和失焦
            element.focus();
            setTimeout(() => {
                element.blur();
                // 再次验证是否填充成功
                if (element.value !== text) {
                    element.value = text;
                    element.dispatchEvent(new Event('input', { bubbles: true }));
                    element.dispatchEvent(new Event('change', { bubbles: true }));
                }
            }, 100);
            
            console.log(`✓ 成功填入: "${text}" 到元素 ${element.id || element.name || '(无ID)'}`);
            return true;
        } catch (e) {
            console.error('填充输入框失败:', e);
            return false;
        }
    }

    // 自动填写验证码
    async function autoFillCaptcha() {
        const captchaInput = findCaptchaInput();
        if (!captchaInput) {
            console.log('⚠️ 未找到验证码输入框');
            return false;
        }
        
        console.log('✓ 找到验证码输入框:', captchaInput.id || captchaInput.name);
        
        const code = await recognizeCaptcha();
        if (code && code.length === 4) {
            console.log(`📝 准备填入验证码: "${code}"`);
            
            // 使用增强的填充函数
            const success = fillInput(captchaInput, code);
            
            // 额外验证
            setTimeout(() => {
                if (captchaInput.value === code) {
                    console.log('✅ 验证码填入成功,当前值:', captchaInput.value);
                } else {
                    console.log('⚠️ 验证码可能未正确填入,尝试再次填入...');
                    captchaInput.value = code;
                    captchaInput.dispatchEvent(new Event('input', { bubbles: true }));
                    captchaInput.dispatchEvent(new Event('change', { bubbles: true }));
                }
            }, 200);
            
            return success;
        } else {
            console.log('❌ 识别结果无效:', code);
            return false;
        }
    }

    // ==================== 原有的填写函数 ====================

    // 增强的文本框填充函数
    function fillTextArea(element, text) {
        return fillInput(element, text);
    }

    // 获取页面上所有可填写的文本框(排除配置面板的)
    function getPageTextAreas() {
        const allTextareas = Array.from(document.querySelectorAll('textarea'));
        return allTextareas.filter(ta => !ta.id.startsWith('config_'));
    }

    // 智能选择单选题和多选题
    function selectRadiosAndCheckboxes(isCoursePage) {
        const config = isCoursePage ? 
            { radios: radioCheckboxConfig.courseRadios, checkboxes: radioCheckboxConfig.courseCheckboxes } :
            { radios: [], checkboxes: [] };

        let selectedCount = 0;

        config.radios.forEach(radioConfig => {
            const allLabels = document.querySelectorAll('label, td, th, div, span');
            let questionElement = null;
            
            for (let label of allLabels) {
                const text = label.textContent || '';
                if (radioConfig.keywords.some(kw => text.includes(kw))) {
                    questionElement = label;
                    break;
                }
            }

            if (questionElement) {
                console.log(`  找到单选题: "${questionElement.textContent.substring(0, 30)}..."`);
                
                let radioGroup = questionElement.closest('tr, div, fieldset');
                if (!radioGroup) radioGroup = questionElement.parentElement;
                
                const radios = radioGroup ? radioGroup.querySelectorAll('input[type="radio"]') : [];
                
                radioConfig.selectOptions.forEach(optionText => {
                    for (let radio of radios) {
                        const radioLabel = document.querySelector(`label[for="${radio.id}"]`) || 
                                         radio.closest('label') ||
                                         radio.parentElement;
                        
                        if (radioLabel && radioLabel.textContent.includes(optionText)) {
                            radio.checked = true;
                            radio.dispatchEvent(new Event('change', { bubbles: true }));
                            console.log(`    ✓ 选择: ${optionText}`);
                            selectedCount++;
                            break;
                        }
                    }
                });
            }
        });

        config.checkboxes.forEach(checkboxConfig => {
            const allLabels = document.querySelectorAll('label, td, th, div, span');
            let questionElement = null;
            
            for (let label of allLabels) {
                const text = label.textContent || '';
                if (checkboxConfig.keywords.some(kw => text.includes(kw))) {
                    questionElement = label;
                    break;
                }
            }

            if (questionElement) {
                console.log(`  找到多选题: "${questionElement.textContent.substring(0, 30)}..."`);
                
                let checkboxGroup = questionElement.closest('tr, div, fieldset');
                if (!checkboxGroup) checkboxGroup = questionElement.parentElement;
                
                const checkboxes = checkboxGroup ? checkboxGroup.querySelectorAll('input[type="checkbox"]') : [];
                
                checkboxConfig.selectOptions.forEach(optionText => {
                    for (let checkbox of checkboxes) {
                        const checkboxLabel = document.querySelector(`label[for="${checkbox.id}"]`) || 
                                             checkbox.closest('label') ||
                                             checkbox.parentElement;
                        
                        if (checkboxLabel && checkboxLabel.textContent.includes(optionText)) {
                            checkbox.checked = true;
                            checkbox.dispatchEvent(new Event('change', { bubbles: true }));
                            console.log(`    ✓ 选择: ${optionText}`);
                            selectedCount++;
                            break;
                        }
                    }
                });
            }
        });

        return selectedCount;
    }

    // ==================== UI 界面 ====================

    // 创建配置面板
    const configPanel = document.createElement('div');
    configPanel.style.cssText = `
        position: fixed;
        top: 100px;
        right: 10px;
        width: 450px;
        padding: 15px;
        background-color: white;
        border: 1px solid #ccc;
        border-radius: 5px;
        z-index: 9998;
        display: none;
        max-height: 80vh;
        overflow-y: auto;
        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    `;

    function generateConfigPanelHTML() {
        return `
            <h3 style="margin-top: 0;">评价内容设置</h3>
            <p style="color: #2196F3; margin: 5px 0 15px 0; font-size: 13px;">
                💡 脚本会按顺序自动填写页面上的所有文本框,并智能识别单选题和多选题
            </p>
            <div style="margin-bottom: 20px;">
                <h4 style="margin-bottom: 10px;">课程评价(共${evaluationTexts.course.length}条):</h4>
                ${evaluationTexts.course.map((text, index) => `
                    <div style="margin-bottom: 10px;">
                        <p style="margin: 5px 0;">评价 ${index + 1}:</p>
                        <textarea id="config_course_${index}" class="eval-textarea" 
                            style="width: 100%; height: 60px; padding: 5px; border: 1px solid #ddd; border-radius: 3px;">${text}</textarea>
                    </div>
                `).join('')}
                <button id="addCourse" style="padding: 5px 15px; background-color: #4CAF50; 
                    color: white; border: none; border-radius: 3px; cursor: pointer; margin-top: 5px;">
                    ➕ 添加课程评价
                </button>
            </div>
            <div style="margin-bottom: 20px;">
                <h4 style="margin-bottom: 10px;">教师评价(共${evaluationTexts.teacher.length}条):</h4>
                ${evaluationTexts.teacher.map((text, index) => `
                    <div style="margin-bottom: 10px;">
                        <p style="margin: 5px 0;">评价 ${index + 1}:</p>
                        <textarea id="config_teacher_${index}" class="eval-textarea" 
                            style="width: 100%; height: 60px; padding: 5px; border: 1px solid #ddd; border-radius: 3px;">${text}</textarea>
                    </div>
                `).join('')}
                <button id="addTeacher" style="padding: 5px 15px; background-color: #4CAF50; 
                    color: white; border: none; border-radius: 3px; cursor: pointer; margin-top: 5px;">
                    ➕ 添加教师评价
                </button>
            </div>
            <div style="text-align: center; margin-bottom: 15px;">
                <button id="saveConfig" style="padding: 8px 20px; background-color: #4CAF50; 
                    color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px;">
                    💾 保存设置
                </button>
                <button id="resetConfig" style="padding: 8px 20px; background-color: #FF9800; 
                    color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px;">
                    🔄 重置默认
                </button>
                <button id="clearConfig" style="padding: 8px 20px; background-color: #f44336; 
                    color: white; border: none; border-radius: 4px; cursor: pointer;">
                    🗑️ 清除配置
                </button>
            </div>
            <div style="padding: 10px; background-color: #f0f0f0; border-radius: 4px;">
                <button id="debugButton" style="padding: 6px 15px; background-color: #9C27B0; 
                    color: white; border: none; border-radius: 4px; cursor: pointer; width: 100%;">
                    🔍 调试模式 - 查看页面元素
                </button>
            </div>
        `;
    }

    configPanel.innerHTML = generateConfigPanelHTML();
    
    // 创建自动填写按钮
    const autoButton = document.createElement('button');
    autoButton.innerText = '✨ 自动填写评估';
    autoButton.style.cssText = `
        position: fixed;
        top: 80px;
        right: 10px;
        z-index: 9999;
        padding: 10px 15px;
        background-color: #18d822ff;
        color: white;
        border: none;
        border-radius: 20px;
        cursor: pointer;
        box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    `;

    // 创建设置按钮
    const configButton = document.createElement('button');
    configButton.innerText = '⚙️ 设置评价内容';
    configButton.style.cssText = `
        position: fixed;
        top: 130px;
        right: 10px;
        z-index: 9999;
        padding: 10px 15px;
        background-color: #514f4fff;
        color: white;
        border: none;
        border-radius: 20px;
        cursor: pointer;
        box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    `;

    // 创建验证码识别按钮
    const captchaButton = document.createElement('button');
    captchaButton.innerText = '🔐 识别验证码';
    captchaButton.style.cssText = `
        position: fixed;
        top: 180px;
        right: 10px;
        z-index: 9999;
        padding: 10px 15px;
        background-color: #FF9800;
        color: white;
        border: none;
        border-radius: 20px;
        cursor: pointer;
        box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    `;

    // 设置按钮点击事件
    configButton.onclick = function() {
        if (configPanel.style.display === 'none') {
            configPanel.innerHTML = generateConfigPanelHTML();
            configPanel.style.display = 'block';
        } else {
            configPanel.style.display = 'none';
        }
    };

    // 验证码识别按钮点击事件
    captchaButton.onclick = async function() {
        captchaButton.disabled = true;
        captchaButton.innerText = '⏳ 识别中...';
        
        const success = await autoFillCaptcha();
        
        if (success) {
            captchaButton.innerText = '✅ 识别成功';
            setTimeout(() => {
                captchaButton.innerText = '🔐 识别验证码';
                captchaButton.disabled = false;
            }, 2000);
        } else {
            alert('⚠️ 验证码识别失败,请手动输入或重试');
            captchaButton.innerText = '🔐 识别验证码';
            captchaButton.disabled = false;
        }
    };

    // 事件委托处理
    document.addEventListener('click', function(e) {
        if (e.target && e.target.id === 'saveConfig') {
            evaluationTexts.course = [];
            evaluationTexts.teacher = [];
            
            let i = 0;
            while (true) {
                const textarea = document.getElementById(`config_course_${i}`);
                if (!textarea) break;
                evaluationTexts.course.push(textarea.value);
                i++;
            }
            
            i = 0;
            while (true) {
                const textarea = document.getElementById(`config_teacher_${i}`);
                if (!textarea) break;
                evaluationTexts.teacher.push(textarea.value);
                i++;
            }

            localStorage.setItem('evaluationTexts', JSON.stringify(evaluationTexts));
            configPanel.style.display = 'none';
            alert('✅ 设置已保存!');
        }
        
        else if (e.target && e.target.id === 'resetConfig') {
            evaluationTexts = {
                course: [
                    '这门课程我最喜欢它深入浅出的讲解方式,理论与实践紧密结合,让我在轻松氛围中扎实掌握核心知识,收获满满。',
                    '建议增加更多互动式教学环节,丰富案例分析,并优化课程节奏,以进一步提升学习参与度和知识吸收效率。',
                    '我平均每周在这门课程上花费约6小时,包括听课、完成作业和复习预习,时间投入合理,学习节奏适中。',
                    '在参与这门课之前,我对这个学科领域了解较少,兴趣一般,但课程生动的内容和实用的应用激发了我深入学习的热情。',
                    '我始终保持全勤,积极参与课堂讨论,主动回答问题,与老师同学互动频繁,展现出高度的投入和良好的学习态度。'
                ],
                teacher: [
                    '我最喜欢老师清晰的逻辑讲解和生动的案例分析,这种富有情感的授课方式让复杂知识变得易于理解,启发思考。',
                    '老师专业素养高,讲解清晰,建议继续保持互动式教学,并适当拓展前沿知识,进一步提升课程的深度与广度。'
                ]
            };
            localStorage.setItem('evaluationTexts', JSON.stringify(evaluationTexts));
            configPanel.innerHTML = generateConfigPanelHTML();
            alert('✅ 已重置为默认配置!');
        }
        
        else if (e.target && e.target.id === 'clearConfig') {
            localStorage.removeItem('evaluationTexts');
            evaluationTexts = {
                course: [''],
                teacher: ['']
            };
            configPanel.innerHTML = generateConfigPanelHTML();
            alert('✅ 配置已清除!');
        }
        
        else if (e.target && e.target.id === 'addCourse') {
            evaluationTexts.course.push('');
            configPanel.innerHTML = generateConfigPanelHTML();
        }
        
        else if (e.target && e.target.id === 'addTeacher') {
            evaluationTexts.teacher.push('');
            configPanel.innerHTML = generateConfigPanelHTML();
        }
        
        else if (e.target && e.target.id === 'debugButton') {
            console.log('=== 页面元素调试信息 ===');
            
            const textareas = getPageTextAreas();
            console.log(`📝 找到 ${textareas.length} 个可填写的 textarea:`);
            textareas.forEach((ta, idx) => {
                console.log(`  [${idx}] ID: "${ta.id}", Name: "${ta.name}", 当前值长度: ${ta.value.length}`);
            });
            
            const textInputs = document.querySelectorAll('input[type="text"]');
            console.log(`📝 找到 ${textInputs.length} 个 text input:`);
            textInputs.forEach((inp, idx) => {
                console.log(`  [${idx}] ID: "${inp.id}", Name: "${inp.name}"`);
            });
            
            const radios = document.querySelectorAll('input[type="radio"]');
            console.log(`🔘 找到 ${radios.length} 个单选按钮`);
            
            const checkboxes = document.querySelectorAll('input[type="checkbox"]');
            console.log(`☑️ 找到 ${checkboxes.length} 个多选框`);
            
            const captchaImg = findCaptchaImage();
            console.log(`🔐 验证码图片:`, captchaImg ? captchaImg.src : '未找到');
            
            const captchaInput = findCaptchaInput();
            console.log(`📝 验证码输入框:`, captchaInput ? `ID="${captchaInput.id}", Name="${captchaInput.name}"` : '未找到');
            
            alert('📊 调试信息已输出到控制台!\n请按 F12 打开开发者工具查看 Console 标签页。');
        }
    });

    // 从localStorage加载配置
    const savedConfig = localStorage.getItem('evaluationTexts');
    if (savedConfig) {
        try {
            evaluationTexts = JSON.parse(savedConfig);
            configPanel.innerHTML = generateConfigPanelHTML();
        } catch (e) {
            localStorage.removeItem('evaluationTexts');
        }
    }

    // 自动填写逻辑
    autoButton.onclick = async function() {
        let successCount = 0;
        let failCount = 0;
        
        console.log('==================');
        console.log('🚀 开始自动填写评估...');
        
        if (window.location.pathname.includes('/evaluateCourse')) {
            // 1. 选择所有5分选项
            const radios = document.querySelectorAll('input[type="radio"][value="5"]');
            radios.forEach(radio => {
                radio.checked = true;
                radio.dispatchEvent(new Event('change', { bubbles: true }));
            });
            console.log(`✓ 已选择 ${radios.length} 个5分选项`);

            // 2. 获取页面上的所有文本框并按顺序填写
            const textareas = getPageTextAreas();
            console.log(`📝 找到 ${textareas.length} 个文本框`);
            console.log(`📋 准备填写 ${evaluationTexts.course.length} 条评价`);
            
            evaluationTexts.course.forEach((text, index) => {
                if (index < textareas.length && text.trim()) {
                    const textarea = textareas[index];
                    console.log(`正在填写第 ${index + 1} 个文本框 (ID: ${textarea.id})...`);
                    
                    if (fillTextArea(textarea, text)) {
                        console.log(`  ✓ 成功填写: "${text.substring(0, 20)}..."`);
                        successCount++;
                    } else {
                        console.log(`  ✗ 填写失败`);
                        failCount++;
                    }
                } else if (index >= textareas.length) {
                    console.log(`⚠️ 评价数量(${evaluationTexts.course.length})超过文本框数量(${textareas.length})`);
                }
            });

            // 3. 自动选择特定的单选题和多选题
            const specificRadio = document.querySelector('input[type="radio"][id="1462"]');
            if (specificRadio) {
                specificRadio.checked = true;
                specificRadio.dispatchEvent(new Event('change', { bubbles: true }));
                console.log('✓ 已选择特定单选题');
            }

            const checkboxIds = ['1469', '1471'];
            checkboxIds.forEach(id => {
                const cb = document.querySelector(`input[type="checkbox"][id="${id}"]`);
                if (cb) {
                    cb.checked = true;
                    cb.dispatchEvent(new Event('change', { bubbles: true }));
                    console.log(`✓ 已选择多选框: ${id}`);
                }
            });

            // 4. 智能选择单选题和多选题
            console.log('📋 开始处理单选题和多选题...');
            const extraSelected = selectRadiosAndCheckboxes(true);
            console.log(`✓ 额外选择了 ${extraSelected} 个选项`);

            // 5. 自动识别验证码
            console.log('🔐 开始识别验证码...');
            const captchaSuccess = await autoFillCaptcha();

            console.log('==================');
            alert(`📝 课程评估填写完成!\n\n✅ 成功: ${successCount} 项\n❌ 失败: ${failCount} 项\n${captchaSuccess ? '🔐 验证码已自动识别' : '⚠️ 验证码需要手动输入或点击"识别验证码"按钮'}\n\n${failCount > 0 ? '⚠️ 部分填写失败,请检查控制台或手动补充' : '🎉 全部填写成功!'}`);
            
        } else if (window.location.pathname.includes('/evaluateTeacher')) {
            // 1. 选择所有5分选项
            const radios = document.querySelectorAll('input[type="radio"][value="5"]');
            radios.forEach(radio => {
                radio.checked = true;
                radio.dispatchEvent(new Event('change', { bubbles: true }));
            });
            console.log(`✓ 已选择 ${radios.length} 个5分选项`);

            // 2. 获取页面上的所有文本框并按顺序填写
            const textareas = getPageTextAreas();
            console.log(`📝 找到 ${textareas.length} 个文本框`);
            console.log(`📋 准备填写 ${evaluationTexts.teacher.length} 条评价`);
            
            evaluationTexts.teacher.forEach((text, index) => {
                if (index < textareas.length && text.trim()) {
                    const textarea = textareas[index];
                    console.log(`正在填写第 ${index + 1} 个文本框 (ID: ${textarea.id})...`);
                    
                    if (fillTextArea(textarea, text)) {
                        console.log(`  ✓ 成功填写: "${text.substring(0, 20)}..."`);
                        successCount++;
                    } else {
                        console.log(`  ✗ 填写失败`);
                        failCount++;
                    }
                } else if (index >= textareas.length) {
                    console.log(`⚠️ 评价数量(${evaluationTexts.teacher.length})超过文本框数量(${textareas.length})`);
                }
            });

            // 3. 自动识别验证码
            console.log('🔐 开始识别验证码...');
            const captchaSuccess = await autoFillCaptcha();

            console.log('==================');
            alert(`👨‍🏫 教师评估填写完成!\n\n✅ 成功: ${successCount} 项\n❌ 失败: ${failCount} 项\n${captchaSuccess ? '🔐 验证码已自动识别' : '⚠️ 验证码需要手动输入或点击"识别验证码"按钮'}\n\n${failCount > 0 ? '⚠️ 部分填写失败,请检查控制台或手动补充' : '🎉 全部填写成功!'}`);
        }
    };

    // 将新元素添加到页面
    document.body.appendChild(configButton);
    document.body.appendChild(configPanel);
    document.body.appendChild(autoButton);
    document.body.appendChild(captchaButton);

    console.log('✅ UCAS自动评估脚本已加载');
    console.log('💡 提示: 脚本会按顺序自动填写页面上的所有文本框');
    console.log('🔐 新功能: 支持自动识别验证码(4位数字+字母)');
    
    // 页面加载完成后,自动尝试识别验证码
    setTimeout(() => {
        const captchaImg = findCaptchaImage();
        const captchaInput = findCaptchaInput();
        if (captchaImg && captchaInput) {
            console.log('🔍 检测到验证码,可点击"识别验证码"按钮自动识别');
        }
    }, 1000);
})();