在浙学题库搜索

搜索在浙学题库中的答案,进行展示,官网:pages.zaizhexue.top

As of 2024-12-09. See the latest version.

// ==UserScript==
// @name         在浙学题库搜索
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  搜索在浙学题库中的答案,进行展示,官网:pages.zaizhexue.top
// @author       Miaoz
// @match        *://www.zjooc.cn/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    var originalJson = {};
    const STORAGE_KEY_TOKEN = 'floating_window_token';
    

    // 初始化时从localStorage载入配置
    function loadSettings() {
        return {
            token: localStorage.getItem(STORAGE_KEY_TOKEN) || '',
            
        };
    }

    function saveSettings(settings) {
        localStorage.setItem(STORAGE_KEY_TOKEN, settings.token);

    }

    let settings = loadSettings();

    // 创建浮动窗口元素
    const windowDiv = document.createElement('div');
    windowDiv.style.position = 'fixed';
    windowDiv.style.top = '10px';
    windowDiv.style.left = '10px';
    windowDiv.style.width = '300px';
    windowDiv.style.height = '400px';
    windowDiv.style.border = '1px solid #000';
    windowDiv.style.backgroundColor = '#fff';
    windowDiv.style.zIndex = '10000';
    windowDiv.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
    windowDiv.style.overflow = 'hidden';

    // 创建header
    const header = document.createElement('div');
    header.style.height = '40px';
    header.style.backgroundColor = '#007bff';
    header.style.color = '#fff';
    header.style.padding = '10px';
    header.style.display = 'flex';
    header.style.justifyContent = 'space-between';
    header.style.cursor = 'move';
    header.innerHTML = '📖在浙学题库';

    // 创建最小化按钮
    const minimizeButton = document.createElement('button');
    minimizeButton.innerHTML = '-';
    minimizeButton.style.cursor = 'pointer';
    header.appendChild(minimizeButton);

    // 创建主体容器
    const contentDiv = document.createElement('div');
    contentDiv.style.display = 'flex';
    contentDiv.style.height = 'calc(100% - 40px)';

    // 创建导航栏
    const navBar = document.createElement('div');
    navBar.style.width = '80px';
    navBar.style.backgroundColor = '#f0f0f0';
    navBar.style.borderRight = '1px solid #ccc';

    // 创建示例页面链接
    const pages = ['首页', '设置'];

    pages.forEach(page => {
        const pageLink = document.createElement('div');
        pageLink.style.padding = '5px';
        pageLink.style.cursor = 'pointer';
        pageLink.textContent = page;
        pageLink.addEventListener('click', () => {
            if (page === '设置') {
                renderSettingsPage();
            }else if (page === '首页'){
                renderHomePage();
            }else if(page === '在线搜题'){
                //renderWebPage();
            }
            else {
                displayArea.innerHTML = page + ' 内容';
            }
        });
        navBar.appendChild(pageLink);
    });

    // 创建内容显示区域
    const displayArea = document.createElement('div');
    displayArea.style.flex = '1';
    displayArea.style.padding = '10px';
    displayArea.innerHTML = '内容显示';

    // 组装元素
    contentDiv.appendChild(navBar);
    contentDiv.appendChild(displayArea);
    windowDiv.appendChild(header);
    windowDiv.appendChild(contentDiv);
    document.body.appendChild(windowDiv);

    let isMinimized = false;

    // 最小化/还原功能
    minimizeButton.addEventListener('click', () => {
        if (!isMinimized) {
            contentDiv.style.display = 'none';
            windowDiv.style.height = '50px';
            minimizeButton.innerHTML = '+';
        } else {
            contentDiv.style.display = 'flex';
            windowDiv.style.height = '400px';
            minimizeButton.innerHTML = '-';
        }
        isMinimized = !isMinimized;
    });

    // 拖动功能
    let isDragging = false;
    let offsetX, offsetY;

    header.addEventListener('mousedown', (e) => {
        isDragging = true;
        offsetX = e.clientX - windowDiv.offsetLeft;
        offsetY = e.clientY - windowDiv.offsetTop;
        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
    });

    function onMouseMove(e) {
        if (isDragging) {
            windowDiv.style.left = `${e.clientX - offsetX}px`;
            windowDiv.style.top = `${e.clientY - offsetY}px`;
        }
    }

    function onMouseUp() {
        isDragging = false;
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('mouseup', onMouseUp);
    }

    // 设置页面
    function renderSettingsPage() {
    displayArea.innerHTML = '';

    // 创建并设置标签
    const tokenInputLabel = document.createElement('label');
    tokenInputLabel.textContent = 'Token: ';
    tokenInputLabel.style.display = 'block';
    tokenInputLabel.style.marginBottom = '5px';


    // 创建并设置输入框
        const tokenInput = document.createElement('input');
        tokenInput.type = 'text';
        tokenInput.style.width = '100%';
        tokenInput.style.marginBottom = '10px';
        tokenInput.style.padding = '10px';
        tokenInput.style.boxSizing = 'border-box';
        tokenInput.style.border = '2px solid #ccc';
        tokenInput.style.borderRadius = '4px';
        tokenInput.style.fontSize = '16px';
        tokenInput.style.outline = 'none'; // 移除默认 outline,下面我们会自定义聚焦颜色
        tokenInput.value = settings.token;

        // 添加聚焦时的样式变化
        tokenInput.addEventListener('focus', () => {
            tokenInput.style.border = '2px solid #4A90E2'; // 聚焦时改变边框颜色
        });

        tokenInput.addEventListener('blur', () => {
            tokenInput.style.border = '2px solid #ccc'; // 失去焦点时恢复边框颜色
        });

        // 创建提示信息显示区域
        const saveMessage = document.createElement('div');
        saveMessage.style.color = 'green';
        saveMessage.style.marginTop = '10px';

        // 创建保存按钮
        const saveButton = document.createElement('button');
        saveButton.textContent = '保存';
        saveButton.style.padding = '8px 16px';
        saveButton.style.marginTop = '10px';
        saveButton.addEventListener('click', saveToken);
        // 保存功能
        function saveToken() {
            const token = tokenInput.value;

            // 使用 fetch 发送 POST 请求到验证路由
            fetch('https://app.zaizhexue.top/validateToken', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ token: token })
            })
                .then(response => {
                if (response.ok) {
                    return response.json(); // 确保解析响应体
                } else {
                    // 如果响应失败,直接抛出或者处理错误
                    return response.json().then(data => {
                        return Promise.reject(data);
                    });
                }
            })
                .then(data => {
                // 如果响应成功
                settings.token = token;
                saveSettings(settings);
                saveMessage.textContent = 'Token 已保存并有效!';
            })
                .catch(error => {
                // 错误处理:展示响应中的错误信息
                console.error('Error:', error);
                saveMessage.style.color = 'red';
                if (error.error === 'Token expired') {
                    saveMessage.textContent = 'Token 已过期,请重新获取。';
                } else {
                    saveMessage.textContent = 'Token 无效,请重新输入。';
                }
            });

            // 3秒后清空提示信息
            setTimeout(() => {
                saveMessage.textContent = '';
                saveMessage.style.color = 'green'; // 重置颜色
            }, 3000);
        }

        // 保持 keydown 事件监听器不变
        tokenInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                saveToken();
            }
        });

        // 保持保存按钮添加事件监听不变
        saveButton.addEventListener('click', saveToken);



    // 将元素添加到 displayArea
    displayArea.appendChild(tokenInputLabel);
    displayArea.appendChild(tokenInput);
    displayArea.appendChild(saveButton);
    displayArea.appendChild(saveMessage);

    // 创建并设置描述文本或提示
        const infoText = document.createElement('p');
        infoText.innerHTML = '请访问 <a href="https://app.zaizhexue.top/" target="_blank" style="color: #4A90E2; text-decoration: none;">在浙学题库搜索网站</a> 登陆以获取 Token。所有题库查询功能都是免费的';
        infoText.style.marginTop = '20px';
        infoText.style.fontSize = '14px';
        infoText.style.color = '#555';

        // 为链接添加下划线效果,并在悬停时变色
        const linkStyle = document.createElement('style');
        linkStyle.textContent = `
  a:hover {
    text-decoration: underline;
    color: #0056b3;
  }
`;
        document.head.appendChild(linkStyle);

        displayArea.appendChild(infoText);
}
    function renderHomePage(){
        displayArea.innerHTML = '';

        const showAnswerbutton = document.createElement('button');
        showAnswerbutton.textContent = '显示答案';

        showAnswerbutton.style.bottom = '20px';
        showAnswerbutton.style.right = '20px';
        showAnswerbutton.style.padding = '10px 20px';
        showAnswerbutton.style.backgroundColor = '#007bff';
        showAnswerbutton.style.color = '#fff';
        showAnswerbutton.style.border = 'none';
        showAnswerbutton.style.borderRadius = '5px';
        showAnswerbutton.style.cursor = 'pointer';
        showAnswerbutton.style.zIndex = 1000;
        showAnswerbutton.addEventListener('click', displayRightAnswers);

        // 创建“自动播放视频”按钮(不可点击)
        const autoplayButton = document.createElement('button');
        autoplayButton.textContent = '自动播放视频(测试)';

        autoplayButton.style.top = '20px';
        autoplayButton.style.right = '120px';
        autoplayButton.style.padding = '10px 20px';
        autoplayButton.style.backgroundColor = '#007bff';
        autoplayButton.style.color = '#fff';
        autoplayButton.style.border = 'none';
        autoplayButton.style.borderRadius = '5px';
        autoplayButton.style.zIndex = 1000;
        autoplayButton.addEventListener('click', startAutomation);

        // 鼠标悬浮时显示提示
        autoplayButton.title = '尽情期待';

    
        
        displayArea.appendChild(showAnswerbutton);
        displayArea.appendChild(autoplayButton);
    };
    if(!settings.token){
        renderSettingsPage();
    }else{
        // 使用 fetch 发送 POST 请求到验证路由
            fetch('https://app.zaizhexue.top/validateToken', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ token: settings.token })
            })
                .then(response => {
                if (response.ok) {
                    return response.json(); // 确保解析响应体
                } else {
                    // 如果响应失败,直接抛出或者处理错误
                    return response.json().then(data => {
                        return Promise.reject(data);
                    });
                }
            })
                .then(data => {
                // 如果响应成功


            })
                .catch(error => {
                // 错误处理:展示响应中的错误信息
                console.error('Error:', error);

                if (error.error === 'Token expired') {
                    alert("token过期");
                    localStorage.setItem(STORAGE_KEY_TOKEN, "");
                    renderSettingsPage();
                } else {
                    alert("token无效");
                    renderSettingsPage();
                }
            });
        renderHomePage();
    }



    let cachedPaperSubjectList = null;
    let alerted = 0;
    // Function to recursively display rightAnswer in questions
    function displayAnswersRecursively(questions, subject, index) {

        // Check if subject has no rightAnswer and childrenList is empty

        if (!subject.rightAnswer && (!subject.childrenList || subject.childrenList.length === 0) && alerted == 0) {
            alert('请求中没有答案');
            alerted = 1;
            return; // Return the current index without further processing
        }

        if (subject.rightAnswer && questions[index]) {
            // Add the answer to the question
            const answerElement = document.createElement('div');
            if (subject.subjectType == 7) {
                console.log('填空题');
                // 处理填空题
                try {
                    let rawAnswer = subject.rightAnswer.replace(/\|/g, ',');
                    let parsedAnswers = JSON.parse(rawAnswer);
                    let formattedAnswers = ''; // 初始化结果字符串

                    // 遍历数组,按要求拼接内容
                    parsedAnswers.forEach(answerSet => {
                        if (Array.isArray(answerSet)) {
                            formattedAnswers += answerSet.join(',') + '\n'; // 子数组元素拼接后换行
                        } else {
                            formattedAnswers += answerSet + '\n'; // 单个元素直接换行
                        }
                    });

                    formattedAnswers = formattedAnswers.trim(); // 移除末尾多余换行符

                    // 设置最终结果,使用 innerHTML 并替换 \n 为 <br>
                    answerElement.innerHTML = `正确答案:<br>${formattedAnswers.replace(/\n/g, '<br>')}`;
                } catch (e) {
                    console.error('Failed to parse rightAnswer:', subject.rightAnswer, e);
                    answerElement.textContent = `正确答案: 格式错误`;
                }
            } else {
                // Handle other question types
                answerElement.textContent = `正确答案: ${subject.rightAnswer}`;
            }


            console.log(subject.rightAnswer);
            answerElement.style.color = 'green';
            answerElement.style.fontWeight = 'bold';
            answerElement.className = 'answer-display';
            questions[index].appendChild(answerElement);


        }

        // If childrenList is not empty, recursively process it
        if (subject.childrenList && Array.isArray(subject.childrenList)) {
            const subquestions = questions[index].querySelectorAll('.questiono-main')

            subject.childrenList.forEach((child, index) => {
                displayAnswersRecursively(subquestions, child, index);
            });
        }


    }

    // Function to display all answers


    function checkRightAnswer(obj) {
        if (!obj || !obj.data || !Array.isArray(obj.data.paperSubjectList)) {
            return false;
        }

        for (const subject of obj.data.paperSubjectList) {
            if ('rightAnswer' in subject) {
                return true;
            }
            if (subject.childrenList) {
                for (const child of subject.childrenList) {
                    if ('rightAnswer' in child) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    async function uploadOriginalJson() {
        console.log(originalJson);
        try {
            const response = await fetch('https://app.zaizhexue.top/upload', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(originalJson) // 确保发送的是JSON字符串
            });
            const data = await response.json();
            console.log('Success:', data);
        } catch (error) {
            console.error('Error:', error);
        }
    }

    async function fetchOriginalJson(id,needData = true) {
        try {
            console.log("正在发请求");
            const token = settings.token;
            const response = await fetch(`https://app.zaizhexue.top/originalJson?id=${id}&need_data=${needData}&token=${token}`);
            if (!response.ok) {
                throw new Error(`Error: ${response.statusText}`);
            }
            const data = await response.json();
            return data
        } catch (error) {
            console.error('Failed to fetch original JSON:', error);
            return null;
        }
    }
    async function displayRightAnswers() {
        const token = settings.token
        if(!token){
            alert("请先设置token");
            renderSettingsPage();
            return;
        }
        let newOriginalJson = null; // 在整个函数的作用域里声明
        if (!originalJson || !originalJson.data || typeof originalJson.data.id === 'undefined') {
            console.error('Invalid originalJson structure or missing id');
            alert("无法获取试卷ID,请退出试卷重新进入");
            return;
        }

        if(!checkRightAnswer(originalJson)){
            const paperId = originalJson.data.id; // Get the ID
            try {
                newOriginalJson = await fetchOriginalJson(paperId);

                if(newOriginalJson){
                    originalJson = newOriginalJson;
                }

            } catch (error) {
                console.error('Error fetching new originalJson:', error);
                alert("暂时没有答案");
                return;
            }
        }else{
            try {
                const paperId = originalJson.data.id;
                const response = await fetchOriginalJson(paperId, false); // 指定 need_data 参数为 false
                if (response && response.has_original_json) {
                    console.log('Paper exists with original JSON');
                    // 可以继续进行其他操作
                } else {
                    console.log('No paper or original JSON found, performing next step...');
                    // 执行下一步操作
                    uploadOriginalJson() // 如果不存在,则执行上传操作
                }
            } catch (error) {
                console.error('Error checking originalJson existence:', error);
                // 可选:执行其他错误处理逻辑
            }
        }

        if(!checkRightAnswer(originalJson)){
            alert("暂时没有答案");
            return;
        };


        
        // 根据 subjectType 对 cachedPaperSubjectList 排序
        const sortedSubjects = [...cachedPaperSubjectList].sort((a, b) => {
            return (a.subjectType || 0) - (b.subjectType || 0);
        });

        // 查询所有 .questiono-item 元素
        const questions = document.querySelectorAll('.questiono-item');

        // 如果存在 .questiono-item 元素
        if (questions.length > 0) {
            sortedSubjects.forEach((subject, index) => {
                console.log(subject);
                // 执行答案显示递归函数
                displayAnswersRecursively(questions, subject, index);
            });
        } else {
            // 如果没有找到 .questiono-item,查询 .index_box 并为 li 元素添加事件监听器
            const indexbox = document.querySelector('.index_box');
            const listItems = indexbox.querySelectorAll('li');
            listItems.forEach((li, index) => {
                if (li.classList.contains('show_select')) {
                    // 找到 class 为 show_select 的 li
                    handleClick(index, sortedSubjects[index]);
                }
            });
            if (indexbox) {
                const listItems = indexbox.querySelectorAll('li');

                listItems.forEach((li, index) => {
                    li.addEventListener('click', () => {
                        handleClick(index,sortedSubjects[index]);
                    });
                });
            } else {
                console.error('没有找到 .index_box 元素');
            }
        }
    };

    // 你想执行的函数
    function handleClick(index, subject) {
        console.log(`第 ${index + 1} 个 li 被点击了`);

         // 先清除上一个显示的答案
        const answerElements = document.querySelectorAll('.answer-display');
        answerElements.forEach(el => el.remove()); // 删除所有已有答案
        const question_content = document.querySelector('.question_content');
        const answerElement = document.createElement('div');
            if (subject.subjectType == 7) {
                console.log('填空题');
                // 处理填空题
                try {
                    let rawAnswer = subject.rightAnswer.replace(/\|/g, ',');
                    let parsedAnswers = JSON.parse(rawAnswer);
                    let formattedAnswers = ''; // 初始化结果字符串

                    // 遍历数组,按要求拼接内容
                    parsedAnswers.forEach(answerSet => {
                        if (Array.isArray(answerSet)) {
                            formattedAnswers += answerSet.join(',') + '\n'; // 子数组元素拼接后换行
                        } else {
                            formattedAnswers += answerSet + '\n'; // 单个元素直接换行
                        }
                    });

                    formattedAnswers = formattedAnswers.trim(); // 移除末尾多余换行符

                    // 设置最终结果,使用 innerHTML 并替换 \n 为 <br>
                    answerElement.innerHTML = `正确答案:<br>${formattedAnswers.replace(/\n/g, '<br>')}`;
                } catch (e) {
                    console.error('Failed to parse rightAnswer:', subject.rightAnswer, e);
                    answerElement.textContent = `正确答案: 格式错误`;
                }
            } else {
                // Handle other question types
                answerElement.textContent = `正确答案: ${subject.rightAnswer}`;
            };
        answerElement.style.color = 'green';
        answerElement.style.fontWeight = 'bold';
        answerElement.className = 'answer-display';

        if (subject.childrenList && Array.isArray(subject.childrenList)) {
            subject.childrenList.forEach(child => {
                answerElement.textContent += `<br>子题答案: ${child.rightAnswer}`;
            });
        }
        
        
        question_content.appendChild(answerElement)

        // 你可以在这里执行其他操作
    };

    // 入口函数
    function startAutomation() {
        console.log('页面加载完成,开始寻找未完成的任务...');

        const spans = document.querySelectorAll('span.of_eno');
        const filteredSpans = Array.from(spans).filter(
            span => span.parentElement.tagName.toLowerCase() === 'li'
        );

        function clickSpanWithDelay(index) {
            if (index >= filteredSpans.length) {
                console.log('所有任务已处理完毕。');
                return; // 如果遍历完成,退出
            }

            console.log(`点击第 ${index + 1} 个任务`);
            filteredSpans[index].click(); // 点击任务

            // 查找并点击未完成的任务
            setTimeout(() => {
                clickUncompletedTasks(() => {
                    // 继续处理下一个 span
                    clickSpanWithDelay(index + 1);
                });
            }, 3000); // 等待3秒再继续处理下一个任务
        }

        function clickUncompletedTasks(callback) {
            const allITags = document.getElementsByTagName('i');
            const spansWithIconFont = Array.from(allITags).filter(tag =>
                tag.classList.contains('iconfont') &&
                tag.parentElement.tagName === 'SPAN'
            );

            const uncompletedTasks = spansWithIconFont.filter(
                tag => !tag.classList.contains('complete')
            ).map(tag => tag.parentElement);

            if (uncompletedTasks.length > 0) {
                console.log(`找到 ${uncompletedTasks.length} 个未完成的任务`);
                handleTasksSequentially(uncompletedTasks, 0, callback);
            } else {
                console.log('所有任务已完成或没有找到未完成的任务。');
                callback(); // 调用回调函数继续下一个任务
            }
        }

        function handleTasksSequentially(tasks, index, callback) {
            if (index >= tasks.length) {
                console.log('所有未完成的任务已处理完毕。');
                callback(); // 继续下一个 span
                return;
            }

            console.log(`处理第 ${index + 1} 个未完成的任务`);
            tasks[index].click(); // 点击任务

            // 等待3秒后尝试点击完成学习按钮
            setTimeout(() => {
                clickCompletionButton(() => {
                    // 处理下一个未完成的任务
                    handleTasksSequentially(tasks, index + 1, callback);
                });
            }, 3000);
        }

        function clickCompletionButton(callback) {
            playVideoAndCheckEnd(() => {
                const button = [...document.querySelectorAll('button')]
                    .find(btn => btn.textContent.includes('完成学习'));

                if (button) {
                    button.disabled = false; // 启用按钮
                    console.log('找到"完成学习"按钮,自动点击...');
                    button.click();
                    setTimeout(callback, 2000); // 等待2秒后继续
                } else {
                    console.log('未找到"完成学习"按钮,直接继续...');
                    setTimeout(callback, 2000); // 即使未找到,也继续下一个任务
                }
            });
        }

        function playVideoAndCheckEnd(callback) {
            const videoElement = document.querySelector('video');
            if (!videoElement) {
                console.log('未找到视频元素');
                callback(); // 没有视频时,直接继续
                return;
            } else {
                videoElement.playbackRate = 4.0; // 设置播放速度为4倍速
            }

            videoElement.addEventListener('ended', function () {
                console.log('视频播放完毕');
                callback(); // 播放完毕后执行回调
            });

            videoElement.addEventListener('error', function () {
                console.log('视频播放出错,继续下一个任务');
                callback(); // 即使出错,也继续执行
            });

            videoElement.play().catch((error) => {
                console.log('无法播放视频:', error);
                callback(); // 无法播放时也继续执行
            });
        }

        setTimeout(() => clickSpanWithDelay(0), 2000); // 页面加载2秒后开始
    }


    // Monitor XMLHttpRequest
    (function(open) {
        XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
            this.addEventListener('load', function() {
                try {
                    const response = JSON.parse(this.responseText);

                    if (response.data && response.data.paperSubjectList) {
                        cachedPaperSubjectList = response.data.paperSubjectList;
                        originalJson = response;
                        if (originalJson && originalJson.data && originalJson.data.hasOwnProperty('stuName')) {
                            delete originalJson.data.stuName; // 删除 stuName 键
                        }

                    }
                } catch (e) {
                    console.log('Error parsing or no paperSubjectList found');
                }
            });
            open.apply(this, arguments);
        };
    })(XMLHttpRequest.prototype.open);

    // Monitor fetch requests
    const originalFetch = window.fetch;
    window.fetch = function(input, init) {
        console.log('Fetch Request Sent: ', input, init);
        return originalFetch(input, init)
            .then(response => {
                return response.clone().json().then(data => {
                    if (data.data && data.data.paperSubjectList) {
                        console.log('paperSubjectList:', data.data.paperSubjectList);
                        cachedPaperSubjectList = data.data.paperSubjectList;
                    }
                    return response;
                }).catch(() => response);
            });
    };

    // Wait for the page to load and create the button
    window.addEventListener('load');
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。