imgUrl上传脚本

在右下角添加悬浮球,点击弹出上传表单对话框

当前为 2024-08-26 提交的版本,查看 最新版本

// ==UserScript==
// @name         imgUrl上传脚本
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  在右下角添加悬浮球,点击弹出上传表单对话框
// @match        *://*/*
// @author       21zys
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 获取当天的日期字符串,用于每日清零
    var today = new Date().toISOString().split('T')[0]; // 格式为 "YYYY-MM-DD"

    // 从localStorage中获取今日上传数量,如果不存在则初始化为0
    var savedUploadDate = localStorage.getItem('uploadDate') || today;
    var uploadCount = savedUploadDate === today ? parseInt(localStorage.getItem('uploadCount'), 10) || 0 : 0;

    // 如果日期已变,重置上传数量
    if (savedUploadDate !== today) {
        uploadCount = 0;
        localStorage.setItem('uploadDate', today);
        localStorage.setItem('uploadCount', uploadCount);
    }

    // 加载时从sessionStorage中获取UID、Token和选项卡选择
    var savedUID = sessionStorage.getItem('uid') || '您的UID';
    var savedToken = sessionStorage.getItem('token') || '您的Token';
    var savedTab = localStorage.getItem('selectedTab') || 'imgURL';

    // 加载时从localStorage中获取悬浮球的位置和相册列表
    var savedPosition = JSON.parse(localStorage.getItem('floatingBallPosition')) || { right: '20px', bottom: '20px' };
    var savedDialogPosition = JSON.parse(localStorage.getItem('dialogPosition')) || null;
    var savedAlbums = JSON.parse(localStorage.getItem('albumList')) || [];
    var savedAlbumId = localStorage.getItem('selectedAlbumId') || 'default';

    // 创建悬浮球
    var floatingBall = document.createElement('div');
    floatingBall.style.position = 'fixed';
    floatingBall.style.right = savedPosition.right;
    floatingBall.style.bottom = savedPosition.bottom;
    floatingBall.style.left = savedPosition.left || 'auto';
    floatingBall.style.top = savedPosition.top || 'auto';
    floatingBall.style.width = '50px';
    floatingBall.style.height = '50px';
    floatingBall.style.borderRadius = '50%';
    floatingBall.style.backgroundColor = '#007bff';
    floatingBall.style.color = '#fff';
    floatingBall.style.textAlign = 'center';
    floatingBall.style.lineHeight = '50px';
    floatingBall.style.cursor = 'pointer';
    floatingBall.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
    floatingBall.style.fontSize = '24px';
    floatingBall.style.zIndex = '1001';
    floatingBall.innerHTML = '+';
    document.body.appendChild(floatingBall);

    let isDraggingBall = false;
    let startX, startY;

    // 添加拖拽功能并保存悬浮球位置
    floatingBall.addEventListener('mousedown', function(e) {
        startX = e.clientX;
        startY = e.clientY;
        var offsetX = e.clientX - floatingBall.getBoundingClientRect().left;
        var offsetY = e.clientY - floatingBall.getBoundingClientRect().top;

        function onMouseMove(e) {
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            if (!isDraggingBall && (Math.abs(dx) > 5 || Math.abs(dy) > 5)) {  // 设置拖拽灵敏度阈值为5px
                isDraggingBall = true;
            }
            if (isDraggingBall) {
                var newLeft = e.clientX - offsetX;
                var newTop = e.clientY - offsetY;
                floatingBall.style.left = newLeft + 'px';
                floatingBall.style.top = newTop + 'px';
                floatingBall.style.right = 'auto';
                floatingBall.style.bottom = 'auto';

                // 保存位置到localStorage
                localStorage.setItem('floatingBallPosition', JSON.stringify({
                    left: floatingBall.style.left,
                    top: floatingBall.style.top
                }));
            }
        }

        function onMouseUp() {
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);
            if (!isDraggingBall) {
                openDialog();
            }
            setTimeout(() => isDraggingBall = false, 100); // 延迟100ms重置拖拽状态
        }

        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
    });

    // 创建对话框
    var dialog = document.createElement('div');
    dialog.style.position = 'fixed';

    // 默认居中对话框
    if (savedDialogPosition) {
        dialog.style.left = savedDialogPosition.left;
        dialog.style.top = savedDialogPosition.top;
        dialog.style.transform = 'none';
    } else {
        dialog.style.left = '50%';
        dialog.style.top = '50%';
        dialog.style.transform = 'translate(-50%, -50%)';
    }

    dialog.style.width = '400px';
    dialog.style.padding = '20px';
    dialog.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
    dialog.style.borderRadius = '12px';
    dialog.style.backdropFilter = 'blur(10px)';
    dialog.style.boxShadow = '0 8px 32px rgba(0, 0, 0, 0.37)';
    dialog.style.display = 'none';
    dialog.style.opacity = '0';
    dialog.style.zIndex = '1000';
    dialog.style.fontFamily = 'Arial, sans-serif';
    dialog.style.transition = 'opacity 0.3s ease';
    document.body.appendChild(dialog);

    let isDraggingDialog = false;

    dialog.addEventListener('mousedown', function(e) {
        if (e.target !== dialog) return; // 只允许在对话框的直接点击区域触发拖拽

        var offsetX = e.clientX - dialog.getBoundingClientRect().left;
        var offsetY = e.clientY - dialog.getBoundingClientRect().top;

        // 允许在边缘20px内触发拖拽
        const edgeThreshold = 20;
        if (offsetX < edgeThreshold || offsetX > dialog.clientWidth - edgeThreshold ||
            offsetY < edgeThreshold || offsetY > dialog.clientHeight - edgeThreshold) {

            function onMouseMove(e) {
                isDraggingDialog = true;
                var newLeft = e.clientX - offsetX;
                var newTop = e.clientY - offsetY;
                dialog.style.left = newLeft + 'px';
                dialog.style.top = newTop + 'px';
                dialog.style.transform = 'none';

                // 保存对话框位置到localStorage
                localStorage.setItem('dialogPosition', JSON.stringify({
                    left: dialog.style.left,
                    top: dialog.style.top
                }));
            }

            function onMouseUp() {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
                setTimeout(() => isDraggingDialog = false, 100); // 延迟100ms重置拖拽状态
            }

            // 将监听器绑定到document, 确保拖拽行为不会中断
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        }
    });

    // 创建关闭按钮(X图标)
    var closeButton = document.createElement('span');
    closeButton.innerHTML = '&times;';
    closeButton.style.position = 'absolute';
    closeButton.style.top = '10px';
    closeButton.style.right = '10px';
    closeButton.style.cursor = 'pointer';
    closeButton.style.fontSize = '20px';
    closeButton.style.color = '#333';
    closeButton.addEventListener('click', function() {
        closeDialog();
    });
    dialog.appendChild(closeButton);

    // 创建表单
    var form = document.createElement('form');
    form.enctype = 'multipart/form-data';
    form.method = 'post';
    form.action = 'https://www.imgurl.org/api/v2/upload';
    form.id = 'upform';
    form.style.display = 'grid';
    form.style.gap = '10px';

    // 创建UID字段
    var uidLabel = document.createElement('label');
    uidLabel.innerText = 'UID:';
    uidLabel.style.fontWeight = 'bold';
    uidLabel.style.color = '#333';
    var uidInput = document.createElement('input');
    uidInput.type = 'text';
    uidInput.name = 'uid';
    uidInput.value = savedUID;
    uidInput.style.padding = '8px';
    uidInput.style.border = '1px solid #ccc';
    uidInput.style.borderRadius = '4px';
    form.appendChild(uidLabel);
    form.appendChild(uidInput);

    // 创建Token字段
    var tokenLabel = document.createElement('label');
    tokenLabel.innerText = 'Token:';
    tokenLabel.style.fontWeight = 'bold';
    tokenLabel.style.color = '#333';
    var tokenInput = document.createElement('input');
    tokenInput.type = 'text';
    tokenInput.name = 'token';
    tokenInput.value = savedToken;
    tokenInput.style.padding = '8px';
    tokenInput.style.border = '1px solid #ccc';
    tokenInput.style.borderRadius = '4px';
    form.appendChild(tokenLabel);
    form.appendChild(tokenInput);

    // 创建刷新相册按钮
    var albumSelectContainer = document.createElement('div');
    albumSelectContainer.style.marginTop = '10px';
    albumSelectContainer.style.textAlign = 'center';

    var albumBtn = document.createElement('button');
    albumBtn.type = 'button';
    albumBtn.innerText = '刷新相册';
    albumBtn.style.padding = '8px 16px';
    albumBtn.style.border = '1px solid #ccc';
    albumBtn.style.borderRadius = '4px';
    albumBtn.style.cursor = 'pointer';
    albumBtn.style.backgroundColor = '#f8f9fa';
    albumSelectContainer.appendChild(albumBtn);

    // 创建相册下拉列表(表单外)
    var albumSelect = document.createElement('select');
    albumSelect.style.width= '175px';
    albumSelect.style.height = '34px';
    albumSelect.style.marginTop = '-3px';
    albumSelect.style.textAlign = 'center';
    albumSelect.style.border = '1px solid #ccc';
    albumSelect.style.borderRadius = '4px';
    albumSelectContainer.appendChild(albumSelect);
    dialog.appendChild(albumSelectContainer);  // 将相册下拉列表放在表单外

    // 加载持久化相册列表和选择
    loadAlbumList();

    // 刷新相册列表
    albumBtn.addEventListener('click', function() {
        fetchAlbums();
    });

    albumSelect.addEventListener('change', function() {
        var selectedAlbumId = albumSelect.value;
        localStorage.setItem('selectedAlbumId', selectedAlbumId);
    });

    function loadAlbumList() {
        albumSelect.innerHTML = ''; // 清空下拉列表
        var defaultOption = document.createElement('option');
        defaultOption.value = 'default';
        defaultOption.textContent = '默认相册';
        albumSelect.appendChild(defaultOption);

        if (savedAlbums.length > 0) {
            savedAlbums.forEach(function(album) {
                var option = document.createElement('option');
                option.value = album.album_id;
                option.textContent = album.name;
                albumSelect.appendChild(option);
            });
        }

        // 选择持久化的相册
        albumSelect.value = savedAlbumId;
    }

    function fetchAlbums() {
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'https://www.imgurl.org/api/v2/albums', true);

        // 构造FormData对象,将UID和Token放入其中
        var formData = new FormData();
        formData.append('uid', uidInput.value);
        formData.append('token', tokenInput.value);

        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var response = JSON.parse(xhr.responseText);
                if (response && response.data && response.data.length) {
                    savedAlbums = response.data;
                    localStorage.setItem('albumList', JSON.stringify(savedAlbums)); // 持久化保存相册列表
                    loadAlbumList(); // 重新加载相册列表
                } else {
                    albumSelect.innerHTML = '<option value="">未找到相册</option>';
                }
            } else if (xhr.readyState === 4) {
                albumSelect.innerHTML = '<option value="">获取相册失败</option>';
            }
        };

        // 发送POST请求
        xhr.send(formData);
    }

    // 创建文件上传输入框
    var fileLabel = document.createElement('label');
    fileLabel.innerText = '选择文件:';
    fileLabel.style.fontWeight = 'bold';
    fileLabel.style.color = '#333';
    var fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.name = 'file';
    fileInput.style.padding = '8px';
    fileInput.style.border = '1px solid #ccc';
    fileInput.style.borderRadius = '4px';
    form.appendChild(fileLabel);
    form.appendChild(fileInput);

    // 上传和清空按钮容器
    var buttonContainer = document.createElement('div');
    buttonContainer.style.marginTop = '10px';
    buttonContainer.style.textAlign = 'right';

    // 创建今日上传数量label
    var uploadCountLabel = document.createElement('label');
    uploadCountLabel.textContent = `今日已上传 ${uploadCount} 张图片`;
    uploadCountLabel.style.fontSize = '1rem';
    uploadCountLabel.style.fontWeight = 'bold';
    uploadCountLabel.style.color = 'rgb(51, 51, 51)';
    uploadCountLabel.style.marginRight = '10px';
    buttonContainer.appendChild(uploadCountLabel);

    // 创建上传按钮
    var uploadBtn = document.createElement('input');
    uploadBtn.type = 'submit';
    uploadBtn.id = 'upload-btn';
    uploadBtn.value = '开始上传';
    uploadBtn.className = 'upload-btn';
    uploadBtn.style.padding = '8px 16px';
    uploadBtn.style.border = 'none';
    uploadBtn.style.backgroundColor = '#007bff';
    uploadBtn.style.color = '#fff';
    uploadBtn.style.borderRadius = '4px';
    uploadBtn.style.cursor = 'pointer';
    uploadBtn.style.marginRight = '10px';
    uploadBtn.style.transition = 'background-color 0.3s ease';
    uploadBtn.addEventListener('mouseover', function() {
        uploadBtn.style.backgroundColor = '#0056b3';
    });
    uploadBtn.addEventListener('mouseout', function() {
        uploadBtn.style.backgroundColor = '#007bff';
    });
    buttonContainer.appendChild(uploadBtn);

    // 创建清空按钮
    var clearBtn = document.createElement('input');
    clearBtn.type = 'button';
    clearBtn.id = 'clear-btn';
    clearBtn.value = '清空';
    clearBtn.className = 'clear-btn';
    clearBtn.style.padding = '8px 16px';
    clearBtn.style.border = 'none';
    clearBtn.style.backgroundColor = '#6c757d';
    clearBtn.style.color = '#fff';
    clearBtn.style.borderRadius = '4px';
    clearBtn.style.cursor = 'pointer';
    clearBtn.style.transition = 'background-color 0.3s ease';
    clearBtn.addEventListener('mouseover', function() {
        clearBtn.style.backgroundColor = '#5a6268';
    });
    clearBtn.addEventListener('mouseout', function() {
        clearBtn.style.backgroundColor = '#6c757d';
    });
    clearBtn.addEventListener('click', function() {
        fileInput.value = '';          // 清空文件输入框
        resultInput.value = '';        // 清空上传结果文本框
        resultInput.style.color = '';  // 重置文本框颜色
        delete resultInput.dataset.url; // 删除已存储的URL
    });
    buttonContainer.appendChild(clearBtn);

    form.appendChild(buttonContainer);

    // 创建进度条容器,移出表单
    var progressContainer = document.createElement('div');
    progressContainer.style.marginTop = '10px';
    progressContainer.style.display = 'none';
    progressContainer.style.width = '100%';
    dialog.appendChild(progressContainer); // 将进度条容器添加到对话框中

    // 创建进度条
    var progressBar = document.createElement('progress');
    progressBar.value = '0';
    progressBar.max = '100';
    progressBar.style.width = '100%';
    progressBar.style.height = '20px';
    progressContainer.appendChild(progressBar);

    // 创建选项卡容器,移出表单
    var tabContainer = document.createElement('div');
    tabContainer.style.display = 'flex';
    tabContainer.style.justifyContent = 'space-around';
    tabContainer.style.marginTop = '10px';
    dialog.appendChild(tabContainer); // 将选项卡容器添加到对话框中

    // 创建选项卡
    var tabs = ['HTML', 'imgURL', 'MarkDown', 'BBCode'];
    tabs.forEach(function(tab) {
        var tabButton = document.createElement('button');
        tabButton.textContent = tab;
        tabButton.style.flex = '1';
        tabButton.style.padding = '10px';
        tabButton.style.border = '1px solid #ccc';
        tabButton.style.borderRadius = '4px 4px 0 0';
        tabButton.style.cursor = 'pointer';
        tabButton.style.backgroundColor = tab === savedTab ? '#007bff' : '#f8f9fa';
        tabButton.style.color = tab === savedTab ? '#fff' : '#333';
        tabButton.addEventListener('click', function() {
            selectTab(tab);
        });
        tabContainer.appendChild(tabButton);
    });

    // 创建用于回显上传结果的文本框容器
    var resultContainer = document.createElement('div');
    resultContainer.style.marginTop = '10px';
    resultContainer.style.textAlign = 'center';

    // 创建回显上传结果的文本框
    var resultInput = document.createElement('input');
    resultInput.type = 'text';
    resultInput.id = 'result-box';
    resultInput.placeholder = '上传结果';
    resultInput.style.width = '100%';
    resultInput.style.padding = '8px';
    resultInput.style.border = '1px solid #ccc';
    resultInput.style.borderRadius = '4px';
    resultInput.readOnly = true; // 设置为不可编辑状态
    resultInput.style.cursor = 'pointer';
    resultContainer.appendChild(resultInput);

    // 单击文本框时复制里面的文本
    resultInput.addEventListener('click', function() {
        navigator.clipboard.writeText(resultInput.value).then(function() {
            alert('已复制到剪贴板');
        });
    });

    // 将表单和结果容器添加到对话框中
    dialog.appendChild(form);
    dialog.appendChild(resultContainer);

    // 处理选项卡切换
    function selectTab(tab) {
        localStorage.setItem('selectedTab', tab); // 持久化选项卡选择
        updateTabUI(tab);
        updateResultFormat(tab);
    }

    function updateTabUI(selectedTab) {
        tabContainer.querySelectorAll('button').forEach(function(button) {
            if (button.textContent === selectedTab) {
                button.style.backgroundColor = '#007bff';
                button.style.color = '#fff';
            } else {
                button.style.backgroundColor = '#f8f9fa';
                button.style.color = '#333';
            }
        });
    }

    function updateResultFormat(tab) {
        const url = resultInput.dataset.url; // 使用保存的上传成功后的URL
        if (!url) return;

        let formattedText = '';
        switch (tab) {
            case 'HTML':
                formattedText = `<img src="${url}" alt="image">`;
                break;
            case 'imgURL':
                formattedText = url;
                break;
            case 'MarkDown':
                formattedText = `![image](${url})`;
                break;
            case 'BBCode':
                formattedText = `[img]${url}[/img]`;
                break;
        }
        if (resultInput.value !== formattedText) {
            resultInput.value = formattedText; // 只有在内容不匹配时才更新文本框内容
        }
    }

    // 初始化选项卡UI和结果格式
    updateTabUI(savedTab);

    // 打开对话框的函数,带淡入效果
    function openDialog() {
        dialog.style.display = 'block';
        setTimeout(function() {
            dialog.style.opacity = '1';
        }, 10);
    }

    // 关闭对话框的函数,带淡出效果
    function closeDialog() {
        dialog.style.opacity = '0';
        setTimeout(function() {
            dialog.style.display = 'none';
        }, 300);
    }

    // 处理表单提交逻辑
    form.addEventListener('submit', function(event) {
        event.preventDefault(); // 阻止表单默认提交行为

        // 获取UID和Token
        var uid = uidInput.value.trim();
        var token = tokenInput.value.trim();

        // 检查UID和Token是否为空
        if (!uid || !token) {
            resultInput.value = '请填写UID和Token后再试。';
            resultInput.style.color = 'red';
            return;
        }

        // 保存UID和Token到sessionStorage
        sessionStorage.setItem('uid', uid);
        sessionStorage.setItem('token', token);

        // 检查是否选择了文件
        if (!fileInput.files.length) {
            resultInput.value = '请选择要上传的文件。';
            resultInput.style.color = 'red';
            return;
        }

        // 检查文件格式是否支持
        var allowedFormats = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
        var file = fileInput.files[0];
        var fileExtension = file.name.split('.').pop().toLowerCase();
        if (!allowedFormats.includes(fileExtension)) {
            resultInput.value = '仅支持格式: ' + allowedFormats.join(', ');
            resultInput.style.color = 'red';
            return;
        }

        // 显示进度条
        progressContainer.style.display = 'block';

        // 创建异步请求
        var xhr = new XMLHttpRequest();
        xhr.open('POST', form.action, true); // 异步请求

        // 监听上传进度
        xhr.upload.addEventListener('progress', function(e) {
            if (e.lengthComputable) {
                var percentComplete = (e.loaded / e.total) * 100;
                progressBar.value = percentComplete;
            }
        });

        // 错误处理
        xhr.addEventListener('load', function() {
            if (xhr.status === 200) {
                var response = JSON.parse(xhr.responseText);
                if (response && response.data && response.data.url) {
                    resultInput.dataset.url = response.data.url; // 保存上传成功后的URL
                    updateResultFormat(savedTab); // 更新结果格式
                    resultInput.style.color = 'green';

                    // 更新并保存上传数量
                    uploadCount++;
                    localStorage.setItem('uploadCount', uploadCount);
                    uploadCountLabel.textContent = `今日已上传 ${uploadCount} 张图片`;

                } else {
                    resultInput.value = '上传失败: ' + (response.message || '未知错误');
                    resultInput.style.color = 'red';
                }
            } else {
                resultInput.value = '上传失败: ' + xhr.statusText + ' (' + xhr.status + ')';
                resultInput.style.color = 'red';
            }
            progressContainer.style.display = 'none';
        });

        xhr.addEventListener('error', function() {
            resultInput.value = '上传失败: 网络错误';
            resultInput.style.color = 'red';
            progressContainer.style.display = 'none';
        });

        var formData = new FormData(form);
        if (albumSelect.value !== 'default') { // 仅当选择了具体相册时才附加album_id
            formData.append('album_id', albumSelect.value);
        }

        xhr.send(formData);
    });
})();