// ==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');
})();