Welearn助手
// ==UserScript==
// @name Welearn助手
// @namespace https://bbs.tampermonkey.net.cn/
// @version 0.1.6
// @author 恶搞之家
// @description 自动选择Welearn平台的选择题、判断题、填空题和下拉框答案
// @match *://course.sflep.com/*
// @match *://welearn.sflep.com/*
// @match *://wetest.sflep.com/*
// @match *://courseappserver.sflep.com/*
// @match *://centercourseware.sflep.com/*
// @match *://*.sflep.com/*
// ==/UserScript==
(function () {
'use strict';
let isAutoLoopActive = false;
let autoLoopTimeout = null;
let waitTimerActive = false;
let waitTimerInterval = null;
let defaultWaitTime = 30;
const createEvent = (type, options = {}) => new Event(type, {bubbles: true, cancelable: true, ...options});
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function autoFillAnswers() {
console.log('开始自动填空');
const answerElements = document.querySelectorAll('span.key, div.key');
const inputBoxes = document.querySelectorAll('span[autocapitalize="none"], textarea.blank');
console.log('找到填空题数量:', inputBoxes.length);
if (answerElements.length === 0 || inputBoxes.length === 0) {
console.log('没有找到填空题');
return;
}
answerElements.forEach((element, index) => {
try {
const answer = element.textContent;
const inputBox = inputBoxes[index];
if (!inputBox) return;
inputBox.tagName === 'SPAN' ? inputBox.textContent = answer : inputBox.value = answer;
['click', 'input'].forEach(evt => inputBox.dispatchEvent(createEvent(evt)));
console.log(`已填写第${index + 1}题答案:`, answer);
} catch (error) {
console.log(`填写第${index + 1}题出错:`, error);
}
});
await sleep(500);
inputBoxes.forEach((inputBox, index) => {
try {
if (!document.body.contains(inputBox)) return;
inputBox.dispatchEvent(createEvent('click'));
inputBox.dispatchEvent(createEvent('focus'));
setTimeout(() => {
inputBox.dispatchEvent(createEvent('blur'));
}, 50);
if (inputBox.tagName === 'TEXTAREA') {
inputBox.dispatchEvent(createEvent('change'));
}
} catch (error) {
console.log(`额外点击第${index + 1}个填空框出错:`, error);
}
});
console.log('填空题处理完成');
}
async function autoSelectCorrectAnswers() {
console.log('开始自动选择答案');
let allQuestions = [];
for (let i = 0; i < 10 && allQuestions.length === 0; i++) {
const choiceQuestions = document.querySelectorAll('et-choice[et-index]');
const tfQuestions = document.querySelectorAll('et-tof[et-index]');
allQuestions = [...choiceQuestions, ...tfQuestions];
if (allQuestions.length === 0) {
console.log('等待题目加载...');
await sleep(700);
}
}
console.log('找到题目数量:', allQuestions.length);
if (allQuestions.length === 0) {
console.log('没有找到题目');
return;
}
for (let i = 0; i < allQuestions.length; i++) {
const question = allQuestions[i];
const isTF = question.tagName.toLowerCase() === 'et-tof';
let options = [];
for (let j = 0; j < 10 && options.length === 0; j++) {
if (isTF) {
options = question.querySelectorAll('span[ng-class*="chosen:tof.value"], div.wrapper span');
} else {
options = question.querySelectorAll('ol > li, div.wrapper li, span[ng-click*="choice.select"], li[class]');
}
if (options.length === 0) {
console.log(`等待第${i + 1}题选项加载...`);
await sleep(500);
}
}
console.log(`处理第${i + 1}题,选项数量:`, options.length);
if (options.length === 0) {
console.log('未找到选项,跳过');
continue;
}
let isCorrect = false;
for (let attempt = 0; attempt < 3 && !isCorrect; attempt++) {
for (const option of options) {
try {
const wrapper = isTF
? option.closest('div[ng-class*="isKeyVisible"]')
: option.closest('div.wrapper');
if (wrapper?.classList.contains('correct')) {
console.log('此选项已经正确');
isCorrect = true;
break;
}
option.click();
await sleep(500);
const updatedWrapper = isTF
? option.closest('div[ng-class*="isKeyVisible"]')
: option.closest('div.wrapper');
if (updatedWrapper?.classList.contains('correct')) {
console.log('找到正确答案:', option.textContent.trim());
isCorrect = true;
break;
}
} catch (error) {
console.log('点击选项出错:', error);
}
}
if (!isCorrect) await sleep(600);
}
await sleep(500);
}
console.log('所有题目处理完成');
}
function autoSelectDropdownAnswers() {
console.log('开始处理下拉框选择题');
const dropdowns = document.querySelectorAll('select[ng-model]');
console.log('找到下拉框数量:', dropdowns.length);
if (dropdowns.length === 0) return;
dropdowns.forEach((dropdown, index) => {
try {
const correctOption = dropdown.querySelector('option.key');
if (!correctOption) return;
dropdown.value = correctOption.value;
dropdown.dispatchEvent(createEvent('change'));
console.log(`已选择第${index + 1}个下拉框答案:`, correctOption.textContent.trim());
} catch (error) {
console.log(`处理第${index + 1}个下拉框出错:`, error);
}
});
}
async function autoMatchLines() {
console.log('开始处理连线题');
const matchingElements = document.querySelectorAll('et-matching[et-index]');
if (matchingElements.length === 0) return;
console.log(`找到连线题数量: ${matchingElements.length}`);
for (const matchingElement of matchingElements) {
const matchingKey = matchingElement.getAttribute('key');
if (!matchingKey) continue;
console.log(`处理连线题,答案key: ${matchingKey}`);
try {
const pairs = matchingKey.split(',').map(pair => {
const [left, right] = pair.split('-');
return {
leftIndex: parseInt(left) - 1,
rightIndex: parseInt(right) - 1
};
});
let angularSuccess = false;
if (typeof angular !== 'undefined') {
try {
const scope = angular.element(matchingElement).scope();
if (scope?.matching) {
if (Array.isArray(scope.matching.lines)) {
scope.matching.lines = [];
}
for (const {leftIndex, rightIndex} of pairs) {
const leftCircle = scope.matching.circles?.A?.[leftIndex];
const rightCircle = scope.matching.circles?.B?.[rightIndex];
if (!leftCircle || !rightCircle) continue;
if (typeof leftCircle.select === 'function') {
leftCircle.select();
await sleep(200);
rightCircle.select?.();
} else if (typeof scope.matching.connect === 'function') {
scope.matching.connect(leftCircle, rightCircle);
} else if (Array.isArray(scope.matching.lines)) {
scope.matching.lines.push({
x1: leftCircle.x,
y1: leftCircle.y,
x2: rightCircle.x,
y2: rightCircle.y,
circleA: leftCircle,
circleB: rightCircle
});
}
await sleep(200);
}
scope.$apply?.();
await sleep(500);
if (Array.isArray(scope.matching.lines) && scope.matching.lines.length > 0) {
angularSuccess = true;
}
}
} catch (e) {
console.error('Angular模型操作失败:', e);
}
}
if (angularSuccess) continue;
console.log('尝试DOM操作方式');
let leftCircles = [];
let rightCircles = [];
leftCircles = Array.from(matchingElement.querySelectorAll('circle[data-circle="A"]'));
rightCircles = Array.from(matchingElement.querySelectorAll('circle[data-circle="B"]'));
if (leftCircles.length === 0 || rightCircles.length === 0) {
const allCircles = matchingElement.querySelectorAll('circle[ng-repeat]');
leftCircles = [];
rightCircles = [];
for (const circle of allCircles) {
const ngRepeat = circle.getAttribute('ng-repeat');
if (ngRepeat?.includes('matching.circles.A')) {
leftCircles.push(circle);
} else if (ngRepeat?.includes('matching.circles.B')) {
rightCircles.push(circle);
}
}
}
if (leftCircles.length === 0 || rightCircles.length === 0) {
const allCircles = matchingElement.querySelectorAll('circle');
if (allCircles.length > 1) {
const circlesWithCoords = Array.from(allCircles)
.map(circle => ({
element: circle,
x: parseFloat(circle.getAttribute('cx')),
y: parseFloat(circle.getAttribute('cy'))
}))
.filter(c => !isNaN(c.x) && !isNaN(c.y));
if (circlesWithCoords.length > 1) {
circlesWithCoords.sort((a, b) => a.x - b.x);
const midX = (circlesWithCoords[0].x + circlesWithCoords[circlesWithCoords.length - 1].x) / 2;
leftCircles = circlesWithCoords.filter(c => c.x < midX).map(c => c.element);
rightCircles = circlesWithCoords.filter(c => c.x >= midX).map(c => c.element);
}
}
}
if (leftCircles.length === 0 || rightCircles.length === 0) continue;
for (const {leftIndex, rightIndex} of pairs) {
if (leftIndex < 0 || leftIndex >= leftCircles.length ||
rightIndex < 0 || rightIndex >= rightCircles.length) continue;
const leftCircle = leftCircles[leftIndex];
const rightCircle = rightCircles[rightIndex];
const leftX = parseFloat(leftCircle.getAttribute('cx'));
const leftY = parseFloat(leftCircle.getAttribute('cy'));
const rightX = parseFloat(rightCircle.getAttribute('cx'));
const rightY = parseFloat(rightCircle.getAttribute('cy'));
try {
const createMouseEvent = (type, x, y) => new MouseEvent(type, {
bubbles: true, cancelable: true, view: window,
clientX: x, clientY: y
});
['mousedown', 'click'].forEach(type =>
leftCircle.dispatchEvent(createMouseEvent(type, leftX, leftY)));
await sleep(300);
['mousedown', 'click'].forEach(type =>
rightCircle.dispatchEvent(createMouseEvent(type, rightX, rightY)));
await sleep(300);
if (typeof angular !== 'undefined') {
const scope = angular.element(leftCircle).scope();
scope?.$apply?.();
}
} catch (e) {
console.error('点击圆圈出错:', e);
}
}
} catch (error) {
console.error(`处理连线题出错:`, error);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log('所有连线题处理完成');
}
function isElementVisible(element) {
if (!element) return false;
try {
const style = window.getComputedStyle(element);
return style.display !== 'none' &&
style.visibility !== 'hidden' &&
element.offsetParent !== null;
} catch (e) {
return true;
}
}
async function clickElement(element) {
if (!element) return;
try {
element.click();
await sleep(100);
const cx = parseFloat(element.getAttribute('cx')) || 0;
const cy = parseFloat(element.getAttribute('cy')) || 0;
const mouseEventOptions = {
view: window,
bubbles: true,
cancelable: true,
clientX: cx,
clientY: cy
};
['mousedown', 'mouseup', 'click'].forEach(type =>
element.dispatchEvent(new MouseEvent(type, mouseEventOptions)));
if (typeof angular !== 'undefined') {
const scope = angular.element(element).scope();
scope?.$apply?.();
if (element.tagName === 'circle' && element.hasAttribute('data-index')) {
const dataIndex = element.getAttribute('data-index');
const dataCircle = element.getAttribute('data-circle');
const matchingElement = element.closest('et-matching[et-index]');
if (matchingElement && dataCircle) {
const matchingScope = angular.element(matchingElement).scope();
const circles = matchingScope?.matching?.circles?.[dataCircle];
const circle = circles?.[dataIndex];
if (circle?.select) {
circle.select();
matchingScope.$apply?.();
}
}
}
}
} catch (error) {
console.error('点击元素出错:', error);
}
}
async function submitAnswers() {
console.log('准备提交答案');
let submitButton;
for (let i = 0; i < 10 && !submitButton; i++) {
try {
const etButtons = document.querySelectorAll('et-button[right][action="item.submit()"]');
for (const etBtn of etButtons) {
const btn = etBtn.querySelector('button[ng-click="btn.doAction()"]');
if (btn) {
submitButton = btn;
break;
}
}
if (!submitButton) {
submitButton = Array.from(document.querySelectorAll('button')).find(btn =>
btn.textContent.trim() === 'Submit' ||
btn.querySelector('span')?.textContent.trim() === 'Submit'
);
}
if (!submitButton) {
submitButton = document.querySelector('button[ng-click*="btn.doAction()"]');
}
if (!submitButton) {
for (const etBtn of document.querySelectorAll('et-button')) {
const btn = etBtn.querySelector('button');
if (btn?.textContent.includes('Submit') || btn?.innerHTML.includes('Submit')) {
submitButton = btn;
break;
}
}
}
if (submitButton && (!submitButton.disabled && submitButton.offsetParent !== null)) {
break;
} else {
submitButton = null;
}
} catch (error) {
console.log('查找Submit按钮出错:', error);
}
if (!submitButton) {
await sleep(1000);
}
}
if (!submitButton) {
console.log('未找到Submit按钮');
return false;
}
console.log('找到Submit按钮');
try {
submitButton.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(1000);
submitButton.click();
await sleep(500);
submitButton.dispatchEvent(new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
}));
if (typeof angular !== 'undefined') {
const etButtonElement = submitButton.closest('et-button');
if (etButtonElement?.getAttribute('action') === 'item.submit()') {
const scope = angular.element(etButtonElement).scope();
scope?.item?.submit?.();
scope?.$apply?.();
}
const scope = angular.element(submitButton).scope();
scope?.btn?.doAction?.();
scope?.$apply?.();
}
await sleep(4000);
return await autoNextSection();
} catch (error) {
console.log('点击Submit按钮出错:', error);
return false;
}
}
async function autoNextSection() {
console.log('准备自动跳转到下一章节');
const isInIframe = window !== window.top;
console.log(`跳转环境: ${isInIframe ? 'iframe内' : '主窗口'}`);
if (isInIframe) {
console.log('尝试在父窗口中执行NextSCO函数');
try {
const scriptForParent = document.createElement('script');
scriptForParent.textContent = `
try {
if (window.parent && window.parent !== window) {
if (typeof window.parent.NextSCO === 'function') {
window.parent.NextSCO();
console.log("在父窗口中成功调用NextSCO");
} else {
console.log("父窗口中不存在NextSCO函数");
window.parent.location.href = 'javascript:void(0); try { NextSCO(); } catch(e) {}';
}
} else {
console.log("无法访问父窗口");
}
} catch(e) {
console.error("向父窗口发送NextSCO调用失败:", e);
}
`;
document.body.appendChild(scriptForParent);
document.body.removeChild(scriptForParent);
try {
window.parent.postMessage({ action: 'executeNextSCO' }, '*');
console.log('已向父窗口发送postMessage请求');
const listenerScript = document.createElement('script');
listenerScript.textContent = `
(function() {
if (window.parent === window) {
window.addEventListener('message', function(event) {
if (event.data && event.data.action === 'executeNextSCO') {
console.log('收到执行NextSCO的消息');
try {
if (typeof NextSCO === 'function') {
NextSCO();
console.log('成功执行NextSCO');
} else {
console.log('NextSCO函数不存在');
try {
location.href = 'javascript:NextSCO();';
} catch(e) {}
}
} catch(e) {
console.error('执行NextSCO失败:', e);
}
}
});
}
})();
`;
document.body.appendChild(listenerScript);
document.body.removeChild(listenerScript);
} catch (e) {
console.log('发送postMessage失败:', e);
}
try {
window.top.location.href = 'javascript:void(0); try { NextSCO(); } catch(e) {}';
console.log('已尝试改变顶层窗口location');
} catch (e) {
console.log('改变顶层窗口location失败:', e);
}
return true;
} catch (e) {
console.log('跳出iframe失败:', e);
}
}
try {
console.log('尝试点击页面中的javascript:NextSCO()链接');
const navLinks = document.querySelectorAll('a[href*="javascript:NextSCO()"], a[href*="javascript:PrevSCO()"]');
if (navLinks.length > 0) {
const nextLink = Array.from(navLinks).find(link =>
link.href.includes('NextSCO') && !link.href.includes('PrevSCO')
);
if (nextLink) {
console.log('找到NextSCO链接,尝试点击');
nextLink.click();
return;
}
}
} catch (e) {
console.log('点击NextSCO链接失败:', e);
}
try {
const textNodes = [];
function findTextNodes(node) {
if (node.nodeType === 3) {
if (node.nodeValue.includes('javascript:NextSCO()')) {
textNodes.push(node);
}
} else if (node.nodeType === 1) {
for (let i = 0; i < node.childNodes.length; i++) {
findTextNodes(node.childNodes[i]);
}
}
}
findTextNodes(document.body);
if (textNodes.length > 0) {
console.log('找到包含NextSCO文本的节点:', textNodes.length, '个');
for (const textNode of textNodes) {
let current = textNode.parentElement;
while (current && current.tagName !== 'A' && current.tagName !== 'BUTTON') {
current = current.parentElement;
}
if (current) {
console.log('点击包含NextSCO文本的元素');
current.click();
return;
}
}
}
} catch (e) {
console.log('查找NextSCO文本失败:', e);
}
try {
console.log('尝试查找侧边栏中的蓝色箭头按钮');
const potentialNavButtons = [
...document.querySelectorAll('.courseware_sidebar a, .courseware_sidebar_2 a'),
...document.querySelectorAll('a[class*="nav"], img[src*="arrow"], img[src*="next"]'),
...document.querySelectorAll('a img[src*="arrow"], a img[src*="next"]'),
...document.querySelectorAll('ul.c_s_3 li a'),
...document.querySelectorAll('li.c_s_3_2 a')
];
const navButtons = Array.from(potentialNavButtons).filter(btn => {
const rect = btn.getBoundingClientRect();
const isAtSide = rect.left < 100 || rect.right > window.innerWidth - 100;
try {
const style = window.getComputedStyle(btn);
const hasTurquoiseColor =
style.color.includes('rgb(0, 255, 255)') ||
style.color.includes('rgb(0, 255, 255)') ||
style.backgroundColor.includes('rgb(0, 255, 255)') ||
style.background.includes('rgb(0, 255, 255)') ||
btn.innerHTML.includes('rgb(0, 255, 255)') ||
btn.innerHTML.includes('#00ffff') ||
btn.innerHTML.includes('cyan');
const isArrow =
btn.textContent.includes('→') ||
btn.textContent.includes('▶') ||
btn.textContent.includes('▷') ||
btn.innerHTML.includes('arrow') ||
(btn.querySelector('img') && (
btn.querySelector('img').src.includes('arrow') ||
btn.querySelector('img').src.includes('next')
));
return isAtSide || hasTurquoiseColor || isArrow;
} catch (e) {
return isAtSide;
}
});
console.log(`找到${navButtons.length}个可能的导航按钮`);
if (navButtons.length > 0) {
for (const btn of navButtons) {
console.log(`点击导航按钮: ${btn.textContent || btn.className || btn.id || '未命名按钮'}`);
btn.click();
await new Promise(resolve => setTimeout(resolve, 500));
}
return;
}
} catch (e) {
console.log('查找导航按钮出错:', e);
}
try {
console.log('尝试直接执行NextSCO函数');
const scriptEl = document.createElement('script');
scriptEl.textContent = `
try {
if (typeof NextSCO === 'function') {
NextSCO();
console.log("成功执行NextSCO函数");
} else {
console.log("NextSCO函数未定义");
}
} catch(e) {
console.error("执行NextSCO出错:", e);
}
`;
document.body.appendChild(scriptEl);
document.body.removeChild(scriptEl);
setTimeout(() => {
try {
window.location.href = 'javascript:void(0); try { NextSCO(); } catch(e) {}';
} catch (e) { }
}, 1000);
} catch (e) {
console.log('执行脚本失败:', e);
}
}
async function ensureCorrectContext() {
const isInIframe = window !== window.top;
console.log(`当前环境: ${isInIframe ? 'iframe内' : '主窗口'}`);
if (isInIframe && window.name === 'contentFrame') {
console.log('已在答题的iframe中,可以直接答题');
return;
}
if (!isInIframe) {
console.log('在主窗口中,尝试切换到contentFrame');
let contentFrame = document.getElementById('contentFrame');
if (!contentFrame) {
const frames = document.getElementsByTagName('iframe');
for (let i = 0; i < frames.length; i++) {
if (frames[i].name === 'contentFrame') {
contentFrame = frames[i];
break;
}
}
}
if (contentFrame) {
console.log('找到contentFrame,准备在其中执行答题');
try {
return executeInContentFrame(contentFrame);
} catch (e) {
console.error('向contentFrame执行答题失败:', e);
}
} else {
const iframes = document.querySelectorAll('iframe');
console.log(`共找到${iframes.length}个iframe`);
for (let i = 0; i < iframes.length; i++) {
try {
const iframe = iframes[i];
if (!iframe.contentDocument) continue;
const hasQuizElements =
iframe.contentDocument.querySelector('et-choice') ||
iframe.contentDocument.querySelector('et-tof') ||
iframe.contentDocument.querySelector('span.key') ||
iframe.contentDocument.querySelector('div.key') ||
iframe.contentDocument.querySelector('select[ng-model]') ||
iframe.contentDocument.querySelector('button');
if (hasQuizElements) {
console.log(`找到可能的答题iframe: ${iframe.name || iframe.id || i}`);
return executeInContentFrame(iframe);
}
} catch (e) {
console.log(`检查iframe ${i}失败:`, e);
}
}
console.log('未找到答题iframe,直接在当前窗口执行');
}
}
}
async function executeInContentFrame(frame) {
console.log('准备在iframe中执行答题');
try {
if (typeof frame.contentWindow.autoSelectCorrectAnswers === 'function') {
console.log('iframe中已存在答题函数,直接调用');
return {
autoSelectCorrectAnswers: frame.contentWindow.autoSelectCorrectAnswers,
autoFillAnswers: frame.contentWindow.autoFillAnswers,
autoSelectDropdownAnswers: frame.contentWindow.autoSelectDropdownAnswers,
autoMatchLines: frame.contentWindow.autoMatchLines,
submitAnswers: frame.contentWindow.submitAnswers
};
}
const scriptContent = `
if (!window.weLearnHelperInjected) {
window.weLearnHelperInjected = true;
window.parent.postMessage({
action: 'iframeReady',
frameId: '${frame.id || frame.name || 'unknown'}'
}, '*');
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const createEvent = (type, options = {}) => new Event(type, {bubbles: true, cancelable: true, ...options});
${autoFillAnswers.toString()}
${autoSelectCorrectAnswers.toString()}
${autoSelectDropdownAnswers.toString()}
${autoMatchLines.toString()}
${submitAnswers.toString()}
window.addEventListener('message', function(event) {
if (event.data && event.data.action === 'runQuizFunctions') {
console.log('iframe收到执行答题请求');
const executeAsync = async () => {
try {
if (event.data.step === 'selectAnswers') {
await autoSelectCorrectAnswers();
window.parent.postMessage({ action: 'stepComplete', step: 'selectAnswers' }, '*');
} else if (event.data.step === 'fillAnswers') {
autoFillAnswers();
window.parent.postMessage({ action: 'stepComplete', step: 'fillAnswers' }, '*');
} else if (event.data.step === 'selectDropdowns') {
autoSelectDropdownAnswers();
window.parent.postMessage({ action: 'stepComplete', step: 'selectDropdowns' }, '*');
} else if (event.data.step === 'matchLines') {
await autoMatchLines();
window.parent.postMessage({ action: 'stepComplete', step: 'matchLines' }, '*');
} else if (event.data.step === 'submitAnswers') {
await submitAnswers();
window.parent.postMessage({ action: 'stepComplete', step: 'submitAnswers' }, '*');
} else if (event.data.step === 'all') {
await autoSelectCorrectAnswers();
autoFillAnswers();
autoSelectDropdownAnswers();
await autoMatchLines();
await submitAnswers();
window.parent.postMessage({ action: 'stepComplete', step: 'all' }, '*');
}
} catch (error) {
console.error('执行iframe中的答题函数出错:', error);
window.parent.postMessage({ action: 'stepError', error: error.message }, '*');
}
};
executeAsync();
}
});
}
`;
const script = document.createElement('script');
script.textContent = scriptContent;
try {
frame.contentDocument.body.appendChild(script);
frame.contentDocument.body.removeChild(script);
console.log('已将答题脚本注入到iframe中');
window.addEventListener('message', handleIframeMessage);
return {
autoSelectCorrectAnswers: () => sendCommandToIframe(frame, 'selectAnswers'),
autoFillAnswers: () => sendCommandToIframe(frame, 'fillAnswers'),
autoSelectDropdownAnswers: () => sendCommandToIframe(frame, 'selectDropdowns'),
autoMatchLines: () => sendCommandToIframe(frame, 'matchLines'),
submitAnswers: () => sendCommandToIframe(frame, 'submitAnswers')
};
} catch (e) {
console.error('无法向iframe注入脚本:', e);
}
console.log('尝试直接在iframe中执行答题代码');
} catch (e) {
console.error('iframe操作失败:', e);
}
return null;
}
function handleIframeMessage(event) {
if (event.data && event.data.action) {
if (event.data.action === 'iframeReady') {
console.log(`iframe已就绪: ${event.data.frameId}`);
} else if (event.data.action === 'stepComplete') {
console.log(`iframe完成步骤: ${event.data.step}`);
} else if (event.data.action === 'stepError') {
console.error(`iframe执行出错: ${event.data.error}`);
}
}
}
function sendCommandToIframe(frame, step) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
cleanup();
reject(new Error(`向iframe发送${step}命令超时`));
}, 30000);
const messageHandler = (event) => {
if (event.data && event.data.action === 'stepComplete' && event.data.step === step) {
cleanup();
resolve();
} else if (event.data && event.data.action === 'stepError') {
cleanup();
reject(new Error(event.data.error || '未知iframe错误'));
}
};
const cleanup = () => {
clearTimeout(timeout);
window.removeEventListener('message', messageHandler);
};
window.addEventListener('message', messageHandler);
try {
frame.contentWindow.postMessage({ action: 'runQuizFunctions', step: step }, '*');
console.log(`已向iframe发送${step}命令`);
} catch (e) {
cleanup();
reject(e);
}
});
}
function createIntegratedUI() {
const globalStyle = document.createElement('style');
globalStyle.id = 'welearn-helper-style';
globalStyle.textContent = `
@keyframes button-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.03); }
100% { transform: scale(1); }
}
.welearn-button {
position: static;
flex: 1;
padding: 10px 15px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
font-size: 14px;
transition: all 0.3s ease;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1);
}
.welearn-button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2), 0 2px 5px rgba(0, 0, 0, 0.1);
}
.welearn-button:active {
transform: translateY(1px);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
}
.welearn-auto-button {
background: linear-gradient(135deg, #00ff9d 0%, #00c17b 100%);
color: rgba(0, 0, 0, 0.8);
}
.welearn-auto-button:hover {
background: linear-gradient(135deg, #00ffaa 0%, #00d38a 100%);
}
.welearn-loop-button {
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
color: white;
}
.welearn-loop-button:hover {
background: linear-gradient(135deg, #5CBF60 0%, #3E8D42 100%);
}
.welearn-loop-active {
background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%);
animation: button-pulse 2s infinite ease-in-out;
}
.welearn-loop-active:hover {
background: linear-gradient(135deg, #ff5252 0%, #e53935 100%);
}
`;
const existingStyle = document.getElementById('welearn-helper-style');
if (existingStyle) existingStyle.remove();
document.head.appendChild(globalStyle);
const mainPanel = document.createElement('div');
mainPanel.id = 'welearnHelperPanel';
mainPanel.style.cssText = `
position: fixed;
top: 10px;
left: 10px;
z-index: 10000;
background-color: rgba(33, 33, 33, 0.85);
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
padding: 12px;
color: white;
font-size: 13px;
width: 240px;
display: flex;
flex-direction: column;
gap: 10px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.1);
`;
const titleBar = document.createElement('div');
titleBar.style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
padding-bottom: 8px;
margin-bottom: 5px;
`;
const title = document.createElement('div');
title.textContent = 'Welearn助手';
title.style.cssText = `
font-weight: bold;
font-size: 15px;
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
`;
const minimizeBtn = document.createElement('button');
minimizeBtn.textContent = '-';
minimizeBtn.style.cssText = `
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.1);
transition: background-color 0.2s ease;
`;
minimizeBtn.onmouseover = () => {
minimizeBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
};
minimizeBtn.onmouseout = () => {
minimizeBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
};
minimizeBtn.onclick = () => {
const content = document.getElementById('welearnHelperContent');
if (content.style.display === 'none') {
content.style.display = 'flex';
minimizeBtn.textContent = '-';
} else {
content.style.display = 'none';
minimizeBtn.textContent = '+';
}
};
titleBar.appendChild(title);
titleBar.appendChild(minimizeBtn);
mainPanel.appendChild(titleBar);
const content = document.createElement('div');
content.id = 'welearnHelperContent';
content.style.cssText = `
display: flex;
flex-direction: column;
gap: 10px;
`;
mainPanel.appendChild(content);
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = `
display: flex;
gap: 10px;
`;
const autoAnswerButton = document.createElement('button');
autoAnswerButton.id = 'autoAnswerButton';
autoAnswerButton.textContent = '一键全自动';
autoAnswerButton.className = 'welearn-button welearn-auto-button';
const autoLoopButton = document.createElement('button');
autoLoopButton.id = 'autoLoopButton';
autoLoopButton.textContent = '开始自动挂机';
autoLoopButton.className = 'welearn-button welearn-loop-button';
autoAnswerButton.onclick = async function() {
await runAutoAnswerProcess(false);
};
autoLoopButton.onclick = async function() {
await toggleAutoLoop();
};
buttonContainer.appendChild(autoAnswerButton);
buttonContainer.appendChild(autoLoopButton);
content.appendChild(buttonContainer);
const timerContainer = document.createElement('div');
timerContainer.id = 'waitTimerContainer';
timerContainer.style.cssText = `
display: flex;
align-items: center;
background-color: rgba(255, 255, 255, 0.08);
padding: 8px 10px;
border-radius: 6px;
border: 1px solid rgba(255, 255, 255, 0.05);
`;
const timeInput = document.createElement('input');
timeInput.id = 'waitTimeInput';
timeInput.type = 'number';
timeInput.min = '1';
timeInput.max = '3600';
timeInput.value = defaultWaitTime.toString();
timeInput.style.cssText = `
width: 45px;
padding: 5px;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.2);
text-align: center;
margin: 0 5px;
color: black;
background-color: rgba(255, 255, 255, 0.9);
font-weight: bold;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
`;
timeInput.addEventListener('change', function() {
const newTime = parseInt(this.value, 10);
if (!isNaN(newTime) && newTime > 0) {
defaultWaitTime = newTime;
const countdownDisplay = document.getElementById('countdownDisplay');
if (countdownDisplay) {
const originalText = countdownDisplay.textContent;
const originalColor = countdownDisplay.style.color;
countdownDisplay.textContent = '已保存';
countdownDisplay.style.color = '#4CAF50';
setTimeout(() => {
if (countdownDisplay) {
countdownDisplay.textContent = originalText;
countdownDisplay.style.color = originalColor;
}
}, 1000);
}
}
});
const label = document.createElement('span');
label.textContent = '刷时长:';
label.style.cssText = `
margin-right: 4px;
font-weight: 500;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
`;
const secondsLabel = document.createElement('span');
secondsLabel.textContent = '秒';
secondsLabel.style.cssText = `
margin-right: 5px;
font-weight: 500;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
`;
const statusDisplay = document.createElement('div');
statusDisplay.id = 'countdownDisplay';
statusDisplay.textContent = '未开始';
statusDisplay.style.cssText = `
margin-left: auto;
font-weight: bold;
color: #00ffcc;
min-width: 60px;
text-align: right;
text-shadow: 0 0 10px rgba(0, 255, 204, 0.5);
`;
timerContainer.appendChild(label);
timerContainer.appendChild(timeInput);
timerContainer.appendChild(secondsLabel);
timerContainer.appendChild(statusDisplay);
content.appendChild(timerContainer);
const statusContainer = document.createElement('div');
statusContainer.style.cssText = `
font-size: 12px;
color: #ccc;
text-align: center;
padding: 5px;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 4px;
`;
statusContainer.textContent = 'by恶搞之家';
content.appendChild(statusContainer);
const loopStatus = document.createElement('div');
loopStatus.id = 'loopStatusIndicator';
loopStatus.style.cssText = `
display: none;
align-items: center;
justify-content: center;
background-color: rgba(220, 20, 60, 0.2);
padding: 6px;
border-radius: 5px;
margin-top: 2px;
font-weight: bold;
font-size: 12px;
border: 1px solid rgba(220, 20, 60, 0.3);
`;
content.appendChild(loopStatus);
return mainPanel;
}
async function executeWaitTimer(callback) {
const timeInput = document.getElementById('waitTimeInput');
const countdownDisplay = document.getElementById('countdownDisplay');
let waitTime = parseInt(timeInput?.value || defaultWaitTime);
if (isNaN(waitTime) || waitTime < 1) {
waitTime = defaultWaitTime;
if (timeInput) timeInput.value = defaultWaitTime.toString();
} else {
defaultWaitTime = waitTime;
}
if (waitTime <= 0) {
if (typeof callback === 'function') callback();
return;
}
if (countdownDisplay) {
countdownDisplay.textContent = waitTime + ' 秒';
countdownDisplay.style.color = '#00ffcc';
countdownDisplay.style.display = 'block';
}
if (waitTimerInterval) {
clearInterval(waitTimerInterval);
}
waitTimerActive = true;
let remainingTime = waitTime;
return new Promise(resolve => {
waitTimerInterval = setInterval(() => {
remainingTime--;
if (countdownDisplay) {
countdownDisplay.textContent = remainingTime + ' 秒';
if (remainingTime <= 10) {
countdownDisplay.style.color = '#ff6b6b';
}
}
if (remainingTime <= 0) {
clearInterval(waitTimerInterval);
waitTimerInterval = null;
waitTimerActive = false;
if (countdownDisplay) {
countdownDisplay.textContent = '完成!';
countdownDisplay.style.color = '#4CAF50';
setTimeout(() => {
if (countdownDisplay) {
countdownDisplay.textContent = '未开始';
countdownDisplay.style.color = '#00ffcc';
}
}, 3000);
}
if (typeof callback === 'function') callback();
resolve();
}
}, 1000);
});
}
function cancelWaitTimer() {
if (waitTimerInterval) {
clearInterval(waitTimerInterval);
waitTimerInterval = null;
}
waitTimerActive = false;
const countdownDisplay = document.getElementById('countdownDisplay');
if (countdownDisplay) {
countdownDisplay.textContent = '已取消';
countdownDisplay.style.color = '#ff9800';
setTimeout(() => {
if (countdownDisplay) countdownDisplay.textContent = '';
}, 3000);
}
}
function addButton() {
if (window !== window.top) {
console.log('当前在iframe中,不添加按钮');
return;
}
removeExistingButtons();
const uiPanel = createIntegratedUI();
document.body.appendChild(uiPanel);
updateButtonsState();
}
function updateButtonsState() {
const loopButton = document.getElementById('autoLoopButton');
if (loopButton) {
if (isAutoLoopActive) {
loopButton.textContent = '停止自动挂机';
loopButton.className = 'welearn-button welearn-loop-active';
showLoopStatus(true);
} else {
loopButton.textContent = '开始自动挂机';
loopButton.className = 'welearn-button welearn-loop-button';
showLoopStatus(false);
}
}
}
function showLoopStatus(isActive) {
let statusIndicator = document.getElementById('loopStatusIndicator');
if (!statusIndicator) return;
if (isActive) {
statusIndicator.innerHTML = '<span style="display:inline-block;width:10px;height:10px;background-color:red;border-radius:50%;margin-right:5px;animation:blink 1s infinite;"></span> 自动挂机中';
statusIndicator.style.display = 'flex';
const style = document.createElement('style');
style.id = 'loopStatusStyle';
style.textContent = `
@keyframes blink {
0% { opacity: 1; }
50% { opacity: 0.3; }
100% { opacity: 1; }
}
`;
if (!document.getElementById('loopStatusStyle')) {
document.head.appendChild(style);
}
} else {
statusIndicator.style.display = 'none';
}
}
async function toggleAutoLoop() {
if (!isAutoLoopActive) {
isAutoLoopActive = true;
updateButtonsState();
showLoopStatus(true);
await runAutoAnswerProcess(true);
} else {
stopAutoLoop();
}
}
function stopAutoLoop() {
console.log('停止自动挂机');
isAutoLoopActive = false;
if (autoLoopTimeout) {
clearTimeout(autoLoopTimeout);
autoLoopTimeout = null;
}
cancelWaitTimer();
updateButtonsState();
showLoopStatus(false);
}
async function runAutoAnswerProcess(isLoop) {
if (!isLoop && isAutoLoopActive) {
console.log('自动挂机已激活,单次执行被忽略');
return;
}
try {
const answerButton = document.getElementById('autoAnswerButton');
if (isLoop) {
if (answerButton) {
answerButton.textContent = '等待中...';
answerButton.style.backgroundColor = 'orange';
}
await executeWaitTimer(() => {
if (answerButton) {
answerButton.textContent = '开始答题...';
}
});
if (!isAutoLoopActive) return;
}
if (answerButton) {
answerButton.textContent = '检测环境...';
answerButton.style.backgroundColor = 'orange';
}
const iframeFunctions = await ensureCorrectContext();
const selectAnswersFn = iframeFunctions ? iframeFunctions.autoSelectCorrectAnswers : autoSelectCorrectAnswers;
const fillAnswersFn = iframeFunctions ? iframeFunctions.autoFillAnswers : autoFillAnswers;
const selectDropdownsFn = iframeFunctions ? iframeFunctions.autoSelectDropdownAnswers : autoSelectDropdownAnswers;
const matchLinesFn = iframeFunctions ? iframeFunctions.autoMatchLines : autoMatchLines;
const submitAnswersFn = iframeFunctions ? iframeFunctions.submitAnswers : submitAnswers;
if (answerButton) {
answerButton.textContent = '答题中...';
}
await selectAnswersFn();
if (answerButton) {
answerButton.textContent = '填写空白...';
}
await fillAnswersFn();
if (answerButton) {
answerButton.textContent = '处理下拉框...';
}
await selectDropdownsFn();
if (answerButton) {
answerButton.textContent = '处理连线题...';
}
await matchLinesFn();
if (answerButton) {
answerButton.textContent = '提交中...';
}
const submitResult = await submitAnswersFn();
if (!submitResult) {
if (answerButton) {
answerButton.textContent = '跳转中...';
answerButton.style.backgroundColor = 'blue';
}
await autoNextSection();
}
if (isLoop && isAutoLoopActive) {
if (answerButton) {
answerButton.textContent = '准备下一轮...';
answerButton.style.backgroundColor = 'purple';
}
console.log('等待页面加载,准备下一轮答题...');
autoLoopTimeout = setTimeout(async () => {
console.log('开始下一轮答题');
if (isAutoLoopActive) {
await runAutoAnswerProcess(true);
}
}, 3000);
} else {
if (answerButton) {
answerButton.textContent = '已完成';
answerButton.style.backgroundColor = 'green';
setTimeout(() => {
if (answerButton && !isAutoLoopActive) {
answerButton.textContent = '一键全自动';
answerButton.style.backgroundColor = 'rgb(0, 255, 127)';
}
}, 3000);
}
}
} catch (error) {
console.error('自动化流程出错:', error);
const answerButton = document.getElementById('autoAnswerButton');
if (answerButton) {
answerButton.textContent = '出错了!';
answerButton.style.backgroundColor = 'red';
}
if (isLoop && isAutoLoopActive) {
console.log('自动挂机出错,10秒后重试...');
autoLoopTimeout = setTimeout(async () => {
console.log('重新开始挂机');
if (isAutoLoopActive) {
await runAutoAnswerProcess(true);
}
}, 10000);
} else {
setTimeout(() => {
if (answerButton && !isAutoLoopActive) {
answerButton.textContent = '一键全自动';
answerButton.style.backgroundColor = 'rgb(0, 255, 127)';
}
}, 5000);
}
}
}
function removeExistingButtons() {
const welearnPanel = document.getElementById('welearnHelperPanel');
if (welearnPanel && welearnPanel.parentNode) {
welearnPanel.parentNode.removeChild(welearnPanel);
}
const oldElements = [
document.getElementById('autoAnswerButton'),
document.getElementById('autoLoopButton'),
document.getElementById('autoAnswerButtonContainer'),
document.getElementById('autoAnswerInfoText'),
document.getElementById('loopStatusIndicator'),
document.getElementById('waitTimerContainer')
];
oldElements.forEach(element => {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
});
const allFixedElements = document.querySelectorAll('*[style*="position: fixed"], *[style*="position:fixed"]');
for (const element of allFixedElements) {
const computedStyle = window.getComputedStyle(element);
const top = parseInt(computedStyle.top);
const left = parseInt(computedStyle.left);
if (top <= 50 && left <= 300 && !element.closest('#welearnHelperPanel') &&
(element.tagName === 'BUTTON' ||
element.tagName === 'DIV' ||
element.id.includes('auto') ||
element.id.includes('button') ||
(element.textContent && (
element.textContent.includes('一键') ||
element.textContent.includes('全自动') ||
element.textContent.includes('自动') ||
element.textContent.includes('挂机') ||
element.textContent.includes('答题') ||
element.textContent.includes('提交')
))
)) {
console.log('强制移除外部元素:', element.tagName, element.id || element.className, element.textContent?.substring(0, 20));
if (element.parentNode) {
element.parentNode.removeChild(element);
}
}
}
const loopStyle = document.getElementById('loopStatusStyle');
if (loopStyle && loopStyle.parentNode) {
loopStyle.parentNode.removeChild(loopStyle);
}
cancelWaitTimer();
console.log('移除已存在的UI元素');
}
function clearExternalButtons() {
const allButtons = document.querySelectorAll('button');
allButtons.forEach(button => {
if (button.closest('#welearnHelperPanel')) {
return;
}
if (button.textContent && (
button.textContent.includes('一键') ||
button.textContent.includes('全自动') ||
button.textContent.includes('自动') ||
button.textContent.includes('挂机') ||
button.textContent === '一键全自动' ||
button.textContent === '开始自动挂机' ||
button.textContent === '停止自动挂机'
)) {
console.log('强制移除外部按钮:', button.textContent);
if (button.parentNode) {
button.parentNode.removeChild(button);
}
}
});
const containers = document.querySelectorAll('div[style*="position: fixed"], div[style*="position:fixed"]');
containers.forEach(container => {
if (container.id === 'welearnHelperPanel' || container.closest('#welearnHelperPanel')) {
return;
}
const style = window.getComputedStyle(container);
const top = parseInt(style.top);
const left = parseInt(style.left);
if (top <= 50 && left <= 50) {
const innerButtons = container.querySelectorAll('button');
if (innerButtons.length > 0) {
console.log('移除外部按钮容器:', container.id || container.className);
if (container.parentNode) {
container.parentNode.removeChild(container);
}
}
}
});
const buttonContainer = document.getElementById('autoAnswerButtonContainer');
if (buttonContainer) {
console.log('移除autoAnswerButtonContainer');
buttonContainer.parentNode.removeChild(buttonContainer);
}
}
window.addEventListener('beforeunload', () => {
stopAutoLoop();
});
let observer;
function setupObserver() {
if (observer) {
observer.disconnect();
}
observer = new MutationObserver((mutations) => {
clearExternalButtons();
const hasPanel = document.getElementById('welearnHelperPanel');
const hasButton = document.getElementById('autoAnswerButton');
if ((!hasPanel || !hasButton) && window === window.top) {
console.log('DOM变化检测到面板或按钮不存在,添加面板');
removeExistingButtons();
addButton();
}
setTimeout(clearExternalButtons, 100);
});
if (window === window.top) {
observer.observe(document.body, {
childList: true,
subtree: true
});
clearExternalButtons();
}
}
function setupPageListeners() {
window.removeEventListener('load', handlePageLoad);
window.addEventListener('load', handlePageLoad);
if (window === window.top) {
let lastUrl = window.location.href;
const checkURLChange = () => {
if (lastUrl !== window.location.href) {
console.log('URL已变化,重新添加按钮');
lastUrl = window.location.href;
setTimeout(() => {
removeExistingButtons();
addButton();
}, 1000);
}
};
setInterval(checkURLChange, 1000);
}
}
function handlePageLoad() {
console.log('页面加载完成,检查按钮');
if (window === window.top) {
removeExistingButtons();
addButton();
}
}
function initialize() {
if (window.weLearnHelperInitialized) {
console.log('助手已初始化,跳过');
return;
}
window.weLearnHelperInitialized = true;
console.log('初始化WeLearn助手');
if (window === window.top) {
removeExistingButtons();
addButton();
setupObserver();
setupPageListeners();
}
}
initialize();
async function simulateHTML5DragDrop(sourceElement, targetElement) {
if (!sourceElement || !targetElement) return;
console.log('使用HTML5拖放API模拟拖拽');
try {
const dragStartEvent = new DragEvent('dragstart', {
bubbles: true,
cancelable: true,
dataTransfer: new DataTransfer()
});
dragStartEvent.dataTransfer.setData('text/plain', 'dragged');
sourceElement.dispatchEvent(dragStartEvent);
await new Promise(resolve => setTimeout(resolve, 200));
const dragOverEvent = new DragEvent('dragover', {
bubbles: true,
cancelable: true,
dataTransfer: dragStartEvent.dataTransfer
});
targetElement.dispatchEvent(dragOverEvent);
await new Promise(resolve => setTimeout(resolve, 200));
const dropEvent = new DragEvent('drop', {
bubbles: true,
cancelable: true,
dataTransfer: dragStartEvent.dataTransfer
});
targetElement.dispatchEvent(dropEvent);
const dragEndEvent = new DragEvent('dragend', {
bubbles: true,
cancelable: true,
dataTransfer: dragStartEvent.dataTransfer
});
sourceElement.dispatchEvent(dragEndEvent);
console.log('HTML5拖放事件序列完成');
} catch (e) {
console.error('HTML5拖放模拟失败:', e);
}
}
async function simulateMouseDrag(sourceElement, targetElement, coords) {
if (!sourceElement || !targetElement) return;
console.log('使用鼠标事件模拟拖拽');
const { fromX, fromY, toX, toY } = coords;
try {
const mouseDownEvent = new MouseEvent('mousedown', {
bubbles: true,
cancelable: true,
view: window,
clientX: fromX,
clientY: fromY,
button: 0
});
sourceElement.dispatchEvent(mouseDownEvent);
await new Promise(resolve => setTimeout(resolve, 100));
const steps = 5;
for (let i = 1; i <= steps; i++) {
const moveX = fromX + (toX - fromX) * (i / steps);
const moveY = fromY + (toY - fromY) * (i / steps);
const mouseMoveEvent = new MouseEvent('mousemove', {
bubbles: true,
cancelable: true,
view: window,
clientX: moveX,
clientY: moveY,
button: 0
});
document.elementFromPoint(moveX, moveY)?.dispatchEvent(mouseMoveEvent);
await new Promise(resolve => setTimeout(resolve, 50));
}
const mouseUpEvent = new MouseEvent('mouseup', {
bubbles: true,
cancelable: true,
view: window,
clientX: toX,
clientY: toY,
button: 0
});
targetElement.dispatchEvent(mouseUpEvent);
console.log('鼠标拖拽事件序列完成');
} catch (e) {
console.error('鼠标拖拽模拟失败:', e);
}
}
async function simulateDragDrop(sourceElement, targetElement, coords) {
if (!sourceElement || !targetElement) return;
console.log('使用组合事件模拟拖拽');
const { fromX, fromY, toX, toY } = coords;
try {
sourceElement.dispatchEvent(new MouseEvent('mousedown', {
bubbles: true,
cancelable: true,
view: window,
clientX: fromX,
clientY: fromY
}));
await new Promise(resolve => setTimeout(resolve, 100));
const dragStartEvent = new Event('dragstart', {bubbles: true, cancelable: true});
sourceElement.dispatchEvent(dragStartEvent);
await new Promise(resolve => setTimeout(resolve, 100));
document.dispatchEvent(new MouseEvent('mousemove', {
bubbles: true,
cancelable: true,
view: window,
clientX: toX,
clientY: toY
}));
await new Promise(resolve => setTimeout(resolve, 100));
targetElement.dispatchEvent(new Event('dragover', {bubbles: true, cancelable: true}));
await new Promise(resolve => setTimeout(resolve, 100));
targetElement.dispatchEvent(new Event('drop', {bubbles: true, cancelable: true}));
sourceElement.dispatchEvent(new Event('dragend', {bubbles: true, cancelable: true}));
targetElement.dispatchEvent(new MouseEvent('mouseup', {
bubbles: true,
cancelable: true,
view: window,
clientX: toX,
clientY: toY
}));
console.log('组合拖拽事件序列完成');
} catch (e) {
console.error('组合拖拽模拟失败:', e);
}
}
})();