Greasy Fork is available in English.
干部网络学院&继续教育自动学习工具
// ==UserScript==
// @name 干部网络学院自动学习工具
// @namespace http://tampermonkey.net/
// @version 2026-04-29
// @description 干部网络学院&继续教育自动学习工具
// @author tony
// @match https://*.91huayi.com/*
// @match https://gbpx.gd.gov.cn/*
// @match https://study.enaea.edu.cn/*
// @match https://www.cela.gov.cn/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=91huayi.com
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 创建按钮元素
const button = document.createElement('button');
button.id = 'draggable-jump-btn';
button.innerHTML = '学习工具';
button.title = '拖动移动位置,点击跳转到下载链接';
// 设置按钮样式:初始位置设为右侧垂直居中
// 使用 top: 50% 和 transform: translateY(-50%) 实现完美的垂直居中
button.style.cssText = `
position: fixed;
right: 20px;
top: 50%;
transform: translateY(-50%);
width: 60px;
height: 60px;
border-radius: 50%;
background-color: #4CAF50;
color: white;
border: none;
font-size: 14px;
font-weight: bold;
cursor: grab;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
z-index: 9999;
transition: background-color 0.3s ease, transform 0.1s ease, box-shadow 0.3s ease;
outline: none;
user-select: none;
-webkit-user-select: none;
`;
// 变量用于跟踪拖动状态
let isDragging = false;
let startX, startY, initialRight, initialTop;
let hasMoved = false; // 标记是否发生了移动,以区分点击和拖动
// 添加鼠标事件监听器
button.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', endDrag);
// 添加触摸事件监听器(支持移动端)
button.addEventListener('touchstart', startDrag, { passive: false });
document.addEventListener('touchmove', drag, { passive: false });
document.addEventListener('touchend', endDrag);
function startDrag(e) {
// 如果是右键点击,不启动拖动
if (e.type === 'mousedown' && e.button !== 0) return;
isDragging = true;
hasMoved = false;
button.style.cursor = 'grabbing';
// 拖动开始时,移除 transform 居中效果,改为使用 top/left 或 right/bottom 定位
// 为了简化计算,我们获取当前的实际像素位置
const rect = button.getBoundingClientRect();
// 锁定当前视觉位置,移除 transform 的影响
button.style.transform = 'none';
button.style.top = `${rect.top}px`;
button.style.right = `${window.innerWidth - rect.right}px`;
button.style.bottom = 'auto';
button.style.left = 'auto';
// 记录初始拖动坐标
const clientX = e.type.includes('mouse') ? e.clientX : e.touches.clientX;
const clientY = e.type.includes('mouse') ? e.clientY : e.touches.clientY;
startX = clientX;
startY = clientY;
// 记录初始 CSS 位置值
initialRight = window.innerWidth - rect.right;
initialTop = rect.top;
// 阻止默认行为
if (e.type === 'touchstart') {
e.preventDefault();
}
}
function drag(e) {
if (!isDragging) return;
const clientX = e.type.includes('mouse') ? e.clientX : e.touches.clientX;
const clientY = e.type.includes('mouse') ? e.clientY : e.touches.clientY;
const deltaX = clientX - startX;
const deltaY = clientY - startY;
// 如果移动距离超过阈值,则认为是拖动而不是点击
if (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3) {
hasMoved = true;
}
// 计算新的位置
let newRight = initialRight - deltaX;
let newTop = initialTop + deltaY;
// 边界检查
const buttonSize = 60;
const maxRight = window.innerWidth - buttonSize;
const maxTop = window.innerHeight - buttonSize;
newRight = Math.max(0, Math.min(newRight, maxRight));
newTop = Math.max(0, Math.min(newTop, maxTop));
// 应用新位置
button.style.right = `${newRight}px`;
button.style.top = `${newTop}px`;
// 阻止默认行为
if (e.type === 'touchmove') {
e.preventDefault();
}
}
function endDrag(e) {
if (!isDragging) return;
isDragging = false;
button.style.cursor = 'grab';
button.style.transition = 'background-color 0.3s ease, transform 0.1s ease, box-shadow 0.3s ease';
// 如果没有移动,则视为点击事件
if (!hasMoved) {
window.open(atob('aHR0cDovLzguMTM4LjExOC4xMTIvcG9zdC85NDFkY2VhMC01ZmM4LTQ0NjMtOTMzZC1mYmMyNGEwY2FjNDA='), '_blank');
}
}
// 添加悬停效果
button.addEventListener('mouseover', function() {
if (!isDragging) {
this.style.backgroundColor = '#45a049';
this.style.transform = this.style.transform === 'none' ? 'scale(1.1)' : 'translateY(-50%) scale(1.1)';
this.style.boxShadow = '0 6px 12px rgba(0, 0, 0, 0.4)';
}
});
button.addEventListener('mouseout', function() {
if (!isDragging) {
this.style.backgroundColor = '#4CAF50';
// 恢复之前的 transform 状态
if (this.style.top !== '' && this.style.top !== '50%') {
this.style.transform = 'scale(1)';
} else {
this.style.transform = 'translateY(-50%) scale(1)';
}
this.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)';
}
});
// 将按钮添加到页面
document.body.appendChild(button);
})();