// ==UserScript== // @name 【自制】问卷星输入答案自动填写 // @namespace http://tampermonkey.net/ // @version 1.0.2 // @description 使用可配置的选择器来适配不同网站,支持复杂的输入格式。 // @match https://lms.ouchn.cn/exam/* // @match https://ks.wjx.top/vm/mBcE5Ax.aspx // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 全局变量和常量 let questions = []; let isQuestionDetected = false; const GLOBAL = { fillAnswerDelay: 300, debounceDelay: 300, }; const DEFAULT_SELECTORS = { subjectContainer: '.exam-subjects > ol > li.subject', questionText: '.subject-description', options: '.subject-options input[type="radio"], .subject-options input[type="checkbox"]', answerElement: '.answer-options', }; let SELECTORS = {...DEFAULT_SELECTORS}; // 工具函数 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // UI 相关函数 function createMainInterface() { const container = document.createElement('div'); container.id = 'auto-fill-container'; container.className = 'fixed top-5 right-5 bg-white p-6 rounded-lg shadow-xl w-96 max-w-[90%] transition-all duration-300 ease-in-out'; container.innerHTML = `

自动填写答案

`; document.body.appendChild(container); // 添加事件监听器 document.getElementById('fillButton').addEventListener('click', fillAnswers); document.getElementById('clearButton').addEventListener('click', clearInputs); document.getElementById('pasteButton').addEventListener('click', pasteAndRecognize); document.getElementById('configButton').addEventListener('click', showSelectorWizard); document.getElementById('detectButton').addEventListener('click', smartDetectAnswers); document.getElementById('bulk-input').addEventListener('input', debounce(updateQuestionsPreview, GLOBAL.debounceDelay)); } function updateQuestionsPreview() { const bulkInput = document.getElementById('bulk-input'); const questionsPreview = document.getElementById('questions-preview'); const answers = parseAnswers(bulkInput.value); // 添加自定义样式 if (!document.getElementById('custom-question-preview-style')) { const style = document.createElement('style'); style.id = 'custom-question-preview-style'; style.textContent = ` .question-row { transition: all 0.3s ease; } .question-row:hover { transform: translateY(-2px); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } `; document.head.appendChild(style); } questionsPreview.innerHTML = questions .filter(q => q.type !== '未知类型') .map((q, i) => { const answer = answers[i] || '-'; const isValid = validateAnswer(answer, q); const isFilled = answer !== '-'; const backgroundClass = isFilled ? (isValid ? 'bg-green-100' : 'bg-red-100') : 'bg-gray-100'; const answerColorClass = isFilled ? (isValid ? 'text-green-600' : 'text-red-600') : 'text-gray-600'; return `
${i + 1}.
${q.type} ${q.text.length > 10 ? q.text.substring(0, 10) + '...' : q.text} ${answer}
`; }).join(''); } function getTypeColor(type) { switch(type) { case '单选题': return 'bg-blue-500 text-white'; case '多选题': return 'bg-purple-500 text-white'; case '判断题': return 'bg-yellow-500 text-white'; default: return 'bg-gray-500 text-white'; } } function showMessage(message, type = 'info', duration = 0) { const statusElement = document.getElementById('status-message'); statusElement.textContent = message; statusElement.className = `mb-4 text-sm font-medium p-3 rounded ${ type === 'error' ? 'bg-red-100 text-red-700' : type === 'success' ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700' }`; if (duration > 0) { setTimeout(() => { statusElement.textContent = ''; statusElement.className = 'mb-4 text-sm font-medium text-gray-600'; }, duration); } } // 核心功能函数 function determineQuestionType(subject, options) { if (options.length === 2 && options[0].type === 'radio' && options[1].type === 'radio') { return '判断题'; } else if (options.length > 0 && options[0].type === 'radio') { return '单选题'; } else if (options.length > 0 && options[0].type === 'checkbox') { return '多选题'; } return '未知类型'; } function detectQuestions() { const subjectElements = document.querySelectorAll(SELECTORS.subjectContainer); questions = Array.from(subjectElements) .map((subject, index) => { const questionText = subject.querySelector(SELECTORS.questionText)?.textContent.trim(); const options = subject.querySelectorAll(SELECTORS.options); if (!questionText || options.length === 0) { return null; } let questionType = determineQuestionType(subject, options); return { type: questionType, optionCount: options.length, text: questionText, index: index + 1 }; }) .filter(q => q !== null); isQuestionDetected = questions.filter(q => q.type !== '未知类型').length > 0; updateQuestionsPreview(); if (isQuestionDetected) { showMessage(`检测到 ${questions.filter(q => q.type !== '未知类型').length} 道题目`, 'success'); } else { showMessage('未检测到题目,请配置选择器或重新检测', 'error'); } } function parseAnswers(input) { if (!input.trim()) { return []; } input = input.replace(/\s/g, '').toUpperCase(); const patterns = [ { regex: /(\d+)-(\d+)([A-Z]+)/, process: (match, answers) => { const [_, start, end, choices] = match; for (let i = parseInt(start); i <= parseInt(end); i++) { answers[i - 1] = choices[i - parseInt(start)] || ''; } } }, { regex: /(\d+)([A-Z]+)/, process: (match, answers) => { const [_, number, choices] = match; answers[parseInt(number) - 1] = choices; } }, { regex: /([A-Z]+)/, process: (match, answers) => { answers.push(match[1]); } } ]; let answers = []; const segments = input.split(','); segments.forEach(segment => { let matched = false; for (const pattern of patterns) { const match = segment.match(pattern.regex); if (match) { pattern.process(match, answers); matched = true; break; } } if (!matched) { showMessage(`无法解析的输入段: ${segment}`, 'error'); } }); return answers; } function validateAnswer(answer, question) { if (!answer || answer === '') return true; const options = answer.split(''); if (question.type === '单选题' || question.type === '判断题') { return options.length === 1 && options[0].charCodeAt(0) - 64 <= question.optionCount; } else if (question.type === '多选题') { return options.every(option => option.charCodeAt(0) - 64 <= question.optionCount); } return false; } async function fillAnswers() { const bulkInput = document.getElementById('bulk-input'); const answers = parseAnswers(bulkInput.value); let filledCount = 0; for (let i = 0; i < questions.length; i++) { if (i >= answers.length) break; const question = questions[i]; const answer = answers[i]; if (answer && validateAnswer(answer, question)) { const subject = document.querySelector(`${SELECTORS.subjectContainer}:nth-child(${question.index})`); const options = subject.querySelectorAll(SELECTORS.options); for (let optionIndex = 0; optionIndex < options.length; optionIndex++) { const option = options[optionIndex]; const optionLetter = String.fromCharCode(65 + optionIndex); const shouldBeChecked = answer.includes(optionLetter); if (shouldBeChecked !== option.checked) { option.click(); await sleep(GLOBAL.fillAnswerDelay); filledCount++; } } } } showMessage(`已成功填写 ${filledCount} 道题目的答案`, 'success'); } function clearInputs() { document.getElementById('bulk-input').value = ''; updateQuestionsPreview(); showMessage('输入已清空', 'info'); } async function pasteAndRecognize() { try { const text = await navigator.clipboard.readText(); const bulkInput = document.getElementById('bulk-input'); bulkInput.value = text; updateQuestionsPreview(); showMessage('已从剪贴板粘贴并识别答案', 'success'); } catch (err) { showMessage('无法访问剪贴板,请手动粘贴', 'error'); } } function smartDetectAnswers() { const subjectContainers = document.querySelectorAll(SELECTORS.subjectContainer); console.log(`找到 ${subjectContainers.length} 个题目容器`); const detectedAnswers = questions.filter(q => q.type !== '未知类型').map((question, index) => { const subject = document.querySelector(`${SELECTORS.subjectContainer}:nth-child(${question.index})`); if (!subject) return ''; const answerElement = subject.querySelector(SELECTORS.answerElement); if (!answerElement) return ''; const answerText = answerElement.textContent.trim(); if (!answerText) return ''; return processAnswer(answerText, question.type); }); const bulkInput = document.getElementById('bulk-input'); bulkInput.value = detectedAnswers.join(','); updateQuestionsPreview(); showMessage('已智能识别当前答案', 'success'); } function processAnswer(answerText, questionType) { answerText = answerText.toUpperCase(); switch (questionType) { case '单选题': return answerText.match(/[A-Z]/)?.[0] || ''; case '多选题': return answerText.match(/[A-Z]/g)?.join('') || ''; case '判断题': if (answerText.includes('对') || answerText.includes('A') || answerText === 'T') { return 'A'; } else if (answerText.includes('错') || answerText.includes('B') || answerText === 'F') { return 'B'; } return ''; default: return answerText; } } // 新的选择器配置工具 function showSelectorWizard() { const wizard = document.createElement('div'); wizard.id = 'selector-wizard'; wizard.className = 'fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-lg shadow-xl z-50 w-96 max-w-[90%]'; wizard.innerHTML = `

DOM 选择器配置

${createSelectorInput('subjectContainer', '题目容器选择器')} ${createSelectorInput('questionText', '问题文本选择器')} ${createSelectorInput('options', '选项选择器')} ${createSelectorInput('answerElement', '答案元素选择器')}
`; document.body.appendChild(wizard); let highlightedElements = []; let pickerActive = false; let currentSelectorType = ''; function highlightElements(elements) { highlightedElements.forEach(el => el.style.outline = ''); highlightedElements = Array.from(elements); highlightedElements.forEach(el => { el.style.outline = '2px solid red'; }); } function clearHighlights() { highlightedElements.forEach(el => el.style.outline = ''); highlightedElements = []; } function testSelector() { const selector = document.getElementById('selector-input').value; const elements = document.querySelectorAll(selector); const results = document.getElementById('selector-results'); results.textContent = `找到 ${elements.length} 个元素`; highlightElements(elements); } function pickElement(e) { if (!pickerActive) return; e.preventDefault(); e.stopPropagation(); const path = e.composedPath(); const selector = uniqueSelector(path[0]); document.getElementById('selector-input').value = selector; if (currentSelectorType) { document.getElementById(currentSelectorType).value = selector; } testSelector(); pickerActive = false; } function uniqueSelector(el) { if (el.id) return '#' + el.id; if (el.className) return '.' + el.className.split(' ').join('.'); let selector = el.tagName.toLowerCase(); let siblings = Array.from(el.parentNode.children); if (siblings.length > 1) { let index = siblings.indexOf(el) + 1; selector += `:nth-child(${index})`; } return el.parentNode ? uniqueSelector(el.parentNode) + ' > ' + selector : selector; } document.getElementById('test-selector').addEventListener('click', testSelector); document.getElementById('pick-element').addEventListener('click', () => { pickerActive = true; showMessage('请点击页面元素以选择', 'info'); }); document.addEventListener('click', pickElement, true); document.getElementById('save-selector').addEventListener('click', () => { SELECTORS = { subjectContainer: document.getElementById('subjectContainer').value, questionText: document.getElementById('questionText').value, options: document.getElementById('options').value, answerElement: document.getElementById('answerElement').value }; GM_setValue('customSelectors', JSON.stringify(SELECTORS)); clearHighlights(); wizard.remove(); showMessage('选择器配置已保存,正在重新检测题目', 'success'); detectQuestions(); }); document.getElementById('close-wizard').addEventListener('click', () => { clearHighlights(); wizard.remove(); }); document.getElementById('reset-selectors').addEventListener('click', () => { SELECTORS = {...DEFAULT_SELECTORS}; ['subjectContainer', 'questionText', 'options', 'answerElement'].forEach(id => { document.getElementById(id).value = DEFAULT_SELECTORS[id]; }); document.getElementById('selector-input').value = ''; clearHighlights(); showMessage('选择器已重置为默认值', 'info'); }); // 为每个选择器输入框添加事件监听器 ['subjectContainer', 'questionText', 'options', 'answerElement'].forEach(id => { document.getElementById(id).addEventListener('focus', () => { currentSelectorType = id; document.getElementById('selector-input').value = document.getElementById(id).value; }); }); } function createSelectorInput(id, label) { return `
`; } // 初始化函数 function init() { // 加载 Tailwind CSS const tailwindCSS = `https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css`; const link = document.createElement('link'); link.href = tailwindCSS; link.rel = 'stylesheet'; document.head.appendChild(link); // 加载自定义选择器 const savedSelectors = GM_getValue('customSelectors'); if (savedSelectors) { SELECTORS = JSON.parse(savedSelectors); } // 创建主界面 createMainInterface(); // 延迟执行检测题目,确保页面完全加载 setTimeout(detectQuestions, 2000); } // 当 DOM 加载完成时初始化脚本 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();