// ==UserScript==
// @name 创新创新-贤程教育
// @namespace https://www.example.com/
// @version 10.0
// @description 修复题库合并问题,添加导出功能,优化面板位置记忆
// @author xixi
// @match *://*.gdcxxy.net/*
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
// 题库系统
let questionBank = {};
let currentCourse = 'CB030116'; // 固定课程编码
let logs = [];
/**
* 初始化题库
*/
function initQuestionBank() {
const savedBank = GM_getValue('localQuestionBank');
if (savedBank) {
questionBank = savedBank;
addLog(`已加载题库,总计 ${countQuestions()} 题`, 'success');
updateStats();
} else {
addLog('未找到题库,请导入JSON文件', 'warning');
}
}
/**
* 统计题目数量
*/
function countQuestions() {
let count = 0;
for (const course in questionBank) {
for (const chapter in questionBank[course].chapters) {
count += questionBank[course].chapters[chapter].length;
}
}
return count;
}
/**
* 更新统计信息
*/
function updateStats() {
const totalQuestions = countQuestions();
let coursesCount = 0;
let singleChoice = 0;
let judgment = 0;
let shortAnswer = 0;
for (const course in questionBank) {
coursesCount++;
for (const chapter in questionBank[course].chapters) {
questionBank[course].chapters[chapter].forEach(q => {
if (q.type === '单选') singleChoice++;
if (q.type === '判断') judgment++;
if (q.type === '简答') shortAnswer++;
});
}
}
const statsDisplay = document.getElementById('statsDisplay');
if (statsDisplay) {
statsDisplay.innerHTML = `
📊 题库统计
📚 总题目: ${totalQuestions} 题
🔘 单选题: ${singleChoice} 题
✅ 判断题: ${judgment} 题
📝 简答题: ${shortAnswer} 题
🎯 课程: ${coursesCount} 门
上次更新: ${new Date().toLocaleString()}
`;
}
}
/**
* 加载JSON题库文件(合并而不是覆盖)
*/
function loadQuestionBankFromFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
try {
const newBank = JSON.parse(e.target.result);
// 验证数据结构
if (isValidQuestionBank(newBank)) {
// 合并题库而不是覆盖
mergeQuestionBanks(newBank);
GM_setValue('localQuestionBank', questionBank);
updateStats();
const addedCount = countQuestionsInBank(newBank);
addLog(`题库合并成功: 新增 ${addedCount} 题,总计 ${countQuestions()} 题`, 'success');
resolve(questionBank);
} else {
addLog('题库文件格式不正确', 'error');
reject(new Error('无效的题库格式'));
}
} catch (error) {
addLog('解析题库文件失败: ' + error.message, 'error');
reject(error);
}
};
reader.onerror = function() {
addLog('读取文件失败', 'error');
reject(new Error('文件读取失败'));
};
reader.readAsText(file);
});
}
/**
* 计算题库中的题目数量
*/
function countQuestionsInBank(bank) {
let count = 0;
for (const course in bank) {
for (const chapter in bank[course].chapters) {
count += bank[course].chapters[chapter].length;
}
}
return count;
}
/**
* 合并两个题库
*/
function mergeQuestionBanks(newBank) {
const startCount = countQuestions();
for (const course in newBank) {
if (!questionBank[course]) {
// 新课程,直接添加
questionBank[course] = newBank[course];
continue;
}
// 现有课程,合并章节
for (const chapter in newBank[course].chapters) {
if (!questionBank[course].chapters[chapter]) {
// 新章节,直接添加
questionBank[course].chapters[chapter] = newBank[course].chapters[chapter];
continue;
}
// 现有章节,合并题目(去重)
const existingQuestions = questionBank[course].chapters[chapter];
const newQuestions = newBank[course].chapters[chapter];
// 创建现有题目的哈希表用于快速查找
const existingQuestionMap = new Map();
existingQuestions.forEach(q => {
const key = generateQuestionKey(q);
existingQuestionMap.set(key, q);
});
// 添加新题目(如果不存在)
newQuestions.forEach(newQ => {
const key = generateQuestionKey(newQ);
if (!existingQuestionMap.has(key)) {
existingQuestions.push(newQ);
existingQuestionMap.set(key, newQ);
}
});
}
}
const endCount = countQuestions();
return endCount - startCount;
}
/**
* 生成题目唯一标识键
*/
function generateQuestionKey(question) {
// 使用清理后的题目文本作为键
const cleanText = cleanQuestionText(question.question);
return `${question.type}_${cleanText.substring(0, 100)}`;
}
/**
* 验证题库数据结构
*/
function isValidQuestionBank(data) {
if (typeof data !== 'object' || data === null) return false;
for (const course in data) {
const courseData = data[course];
if (!courseData.course_name || !courseData.chapters) return false;
if (typeof courseData.chapters !== 'object') return false;
for (const chapter in courseData.chapters) {
if (!Array.isArray(courseData.chapters[chapter])) return false;
for (const question of courseData.chapters[chapter]) {
if (!question.question || !question.type || question.answer === undefined) {
console.warn('无效的题目:', question);
return false;
}
}
}
}
return true;
}
/**
* 导出题库为JSON文件
*/
function exportQuestionBank() {
if (Object.keys(questionBank).length === 0) {
addLog('题库为空,无法导出', 'warning');
return;
}
try {
const dataStr = JSON.stringify(questionBank, null, 2);
const dataBlob = new Blob([dataStr], {type: 'application/json'});
const downloadLink = document.createElement('a');
downloadLink.download = `题库备份_${new Date().toISOString().split('T')[0]}.json`;
downloadLink.href = URL.createObjectURL(dataBlob);
downloadLink.click();
addLog(`题库导出成功,共 ${countQuestions()} 题`, 'success');
} catch (error) {
addLog(`导出失败: ${error.message}`, 'error');
}
}
/**
* 清理题库(删除重复题目)
*/
function cleanQuestionBank() {
const startCount = countQuestions();
for (const course in questionBank) {
for (const chapter in questionBank[course].chapters) {
const questions = questionBank[course].chapters[chapter];
const uniqueQuestions = [];
const seenQuestions = new Set();
questions.forEach(q => {
const key = generateQuestionKey(q);
if (!seenQuestions.has(key)) {
seenQuestions.add(key);
uniqueQuestions.push(q);
}
});
questionBank[course].chapters[chapter] = uniqueQuestions;
}
}
GM_setValue('localQuestionBank', questionBank);
const endCount = countQuestions();
const removedCount = startCount - endCount;
if (removedCount > 0) {
addLog(`已清理 ${removedCount} 个重复题目,剩余 ${endCount} 题`, 'success');
} else {
addLog('未发现重复题目', 'info');
}
updateStats();
}
/**
* 智能搜索答案
*/
function findAnswer(questionText) {
// 清理题目文本
const cleanQuestion = cleanQuestionText(questionText);
// 优先在当前课程中搜索
if (questionBank[currentCourse]) {
const answer = searchInCourse(cleanQuestion, currentCourse);
if (answer) return answer;
}
// 如果没有,在所有课程中搜索
for (const course in questionBank) {
if (course === currentCourse) continue;
const answer = searchInCourse(cleanQuestion, course);
if (answer) return answer;
}
return null;
}
/**
* 在指定课程中搜索
*/
function searchInCourse(questionText, course) {
for (const chapter in questionBank[course].chapters) {
for (const questionData of questionBank[course].chapters[chapter]) {
const cleanBankQuestion = cleanQuestionText(questionData.question);
// 使用多种匹配策略
if (isQuestionMatch(questionText, cleanBankQuestion)) {
return {
...questionData,
source: `${questionBank[course].course_name} - ${chapter}`
};
}
}
}
return null;
}
/**
* 判断题目是否匹配
*/
function isQuestionMatch(q1, q2) {
if (!q1 || !q2) return false;
// 完全相同
if (q1 === q2) return true;
// 相互包含
if (q1.includes(q2) || q2.includes(q1)) return true;
// 长度大于5时,使用关键词匹配
if (q1.length > 5 && q2.length > 5) {
const keywords1 = extractKeywords(q1);
const keywords2 = extractKeywords(q2);
if (keywords1.length > 0 && keywords2.length > 0) {
const commonKeywords = keywords1.filter(k => keywords2.includes(k));
const similarity = commonKeywords.length / Math.max(keywords1.length, keywords2.length);
return similarity > 0.4;
}
}
return false;
}
/**
* 清理题目文本
*/
function cleanQuestionText(text) {
if (!text) return '';
// 1. 移除HTML标签
let cleaned = text.replace(/<[^>]*>/g, '');
// 2. 移除题目类型标记
cleaned = cleaned.replace(/\[.*?\]/g, '');
// 3. 移除题号前缀
cleaned = cleaned.replace(/^\d+[.\.]\s*/, '');
// 4. 移除特殊字符和多余空格
cleaned = cleaned.replace(/[,。;:()【】《》]/g, '')
.replace(/\s+/g, ' ')
.trim();
return cleaned.toLowerCase();
}
/**
* 提取关键词
*/
function extractKeywords(text) {
// 移除常见停用词
const stopWords = ['的', '了', '在', '是', '和', '与', '或', '及', '等', '对', '就', '都', '而', '且', '但', '也', '又', '再', '还', '已经'];
return text.split(/[\s,,。.;;?!?!?]/)
.filter(word => word.length > 1 && !stopWords.includes(word))
.map(word => word.toLowerCase());
}
/**
* 处理单选题 - 专门修复选项查找
*/
async function handleSingleChoice(questionElement) {
const questionText = getQuestionText(questionElement);
const cleanQuestion = cleanQuestionText(questionText);
addLog(`处理单选题: ${cleanQuestion.substring(0, 50)}...`, 'info');
const questionData = findAnswer(questionText);
if (!questionData) {
addLog(`未找到答案`, 'warning');
return;
}
// 查找所有选项 - 针对新的页面结构
const options = findSingleChoiceOptions(questionElement);
if (options.length === 0) {
addLog('未找到选项', 'warning');
// 尝试其他查找方法
const alternativeOptions = findAlternativeOptions(questionElement);
if (alternativeOptions.length > 0) {
addLog(`通过备用方法找到 ${alternativeOptions.length} 个选项`, 'info');
await selectSingleChoiceOption(questionData, alternativeOptions);
}
return;
}
// 记录找到的选项
addLog(`找到 ${options.length} 个选项: ${options.map(opt => `${opt.text}(value=${opt.value})`).join(', ')}`, 'info');
// 匹配答案
await selectSingleChoiceOption(questionData, options);
}
/**
* 查找单选题选项 - 新的查找逻辑
*/
function findSingleChoiceOptions(questionElement) {
const options = [];
// 方法1:查找题目容器后的所有选项
const questionContainer = questionElement.closest('.form-group.row');
if (!questionContainer) return options;
// 查找当前题目所在块中的所有选项
const blockContent = questionElement.closest('.block-content');
if (!blockContent) return options;
// 找到当前题目的索引位置
const allQuestions = blockContent.querySelectorAll('.form-material-success:has(b)');
let currentQuestionIndex = -1;
for (let i = 0; i < allQuestions.length; i++) {
if (allQuestions[i] === questionElement) {
currentQuestionIndex = i;
break;
}
}
if (currentQuestionIndex === -1) return options;
// 查找当前题目的选项
const allOptionRows = blockContent.querySelectorAll('.form-group.row');
const questionRow = questionContainer;
// 从当前题目行开始,向后查找选项
let currentRow = questionRow;
while (currentRow) {
currentRow = currentRow.nextElementSibling;
if (!currentRow) break;
// 如果遇到下一个题目,停止查找
if (currentRow.querySelector('.form-material-success:has(b)')) {
break;
}
// 查找单选按钮
const radios = currentRow.querySelectorAll('input[type="radio"]');
if (radios.length > 0) {
radios.forEach(radio => {
const label = radio.closest('label');
if (label) {
const optionText = getOptionText(label);
options.push({
radio: radio,
value: radio.value,
text: optionText
});
}
});
}
}
return options;
}
/**
* 备用选项查找方法
*/
function findAlternativeOptions(questionElement) {
const options = [];
// 方法2:在整个区块中查找所有选项
const blockContent = questionElement.closest('.block-content');
if (!blockContent) return options;
// 查找所有的单选按钮
const allRadios = blockContent.querySelectorAll('input[type="radio"]');
allRadios.forEach(radio => {
const label = radio.closest('label');
if (label) {
const optionText = getOptionText(label);
options.push({
radio: radio,
value: radio.value,
text: optionText
});
}
});
// 去重
const uniqueOptions = [];
const seen = new Set();
options.forEach(opt => {
const key = `${opt.value}-${opt.text}`;
if (!seen.has(key)) {
seen.add(key);
uniqueOptions.push(opt);
}
});
return uniqueOptions;
}
/**
* 选择单选题选项
*/
async function selectSingleChoiceOption(questionData, options) {
const answer = questionData.answer.trim().toUpperCase();
let targetOption = null;
// 策略1:根据答案字母直接匹配(A=0, B=1, C=2, D=3)
const answerIndex = answer.charCodeAt(0) - 65; // A=65, B=66...
// 检查答案字母是否有效
if (answerIndex >= 0 && answerIndex < 26) {
// 先尝试根据选项文本中的字母匹配
targetOption = options.find(opt => {
const optText = opt.text.toUpperCase();
return optText.startsWith(answer) || optText.includes(`.${answer}`);
});
}
// 策略2:如果题库有选项内容,尝试匹配选项文本
if (!targetOption && questionData.options && questionData.options.length > 0) {
const answerIndex = answer.charCodeAt(0) - 65;
if (answerIndex >= 0 && answerIndex < questionData.options.length) {
const correctOptionText = questionData.options[answerIndex] || '';
if (correctOptionText) {
targetOption = options.find(opt => {
const cleanOption = cleanQuestionText(opt.text);
const cleanCorrect = cleanQuestionText(correctOptionText);
return cleanOption.includes(cleanCorrect) || cleanCorrect.includes(cleanOption);
});
}
}
}
// 策略3:根据选项值匹配(假设value="0"对应A,value="1"对应B等)
if (!targetOption) {
const valueMap = { 'A': '0', 'B': '1', 'C': '2', 'D': '3' };
const targetValue = valueMap[answer];
if (targetValue) {
targetOption = options.find(opt => opt.value === targetValue);
}
}
if (targetOption) {
if (!targetOption.radio.checked) {
targetOption.radio.click();
addLog(`已选择: ${answer} (${targetOption.text})`, 'success');
} else {
addLog(`选项 ${answer} 已选中`, 'info');
}
} else {
addLog(`无法匹配选项,答案: ${answer}`, 'warning');
console.log('可用选项:', options.map(opt => ({text: opt.text, value: opt.value})));
console.log('题库选项:', questionData.options);
}
}
/**
* 处理判断题
*/
async function handleJudgment(questionElement) {
const questionText = getQuestionText(questionElement);
const cleanQuestion = cleanQuestionText(questionText);
addLog(`处理判断题: ${cleanQuestion.substring(0, 50)}...`, 'info');
const questionData = findAnswer(questionText);
if (!questionData) {
addLog(`未找到答案`, 'warning');
return;
}
// 查找选项
const options = findRadioOptions(questionElement);
if (options.length === 0) {
addLog('未找到判断题选项', 'warning');
return;
}
// 记录找到的选项
addLog(`找到 ${options.length} 个选项`, 'info');
// 判断题答案标准化
const answer = questionData.answer.trim();
let targetOption = null;
// 策略1:根据答案文本匹配选项
if (answer === '正确' || answer === '对' || answer === '是') {
targetOption = options.find(opt =>
opt.text.includes('正确') || opt.text.includes('对') || opt.text.includes('是')
);
} else if (answer === '错误' || answer === '错' || answer === '否') {
targetOption = options.find(opt =>
opt.text.includes('错误') || opt.text.includes('错') || opt.text.includes('否')
);
}
// 策略2:根据value匹配
if (!targetOption) {
if (answer === '正确' || answer === '对' || answer === '是') {
targetOption = options.find(opt => opt.value === '1');
} else if (answer === '错误' || answer === '错' || answer === '否') {
targetOption = options.find(opt => opt.value === '0');
}
}
// 策略3:根据选项顺序匹配
if (!targetOption && options.length >= 2) {
if (answer === '正确' || answer === '对' || answer === '是') {
targetOption = options[0];
} else if (answer === '错误' || answer === '错' || answer === '否') {
targetOption = options[1];
}
}
if (targetOption) {
if (!targetOption.radio.checked) {
targetOption.radio.click();
addLog(`已选择: ${answer}`, 'success');
}
} else {
addLog(`无法匹配判断题选项,答案: ${answer}`, 'warning');
}
}
/**
* 查找单选/判断题选项
*/
function findRadioOptions(questionElement) {
const options = [];
// 查找当前题目所在容器的后续兄弟节点
let currentElement = questionElement.closest('.form-group.row');
while (currentElement) {
const radios = currentElement.querySelectorAll('input[type="radio"]');
if (radios.length > 0) {
radios.forEach(radio => {
const label = radio.closest('label');
if (label) {
const optionText = getOptionText(label);
options.push({
radio: radio,
value: radio.value,
text: optionText
});
}
});
break; // 找到选项后就停止
}
currentElement = currentElement.nextElementSibling;
// 如果遇到下一个题目,停止查找
if (currentElement && currentElement.querySelector('.form-material-success:has(b)')) {
break;
}
}
return options;
}
/**
* 处理简答题
*/
async function handleShortAnswer(questionElement) {
const questionText = getQuestionText(questionElement);
addLog(`处理简答题: ${questionText.substring(0, 50)}...`, 'info');
const questionData = findAnswer(questionText);
if (!questionData) {
addLog(`未找到答案`, 'warning');
return;
}
// 查找textarea
const questionRow = questionElement.closest('.form-group.row');
if (!questionRow) return;
let currentRow = questionRow.nextElementSibling;
let textarea = null;
while (currentRow) {
textarea = currentRow.querySelector('textarea');
if (textarea) break;
currentRow = currentRow.nextElementSibling;
}
if (!textarea) {
addLog('未找到文本输入框', 'warning');
return;
}
// 填入答案
textarea.value = questionData.answer;
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
addLog('简答题答案已填入', 'success');
}
/**
* 获取题目文本
*/
function getQuestionText(element) {
if (!element) return '';
// 复制元素以避免修改原DOM
const clone = element.cloneNode(true);
// 移除所有.luanma元素
clone.querySelectorAll('.luanma').forEach(el => el.remove());
// 获取文本并清理
let text = clone.textContent || clone.innerText || '';
return text.trim();
}
/**
* 获取选项文本
*/
function getOptionText(label) {
if (!label) return '';
// 复制元素以避免修改原DOM
const clone = label.cloneNode(true);
// 移除.luanma元素
clone.querySelectorAll('.luanma').forEach(el => el.remove());
let text = clone.textContent || clone.innerText || '';
return text.trim();
}
/**
* 自动答题主函数
*/
async function autoAnswer() {
addLog('=== 开始自动答题 ===', 'info');
if (Object.keys(questionBank).length === 0) {
addLog('错误:请先导入题库文件', 'error');
return;
}
// 等待页面加载
await new Promise(resolve => setTimeout(resolve, 1000));
// 查找所有题目
const questionElements = document.querySelectorAll('.form-material-success:has(b)');
addLog(`找到 ${questionElements.length} 道题目`, 'info');
// 处理每道题
for (const questionElement of questionElements) {
const questionText = getQuestionText(questionElement);
if (questionText.includes('单选')) {
await handleSingleChoice(questionElement);
} else if (questionText.includes('判断')) {
await handleJudgment(questionElement);
} else if (questionText.includes('简答')) {
await handleShortAnswer(questionElement);
}
// 每道题之间短暂延迟
await new Promise(resolve => setTimeout(resolve, 300));
}
addLog('=== 答题完成 ===', 'success');
// 自动提交
setTimeout(autoSubmit, 1500);
}
/**
* 自动提交
*/
function autoSubmit() {
const submitSelectors = [
'#submit',
'.submit-btn',
'[type="submit"]',
'.btn-submit',
'#btnSubmit',
'button:contains("提交")'
];
for (const selector of submitSelectors) {
const submitBtn = document.querySelector(selector);
if (submitBtn) {
submitBtn.click();
addLog('已自动提交答案', 'success');
return;
}
}
addLog('未找到提交按钮,请手动提交', 'warning');
}
/**
* 调试函数:显示所有题目和选项
*/
function debugAllQuestions() {
const questionElements = document.querySelectorAll('.form-material-success:has(b)');
console.log(`调试:找到 ${questionElements.length} 道题目`);
questionElements.forEach((element, index) => {
const questionText = getQuestionText(element);
const options = findSingleChoiceOptions(element);
console.log(`题目${index+1}: ${questionText.substring(0, 60)}...`);
console.log(` 选项数: ${options.length}`);
options.forEach((opt, optIndex) => {
console.log(` 选项${optIndex+1}: value="${opt.value}", text="${opt.text}"`);
});
console.log('---');
});
addLog('调试信息已在控制台输出', 'success');
}
// 日志系统
function addLog(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString();
const logEntry = {
time: timestamp,
message: message,
type: type
};
logs.unshift(logEntry);
if (logs.length > 100) logs.length = 100;
updateLogDisplay();
}
function updateLogDisplay() {
const logList = document.getElementById('answerScriptLogs');
if (!logList) return;
logList.innerHTML = '';
logs.forEach(log => {
const logItem = document.createElement('div');
logItem.className = `log-item log-${log.type}`;
logItem.innerHTML = `[${log.time}] ${log.message}`;
logList.appendChild(logItem);
});
}
// 控制面板创建函数
function createControlPanel() {
const panel = document.createElement('div');
panel.id = 'answerScriptPanel';
// 获取保存的位置
const savedPosition = GM_getValue('panelPosition') || { top: '20px', right: '20px' };
panel.style.cssText = `
position: fixed;
top: ${savedPosition.top};
right: ${savedPosition.right};
width: 400px;
max-height: 600px;
background: white;
border: 2px solid #4CAF50;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
z-index: 9999;
font-family: 'Microsoft YaHei', Arial, sans-serif;
overflow: hidden;
resize: both;
`;
panel.innerHTML = `
导入题库
请选择JSON题库文件(支持多个文件合并)
`;
document.body.appendChild(panel);
initPanelEvents();
makePanelDraggable();
}
// 初始化面板事件
function initPanelEvents() {
document.getElementById('autoAnswerBtn').addEventListener('click', () => {
addLog('开始自动答题...', 'info');
autoAnswer();
});
document.getElementById('debugBtn').addEventListener('click', () => {
debugAllQuestions();
});
document.getElementById('exportBtn').addEventListener('click', () => {
exportQuestionBank();
});
document.getElementById('cleanBtn').addEventListener('click', () => {
cleanQuestionBank();
});
document.getElementById('clearLogsBtn').addEventListener('click', () => {
logs = [];
updateLogDisplay();
addLog('日志已清空', 'info');
});
document.getElementById('importFile').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
addLog(`正在导入: ${file.name}`, 'info');
try {
await loadQuestionBankFromFile(file);
e.target.value = '';
addLog('题库导入成功,可以开始答题', 'success');
} catch (error) {
addLog(`导入失败: ${error.message}`, 'error');
}
});
document.getElementById('minimizeBtn').addEventListener('click', () => {
const panelBody = document.getElementById('panelBody');
const minimizeBtn = document.getElementById('minimizeBtn');
if (panelBody.style.display === 'none') {
panelBody.style.display = 'block';
minimizeBtn.textContent = '−';
} else {
panelBody.style.display = 'none';
minimizeBtn.textContent = '+';
}
});
document.getElementById('closeBtn').addEventListener('click', () => {
const panel = document.getElementById('answerScriptPanel');
if (panel) {
panel.style.display = 'none';
addLog('控制面板已隐藏,刷新页面可重新显示', 'warning');
}
});
}
// 使面板可拖动
function makePanelDraggable() {
const panel = document.getElementById('answerScriptPanel');
const header = panel.querySelector('div[style*="gradient"]');
let isDragging = false;
let offsetX, offsetY;
header.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);
function startDrag(e) {
isDragging = true;
const rect = panel.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
header.style.cursor = 'grabbing';
}
function drag(e) {
if (!isDragging) return;
e.preventDefault();
// 计算新位置
let newLeft = e.clientX - offsetX;
let newTop = e.clientY - offsetY;
// 限制在视窗内
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const panelWidth = panel.offsetWidth;
const panelHeight = panel.offsetHeight;
newLeft = Math.max(0, Math.min(newLeft, viewportWidth - panelWidth));
newTop = Math.max(0, Math.min(newTop, viewportHeight - panelHeight));
// 应用新位置
panel.style.left = newLeft + 'px';
panel.style.top = newTop + 'px';
panel.style.right = 'auto';
}
function stopDrag() {
if (!isDragging) return;
isDragging = false;
header.style.cursor = 'move';
// 保存位置
const position = {
top: panel.style.top,
left: panel.style.left
};
GM_setValue('panelPosition', position);
}
}
/**
* 初始化脚本
*/
async function init() {
console.log('=== 国际贸易实务自动答题脚本启动 ===');
// 添加样式
addStyles();
// 创建控制面板
createControlPanel();
// 初始化题库
initQuestionBank();
addLog('脚本初始化完成', 'success');
addLog('请导入题库JSON文件,然后点击"自动答题"', 'info');
}
function addStyles() {
const style = document.createElement('style');
style.textContent = `
.log-item {
padding: 4px 0;
border-bottom: 1px solid #eee;
font-family: 'Microsoft YaHei', Arial, sans-serif;
font-size: 11px;
line-height: 1.3;
}
.log-item:last-child {
border-bottom: none;
}
.log-info {
color: #2196F3;
}
.log-success {
color: #4CAF50;
font-weight: bold;
}
.log-error {
color: #F44336;
}
.log-warning {
color: #FF9800;
}
.log-time {
color: #9E9E9E;
font-size: 10px;
margin-right: 5px;
}
/* 面板调整大小样式 */
#answerScriptPanel {
resize: both;
overflow: hidden;
min-width: 350px;
min-height: 200px;
max-width: 90vw;
max-height: 90vh;
}
#answerScriptPanel::-webkit-resizer {
background-color: #4CAF50;
padding: 5px;
}
`;
document.head.appendChild(style);
}
// 添加Font Awesome图标
const faLink = document.createElement('link');
faLink.rel = 'stylesheet';
faLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css';
document.head.appendChild(faLink);
// 页面加载完成后执行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();