// ==UserScript==
// @name 广州华商答题助手
// @namespace http://tampermonkey.net/
// @version 5.0
// @description 自动答题、记录题库、智能匹配答案,支持单选、多选、判断、填空、简答题型
// @author ScriptCat
// @match *://*.cx-online.net/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// 配置对象
const config = {
// 运行模式
mode: 'idle', // 'idle':空闲, 'auto':自动答题, 'record':仅记录
isProcessing: false, // 防止重复处理标志
// 题库数据
questionBank: {},
currentQuestionId: '', // 当前题目ID
// 控制面板
panelVisible: true,
// 设置
autoDelay: 2500, // 自动答题延迟
autoSwitch: true, // 自动切换到下一题
useBankAnswers: true // 优先使用题库答案
};
// 初始化
function init() {
console.log('🚀 初始化智能答题与题库记录助手 v5.0...');
// 加载题库
loadQuestionBank();
// 添加样式
addGlobalStyles();
// 创建控制面板
createControlPanel();
// 初始检查页面
setTimeout(() => {
if (config.mode === 'auto') {
startAutoMode();
}
}, 1000);
console.log('✅ 初始化完成');
}
// 题库管理函数
function loadQuestionBank() {
try {
const saved = GM_getValue('questionBank', '{}');
config.questionBank = JSON.parse(saved);
console.log(`📚 题库已加载,共有 ${Object.keys(config.questionBank).length} 道题目`);
} catch (e) {
console.error('❌ 加载题库失败:', e);
config.questionBank = {};
}
}
function saveQuestionBank() {
try {
GM_setValue('questionBank', JSON.stringify(config.questionBank));
updateQuestionBankCount();
return true;
} catch (e) {
console.error('❌ 保存题库失败:', e);
return false;
}
}
// 核心功能:智能答题
function intelligentAnswer() {
if (config.isProcessing) return;
config.isProcessing = true;
updateControlPanel();
console.log('🤖 开始智能答题...');
setTimeout(() => {
try {
// 获取当前题目
const currentQuestion = document.querySelector('.homeWork_cxt--left_item:not([style*="display: none"])');
if (!currentQuestion) {
console.log('❌ 未找到当前题目');
config.isProcessing = false;
return;
}
// 提取题目数据
const questionData = extractQuestionData(currentQuestion);
if (!questionData) {
console.log('❌ 无法提取题目数据');
config.isProcessing = false;
return;
}
// 生成题目ID
const questionId = generateQuestionId(questionData);
config.currentQuestionId = questionId;
console.log(`📝 题目类型: ${questionData.type}`);
console.log(`📝 题目内容: ${questionData.title.substring(0, 50)}...`);
// 1. 优先从题库获取答案
let answerToUse = null;
if (config.useBankAnswers && config.questionBank[questionId]) {
console.log('🎯 从题库中找到答案');
answerToUse = config.questionBank[questionId].answer;
showNotification(`从题库获取答案: ${answerToUse}`, 'success');
} else {
// 2. 从页面提取正确答案
answerToUse = extractCorrectAnswer(currentQuestion, questionData.type);
if (!answerToUse) {
console.log('❌ 无法提取正确答案');
config.isProcessing = false;
showNotification('无法提取正确答案', 'error');
return;
}
// 3. 如果开启了记录模式,保存到题库
if (config.mode === 'record' || config.mode === 'auto') {
saveToQuestionBank(questionId, questionData, answerToUse);
}
}
// 4. 自动答题
const result = autoAnswerQuestion(currentQuestion, questionData.type, answerToUse);
if (result.success) {
showNotification(result.message, 'success');
// 5. 自动切换到下一题
if (config.autoSwitch && config.mode === 'auto') {
setTimeout(switchToNextQuestion, config.autoDelay);
} else {
config.isProcessing = false;
updateControlPanel();
}
} else {
showNotification(result.message, 'warning');
config.isProcessing = false;
updateControlPanel();
}
} catch (error) {
console.error('❌ 智能答题出错:', error);
showNotification('答题过程中出错', 'error');
config.isProcessing = false;
updateControlPanel();
}
}, 1000);
}
// 提取题目数据
function extractQuestionData(currentQuestion) {
const typeElement = currentQuestion.querySelector('.multiple_head--queType');
const titleElement = currentQuestion.querySelector('.multiple_ctx--title');
if (!typeElement || !titleElement) {
return null;
}
const questionType = typeElement.textContent.trim();
const questionTitle = titleElement.textContent.trim();
// 提取选项(如果是选择题)
let options = null;
if (questionType.includes('单项') || questionType.includes('多项') || questionType.includes('判断') || questionType.includes('选择')) {
const optionElements = currentQuestion.querySelectorAll('.multiple_ctx--option');
if (optionElements.length > 0) {
options = Array.from(optionElements).map(opt => {
const title = opt.querySelector('.multiple_ctx--option_title');
return title ? title.textContent.trim() : '';
}).filter(text => text);
}
}
return {
type: questionType,
title: questionTitle,
options: options
};
}
// 提取正确答案
function extractCorrectAnswer(currentQuestion, questionType) {
let correctAnswer = '';
// 方法1: 从正确答案区域提取
const rightAnsElement = currentQuestion.querySelector('.multiple_ctx--rightAns');
if (rightAnsElement) {
const rightAnsText = rightAnsElement.textContent.trim();
// 提取答案部分
if (rightAnsText.includes('正确答案:')) {
correctAnswer = rightAnsText.split('正确答案:')[1].trim();
} else if (rightAnsText.includes('正确答案:')) {
correctAnswer = rightAnsText.split('正确答案:')[1].trim();
} else {
// 尝试直接获取span中的内容
const answerSpan = rightAnsElement.querySelector('span');
if (answerSpan) {
correctAnswer = answerSpan.textContent.trim();
} else {
// 去除"正确答案"字样
correctAnswer = rightAnsText.replace(/正确答案[::]\s*/, '').trim();
}
}
if (correctAnswer) {
console.log(`✅ 从正确答案区域提取: ${correctAnswer}`);
return correctAnswer;
}
}
// 方法2: 从解析中提取注释答案
const analysisElement = currentQuestion.querySelector('.multiple_ctx--analysis');
if (analysisElement) {
const analysisText = analysisElement.textContent;
if (questionType.includes('简答题') || questionType.includes('简答')) {
// 简答题特殊处理
const answerDiv = analysisElement.querySelector('div:last-child');
if (answerDiv) {
correctAnswer = answerDiv.textContent.trim();
} else if (analysisText.includes('详情解析:')) {
const parts = analysisText.split('详情解析:');
if (parts.length > 1) {
correctAnswer = parts[1].trim();
}
}
} else {
// 选择题、判断题等提取注释
const answerMatches = analysisText.match(/\/\*([^*]+)\*\//g);
if (answerMatches && answerMatches.length > 0) {
// 取最后一个注释作为答案
const lastMatch = answerMatches[answerMatches.length - 1];
correctAnswer = lastMatch.replace(/\/\*/g, '').replace(/\*\//g, '').trim();
}
}
if (correctAnswer) {
console.log(`✅ 从解析中提取: ${correctAnswer}`);
return cleanAnswerText(correctAnswer);
}
}
return null;
}
// 清理答案文本
function cleanAnswerText(answer) {
return answer
.replace(/\/\*/g, '')
.replace(/\*\//g, '')
.replace(/^答案[::]\s*/, '')
.trim();
}
// 保存到题库
function saveToQuestionBank(questionId, questionData, answer) {
// 检查是否已存在
if (!config.questionBank[questionId]) {
config.questionBank[questionId] = {
type: questionData.type,
title: questionData.title,
answer: answer,
options: questionData.options,
timestamp: new Date().toISOString(),
source: 'auto_record'
};
if (saveQuestionBank()) {
console.log(`✅ 已记录到题库: ${questionData.title.substring(0, 30)}...`);
return true;
}
} else {
console.log('⚠️ 题目已在题库中');
return false;
}
}
// 自动答题
function autoAnswerQuestion(currentQuestion, questionType, answer) {
console.log(`🤖 开始自动答题,答案: ${answer}`);
if (!answer || answer.trim() === '') {
return { success: false, message: '答案为空,无法答题' };
}
try {
if (questionType.includes('单项') || questionType.includes('单选')) {
return handleSingleChoice(currentQuestion, answer);
} else if (questionType.includes('多项') || questionType.includes('多选')) {
return handleMultipleChoice(currentQuestion, answer);
} else if (questionType.includes('判断')) {
return handleJudgment(currentQuestion, answer);
} else if (questionType.includes('填空')) {
return handleFillInBlank(currentQuestion, answer);
} else if (questionType.includes('简答')) {
return handleShortAnswer(currentQuestion, answer);
} else {
return { success: false, message: '未知题型,无法答题' };
}
} catch (error) {
console.error('自动答题出错:', error);
return { success: false, message: '答题过程中出错' };
}
}
// 处理单选题
function handleSingleChoice(currentQuestion, correctAnswer) {
const options = currentQuestion.querySelectorAll('.multiple_ctx--option');
// 方法1: 按选项文本匹配
for (let i = 0; i < options.length; i++) {
const option = options[i];
const optionText = option.querySelector('.multiple_ctx--option_title').textContent;
if (optionText.includes(correctAnswer)) {
simulateClick(option);
return { success: true, message: `单选题已选择: ${optionText}` };
}
}
// 方法2: 按字母匹配
const letterMatch = correctAnswer.match(/^[A-E]/i);
if (letterMatch) {
const letter = letterMatch[0].toUpperCase();
for (let i = 0; i < options.length; i++) {
const option = options[i];
const optionText = option.querySelector('.multiple_ctx--option_title').textContent;
if (optionText.startsWith(letter)) {
simulateClick(option);
return { success: true, message: `单选题已选择: ${optionText}` };
}
}
}
return { success: false, message: '未找到匹配的单选题选项' };
}
// 处理多选题
function handleMultipleChoice(currentQuestion, correctAnswer) {
const options = currentQuestion.querySelectorAll('.multiple_ctx--option');
let selectedCount = 0;
// 分割多选题答案
const answerParts = splitMultipleChoiceAnswer(correctAnswer);
if (answerParts.length === 0) {
return { success: false, message: '多选题答案格式错误' };
}
// 收集已选择的选项字母
const selectedLetters = new Set();
// 先处理字母答案
const letterAnswers = answerParts.filter(part => /^[A-E]$/i.test(part));
for (const letter of letterAnswers) {
const upperLetter = letter.toUpperCase();
for (let i = 0; i < options.length; i++) {
const option = options[i];
const optionText = option.querySelector('.multiple_ctx--option_title').textContent;
if (optionText.startsWith(upperLetter) && !selectedLetters.has(upperLetter)) {
simulateClick(option);
selectedCount++;
selectedLetters.add(upperLetter);
}
}
}
// 处理文本答案
const textAnswers = answerParts.filter(part => !/^[A-E]$/i.test(part));
for (const textAnswer of textAnswers) {
for (let i = 0; i < options.length; i++) {
const option = options[i];
const optionText = option.querySelector('.multiple_ctx--option_title').textContent;
const optionLetter = optionText.charAt(0);
if (selectedLetters.has(optionLetter)) continue;
// 提取选项内容(去掉字母和标点)
let optionContent = optionText;
if (optionText.includes('、')) {
optionContent = optionText.split('、')[1] || optionText;
} else if (optionText.includes('. ')) {
optionContent = optionText.split('. ')[1] || optionText;
} else if (/^[A-E]/.test(optionText)) {
optionContent = optionText.substring(1).trim();
}
// 检查是否匹配
if (optionContent.includes(textAnswer) || textAnswer.includes(optionContent)) {
simulateClick(option);
selectedCount++;
selectedLetters.add(optionLetter);
break;
}
}
}
if (selectedCount > 0) {
return { success: true, message: `多选题已选择 ${selectedCount} 个选项` };
} else {
return { success: false, message: '未找到匹配的多选题选项' };
}
}
// 分割多选题答案
function splitMultipleChoiceAnswer(answer) {
if (!answer) return [];
let parts = [];
// 按常见分隔符分割
const separators = ['、', ',', ',', ';', ';', ' ', ' '];
for (const sep of separators) {
if (answer.includes(sep)) {
parts = answer.split(sep).map(part => part.trim()).filter(part => part.length > 0);
if (parts.length > 1) break;
}
}
// 如果没有找到分隔符,尝试按连接词分割
if (parts.length <= 1) {
const connectors = ['和', '与', '及', '以及'];
for (const connector of connectors) {
if (answer.includes(connector)) {
parts = answer.split(connector).map(part => part.trim()).filter(part => part.length > 0);
if (parts.length > 1) break;
}
}
}
// 如果还是没有分割,返回整个答案
if (parts.length === 0) {
parts = [answer];
}
return parts;
}
// 处理判断题
function handleJudgment(currentQuestion, correctAnswer) {
const options = currentQuestion.querySelectorAll('.multiple_ctx--option');
// 规范化答案
const normalizedAnswer = correctAnswer.includes('正确') ? '正确' :
correctAnswer.includes('错误') ? '错误' : correctAnswer;
for (let i = 0; i < options.length; i++) {
const option = options[i];
const optionText = option.querySelector('.multiple_ctx--option_title').textContent;
if (optionText.includes(normalizedAnswer)) {
simulateClick(option);
return { success: true, message: `判断题已选择: ${optionText}` };
}
}
return { success: false, message: '未找到匹配的判断题选项' };
}
// 处理填空题
function handleFillInBlank(currentQuestion, correctAnswer) {
const inputElements = currentQuestion.querySelectorAll('input.el-input__inner');
if (inputElements.length === 0) {
return { success: false, message: '未找到填空题输入框' };
}
// 分割答案
let answers = [correctAnswer];
const separators = ['/', ';', ';', '、', ',', ','];
for (const sep of separators) {
if (correctAnswer.includes(sep)) {
answers = correctAnswer.split(sep).map(a => a.trim());
break;
}
}
// 填充输入框
let filledCount = 0;
for (let i = 0; i < inputElements.length; i++) {
const input = inputElements[i];
const answerIndex = i < answers.length ? i : 0; // 复用第一个答案
const answer = answers[answerIndex];
if (answer && answer !== '') {
input.value = answer;
triggerInputEvent(input);
input.style.backgroundColor = '#f0f9ff';
input.style.borderColor = '#409EFF';
filledCount++;
setTimeout(() => {
input.style.backgroundColor = '';
input.style.borderColor = '';
}, 500);
}
}
if (filledCount > 0) {
return { success: true, message: `填空题已填充 ${filledCount} 个空` };
} else {
return { success: false, message: '填空题答案为空' };
}
}
// 处理简答题
function handleShortAnswer(currentQuestion, correctAnswer) {
const editorElement = currentQuestion.querySelector('.ql-editor');
if (!editorElement) {
return { success: false, message: '未找到简答题编辑器' };
}
// 清理答案中的注释标记
let cleanAnswer = correctAnswer.replace(/\/\*/g, '').replace(/\*\//g, '').trim();
if (!cleanAnswer || cleanAnswer === '') {
return { success: false, message: '简答题答案为空' };
}
// 检查编辑器是否为空
const editorContent = editorElement.innerHTML.trim();
if (editorContent === '
' || editorContent === '' || editorContent === '
') {
editorElement.innerHTML = '';
const p = document.createElement('p');
p.textContent = cleanAnswer;
editorElement.appendChild(p);
triggerEditorEvent(editorElement);
editorElement.classList.add('auto-filled');
setTimeout(() => {
editorElement.classList.remove('auto-filled');
}, 1000);
return { success: true, message: '简答题已填写答案' };
} else {
return { success: false, message: '简答题已有内容,跳过填写' };
}
}
// 切换到下一题
function switchToNextQuestion() {
console.log('🔄 切换到下一题...');
const currentQuestion = document.querySelector('.homeWork_cxt--left_item:not([style*="display: none"])');
if (!currentQuestion) {
config.isProcessing = false;
return false;
}
// 查找下一题按钮
const nextButtons = currentQuestion.querySelectorAll('.el-button.el-button--primary:not(.is-disabled)');
for (const button of nextButtons) {
if (button.textContent.includes('下一题')) {
simulateClick(button);
showNotification('已切换到下一题', 'info');
// 等待新题目加载后继续处理
setTimeout(() => {
config.isProcessing = false;
if (config.mode === 'auto') {
setTimeout(intelligentAnswer, 1000);
}
}, 2000);
return true;
}
}
// 如果没有找到按钮,尝试手动切换
const items = document.querySelectorAll('.homeWork_cxt--left_item');
let currentIndex = -1;
for (let i = 0; i < items.length; i++) {
if (!items[i].style.display || items[i].style.display === '') {
currentIndex = i;
break;
}
}
if (currentIndex >= 0 && currentIndex < items.length - 1) {
items[currentIndex].style.display = 'none';
items[currentIndex + 1].style.display = '';
setTimeout(() => {
config.isProcessing = false;
if (config.mode === 'auto') {
setTimeout(intelligentAnswer, 1000);
}
}, 1500);
return true;
}
// 已到最后一道题
config.isProcessing = false;
if (config.mode === 'auto') {
stopAutoMode();
showNotification('所有题目已完成!', 'success');
}
return false;
}
// 事件触发函数
function simulateClick(element) {
const mouseEvents = ['mousedown', 'mouseup', 'click'];
mouseEvents.forEach(eventType => {
element.dispatchEvent(new MouseEvent(eventType, {
bubbles: true,
cancelable: true
}));
});
// 视觉反馈
element.style.transition = 'background-color 0.3s';
element.style.backgroundColor = '#f0f9ff';
setTimeout(() => element.style.backgroundColor = '', 500);
}
function triggerInputEvent(input) {
const events = ['input', 'change', 'blur', 'focus'];
events.forEach(eventType => {
input.dispatchEvent(new Event(eventType, { bubbles: true }));
});
}
function triggerEditorEvent(editor) {
const events = ['input', 'change', 'text-change'];
events.forEach(eventType => {
editor.dispatchEvent(new Event(eventType, { bubbles: true }));
});
}
// 生成题目ID
function generateQuestionId(questionData) {
const cleanTitle = questionData.title.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '');
const cleanType = questionData.type.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '');
return `${cleanType}-${cleanTitle}`;
}
// 模式控制函数
function startAutoMode() {
if (config.mode === 'auto') return;
config.mode = 'auto';
console.log('🚀 开启自动答题模式');
showNotification('已开启自动答题模式', 'success');
updateControlPanel();
// 立即开始处理当前题目
intelligentAnswer();
}
function startRecordMode() {
if (config.mode === 'record') return;
config.mode = 'record';
console.log('📝 开启仅记录模式');
showNotification('已开启仅记录模式,将记录题目但不自动答题', 'info');
updateControlPanel();
}
function stopAllModes() {
config.mode = 'idle';
config.isProcessing = false;
console.log('🛑 停止所有模式');
showNotification('已停止所有模式', 'info');
updateControlPanel();
}
// 控制面板功能
function createControlPanel() {
const panel = document.createElement('div');
panel.id = 'smart-assistant-panel';
panel.innerHTML = `
题库数量: 0
当前模式: 空闲
处理状态: 空闲
`;
document.body.appendChild(panel);
// 添加事件监听
document.getElementById('panel-toggle').addEventListener('click', togglePanel);
document.getElementById('btn-auto-mode').addEventListener('click', startAutoMode);
document.getElementById('btn-record-mode').addEventListener('click', startRecordMode);
document.getElementById('btn-stop-all').addEventListener('click', stopAllModes);
document.getElementById('btn-single-answer').addEventListener('click', intelligentAnswer);
document.getElementById('btn-clear-bank').addEventListener('click', clearQuestionBank);
document.getElementById('btn-export-bank').addEventListener('click', exportQuestionBank);
document.getElementById('btn-import-bank').addEventListener('click', importQuestionBank);
document.getElementById('btn-prev-question').addEventListener('click', switchToPreviousQuestion);
document.getElementById('btn-next-question').addEventListener('click', switchToNextQuestion);
document.getElementById('cb-use-bank').addEventListener('change', (e) => {
config.useBankAnswers = e.target.checked;
});
document.getElementById('cb-auto-switch').addEventListener('change', (e) => {
config.autoSwitch = e.target.checked;
});
// 更新状态
updateQuestionBankCount();
updateControlPanel();
// 添加拖拽功能
makePanelDraggable(panel);
}
function togglePanel() {
const panel = document.getElementById('smart-assistant-panel');
const toggleBtn = document.getElementById('panel-toggle');
if (panel.classList.contains('minimized')) {
panel.classList.remove('minimized');
toggleBtn.textContent = '−';
} else {
panel.classList.add('minimized');
toggleBtn.textContent = '+';
}
}
function makePanelDraggable(panel) {
const header = panel.querySelector('.panel-header');
let isDragging = false;
let dragOffset = { x: 0, y: 0 };
header.addEventListener('mousedown', (e) => {
if (e.target.classList.contains('panel-toggle')) return;
isDragging = true;
const rect = panel.getBoundingClientRect();
dragOffset = {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
header.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
panel.style.left = `${e.clientX - dragOffset.x}px`;
panel.style.top = `${e.clientY - dragOffset.y}px`;
panel.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
header.style.cursor = '';
}
});
}
function updateControlPanel() {
const modeText = document.getElementById('mode-text');
const statusText = document.getElementById('status-text');
if (modeText) {
modeText.textContent =
config.mode === 'auto' ? '自动答题' :
config.mode === 'record' ? '仅记录' : '空闲';
modeText.className = `mode-${config.mode}`;
}
if (statusText) {
statusText.textContent = config.isProcessing ? '处理中...' : '空闲';
statusText.className = config.isProcessing ? 'processing' : '';
}
}
function updateQuestionBankCount() {
const count = Object.keys(config.questionBank).length;
const countElement = document.getElementById('bank-count');
if (countElement) {
countElement.textContent = count;
}
}
// 切换到上一题
function switchToPreviousQuestion() {
const currentQuestion = document.querySelector('.homeWork_cxt--left_item:not([style*="display: none"])');
if (!currentQuestion) return false;
const prevButtons = currentQuestion.querySelectorAll('.el-button.el-button--primary:not(.is-disabled)');
for (const button of prevButtons) {
if (button.textContent.includes('上一题')) {
simulateClick(button);
showNotification('已切换到上一题', 'info');
return true;
}
}
// 手动切换
const items = document.querySelectorAll('.homeWork_cxt--left_item');
let currentIndex = -1;
for (let i = 0; i < items.length; i++) {
if (!items[i].style.display || items[i].style.display === '') {
currentIndex = i;
break;
}
}
if (currentIndex > 0) {
items[currentIndex].style.display = 'none';
items[currentIndex - 1].style.display = '';
return true;
}
return false;
}
// 题库管理函数
function clearQuestionBank() {
if (confirm('确定要清空题库吗?此操作不可恢复!')) {
config.questionBank = {};
saveQuestionBank();
showNotification('题库已清空', 'success');
}
}
function exportQuestionBank() {
try {
const data = {
version: '5.0',
timestamp: new Date().toISOString(),
count: Object.keys(config.questionBank).length,
questions: config.questionBank
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `题库备份_${new Date().toISOString().slice(0,10)}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showNotification('题库导出成功', 'success');
} catch (e) {
console.error('导出失败:', e);
showNotification('导出失败', 'error');
}
}
function importQuestionBank() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
try {
const data = JSON.parse(event.target.result);
let imported = 0;
if (data.questions) {
for (const [key, value] of Object.entries(data.questions)) {
if (!config.questionBank[key]) {
config.questionBank[key] = value;
imported++;
}
}
}
saveQuestionBank();
showNotification(`成功导入 ${imported} 道新题目`, 'success');
} catch (e) {
console.error('导入失败:', e);
showNotification('导入失败,文件格式错误', 'error');
}
};
reader.readAsText(file);
};
input.click();
}
// 显示通知
function showNotification(message, type = 'info') {
const id = 'smart-assistant-notification';
let notification = document.getElementById(id);
if (!notification) {
notification = document.createElement('div');
notification.id = id;
document.body.appendChild(notification);
}
notification.textContent = message;
notification.className = `notification notification-${type}`;
notification.style.display = 'block';
setTimeout(() => {
if (notification.parentNode) {
notification.style.opacity = '0';
setTimeout(() => {
if (notification.parentNode) {
notification.style.display = 'none';
notification.style.opacity = '1';
}
}, 300);
}
}, 3000);
}
// 添加全局样式
function addGlobalStyles() {
const styles = `
/* 控制面板样式 */
#smart-assistant-panel {
position: fixed !important;
top: 100px !important;
right: 20px !important;
width: 300px !important;
background: white !important;
border-radius: 12px !important;
box-shadow: 0 8px 32px rgba(0,0,0,0.2) !important;
z-index: 100000 !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
overflow: hidden !important;
border: 1px solid #e1e4e8 !important;
transition: all 0.3s ease !important;
}
#smart-assistant-panel.minimized {
height: 40px !important;
}
#smart-assistant-panel.minimized .panel-body {
display: none !important;
}
.panel-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
padding: 12px 16px !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
cursor: move !important;
user-select: none !important;
}
.panel-title {
font-weight: 600 !important;
font-size: 14px !important;
display: flex !important;
align-items: center !important;
gap: 8px !important;
}
.panel-toggle {
background: rgba(255,255,255,0.2) !important;
border: none !important;
color: white !important;
font-size: 18px !important;
cursor: pointer !important;
width: 24px !important;
height: 24px !important;
border-radius: 4px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.panel-body {
padding: 16px !important;
}
.panel-section {
margin-bottom: 16px !important;
padding-bottom: 16px !important;
border-bottom: 1px solid #f0f0f0 !important;
}
.panel-section:last-child {
margin-bottom: 0 !important;
padding-bottom: 0 !important;
border-bottom: none !important;
}
.section-title {
font-size: 12px !important;
color: #666 !important;
margin-bottom: 8px !important;
font-weight: 600 !important;
text-transform: uppercase !important;
letter-spacing: 0.5px !important;
}
.btn {
display: block !important;
width: 100% !important;
padding: 10px 12px !important;
margin: 6px 0 !important;
border: none !important;
border-radius: 8px !important;
font-size: 14px !important;
font-weight: 500 !important;
cursor: pointer !important;
text-align: center !important;
transition: all 0.2s ease !important;
position: relative !important;
overflow: hidden !important;
}
.btn::after {
content: '' !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
width: 5px !important;
height: 5px !important;
background: rgba(255, 255, 255, 0.5) !important;
opacity: 0 !important;
border-radius: 100% !important;
transform: scale(1, 1) translate(-50%) !important;
transform-origin: 50% 50% !important;
}
.btn:focus:not(:active)::after {
animation: ripple 1s ease-out !important;
}
@keyframes ripple {
0% {
transform: scale(0, 0) !important;
opacity: 0.5 !important;
}
20% {
transform: scale(60, 60) !important;
opacity: 0.3 !important;
}
100% {
transform: scale(100, 100) !important;
opacity: 0 !important;
}
}
.btn:hover {
transform: translateY(-2px) !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.1) !important;
}
.btn:active {
transform: translateY(0) !important;
}
.btn:disabled {
opacity: 0.5 !important;
cursor: not-allowed !important;
transform: none !important;
box-shadow: none !important;
}
.btn-success {
background: linear-gradient(135deg, #67C23A 0%, #5db333 100%) !important;
color: white !important;
}
.btn-danger {
background: linear-gradient(135deg, #F56C6C 0%, #e05c5c 100%) !important;
color: white !important;
}
.btn-primary {
background: linear-gradient(135deg, #409EFF 0%, #3a8fe6 100%) !important;
color: white !important;
}
.btn-secondary {
background: linear-gradient(135deg, #909399 0%, #82848a 100%) !important;
color: white !important;
}
.btn-outline {
background: transparent !important;
border: 1px solid #dcdfe6 !important;
color: #606266 !important;
}
.btn-outline:hover {
background: #f5f7fa !important;
}
.btn-sm {
padding: 6px 10px !important;
font-size: 12px !important;
}
.btn-group {
display: flex !important;
gap: 8px !important;
margin-top: 8px !important;
}
.btn-group .btn {
flex: 1 !important;
margin: 0 !important;
}
.form-group {
margin-top: 12px !important;
display: flex !important;
flex-direction: column !important;
gap: 8px !important;
}
.form-group label {
display: flex !important;
align-items: center !important;
gap: 6px !important;
font-size: 13px !important;
color: #606266 !important;
cursor: pointer !important;
}
.form-group input[type="checkbox"] {
width: 14px !important;
height: 14px !important;
cursor: pointer !important;
}
.panel-status {
font-size: 12px !important;
color: #666 !important;
margin-top: 12px !important;
padding: 12px !important;
background: linear-gradient(135deg, #f5f7fa 0%, #f0f2f5 100%) !important;
border-radius: 8px !important;
}
.panel-status div {
margin: 4px 0 !important;
display: flex !important;
justify-content: space-between !important;
}
#mode-text.mode-auto {
color: #67C23A !important;
font-weight: bold !important;
}
#mode-text.mode-record {
color: #409EFF !important;
font-weight: bold !important;
}
#mode-text.mode-idle {
color: #909399 !important;
font-weight: normal !important;
}
#status-text.processing {
color: #E6A23C !important;
font-weight: bold !important;
animation: pulse 2s infinite !important;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
/* 通知样式 */
.notification {
position: fixed !important;
top: 20px !important;
right: 20px !important;
padding: 14px 20px !important;
border-radius: 8px !important;
color: white !important;
font-weight: 500 !important;
z-index: 100001 !important;
box-shadow: 0 8px 24px rgba(0,0,0,0.2) !important;
transition: all 0.3s ease !important;
max-width: 400px !important;
font-size: 14px !important;
display: none !important;
backdrop-filter: blur(10px) !important;
background: rgba(0,0,0,0.8) !important;
border: 1px solid rgba(255,255,255,0.1) !important;
}
.notification-info {
background: rgba(64, 158, 255, 0.9) !important;
}
.notification-success {
background: rgba(103, 194, 58, 0.9) !important;
}
.notification-warning {
background: rgba(230, 162, 60, 0.9) !important;
}
.notification-error {
background: rgba(245, 108, 108, 0.9) !important;
}
/* 答题高亮样式 */
.multiple_ctx--option.auto-selected {
background-color: #f0f9ff !important;
border: 2px solid #409EFF !important;
border-radius: 8px !important;
transition: all 0.3s ease !important;
}
.multiple_ctx--option_radio.auto-selected {
background-color: #409EFF !important;
}
input.el-input__inner.auto-filled {
background-color: #f0f9ff !important;
border-color: #409EFF !important;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2) !important;
}
.ql-editor.auto-filled {
background-color: #f0f9ff !important;
border: 2px solid #409EFF !important;
border-radius: 8px !important;
}
`;
const style = document.createElement('style');
style.textContent = styles;
document.head.appendChild(style);
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
setTimeout(init, 1000);
}
// 监听页面变化
const observer = new MutationObserver(function(mutations) {
if (config.mode === 'auto' && !config.isProcessing) {
// 延迟执行,避免频繁触发
clearTimeout(window.delayedAnswer);
window.delayedAnswer = setTimeout(() => {
intelligentAnswer();
}, 1500);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class']
});
// 暴露API供调试
window.SmartAnswerAssistant = {
getConfig: () => config,
startAutoMode: startAutoMode,
startRecordMode: startRecordMode,
stopAllModes: stopAllModes,
answerCurrent: intelligentAnswer,
clearBank: clearQuestionBank,
exportBank: exportQuestionBank
};
console.log('🎉 智能答题与题库记录助手已加载');
console.log('📋 使用指南:');
console.log(' 1. 自动答题模式:自动答题并记录新题目');
console.log(' 2. 仅记录模式:只记录题目不自动答题');
console.log(' 3. 优先使用题库答案:如果题库中有题目,直接使用题库答案');
console.log(' 4. 可在控制台使用 window.SmartAnswerAssistant 进行调试');
})();