宜宾智慧校园助手

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

目前為 2024-11-27 提交的版本,檢視 最新版本

    // ==UserScript==
    // @name         宜宾智慧校园助手
    // @namespace    智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案===来自计算机科学与技术学院--修改自若离智慧校园
    // @version      4.0
    // @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
    // @run-at       document-end
    // @grant        GM_xmlhttpRequest
    // @grant        GM_addStyle
    // @grant        GM_getResourceText
    // ==/UserScript==

// 脚本初始化
var setting = {'logs':['初始化脚本完成,','当前脚本版本:V4.0'],'datas': []};

// 日志
function log(logText){
    setting.logs.unshift(logText);
    if(Math.random() > 0.92){
        setting.logs.unshift('请勿用于非法用途哦!!');
    }
}


// 从后台获取答案
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;
            }

            var question = 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(){
    var $div =$('<div class="rlBox" :style="{opacity: isShow}">' +
        '   <a-card title="宜宾学院智慧校园助手" style="width: 100%;height: 100%;">' +
        '       <template slot="extra">' +
        '           <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: 33.33%;" @click="passVideo()">秒过视频</a-button>' +
        '               <a-button type="primary" style="width: 33.33%;" @click="exportExcel()">导出题库</a-button>' +
        '               <a-button type="success" style="width: 33.33%;" @click="clearLogs()">清除日志</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"' +
        '                   :scroll="{y: 275}" :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;
        }
        .rlBox.minimized {
            width: 200px !important;
            height: auto !important;
        }
        .ant-card-head {
            cursor: move;
            background: #f0f2f5;
            border-radius: 8px 8px 0 0;
            padding: 0 12px;
            min-height: 40px;
        }
        .ant-card-head-title {
            padding: 8px 0;
            font-size: 14px;
        }
        .ant-card-extra {
            padding: 8px 0;
        }
        .ant-btn-circle {
            min-width: 24px;
            width: 24px;
            height: 24px;
        }
        .ant-card-body {
            padding: 12px;
        }
        @media screen and (max-width: 768px) {
            .rlBox {
                width: 300px;
                right: 5px;
                top: 5px;
            }
            .ant-table {
                font-size: 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: false,
            key: '1',
            columns:[
                {
                    title: '序号.选项',
                    dataIndex: 'idAndOptions',
                    key: 'idAndOptions',
                    width: '120px',
                    fixed: 'left',
                    align: 'center'
                },
                {
                    title: '题目',
                    dataIndex: 'question',
                    key: 'question',
                    width: '300px',
                    ellipsis: true
                },
                {
                    title: '答案',
                    dataIndex: 'answer',
                    key: 'answer',
                    width: '200px',
                    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, // 添加答案获取状态标志
        },
        mounted() {
            // 保存 Vue 实例的引用,以便在其他函数中使用
            window.vue = this;
            this.initDragEvents();

            // 设置初始位置
            const box = document.querySelector('.rlBox');
            box.style.right = '10px';
            box.style.top = '10px';
        },
        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 ? '答案列表' : '答案列表 (等待获取...)';
            }
        },
        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');
                if (this.close) {
                    box.classList.add('minimized');
                } else {
                    box.classList.remove('minimized');
                }
            },
            passVideo(){
                let video = document.getElementsByTagName("video");
                if(video.length == 0){
                    log("您当前页面不存在视频,请先打开学习视频页面");
                }else{
                    document.getElementsByTagName("video")[0].currentTime=document.getElementsByTagName("video")[0].duration;
                    log("视频已秒刷完成!");
                }
            },
            exportExcel(){
                // 准备数据
                const data = this.answerList.map(item => ({
                    '序号': item.id,
                    '选项': item.options,
                    '题目': item.question,
                    '答案': Array.isArray(item.answer) ? item.answer.join('; ') : item.answer
                }));

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

                // 设置列宽
                const colWidths = {
                    '序号': 4,
                    '选项': 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 = ['日志已清除'];
            },
            // 优化拖动处理
            initDragEvents() {
                const box = document.querySelector('.rlBox');
                const dragZone = document.querySelector('.ant-card-head');
                let isDragging = false;
                let currentX;
                let currentY;
                let initialX;
                let initialY;

                const setTranslate = (xPos, yPos) => {
                    box.style.left = `${xPos}px`;
                    box.style.top = `${yPos}px`;
                };

                dragZone.addEventListener('mousedown', (e) => {
                    if (e.target.closest('.ant-btn')) return; // 避免按钮区域触发拖动

                    const rect = box.getBoundingClientRect();
                    isDragging = true;
                    initialX = e.clientX - rect.left;
                    initialY = e.clientY - rect.top;

                    box.style.transition = 'none';
                    box.style.cursor = 'grabbing';
                });

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

                    e.preventDefault();
                    currentX = e.clientX - initialX;
                    currentY = e.clientY - initialY;

                    // 边界检查
                    const maxX = window.innerWidth - box.offsetWidth;
                    const maxY = window.innerHeight - box.offsetHeight;

                    currentX = Math.min(Math.max(0, currentX), maxX);
                    currentY = Math.min(Math.max(0, currentY), maxY);

                    setTranslate(currentX, currentY);
                });

                document.addEventListener('mouseup', () => {
                    if (!isDragging) return;

                    isDragging = false;
                    box.style.transition = 'all 0.2s';
                    box.style.cursor = 'default';
                });
            }
        }
    });
}





// 初始化获取答案,延迟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);
}




// 脚本入口
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);
        }
    });
})

QingJ © 2025

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