Greasy Fork

Greasy Fork is available in English.

干部网络学院自动学习工具

干部网络学院&继续教育自动学习工具

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);
})();