Greasy Fork is available in English.
提前报名所需课程,进入【学习中心】,点击脚本开始按钮。鼠标移到左上角显示控制窗口,移开自动隐藏。
// ==UserScript==
// @name 学习教育公需课和专业课视频
// @namespace http://tampermonkey.net/
// @version 1.4
// @description 提前报名所需课程,进入【学习中心】,点击脚本开始按钮。鼠标移到左上角显示控制窗口,移开自动隐藏。
// @author shadow
// @match http://sdld.gxk.yxlearning.com/my/learning*
// @match http://sdld.gxk.yxlearning.com/learning*
// @match https://sdld.zyk.yxlearning.com/my/learning*
// @match https://sdld.zyk.yxlearning.com/learning*
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
'use strict';
const SCRIPT_RUNNING_KEY = 'script_running';
const ORIGIN_URL_KEY = 'script_origin_url';
const MAX_LOG_LINES = 100;
// 浮动窗口样式
const styles = `
#floating-window {
position: fixed;
top: 10px;
left: 10px;
padding: 10px;
background-color: #f0f0f0;
border: 1px solid #ccc;
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
z-index: 1000;
font-family: Arial, sans-serif;
}
#floating-window button {
margin: 5px;
}
`;
// 创建浮动窗口
const createFloatingWindow = () => {
const styleElement = document.createElement('style');
styleElement.innerHTML = styles;
document.head.appendChild(styleElement);
const container = document.createElement('div');
container.id = 'floating-window';
const startButton = document.createElement('button');
startButton.textContent = '开始';
startButton.id = 'start-button';
// container.appendChild(startButton);
const pauseButton = document.createElement('button');
pauseButton.textContent = '暂停';
pauseButton.id = 'pause-button';
// container.appendChild(pauseButton);
// 创建一个div来容纳按钮
const buttonContainer = document.createElement('div');
buttonContainer.className = 'button-container';
buttonContainer.appendChild(startButton);
buttonContainer.appendChild(pauseButton);
// 将按钮容器和日志文本区域添加到容器中
container.appendChild(buttonContainer);
document.body.appendChild(container);
const log = document.createElement('textarea');
log.id = 'log';
log.rows = 5;
log.cols = 40;
log.readOnly = true;
container.appendChild(log);
// 鼠标移动到页面左上角时显示浮窗
document.addEventListener('mousemove', (event) => {
if (event.clientX < 20 && event.clientY < 20) { // 根据需要调整触发区域大小
container.style.display = 'block';
}
});
// 鼠标在浮窗上时不隐藏浮窗
container.addEventListener('mouseenter', () => {
container.style.display = 'block';
});
container.addEventListener('mouseleave', () => {
container.style.display = 'none';
});
container.style.display = 'none';
return {log, startButton, pauseButton};
};
// 写入日志
const writeLog = (message) => {
console.log(message); // 输出到控制台
const timestamp = new Date().toISOString();
const logTextarea = document.getElementById('log');
if (logTextarea) {
const lines = logTextarea.value.split('\n');
if (lines.length >= MAX_LOG_LINES) {
logTextarea.value = ''; // 清空日志
}
logTextarea.value += `[${timestamp}] ${message}` + '\n';
logTextarea.scrollTop = logTextarea.scrollHeight;
}
};
// 判断当前 URL 是否以特定开头
const isLearningIndexPage = () => {
const currentUrl = window.location.href;
return currentUrl.startsWith('http://sdld.gxk.yxlearning.com/learning/index') || currentUrl.startsWith('https://sdld.zyk.yxlearning.com/learning/index');
};
// 检查元素是否可见
const isElementVisible = (element) => {
if (!(element instanceof HTMLElement)) {
throw new Error('参数必须是一个 HTMLElement 对象');
}
// 检查元素是否在视口内
const rect = element.getBoundingClientRect();
const inViewport = (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
// 检查元素的 CSS 样式
const style = getComputedStyle(element);
return (
inViewport &&
style.display !== 'none' &&
style.visibility !== 'hidden' &&
style.opacity !== '0'
);
};
// 脚本功能
let isRunning = false;
let initialUrl = ''; // 记录初始url,用于返回
let intervalId = null;
let courseLinks;
let last_index = -1;
const startScript = () => {
if (intervalId) return;
GM_setValue(SCRIPT_RUNNING_KEY, 'true');
isRunning = true;
writeLog('脚本开始运行');
debugger; // 在此位置暂停脚本的执行
intervalId = setInterval(async () => {
if (!isLearningIndexPage()) {
// 查找并点击内容为【学习中】的a标签
initialUrl = window.location.href
GM_setValue(ORIGIN_URL_KEY, initialUrl);
const learningLink = Array.from(document.querySelectorAll('a')).find(a => a.textContent.trim() === '学习中');
if (learningLink && isElementVisible(learningLink)) {
learningLink.click();
writeLog('点击了内容为【学习中】的标签');
// 等待页面跳转并加载课程链接
await new Promise(resolve => setTimeout(resolve, 2000));
// 查找所有课程链接
courseLinks = Array.from(document.querySelectorAll('div.active[content-index="2"] a[du-click="courseck"]')).filter(isElementVisible);
if (courseLinks.length > 0) {
writeLog(`找到 ${courseLinks.length} 个可见课程链接`);
// 点击当前课程链接
const link = courseLinks[0];
link.click();
writeLog(`点击了课程链接: 1`);
// 等待页面跳转并加载视频
await new Promise(resolve => setTimeout(resolve, 2000));
} else {
writeLog('未找到任何可见课程链接');
isRunning = false;
}
} else {
writeLog('未找到内容为【学习中】的a标签或该标签不可见');
isRunning = false;
}
} else {
// 检测视频播放完成并检查条件
// writeLog('检测视频播放完成并检查条件');
checkAndClickVideo();
await waitForVideoCompletionAndCheckCondition();
// 增加索引,准备点击下一个课程链接
// 返回到初始 URL
window.location.href = initialUrl;
writeLog('返回到初始网页');
// 等待返回操作完成
await new Promise(resolve => setTimeout(resolve, 2000));
}
if (!isRunning) {
writeLog('所有课程链接已处理完毕');
pauseScript()
}
}, 5000); // 每10秒运行一次
};
const pauseScript = () => {
isRunning = false;
GM_setValue(SCRIPT_RUNNING_KEY, 'false');
if (intervalId) {
clearInterval(intervalId);
}
intervalId = null;
writeLog('脚本已暂停');
};
const checkAndClickVideo = () => {
// 循环检测视频列表,若不为100则点击播放
let lst = document.querySelectorAll('div.course-list li.videoLi')
// console.log(lst)
for (let i = 0; i < lst.length; i++) {
let row = lst[i];
let span = row.querySelector('span.badge');
const content = span.textContent.trim();
// console.log(content)
if (content !== '100%') {
let hasActiveDiv = row.querySelector('div.active') !== null;
// writeLog(`未完成:${index},last_index:${last_index}`);
if (!hasActiveDiv) {
last_index = i;
writeLog('点击视频');
row.click()
}
break
}
}
};
// 等待视频播放完成并检查条件
const waitForVideoCompletionAndCheckCondition = () => {
return new Promise((resolve) => {
checkCondition(resolve);
});
};
// 检查条件
const checkCondition = (callback) => {
const spanElement = document.querySelector('span[du-html="sumschedule"]');
if (spanElement) {
const content = spanElement.textContent.trim();
if (content === '100.00') {
// writeLog('条件满足:span 内容等于 100');
callback();
} else {
// writeLog(`条件不满足:span 内容不等于 100 ${content} `);
}
} else {
writeLog('未找到 span 元素');
callback();
}
};
// 初始化浮动窗口
const {log, startButton, pauseButton} = createFloatingWindow();
// 绑定按钮事件
startButton.addEventListener('click', startScript);
pauseButton.addEventListener('click', pauseScript);
// 仅在页面加载完成后显示
window.onload = () => {
writeLog('页面加载完成');
isRunning = GM_getValue(SCRIPT_RUNNING_KEY)
initialUrl = GM_getValue(ORIGIN_URL_KEY)
if (isRunning === 'false') {
isRunning = false
}
writeLog(`isRunning: ${isRunning}`);
if (isRunning) {
startScript()
}
};
})();