// ==UserScript==
// @name 黄淮学院简单答题助手v1
// @namespace http://tampermonkey.net/
// @version 7.0
// @description 自动选择答案 - 显示题目、答案和选项
// @author You
// @match https://huanghuai.jijiaox.com/*
// @match http://huanghuai.jijiaox.com/*
// @include *://huanghuai.jijiaox.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
let answersData = null;
// 拦截XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._url = url;
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
const xhr = this;
const originalOnReadyStateChange = xhr.onreadystatechange;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
if (xhr._url && xhr._url.includes('/api/studycenter/classroom/praxise')) {
try {
const response = JSON.parse(xhr.responseText);
if (response.code === 0 && response.data) {
answersData = response.data;
updateStatus('已获取答案数据');
}
} catch (e) {
// 解析失败
}
}
}
if (originalOnReadyStateChange) {
originalOnReadyStateChange.apply(this, arguments);
}
};
return originalSend.apply(this, arguments);
};
// 拦截fetch
const originalFetch = window.fetch;
window.fetch = function(...args) {
const [url] = args;
return originalFetch.apply(this, args).then(response => {
if (url && url.includes('/api/studycenter/classroom/praxise')) {
response.clone().json().then(data => {
if (data.code === 0 && data.data) {
answersData = data.data;
updateStatus('已获取答案数据');
}
}).catch(e => {
// 解析失败
});
}
return response;
});
};
let statusDiv = null;
function updateStatus(message) {
if (statusDiv) {
statusDiv.textContent = message;
}
}
// 等待页面加载
setTimeout(function() {
// 创建主界面
const container = document.createElement('div');
container.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
z-index: 10000;
background: white;
border: 2px solid #4CAF50;
border-radius: 5px;
padding: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
width: 350px;
cursor: move;
`;
container.innerHTML = `
<div id="drag-handle" style="margin: -10px -10px 10px -10px; padding: 10px; background: #4CAF50; color: white; border-radius: 5px 5px 0 0; cursor: move; user-select: none;">
<h4 style="margin: 0; text-align: center;">答题助手 v1</h4>
</div>
<div id="status" style="margin-bottom: 10px; color: #666;">等待答案加载...</div>
<label style="display: block; margin-bottom: 5px; cursor: pointer;">
<input type="checkbox" id="clearSelected" checked> 清除已选答案
</label>
<div style="font-size: 11px; color: #666; margin-bottom: 10px; padding: 8px; background: #e3f2fd; border-left: 3px solid #2196F3; border-radius: 3px;">
<strong>提示:</strong>勾选后会先清除多选题的错误选项,再选择正确答案。<br>
如果不勾选,会保留之前的选择(可能包含错误选项)。建议保持勾选。
</div>
<button id="doAnswer" style="background: #4CAF50; color: white; border: none; padding: 8px 15px; cursor: pointer; width: 100%; margin-bottom: 10px;">开始答题</button>
<div id="loading" style="display: none; text-align: center; margin: 10px 0;">
<style>
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loader {
border: 3px solid #f3f3f3;
border-top: 3px solid #4CAF50;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
display: inline-block;
}
</style>
<div class="loader"></div>
<div id="progress" style="margin-top: 10px; color: #666; font-size: 14px;">正在答题中...</div>
</div>
<div id="result" style="margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 5px; max-height: 300px; overflow-y: auto; display: none; word-break: break-word;"></div>
`;
document.body.appendChild(container);
statusDiv = document.getElementById('status');
// 添加拖动功能
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
const dragHandle = document.getElementById('drag-handle');
// 鼠标按下
dragHandle.addEventListener('mousedown', function(e) {
isDragging = true;
offsetX = e.clientX - container.offsetLeft;
offsetY = e.clientY - container.offsetTop;
container.style.cursor = 'grabbing';
e.preventDefault();
});
// 鼠标移动
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
e.preventDefault();
// 计算新位置
let newX = e.clientX - offsetX;
let newY = e.clientY - offsetY;
// 限制在窗口内
newX = Math.max(0, Math.min(newX, window.innerWidth - container.offsetWidth));
newY = Math.max(0, Math.min(newY, window.innerHeight - container.offsetHeight));
container.style.left = newX + 'px';
container.style.top = newY + 'px';
container.style.right = 'auto';
});
// 鼠标释放
document.addEventListener('mouseup', function() {
if (isDragging) {
isDragging = false;
container.style.cursor = 'move';
}
});
// 防止拖动时选中文本
container.addEventListener('selectstart', function(e) {
if (isDragging) {
e.preventDefault();
}
});
// 初始状态
if (answersData) {
updateStatus(`已获取 ${answersData.length} 道题答案`);
}
// 主答题功能
document.getElementById('doAnswer').onclick = async function() {
const resultDiv = document.getElementById('result');
const loadingDiv = document.getElementById('loading');
const progressDiv = document.getElementById('progress');
const button = this;
// 隐藏结果,显示loading
resultDiv.style.display = 'none';
loadingDiv.style.display = 'block';
button.disabled = true;
button.style.opacity = '0.6';
if (!answersData) {
loadingDiv.style.display = 'none';
resultDiv.style.display = 'block';
resultDiv.innerHTML = '<span style="color: red;">未获取到答案数据</span>';
button.disabled = false;
button.style.opacity = '1';
return;
}
try {
let successCount = 0;
let failCount = 0;
let results = [];
// 获取所有题目
const allQuestions = document.querySelectorAll('div.mt_2');
const validQuestions = [];
// 过滤有效题目
allQuestions.forEach((q) => {
const titleType = q.querySelector('.titleType');
const questionText = q.querySelector('.ml_2.mt_1[style*="color"]');
const hasOptions = q.querySelectorAll('.el-radio, .el-checkbox').length > 0;
if (titleType && questionText && hasOptions) {
validQuestions.push(q);
}
});
// 按顺序处理
const minLength = Math.min(validQuestions.length, answersData.length);
// 使用async/await处理答题
for (let i = 0; i < minLength; i++) {
// 更新进度
progressDiv.textContent = `正在答题中... (${i + 1}/${minLength})`;
const q = validQuestions[i];
const answer = answersData[i];
const questionType = answer.praxise.type;
const questionTextElement = q.querySelector('.ml_2.mt_1[style*="color"]');
const questionText = questionTextElement ? questionTextElement.textContent.trim() : '';
let selectedCount = 0;
let selectedOptions = [];
if (questionType === 'muti') {
// 多选题处理
const checkboxes = q.querySelectorAll('.el-checkbox');
try {
// 解析答案 "[1,2,3]"
const answerArray = JSON.parse(answer.praxise.answer);
// 清除已选(如果需要)
if (document.getElementById('clearSelected').checked) {
const checkedBoxes = q.querySelectorAll('.el-checkbox.is-checked');
for (const box of checkedBoxes) {
box.click();
await new Promise(resolve => setTimeout(resolve, 50));
}
}
// 选择答案
for (const ansIdx of answerArray) {
const targetIdx = ansIdx - 1;
if (targetIdx >= 0 && targetIdx < checkboxes.length) {
const checkbox = checkboxes[targetIdx];
if (!checkbox.classList.contains('is-checked')) {
checkbox.click();
await new Promise(resolve => setTimeout(resolve, 100));
}
// 获取选项文本
const optionText = checkboxes[targetIdx].querySelector('.el-checkbox__label');
if (optionText) {
selectedOptions.push(optionText.textContent.trim());
}
selectedCount++;
}
}
if (selectedCount > 0) {
successCount++;
results.push({
status: '✅',
index: i + 1,
type: '多选',
question: questionText.substring(0, 50) + (questionText.length > 50 ? '...' : ''),
selectedOptions: selectedOptions,
answerValue: answer.praxise.answer
});
} else {
failCount++;
results.push({
status: '❌',
index: i + 1,
type: '多选',
question: questionText.substring(0, 50) + (questionText.length > 50 ? '...' : ''),
selectedOptions: [],
answerValue: answer.praxise.answer,
error: '未找到选项'
});
}
} catch (e) {
failCount++;
results.push({
status: '❌',
index: i + 1,
type: '多选',
question: questionText.substring(0, 50) + (questionText.length > 50 ? '...' : ''),
selectedOptions: [],
answerValue: answer.praxise.answer,
error: '解析答案失败'
});
}
} else if (questionType === 'single' || questionType === 'charge') {
// 单选题和判断题
const radios = q.querySelectorAll('.el-radio');
let answerIndex;
// 判断题特殊处理
if (questionType === 'charge') {
// 判断题: 1 = 正确(第一个选项), -1 = 错误(第二个选项)
answerIndex = answer.praxise.answer === "1" ? 0 : 1;
} else {
// 单选题: 正常处理
answerIndex = parseInt(answer.praxise.answer) - 1;
}
if (answerIndex >= 0 && answerIndex < radios.length) {
if (!radios[answerIndex].classList.contains('is-checked')) {
radios[answerIndex].click();
await new Promise(resolve => setTimeout(resolve, 50));
}
// 获取选中的选项文本
const optionText = radios[answerIndex].querySelector('.el-radio__label');
if (optionText) {
selectedOptions.push(optionText.textContent.trim());
}
successCount++;
results.push({
status: '✅',
index: i + 1,
type: questionType === 'single' ? '单选' : '判断',
question: questionText.substring(0, 50) + (questionText.length > 50 ? '...' : ''),
selectedOptions: selectedOptions,
answerValue: answer.praxise.answer
});
} else {
failCount++;
results.push({
status: '❌',
index: i + 1,
type: questionType === 'single' ? '单选' : '判断',
question: questionText.substring(0, 50) + (questionText.length > 50 ? '...' : ''),
selectedOptions: [],
answerValue: answer.praxise.answer,
error: '未找到选项'
});
}
}
// 每题之间增加延迟,避免卡顿
if (i < minLength - 1) {
await new Promise(resolve => setTimeout(resolve, 200));
}
}
// 显示结果
let html = `
<h5>答题完成</h5>
<p>成功: <span style="color: green;">${successCount}</span> | 失败: <span style="color: red;">${failCount}</span></p>
<div style="font-size: 12px; max-height: 150px; overflow-y: auto;">
`;
results.forEach(r => {
html += `
<div style="margin-bottom: 10px; padding: 5px; background: ${r.status === '✅' ? '#e8f5e9' : '#ffebee'}; border-radius: 3px; overflow: hidden;">
<div>${r.status} 题${r.index} [${r.type}]</div>
<div style="color: #666; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">题目: ${r.question}</div>
<div style="color: #2196F3; font-size: 11px;">答案值: ${r.answerValue}</div>
${r.selectedOptions.length > 0 ?
`<div style="color: #4CAF50; font-size: 11px; white-space: normal; word-break: break-all;">已选: ${r.selectedOptions.join(', ')}</div>` :
`<div style="color: #f44336; font-size: 11px;">错误: ${r.error || '未知'}</div>`
}
</div>
`;
});
html += '</div>';
resultDiv.innerHTML = html;
} catch (e) {
resultDiv.innerHTML = `<span style="color: red;">错误: ${e.message}</span>`;
} finally {
// 隐藏loading,显示结果
loadingDiv.style.display = 'none';
resultDiv.style.display = 'block';
button.disabled = false;
button.style.opacity = '1';
}
};
}, 2000);
})();