// ==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 = `
题库管理 v3.9.1

统计信息

总题目数: 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); } } } })();