// ==UserScript==
// @name 文理文理答题
// @namespace http://tampermonkey.net/
// @version 4.0
// @description 支持ABCDEF多选题答案
// @author You
// @match *://*.ytccr.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_download
// @run-at document-end
// @require https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
// ==/UserScript==
(function() {
'use strict';
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
setTimeout(init, 1000);
}
function init() {
if (window.answerSystemEnhanced) return;
window.answerSystemEnhanced = true;
try {
console.log('文理答题系统增强版初始化...');
const questionBank = new QuestionBank();
const controlPanel = new ControlPanel(questionBank);
setTimeout(() => {
controlPanel.updateDisplay();
if (document.querySelector('.table-list')) {
console.log('检测到结果页面,准备自动记录答案...');
setTimeout(() => {
const hasRecorded = GM_getValue('auto_recorded', false);
if (!hasRecorded) {
controlPanel.recordAnswers();
GM_setValue('auto_recorded', true);
}
}, 2000);
}
}, 1500);
window.addEventListener('beforeunload', () => {
GM_setValue('auto_recorded', false);
});
} catch (error) {
console.error('答题系统初始化失败:', error);
}
}
class QuestionBank {
constructor() {
try {
this.bank = GM_getValue('question_bank', {});
this.stats = GM_getValue('question_stats', {
totalQuestions: 0,
answeredQuestions: 0,
unknownQuestions: 0,
lastUpdate: ''
});
} catch (error) {
console.error('初始化题库失败:', error);
this.bank = {};
this.stats = {
totalQuestions: 0,
answeredQuestions: 0,
unknownQuestions: 0,
lastUpdate: ''
};
}
}
addQuestion(question, answer, type = 'choice') {
const key = this.normalizeQuestion(question);
if (typeof answer === 'string' && answer.length > 1) {
type = 'multi';
}
if (!this.bank[key]) {
this.bank[key] = {
answer: answer,
type: type,
createdAt: new Date().toISOString()
};
this.saveBank();
return true;
} else {
this.bank[key].answer = answer;
this.bank[key].type = type;
this.saveBank();
return false;
}
}
findAnswer(question) {
const key = this.normalizeQuestion(question);
return this.bank[key] || null;
}
normalizeQuestion(question) {
return question.replace(/\s+/g, ' ').trim().replace(/ /g, ' ');
}
saveBank() {
try {
GM_setValue('question_bank', this.bank);
} catch (error) {
console.error('保存题库失败:', error);
}
}
exportBank() {
try {
const data = JSON.stringify(this.bank, null, 2);
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `question_bank_${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('导出题库失败:', error);
alert('导出失败,请检查控制台错误信息');
}
}
importBank(file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const importedBank = JSON.parse(e.target.result);
this.bank = { ...this.bank, ...importedBank };
this.saveBank();
this.updateStats();
alert('题库导入成功!');
} catch (error) {
alert('导入失败:文件格式错误');
}
};
reader.readAsText(file);
}
updateStats() {
try {
const questionElements = document.querySelectorAll('.title.qa-title');
let total = questionElements.length;
let answered = 0;
let unknown = 0;
questionElements.forEach((element) => {
const questionText = this.normalizeQuestion(element.textContent.trim());
if (this.findAnswer(questionText)) {
answered++;
} else {
unknown++;
}
});
this.stats = {
totalQuestions: total,
answeredQuestions: answered,
unknownQuestions: unknown,
lastUpdate: new Date().toLocaleString()
};
GM_setValue('question_stats', this.stats);
return this.stats;
} catch (error) {
console.error('更新统计失败:', error);
return this.stats;
}
}
getStats() {
return this.updateStats();
}
extractCorrectAnswerFromResult(questionElement) {
try {
const parentItem = questionElement.closest('.border-item');
if (!parentItem) return null;
const table = parentItem.querySelector('.table-list');
if (!table) return null;
const resultRows = table.querySelectorAll('tr');
if (!resultRows || resultRows.length === 0) return null;
let resultText = '';
for (let row of resultRows) {
const rowText = row.textContent || '';
if (rowText.includes('正确答案')) {
resultText = rowText;
break;
}
}
if (!resultText) return null;
// 修改正则表达式以支持ABCDEF
const match = resultText.match(/正确答案为\s*([A-F]+)/i);
if (match && match[1]) {
return match[1].toUpperCase();
}
return null;
} catch (error) {
console.error('提取正确答案失败:', error);
return null;
}
}
getQuestionType(questionElement) {
try {
const parentContent = questionElement.closest('.content');
if (!parentContent) return 'unknown';
const optionCheckboxes = parentContent.querySelectorAll('label.el-checkbox');
const optionRadios = parentContent.querySelectorAll('label.el-radio');
if (optionCheckboxes.length > 0) return 'checkbox';
if (optionRadios.length > 0) return 'radio';
return 'unknown';
} catch (error) {
console.error('判断题目类型失败:', error);
return 'unknown';
}
}
}
class ControlPanel {
constructor(questionBank) {
this.questionBank = questionBank;
this.panel = null;
this.isDragging = false;
this.offset = { x: 0, y: 0 };
this.createPanel();
}
createPanel() {
try {
const existingPanel = document.getElementById('auto-answer-panel');
if (existingPanel) {
existingPanel.remove();
}
this.panel = document.createElement('div');
this.panel.id = 'auto-answer-panel';
this.panel.innerHTML = `
统计信息
总题目数:
0
已收录:
0
未收录:
0
最后更新:
-
`;
this.addStyles();
document.body.appendChild(this.panel);
this.bindEvents();
this.updateDisplay();
} catch (error) {
console.error('创建控制面板失败:', error);
}
}
addStyles() {
const styles = `
#auto-answer-panel {
position: fixed;
top: 100px;
right: 20px;
width: 300px;
background: white;
border: 2px solid #409EFF;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 12px;
}
.panel-header {
background: #409EFF;
color: white;
padding: 10px 15px;
cursor: move;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 6px 6px 0 0;
user-select: none;
}
.close-btn {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
}
.panel-content {
padding: 15px;
}
.stats {
margin-bottom: 15px;
}
.stats h4 {
margin: 0 0 10px 0;
color: #333;
font-size: 14px;
}
.stat-item {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
font-size: 12px;
}
.stat-item span:first-child {
color: #666;
}
.stat-item span:last-child {
color: #409EFF;
font-weight: bold;
}
.actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.actions button {
padding: 8px 12px;
border: 1px solid #409EFF;
background: white;
color: #409EFF;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
}
.actions button:hover {
background: #409EFF;
color: white;
}
#auto-answer-btn, #record-answers-btn {
grid-column: 1 / -1;
}
#clear-bank-btn {
background: #ff4d4f;
color: white;
border-color: #ff4d4f;
}
#clear-bank-btn:hover {
background: #ff7875;
}
`;
const styleSheet = document.createElement('style');
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
}
bindEvents() {
try {
const header = this.panel.querySelector('.panel-header');
header.addEventListener('mousedown', this.startDrag.bind(this));
this.panel.querySelector('.close-btn').addEventListener('click', () => {
this.panel.style.display = 'none';
});
const autoAnswerBtn = this.panel.querySelector('#auto-answer-btn');
autoAnswerBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log('自动答题按钮被点击');
this.autoAnswer();
});
this.panel.querySelector('#record-answers-btn').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.recordAnswers();
});
this.panel.querySelector('#export-btn').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.questionBank.exportBank();
});
this.panel.querySelector('#import-btn').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
document.getElementById('import-input').click();
});
this.panel.querySelector('#clear-bank-btn').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (confirm('确定要清空题库吗?此操作不可恢复!')) {
this.questionBank.bank = {};
this.questionBank.saveBank();
this.updateDisplay();
alert('题库已清空!');
}
});
document.getElementById('import-input').addEventListener('change', (e) => {
if (e.target.files.length > 0) {
this.questionBank.importBank(e.target.files[0]);
this.updateDisplay();
}
});
} catch (error) {
console.error('绑定事件失败:', error);
}
}
startDrag(e) {
if (e.target.classList.contains('close-btn')) return;
this.isDragging = true;
const rect = this.panel.getBoundingClientRect();
this.offset.x = e.clientX - rect.left;
this.offset.y = e.clientY - rect.top;
document.addEventListener('mousemove', this.drag.bind(this));
document.addEventListener('mouseup', this.stopDrag.bind(this));
}
drag(e) {
if (!this.isDragging) return;
const x = e.clientX - this.offset.x;
const y = e.clientY - this.offset.y;
this.panel.style.left = x + 'px';
this.panel.style.top = y + 'px';
this.panel.style.right = 'auto';
}
stopDrag() {
this.isDragging = false;
document.removeEventListener('mousemove', this.drag.bind(this));
document.removeEventListener('mouseup', this.stopDrag.bind(this));
}
updateDisplay() {
try {
const stats = this.questionBank.getStats();
document.getElementById('total-questions').textContent = stats.totalQuestions;
document.getElementById('answered-questions').textContent = stats.answeredQuestions;
document.getElementById('unknown-questions').textContent = stats.unknownQuestions;
document.getElementById('last-update').textContent = stats.lastUpdate;
} catch (error) {
console.error('更新显示失败:', error);
}
}
// 自动答题功能
autoAnswer() {
try {
console.log('开始自动答题...');
const questions = document.querySelectorAll('.title.qa-title');
let answeredCount = 0;
if (questions.length === 0) {
alert('未找到任何题目!');
return;
}
console.log(`找到 ${questions.length} 道题目`);
// 使用Promise链确保顺序执行
const answerQuestionsSequentially = async () => {
for (let i = 0; i < questions.length; i++) {
const questionElement = questions[i];
const questionText = this.questionBank.normalizeQuestion(
questionElement.textContent.trim()
);
console.log(`处理题目 ${i + 1}: "${questionText.substring(0, 30)}..."`);
const questionData = this.questionBank.findAnswer(questionText);
if (questionData) {
console.log(`找到答案: ${questionData.answer}, 类型: ${questionData.type}`);
if (questionData.type === 'multi') {
const success = await this.answerCheckboxQuestion(questionElement, questionData.answer);
if (success) {
answeredCount++;
console.log(`✅ 多选题答题成功`);
} else {
console.log(`❌ 多选题答题失败`);
}
} else {
const success = this.answerRadioQuestion(questionElement, questionData.answer);
if (success) {
answeredCount++;
console.log(`✅ 单选题答题成功`);
} else {
console.log(`❌ 单选题答题失败`);
}
}
// 每题之间延迟
await new Promise(resolve => setTimeout(resolve, 300));
} else {
console.log(`❌ 未找到题目答案`);
}
}
return answeredCount;
};
answerQuestionsSequentially().then((count) => {
this.updateDisplay();
alert(`自动答题完成!已回答 ${count} 道题目`);
});
} catch (error) {
console.error('自动答题失败:', error);
alert('自动答题过程中出现错误: ' + error.message);
}
}
// 单选题答题方法
answerRadioQuestion(questionElement, answer) {
try {
const parentContent = questionElement.closest('.content');
if (!parentContent) {
console.log('未找到父级content元素');
return false;
}
const answerValue = answer.toString().trim().toUpperCase();
console.log(`单选题答案: ${answerValue}`);
const optionLabels = parentContent.querySelectorAll('label.el-radio');
console.log(`找到 ${optionLabels.length} 个单选选项`);
if (optionLabels.length === 0) {
console.log('未找到单选选项元素');
return false;
}
// 修改为支持ABCDEF的选项索引
const optionIndex = 'ABCDEF'.indexOf(answerValue);
console.log(`答案索引: ${answerValue} -> ${optionIndex}`);
if (optionIndex >= 0 && optionIndex < optionLabels.length) {
const targetLabel = optionLabels[optionIndex];
console.log(`尝试选择第 ${optionIndex + 1} 个单选选项`);
// 直接点击选项
targetLabel.click();
console.log(`成功选择单选答案: ${answerValue}`);
return true;
} else {
console.log(`答案索引超出范围: ${optionIndex}, 选项数量: ${optionLabels.length}`);
}
return false;
} catch (error) {
console.error('单选题答题失败:', error);
return false;
}
}
// 多选题答题方法 - 支持ABCDEF
async answerCheckboxQuestion(questionElement, answer) {
try {
const parentContent = questionElement.closest('.content');
if (!parentContent) {
console.log('未找到父级content元素');
return false;
}
const answerValues = answer.toString().trim().toUpperCase();
console.log(`多选题答案: ${answerValues}`);
const optionLabels = parentContent.querySelectorAll('label.el-checkbox');
console.log(`找到 ${optionLabels.length} 个多选选项`);
if (optionLabels.length === 0) {
console.log('未找到多选选项元素');
return false;
}
const answers = answerValues.split('');
console.log(`需要选择的多选答案: ${answers.join(', ')}`);
let successCount = 0;
// 逐个选择答案,确保每个选项都有足够的时间处理
for (let i = 0; i < answers.length; i++) {
const answerChar = answers[i];
// 修改为支持ABCDEF的选项索引
const optionIndex = 'ABCDEF'.indexOf(answerChar);
if (optionIndex >= 0 && optionIndex < optionLabels.length) {
const targetLabel = optionLabels[optionIndex];
// 等待前一个选项处理完成
await new Promise(resolve => setTimeout(resolve, 200));
console.log(`选择多选答案: ${answerChar} (选项 ${optionIndex + 1})`);
// 执行双重点击确保选中
const isSelected = await this.ensureCheckboxSelected(targetLabel);
if (isSelected) {
successCount++;
console.log(`✅ 成功选择多选答案: ${answerChar}`);
} else {
console.log(`❌ 选择多选答案 ${answerChar} 失败`);
}
} else {
console.log(`❌ 无效的多选答案: ${answerChar}, 选项索引: ${optionIndex}, 选项数量: ${optionLabels.length}`);
}
}
console.log(`多选题选择结果: 成功选择 ${successCount} 个选项,需要选择 ${answers.length} 个选项`);
return successCount === answers.length;
} catch (error) {
console.error('多选题答题失败:', error);
return false;
}
}
// 确保复选框被选中 - 核心修复方法
async ensureCheckboxSelected(labelElement) {
return new Promise((resolve) => {
let attemptCount = 0;
const maxAttempts = 3;
const trySelect = () => {
attemptCount++;
// 获取复选框状态
const checkbox = labelElement.querySelector('input[type="checkbox"]');
const wasChecked = checkbox ? checkbox.checked : false;
console.log(`尝试 ${attemptCount}/${maxAttempts}: 当前状态 ${wasChecked ? '已选中' : '未选中'}`);
// 点击标签
labelElement.click();
// 等待状态更新
setTimeout(() => {
const isNowChecked = checkbox ? checkbox.checked : false;
if (isNowChecked && !wasChecked) {
// 成功从未选中变为选中
console.log('✅ 点击成功,状态已更新');
resolve(true);
} else if (isNowChecked && wasChecked) {
// 原本就是选中的
console.log('✅ 原本已选中');
resolve(true);
} else if (!isNowChecked && wasChecked) {
// 从选中变为未选中,需要再次点击
console.log('🔄 状态反转,需要再次点击');
labelElement.click();
setTimeout(() => {
const finalChecked = checkbox ? checkbox.checked : false;
if (finalChecked) {
console.log('✅ 第二次点击后成功选中');
resolve(true);
} else {
console.log('❌ 第二次点击后仍未选中');
if (attemptCount < maxAttempts) {
trySelect();
} else {
resolve(false);
}
}
}, 100);
} else {
// 点击后仍未选中
console.log('❌ 点击后仍未选中');
if (attemptCount < maxAttempts) {
trySelect();
} else {
resolve(false);
}
}
}, 150);
};
trySelect();
});
}
// 记录答案功能
recordAnswers() {
try {
const questions = document.querySelectorAll('.title.qa-title');
let recordedCount = 0;
let updatedCount = 0;
if (questions.length === 0) {
alert('未找到任何题目!');
return;
}
questions.forEach((questionElement, index) => {
const questionText = this.questionBank.normalizeQuestion(
questionElement.textContent.trim()
);
const correctAnswer = this.questionBank.extractCorrectAnswerFromResult(questionElement);
if (correctAnswer) {
const type = correctAnswer.length > 1 ? 'multi' : 'choice';
const isNew = this.questionBank.addQuestion(questionText, correctAnswer, type);
if (isNew) {
recordedCount++;
} else {
updatedCount++;
}
}
});
this.updateDisplay();
alert(`记录完成!新增 ${recordedCount} 道题目,更新 ${updatedCount} 道题目`);
} catch (error) {
console.error('记录答案失败:', error);
alert('记录答案过程中出现错误: ' + error.message);
}
}
}
})();