宜宾智慧校园助手

智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案,能够跳过秒看教学视频

当前为 2024-12-20 提交的版本,查看 最新版本

// ==UserScript==
// @name         宜宾智慧校园助手
// @namespace    智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案===来自计算机科学与技术学院--修改自若离智慧校园
// @version      6.5
// @description  智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案,能够跳过秒看教学视频
// @author       计算机科学与技术学院---软工
// @match        https://mooc.yibinu.edu.cn/*
// @icon         https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png
// @resource cs1 https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.css
// @resource cs2 https://pan.ruoli.cc/s/8b0cc4
// @require      https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js
// @require      https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.min.js
// @require      https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @run-at       document-end
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText
// ==/UserScript==

// 脚本初始化
var setting = {
    'logs': ['初始化脚本完成,', '当前脚本版本:V6.5'],
    'datas': [],
    'secretKey': '你好', // 暗号

    '_vt': '274c8b3f2a8c63ffc960dd1e4e3f0eac',
};

// 时间值
function decryptValidDuration() {
    try {
        const validDurations = {

            'c51ce410c124a10e0db5e4b97fc2af39': 86400000,

            '274c8b3f2a8c63ffc960dd1e4e3f0eac': 172800000,

            'e2ef524fbf3d9fe611d5a8e90fefdc9c': 259200000,

            '069059b7ef840f0c74a814ec9237b6ec': 432000000,

            '7f6ffaa6bb0b408017b62254211691b5': 604800000,

            '149e9677a5989fd342ae44213df68868': 2592000000
        };

        return validDurations[setting._vt] || 172800000; // 默认返回2天
    } catch (e) {
        console.error('解密时间值出错:', e);
        return 172800000; // 解密出错时返回默认值(2天)
    }
}

// 简化的验证时间管理
function setValidTime() {
    try {
        const validDuration = decryptValidDuration();
        const validUntil = Date.now() + validDuration;
        const data = {
            time: validUntil,
            hash: CryptoJS.SHA256(validUntil.toString()).toString()
        };
        localStorage.setItem('scriptValidUntil', JSON.stringify(data));
        return true;
    } catch (e) {
        console.error('设置验证时间出错:', e);
        return false;
    }
}

// 检查验证时间
function checkValidTime() {
    try {
        const data = localStorage.getItem('scriptValidUntil');
        if (!data) return false;

        const { time, hash } = JSON.parse(data);
        const now = Date.now();

        // 验证时间和哈希
        if (time && hash &&
            hash === CryptoJS.SHA256(time.toString()).toString() &&
            time > now) {
            return true;
        }

        localStorage.removeItem('scriptValidUntil');
        return false;
    } catch (e) {
        console.error('检查验证时间出错:', e);
        localStorage.removeItem('scriptValidUntil');
        return false;
    }
}

// 将验证函数定义为全局函数
window.verifySecret = function() {
    const input = document.getElementById('secretInput');
    if (!input) {
        console.error('找不到输入框元素');
        return;
    }

    const inputValue = input.value.trim();
    if (!inputValue) {
        input.style.borderColor = '#ff4d4f';
        input.style.animation = 'shake 0.5s';
        setTimeout(() => {
            input.style.borderColor = '#e8e8e8';
            input.style.animation = '';
        }, 1000);
        return;
    }

    if (inputValue !== setting.secretKey) {
        const modalDiv = document.querySelector('#secretModal > div');
        if (modalDiv) {
            modalDiv.style.animation = 'shake 0.5s';
            input.style.borderColor = '#ff4d4f';
            setTimeout(() => {
                modalDiv.style.animation = '';
                input.style.borderColor = '#e8e8e8';
            }, 1000);
        }
        return;
    }

    try {
        // 设置验证时间
        if (!setValidTime()) {
            throw new Error('设置验证时间失败');
        }

        // 添加关闭动画
        const modal = document.getElementById('secretModal');
        if (modal) {
            modal.style.animation = 'modalFadeOut 0.3s ease';
            setTimeout(() => {
                modal.remove();
                continueInit();
                // 添加成功提示
                if (window.vue) {
                    window.vue.$message.success('验证成功!');
                }
            }, 300);
        }
    } catch (error) {
        console.error('验证过程出错:', error);
        if (window.vue) {
            window.vue.$message.error('验证过程出错,请刷新页面重试');
        }
    }
};

// 添加键盘事件监听器的函数
function addKeyboardListener() {
    document.addEventListener('keydown', function(event) {
        if (event.key === 'Enter') {
            const secretInput = document.getElementById('secretInput');
            if (secretInput && document.activeElement === secretInput) {
                verifySecret();
            }
        }
    });
}

// 日志
function log(logText){
    setting.logs.unshift(logText);

    // Ensure Vue instance logs are updated
    if (window.vue) {
        window.vue.$nextTick(() => {
            window.vue.logs = [...setting.logs];
        });
    }
}

// 添加一个清理HTML标签的函数
function cleanHtmlTags(text) {
    if (!text) return '';

    // 将HTML转换为纯文本
    let temp = document.createElement('div');
    temp.innerHTML = text;
    let cleanText = temp.textContent || temp.innerText;

    // 清理多余的空白字符
    cleanText = cleanText.replace(/\s+/g, ' ').trim();

    return cleanText;
}

// 从后台获取答案
function getAnswer(url, data){
    log('正在获取答案...');
    let id = url.match(/\/examSubmit\/(\d+)\/getExamPaper/)[1];
    GM_xmlhttpRequest({
        method: "post",
        url: url,
        data: data,
        dataType: 'json',
        headers: {
            'Origin': location.origin,
            'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
            'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
            'Referer': `https://mooc.yibinu.edu.cn/examTest/stuExamList/${id}.mooc`
        },
        onload: function(res){
            if(res.status == 200){
                try {
                    let response = JSON.parse(res.responseText);
                    if (response && response.paper && response.paper.paperStruct) {
                        log("获取答案成功正在格式化答案!");
                        formatAnswer(response.paper.paperStruct);
                    } else {
                        log("答案数据格式异常,请检查接口返回");
                    }
                } catch (error) {
                    log("解析答案数据失败:" + error.message);
                }
            } else {
                log("获取答案失败,状态码:" + res.status);
            }
        },
        onerror: function(error) {
            log("求答案失败:" + error.message);
        }
    });
}
//格式化答案
function formatAnswer(str) {
    try {
        setting.datas = []; // 清空之前的数据
        if (!Array.isArray(str)) {
            log("答案数据式错误");
            return;
        }

        str.forEach((listItem, index) => {
            if (!listItem.quiz) {
                return;
            }

            // 使用cleanHtmlTags清理题目内容
            var question = cleanHtmlTags(listItem.quiz.quizContent) || "未知题目";
            var options = {};
            var optionContents = {};  // 存储选项内容
            var answer = [];
            const questionNum = (index + 1).toString();

            // 处理选择题
            if (listItem.quiz.quizOptionses && listItem.quiz.quizOptionses.length > 0) {
                listItem.quiz.quizOptionses.forEach((optionItem, idx) => {
                    if (optionItem && optionItem.optionId !== undefined) {
                        const optionLabel = String.fromCharCode(65 + idx);
                        options[optionItem.optionId] = optionLabel;
                        optionContents[optionItem.optionId] = optionItem.optionContent || '';
                    }
                });

                // 处理答案
                if (listItem.quiz.quizResponses) {
                    listItem.quiz.quizResponses.forEach(answerItem => {
                        if (answerItem && options[answerItem.optionId]) {
                            const label = options[answerItem.optionId];
                            const content = optionContents[answerItem.optionId];
                            answer.push(`${label}.${content}`);
                        }
                    });
                }

                // 并序号和选项标签
                const answerLabels = listItem.quiz.quizResponses
                    .map(item => options[item.optionId])
                    .join('');
                const idAndOptions = `${questionNum}.${answerLabels}`;

                setting.datas.push({
                    'key': index.toString(),
                    'idAndOptions': idAndOptions,
                    'question': question,
                    'answer': answer.join('\n')  // 每个选项答案换行显示
                });
            } else {
                // 处理填空题
                if (listItem.quiz.quizResponses) {
                    const fillAnswers = [];
                    listItem.quiz.quizResponses.forEach(answerItem => {
                        if (answerItem && answerItem.responseContent) {
                            fillAnswers.push(answerItem.responseContent);
                        }
                    });

                    setting.datas.push({
                        'key': index.toString(),
                        'idAndOptions': `${questionNum}.(填空)`,
                        'question': question,
                        'answer': fillAnswers.join('\n')  // 多个填空答案换行显示
                    });
                }
            }
        });

        // 更新 Vue 实例中的数据
        if (window.vue) {
            Vue.nextTick(() => {
                window.vue.answerList = [...setting.datas];
                window.vue.hasAnswer = true; // 设置答案获取状态为 true
            });
        }

        log(`成功处理 ${setting.datas.length} 道题目`);
        log('答案获取完成,可以切换到答案列表查看');
    } catch (error) {
        log("格式化答案时出错:" + error.message);
        if (window.vue) {
            window.vue.hasAnswer = false;
        }
    }
}
//初始化界面
function initView(){
    // 检查验证是否有效
    if (checkValidTime()) {
        // 验证仍然有效,直接继续初始化
        continueInit();
        return;
    }

    // 创建验证界面的HTML
    const createModal = () => {
        const modalHtml = `
            <div id="secretModal" style="
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0, 0, 0, 0.5);
                display: flex;
                justify-content: center;
                align-items: center;
                z-index: 10000;
            ">
                <div style="
                    background: white;
                    padding: 30px;
                    border-radius: 15px;
                    box-shadow: 0 8px 24px rgba(0,0,0,0.2);
                    width: 320px;
                    text-align: center;
                    animation: modalFadeIn 0.3s ease;
                ">
                    <img src="https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png" style="
                        width: 64px;
                        height: 64px;
                        margin-bottom: 15px;
                    ">
                    <h2 style="
                        margin: 0 0 20px 0;
                        color: #333;
                        font-size: 20px;
                        font-weight: 500;
                    ">请输入暗号---你好</h2>
                    <input type="text" id="secretInput" style="
                        width: 100%;
                        padding: 12px;
                        margin-bottom: 15px;
                        border: 2px solid #e8e8e8;
                        border-radius: 8px;
                        font-size: 16px;
                        outline: none;
                        transition: all 0.3s;
                        box-sizing: border-box;
                    " placeholder="请输入暗号...">
                    <button id="verifyButton" style="
                        width: 100%;
                        padding: 12px;
                        border: none;
                        background-color: #1890ff;
                        color: white;
                        border-radius: 8px;
                        font-size: 16px;
                        cursor: pointer;
                        transition: all 0.3s;
                        box-sizing: border-box;
                    ">验证</button>
                </div>
            </div>
        `;

        document.body.insertAdjacentHTML('beforeend', modalHtml);

        // 添加事件监听器
        const button = document.getElementById('verifyButton');
        if (button) {
            button.addEventListener('click', window.verifySecret);
        }

        // 添加键盘事件监听
        addKeyboardListener();

        // 自动聚焦输入框
        setTimeout(() => {
            const input = document.getElementById('secretInput');
            if (input) {
                input.focus();
            }
        }, 100);
    };

    // 添加动画样式
    const style = document.createElement('style');
    style.textContent = `
        @keyframes shake {
            0%, 100% { transform: translateX(0); }
            10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
            20%, 40%, 60%, 80% { transform: translateX(5px); }
        }
        @keyframes modalFadeIn {
            from { opacity: 0; transform: translateY(-20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        @keyframes modalFadeOut {
            from { opacity: 1; transform: translateY(0); }
            to { opacity: 0; transform: translateY(-20px); }
        }
    `;
    document.head.appendChild(style);

    createModal();
}

// 将原来的初始化代码移动到新的函数中
function continueInit() {
    var $div =$('<div class="rlBox minimized">' +
        '   <a-card title="宜宾学院智慧校园助手" style="width: 100%;height: 100%;">' +
        '       <template slot="extra">' +
        '           <span v-show="!close" style="margin-right: 10px; font-size: 12px; color: #999;">{{validTimeRemaining}}</span>' +
        '           <a-button :type="buttonColor" shape="circle" :icon="buttonIcon" @click="toClose" size="small"/>' +
        '       </template>' +
        '       <div style="margin-bottom: 15px;" v-show="!close">' +
        '           <a-button-group style="width: 100%;">' +
        '               <a-button type="danger" style="width: 25%;" @click="passVideo()">秒过视频</a-button>' +
        '               <a-button type="primary" style="width: 25%;" @click="exportExcel()">导出题库</a-button>' +
        '               <a-button type="success" style="width: 25%;" @click="clearLogs()">清除日志</a-button>' +
        '               <a-button type="warning" style="width: 25%;" @click="autoAnswer()">自动答题</a-button>' +
        '           </a-button-group>' +
        '       </div>' +
        '       <a-tabs default-active-key="1" @change="callback" v-show="!close">' +
        '           <a-tab-pane key="1" tab="运行日志">' +
        '               <div class="rl-panel log">' +
        '                   <p v-for="item in logs" class="log_content">' +
        '                       {{item}}' +
        '                   </p>' +
        '               </div>' +
        '           </a-tab-pane>' +
        '           <a-tab-pane key="2" :tab="answerTabTitle" :disabled="!hasAnswer">' +
        '               <div class="rl-panel">' +
        '                   <a-table id="rlTable"' +
        '                   :pagination="false" bordered size="small" :columns="columns" :data-source="answerList">' +
        '                   </a-table>' +
        '               </div>' +
        '           </a-tab-pane>' +
        '       </a-tabs>' +
        '   </a-card>' +
        '</div>');

    // 更新样式
    const customStyle = `
        .rlBox {
            position: fixed;
            top: 10px;
            right: 10px;
            width: 400px;
            z-index: 9999;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            padding: 10px;
            transition: all 0.3s ease;
            transform: none !important;
            overflow: hidden;
        }

        /* 优化最小化状态 */
        .rlBox.minimized {
            width: 40px !important;
            height: 40px !important;
            padding: 0 !important;
            overflow: hidden;
            opacity: 0.8;
            cursor: pointer;
            border-radius: 50%;
            background: #1890ff;
            box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }

        /* 按钮样式优化 */
        .ant-btn-danger {
            background: #ff4d4f !important;
            border-color: #ff4d4f !important;
            color: white !important;
        }

        .ant-btn-danger:hover {
            background: #ff7875 !important;
            border-color: #ff7875 !important;
        }

        .ant-btn-primary {
            background: #1890ff !important;
            border-color: #1890ff !important;
        }

        .ant-btn-primary:hover {
            background: #40a9ff !important;
            border-color: #40a9ff !important;
        }

        .ant-btn-success {
            background: #52c41a !important;
            border-color: #52c41a !important;
            color: white !important;
        }

        .ant-btn-success:hover {
            background: #73d13d !important;
            border-color: #73d13d !important;
        }

        /* 最小化状态下的卡片样式 */
        .rlBox.minimized .ant-card {
            background: transparent;
            border: none;
            box-shadow: none;
        }

        /* 最小化状态下的标题隐藏 */
        .rlBox.minimized .ant-card-head-title {
            display: none;
        }

        /* 最小化状态下的开按钮样式 */
        .rlBox.minimized .ant-btn-circle {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: transparent !important;
            border: 2px solid white !important;
            color: white !important;
            box-shadow: none;
        }

        .rlBox.minimized .ant-btn-circle:hover {
            background: rgba(255, 255, 255, 0.2) !important;
        }

        /* 最小化状态悬停效果 */
        .rlBox.minimized:hover {
            opacity: 1;
            box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4);
            transform: translateY(-2px) !important;
        }

        /* 移动端适配 */
        @media screen and (max-width: 768px) {
            .rlBox {
                width: 300px;
            }

            .rlBox.minimized {
                width: 36px !important;
                height: 36px !important;
            }
        }

        /* 表格容器样式 */
        .rl-panel {
            height: auto;
            max-height: calc(100vh - 250px);
            overflow-y: auto;
            overflow-x: hidden;
        }

        /* 表格样式优化 */
        .ant-table-wrapper {
            overflow: visible;
        }

        .ant-table {
            min-width: 100%;
            background: transparent;
        }

        /* 美化滚动条样式 */
        .rl-panel::-webkit-scrollbar {
            width: 6px;
            height: 6px;
        }

        .rl-panel::-webkit-scrollbar-thumb {
            background: #d9d9d9;
            border-radius: 3px;
        }

        .rl-panel::-webkit-scrollbar-track {
            background: #f0f0f0;
            border-radius: 3px;
        }

        /* 表格单元格样式 */
        .ant-table-tbody > tr > td {
            white-space: normal;
            word-break: break-word;
            padding: 8px 16px;
            line-height: 1.5;
            max-width: 0;
        }

        /* 表格头部样式 */
        .ant-table-thead > tr > th {
            background: #f5f5f5;
            padding: 12px 16px;
            white-space: nowrap;
            position: sticky;
            top: 0;
            z-index: 2;
        }

        /* 保表格布局合理 */
        #rlTable {
            table-layout: fixed;
            width: 100%;
        }

        /* 移动端适配 */
        @media screen and (max-width: 768px) {
            .rl-panel {
                max-height: calc(100vh - 200px);
            }

            .ant-table-tbody > tr > td {
                padding: 6px 12px;
            }

            .ant-table-thead > tr > th {
                padding: 8px 12px;
            }
        }
    `;

    $("body").append($div);
    GM_addStyle(GM_getResourceText("cs1"));
    GM_addStyle(GM_getResourceText("cs2"));
    GM_addStyle(customStyle);

    var vue = new Vue({
        el: '.rlBox',
        data:{
            logs: setting.logs,
            close: true,
            key: '1',
            columns:[
                {
                    title: '序号.选项',
                    dataIndex: 'idAndOptions',
                    key: 'idAndOptions',
                    width: '80px',
                    fixed: 'left',
                    align: 'center'
                },
                {
                    title: '题目',
                    dataIndex: 'question',
                    key: 'question',
                    width: '45%',
                    ellipsis: true
                },
                {
                    title: '答案',
                    dataIndex: 'answer',
                    key: 'answer',
                    width: '45%',
                    customRender: (text) => {
                        return text ? text.split('\n').join('<br/>') : '';
                    }
                }
            ],
            answerList: [],  // 初化为空数组
            isDragging: false,
            currentX: 0,
            currentY: 0,
            initialX: 0,
            initialY: 0,
            xOffset: 0,
            yOffset: 0,
            hasAnswer: false, // 添加答案获取状态标志
            validUntil: localStorage.getItem('scriptValidUntil') || null,
        },
        mounted() {
            window.vue = this;
            this.initDragEvents();

            // 修改初始化位置设置
            const box = document.querySelector('.rlBox');
            box.style.right = '0px';
            box.style.left = 'auto';
            box.setAttribute('data-expand-side', 'right'); // 设置默认展开方向

            // 添加窗口大小改变监听
            window.addEventListener('resize', this.checkPosition);

            // 每分钟更新一次验证时间显示
            setInterval(() => {
                this.validUntil = localStorage.getItem('scriptValidUntil');
            }, 60000);
        },
        computed:{
            isShow(){
                return this.close ? 0.8 : 1.0;
            },
            buttonIcon(){
                return this.close ? 'plus' : 'minus';
            },
            buttonColor(){
                return this.close ? 'primary' : 'default';
            },
            answerTabTitle() {
                return this.hasAnswer ? '答案列表' : '答案列表 (等待获取...)';
            },
            validTimeRemaining() {
                const validTime = getValidTime();
                if (!validTime) return '未验证';

                const remaining = validTime - Date.now();
                if (remaining <= 0) return '验证已过期';

                const days = Math.floor(remaining / (24 * 60 * 60 * 1000));
                const hours = Math.floor((remaining % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
                return `验证剩余: ${days}天${hours}小时`;
            }
        },
        methods: {
            callback(key) {
                if (key === '2' && !this.hasAnswer) {
                    this.$message.warning('请等待答案获取完成后再查看答案列表');
                    this.key = '1'; // 保持在日志页面
                    return;
                }
                this.key = key;
            },
            toClose() {
                this.close = !this.close;
                const box = document.querySelector('.rlBox');
                const rect = box.getBoundingClientRect();
                const windowWidth = window.innerWidth;

                if (this.close) {
                    // 最小化时,判断靠近哪边
                    box.style.transition = 'all 0.3s ease';
                    const centerX = rect.left + rect.width / 2;
                    if (centerX > windowWidth / 2) {
                        // 靠右
                        box.style.right = '10px';
                        box.style.left = 'auto';
                        box.setAttribute('data-side', 'right');
                    } else {
                        // 靠左
                        box.style.left = '10px';
                        box.style.right = 'auto';
                        box.setAttribute('data-side', 'left');
                    }
                    box.classList.add('minimized');
                } else {
                    // 展开时
                    box.style.transition = 'all 0.3s ease';
                    box.classList.remove('minimized');
                    const side = box.getAttribute('data-side') || 'right';

                    if (side === 'right') {
                        box.style.right = '10px';
                        box.style.left = 'auto';
                    } else {
                        box.style.left = '10px';
                        box.style.right = 'auto';
                    }
                }
            },
            passVideo() {
                let video = document.getElementsByTagName("video");
                if(video.length == 0){
                    log("当前页面不存在视频,请确保视频已加载");
                    return;
                }

                let currentVideo = video[0];

                // 移除之前的事件监听器(如果存在)
                currentVideo.removeEventListener('ended', this.handleNextVideo);

                // 添加新的视频结束监听器
                currentVideo.addEventListener('ended', () => {
                    log("视频播放完成,准备切换到下一个视频...");
                    this.handleNextVideo();
                });

                try {
                    // 设置视频结束前的最后一秒
                    if (currentVideo.duration && !isNaN(currentVideo.duration)) {
                        currentVideo.currentTime = currentVideo.duration - 0.1;
                        currentVideo.playbackRate = 1;
                        log("正在完成当前视频...");
                    } else {
                        log("无法获取视频时长,请等待视频加载完成");
                        // 等待视频加载完成
                        currentVideo.addEventListener('loadedmetadata', () => {
                            currentVideo.currentTime = currentVideo.duration - 0.1;
                            currentVideo.playbackRate = 1;
                            log("视频加载完成,正在处理...");
                        });
                    }
                } catch (error) {
                    log("处理视频时出错:" + error.message);
                }
            },
            exportExcel(){
                // 检查是否有答案数据
                if (!this.answerList || this.answerList.length === 0) {
                    this.$message.error('没有可导出的答案数!请等待答案获取完成。');
                    log('导出失败:没有答案数据');
                    return;
                }

                // 准备数据
                const data = this.answerList.map(item => ({
                    '序号.选项': item.idAndOptions,
                    '题目': item.question,
                    '答案': item.answer
                }));

                // 创建工作簿
                const wb = XLSX.utils.book_new();
                // 创建工作表
                const ws = XLSX.utils.json_to_sheet(data);

                // 设置列宽
                const colWidths = {
                    '序号.选项': 10,
                    '题目': 50,
                    '答案': 30
                };

                ws['!cols'] = Object.keys(colWidths).map(key => ({
                    wch: colWidths[key]
                }));

                // 将工作表添加到工作簿
                XLSX.utils.book_append_sheet(wb, ws, "题库");

                // 生成并下载文件
                XLSX.writeFile(wb, "题库.xlsx");

                log('题库已导出为 Excel 文件');
            },
            clearLogs() {
                // 清空所有现有日志
                this.logs = [];
                setting.logs = [];

                // 添加清除提示
                const clearMessage = [
                    '日志已清除',
                    '------------------------',

                ];

                // 直接设置新的日志数组,而不是使用 log 函数
                this.logs = clearMessage;
                setting.logs = [...clearMessage];

                // 阻止其他日志添加
                setTimeout(() => {
                    // 确保清除状态保持
                    if (this.logs.length > clearMessage.length) {
                        this.logs = [...clearMessage];
                        setting.logs = [...clearMessage];
                    }
                }, 200);
            },
            // 优化拖动处理
            initDragEvents() {
                const box = document.querySelector('.rlBox');
                const dragZone = document.querySelector('.ant-card-head');
                let startX, startY, initialMouseX, initialMouseY;

                dragZone.addEventListener('mousedown', (e) => {
                    if (this.close) return; // 小化时禁止拖动
                    e.preventDefault();
                    this.isDragging = true;

                    const rect = box.getBoundingClientRect();
                    startX = rect.left;
                    startY = rect.top;
                    initialMouseX = e.clientX;
                    initialMouseY = e.clientY;

                    box.style.transition = 'none';
                    document.body.style.userSelect = 'none';
                });

                document.addEventListener('mousemove', (e) => {
                    if (!this.isDragging) return;

                    const dx = e.clientX - initialMouseX;
                    const dy = e.clientY - initialMouseY;

                    let newX = startX + dx;
                    let newY = startY + dy;

                    box.style.left = `${newX}px`;
                    box.style.top = `${newY}px`;
                    box.style.right = 'auto';

                    // 实时检查位置
                    this.checkPosition();
                });

                document.addEventListener('mouseup', () => {
                    if (this.isDragging) {
                        this.isDragging = false;
                        box.style.transition = 'all 0.2s';
                        document.body.style.userSelect = '';
                    }
                });

                // 添加摸支持
                dragZone.addEventListener('touchstart', (e) => {
                    const touch = e.touches[0];
                    const rect = box.getBoundingClientRect();
                    startX = rect.left;
                    startY = rect.top;
                    initialMouseX = touch.clientX;
                    initialMouseY = touch.clientY;
                    this.isDragging = true;
                });

                document.addEventListener('touchmove', (e) => {
                    if (!this.isDragging) return;
                    e.preventDefault();

                    const touch = e.touches[0];
                    const dx = touch.clientX - initialMouseX;
                    const dy = touch.clientY - initialMouseY;

                    let newX = startX + dx;
                    let newY = startY + dy;

                    const maxX = window.innerWidth - box.offsetWidth;
                    const maxY = window.innerHeight - box.offsetHeight;

                    newX = Math.min(Math.max(0, newX), maxX);
                    newY = Math.min(Math.max(0, newY), maxY);

                    box.style.left = `${newX}px`;
                    box.style.top = `${newY}px`;
                });

                document.addEventListener('touchend', () => {
                    this.isDragging = false;
                });
            },
            handleNextVideo() {
                // 获取所有视频列表项
                const videoItems = document.querySelectorAll('li[title*="mp4"]');
                if (!videoItems || videoItems.length === 0) {
                    log("未找到视频列表");
                    return;
                }

                // 找到当前激活的视频
                const currentVideoItem = Array.from(videoItems).find(item =>
                    item.classList.contains('tab-active') ||
                    item.querySelector('.tab-inner.current')
                );

                if (!currentVideoItem) {
                    log("无法确定当前视频位置");
                    return;
                }

                // 判断是否是最后一个视频
                const isLastVideo = currentVideoItem === videoItems[videoItems.length - 1];
                if (isLastVideo) {
                    log("当前已是最后一个视频");
                    return;
                }

                // 查找"开始下一条目"按钮
                const nextButton = document.querySelector('.view-tip[itemid="1"]');
                if (!nextButton) {
                    log("未找到一个视频按钮");
                    return;
                }

                // 点击"开始下一条目"按钮
                log("正在切换到下一个视频...");
                nextButton.click();

                // 等待新视频加载
                let attempts = 0;
                const maxAttempts = 10;
                const checkInterval = setInterval(() => {
                    const newVideo = document.getElementsByTagName("video")[0];
                    if (newVideo && newVideo.readyState >= 2) { // 确保视频已经加载足够的数据
                        clearInterval(checkInterval);
                        log("新视频加载成功,准备播放");
                        setTimeout(() => {
                            this.passVideo();
                        }, 1500); // 增加延迟以确保视频完全加载
                    } else {
                        attempts++;
                        if (attempts >= maxAttempts) {
                            clearInterval(checkInterval);
                            log("新视频加载超时,请手动切换");
                        }
                    }
                }, 1000);
            },
            checkPosition() {
                const box = document.querySelector('.rlBox');
                const rect = box.getBoundingClientRect();
                const windowWidth = window.innerWidth;
                const windowHeight = window.innerHeight;

                // 检查并修正水平位置
                if (rect.right > windowWidth) {
                    box.style.right = '0px';
                    box.style.left = 'auto';
                }
                if (rect.left < 0) {
                    box.style.left = '0px';
                    box.style.right = 'auto';
                }

                // 检查并修正垂直位置
                if (rect.bottom > windowHeight) {
                    box.style.top = `${windowHeight - rect.height - 10}px`;
                }
                if (rect.top < 0) {
                    box.style.top = '10px';
                }
            },
            autoAnswer() {
                log("开始自动答题...");

                // 检查是否已获取答案
                if (!setting.datas || setting.datas.length === 0) {
                    log("未找到答案信息,请先获取答案");
                    return;
                }

                log(`共发现 ${setting.datas.length} 道题目的答案`);
                let answeredCount = 0;

                // 遍历答案列表
                setting.datas.forEach((answerData, index) => {
                    try {
                        // 获取当前题目的答案
                        const answer = answerData.answer;
                        if (!answer) {
                            log(`第 ${index + 1} 题答案为空`);
                            return;
                        }

                        // 获取题目容器
                        const questionContainer = document.querySelector(`.view-test[data-num="${index + 1}"]`) ||
                                               document.querySelector(`.view-test:nth-child(${index + 1})`);

                        if (!questionContainer) {
                            log(`第 ${index + 1} 题未找到题目容器`);
                            return;
                        }

                        // 判断题目类型
                        const isMultiChoice = questionContainer.querySelector('.input-c') !== null;
                        const isSingleChoice = questionContainer.querySelector('.input-r') !== null;
                        const isFillBlank = questionContainer.querySelector('.fillblank') !== null;

                        if (isMultiChoice || isSingleChoice) {
                            // 处理选择题
                            const answerLetters = answer.split('\n').map(a => a.trim().charAt(0));
                            const options = questionContainer.querySelectorAll('.t-option');

                            options.forEach((option, idx) => {
                                const letter = String.fromCharCode(65 + idx);
                                if (answerLetters.includes(letter)) {
                                    // 查找选项的点击元素
                                    const clickTarget = option.querySelector('a[href="javascript:void(0)"]') ||
                                                      option.querySelector(isMultiChoice ? '.input-c' : '.input-r');

                                    if (clickTarget) {
                                        // 检查是否已经选中
                                        const isChecked = option.querySelector(isMultiChoice ? '.input-c.selected' : '.input-r.selected');
                                        if (!isChecked) {
                                            clickTarget.click();
                                            log(`第 ${index + 1} 题选择了选项 ${letter}`);
                                        }
                                    }
                                }
                            });
                            answeredCount++;
                        } else if (isFillBlank) {
                            // 处理填空题
                            const fillBlanks = questionContainer.querySelectorAll('.fillblank');
                            const answers = answer.split('\n').map(a => a.replace(/^.*?\./, '').trim());

                            // 如果有多个填空框
                            if (fillBlanks.length > 1) {
                                fillBlanks.forEach((input, idx) => {
                                    if (answers[idx]) {
                                        input.value = answers[idx];
                                        // 触发必要的事件
                                        input.dispatchEvent(new Event('input', { bubbles: true }));
                                        input.dispatchEvent(new Event('change', { bubbles: true }));
                                        log(`第 ${index + 1} 题第 ${idx + 1} 个空填写了答案: ${answers[idx]}`);
                                    }
                                });
                            } else if (fillBlanks.length === 1) {
                                // 单个填空框
                                const input = fillBlanks[0];
                                input.value = answers[0];
                                // 触发必要的事件
                                input.dispatchEvent(new Event('input', { bubbles: true }));
                                input.dispatchEvent(new Event('change', { bubbles: true }));
                                log(`第 ${index + 1} 题填写了答案: ${answers[0]}`);
                            }
                            answeredCount++;
                        }
                    } catch (error) {
                        log(`第 ${index + 1} 题自动答题出错: ${error.message}`);
                    }
                });

                // 显示完成提示
                log(`自动答题完成!成功答题 ${answeredCount} 道题目`);
                this.$message.success(`自动答题完成!成功答题 ${answeredCount} 道题目,请检查后手动提交。`);
            }
        }
    });
}





// 初始化获取答案,延迟5秒防止流程崩溃
function initGetAnswer(settings){
    var url = location.origin + settings.url;
    var data = settings.data.replace(/(testPaperId=).*?(&)/,'$1' + '1250' + '$2');
    console.log("=====")
    console.log(url,'url')
    console.log(data)
    getAnswer(url,data);
}

// 自动答题功能
function autoAnswerQuestions() {
  log("开始自动答题...");

  // 获取所有题目
  const questions = document.querySelectorAll('.view-test');
  if(!questions || questions.length === 0) {
    log("未找到题目,请确认是否在答题页面");
    return;
  }

  log(`共发现 ${questions.length} 道题目`);

  questions.forEach((question, index) => {
    // 获取题目类型
    const isMultiChoice = question.querySelector('.t-option .input-c') !== null; // 多选
    const isSingleChoice = question.querySelector('.t-option .input-r') !== null; // 单选
    const isFillBlank = question.querySelector('.fill_b .fillblank') !== null; // 填空

    // 获取正确答案
    const answerElem = question.querySelector('.test-ana[_type="response"]');
    if(!answerElem) {
      log(`第 ${index + 1} 题未找到答案信息`);
      return;
    }

    const answerText = answerElem.textContent.replace('参考答案:','').trim();

    // 根据题型选择答案
    if(isMultiChoice) {
      // 多选题
      const answers = answerText.split(' ');
      answers.forEach(answer => {
        const options = question.querySelectorAll('.t-option');
        options.forEach((option, idx) => {
          if(answer === String.fromCharCode(65 + idx)) {
            const checkbox = option.querySelector('.input-c');
            if(checkbox && !checkbox.classList.contains('selected')) {
              checkbox.click();
            }
          }
        });
      });
    } else if(isSingleChoice) {
      // 单选题
      const options = question.querySelectorAll('.t-option');
      options.forEach((option, idx) => {
        if(answerText === String.fromCharCode(65 + idx)) {
          const radio = option.querySelector('.input-r');
          if(radio && !radio.classList.contains('selected')) {
            radio.click();
          }
        }
      });
    } else if(isFillBlank) {
      // 填空题
      const input = question.querySelector('.fill_b .fillblank');
      if(input) {
        input.value = answerText;
        // 触发input事件
        const event = new Event('input', { bubbles: true });
        input.dispatchEvent(event);
      }
    }
  });

  log("自动答题完成!");
}

// 添加自动答题按钮
function addAutoAnswerButton() {
  const btnArea = document.querySelector('.practice-action');
  if(!btnArea) return;

  const btn = document.createElement('a');
  btn.innerText = '自动答题';
  btn.className = 'btn-public btn-min';
  btn.href = 'javascript:void(0)';
  btn.onclick = autoAnswerQuestions;

  btnArea.appendChild(btn);
  log("已添加自动答题按钮");
}

// 初始化
function initAutoAnswer() {
  if(location.href.includes('/examSubmit/')) {
    log("检测到答题页面");
    setTimeout(addAutoAnswerButton, 2000);
  }
}

initAutoAnswer();

// 脚本入口
initView();
//监听跳过视频按钮
$('#rl_passVideo').click(function(){passVideo();});
//监听url访问,当访问了加载题目的url时,将获取答案
$(document).ready(function(){
    $(document).ajaxComplete(function (evt, request, settings) {
        if(settings.url.search('getExamPaper') != -1){
            setting.logs.unshift("您已打开作业界面,5秒后将为您获取答案")
            setTimeout(initGetAnswer,5000, settings);
        }
    });
})

// 获取验证时间的函数
function getValidTime() {
    try {
        const data = localStorage.getItem('scriptValidUntil');
        if (!data) return null;

        const { time, hash } = JSON.parse(data);
        if (!time || !hash) return null;

        // 验证哈希值
        if (hash !== CryptoJS.SHA256(time.toString()).toString()) {
            localStorage.removeItem('scriptValidUntil');
            return null;
        }

        return time;
    } catch (e) {
        console.error('获取验证时间出错:', e);
        localStorage.removeItem('scriptValidUntil');
        return null;
    }
}

QingJ © 2025

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