浮动上传按钮(支持Excel解析和JSON导出,并附带帮助函数)

在任意网站添加一个可移动的上传按钮,支持上传并解析Excel文件,以及将JSON数据保存为Excel文件,并提供帮助函数

// ==UserScript==
// @name         浮动上传按钮(支持Excel解析和JSON导出,并附带帮助函数)
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  在任意网站添加一个可移动的上传按钮,支持上传并解析Excel文件,以及将JSON数据保存为Excel文件,并提供帮助函数
// @author       你的名字
// @match        *://*/*
// @license      MIT
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 创建上传按钮
    const uploadButton = document.createElement('div');
    uploadButton.style.width = '50px';
    uploadButton.style.height = '50px';
    uploadButton.style.backgroundColor = '#FF5722';
    uploadButton.style.position = 'fixed';
    uploadButton.style.right = '20px';
    uploadButton.style.top = '50%';
    uploadButton.style.transform = 'translateY(-50%)';
    uploadButton.style.cursor = 'move';
    uploadButton.style.zIndex = '10000';
    uploadButton.style.display = 'flex';
    uploadButton.style.justifyContent = 'center';
    uploadButton.style.alignItems = 'center';
    uploadButton.style.borderRadius = '8px';
    uploadButton.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
    uploadButton.title = '点击上传Excel文件';

    // 添加上传图标或文字
    uploadButton.innerHTML = '📁';

    document.body.appendChild(uploadButton);

    // 创建隐藏的文件输入
    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.xlsx, .xls';
    fileInput.style.display = 'none';
    document.body.appendChild(fileInput);

    // 点击按钮触发文件选择
    uploadButton.addEventListener('click', function(e) {
        // 阻止拖动时触发点击事件
        if (e.target === uploadButton) {
            fileInput.click();
        }
    });

    // 文件选择后处理
    fileInput.addEventListener('change', function() {
        const files = fileInput.files;
        if (files.length > 0) {
            const file = files[0];
            const fileName = file.name;
            const fileExtension = fileName.split('.').pop().toLowerCase();
            const allowedExtensions = ['xlsx', 'xls'];

            if (!allowedExtensions.includes(fileExtension)) {
                console.warn('上传的文件不是Excel文件。');
                // 可选:显示提示信息
                // alert('请上传Excel文件(.xlsx 或 .xls)。');
                return;
            }

            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const data = new Uint8Array(e.target.result);
                    const workbook = XLSX.read(data, {type: 'array'});

                    const jsonData = {};

                    workbook.SheetNames.forEach(function(sheetName) {
                        const roa = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], {header:1});
                        if(roa.length) jsonData[sheetName] = roa;
                    });

                    console.log('解析后的Excel JSON数据:', jsonData);
                } catch (error) {
                    console.error('解析Excel文件时出错:', error);
                }
            };

            reader.onerror = function(error) {
                console.error('读取文件时出错:', error);
            };

            reader.readAsArrayBuffer(file);
        }
        // 清除选择
        fileInput.value = '';
    });

    // 实现拖动功能
    let isDragging = false;
    let offsetX, offsetY;

    uploadButton.addEventListener('mousedown', function(e) {
        isDragging = true;
        const rect = uploadButton.getBoundingClientRect();
        offsetX = e.clientX - rect.left;
        offsetY = e.clientY - rect.top;
        document.body.style.userSelect = 'none'; // 禁止文本选择
    });

    document.addEventListener('mousemove', function(e) {
        if (isDragging) {
            let newX = e.clientX - offsetX;
            let newY = e.clientY - offsetY;

            // 限制在视口内
            const maxX = window.innerWidth - uploadButton.offsetWidth;
            const maxY = window.innerHeight - uploadButton.offsetHeight;

            newX = Math.max(0, Math.min(newX, maxX));
            newY = Math.max(0, Math.min(newY, maxY));

            uploadButton.style.left = `${newX}px`;
            uploadButton.style.top = `${newY}px`;
            uploadButton.style.right = 'auto';
            uploadButton.style.transform = 'none';
        }
    });

    document.addEventListener('mouseup', function() {
        if (isDragging) {
            isDragging = false;
            document.body.style.userSelect = 'auto'; // 恢复文本选择
        }
    });

    // 全局函数:将JSON保存为Excel文件
    window.jsonToExcel = function(jsonData) {
        if (typeof jsonData !== 'object' || jsonData === null) {
            console.error('传入的参数必须是一个有效的JSON对象。');
            return;
        }

        const filename = prompt('请输入要保存的Excel文件名(不需要扩展名):', 'data');
        if (!filename) {
            console.warn('未输入文件名,操作取消。');
            return;
        }

        const workbook = XLSX.utils.book_new();

        try {
            for (const sheetName in jsonData) {
                if (Object.prototype.hasOwnProperty.call(jsonData, sheetName)) {
                    const sheetData = jsonData[sheetName];
                    let worksheet;

                    // 判断sheetData是数组的数组还是数组的对象
                    if (Array.isArray(sheetData) && sheetData.length > 0 && Array.isArray(sheetData[0])) {
                        // 数组的数组
                        worksheet = XLSX.utils.aoa_to_sheet(sheetData);
                    } else if (Array.isArray(sheetData) && sheetData.length > 0 && typeof sheetData[0] === 'object') {
                        // 数组的对象
                        worksheet = XLSX.utils.json_to_sheet(sheetData);
                    } else {
                        console.warn(`工作表 "${sheetName}" 的数据格式不支持。`);
                        continue;
                    }

                    XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
                }
            }

            const wbout = XLSX.write(workbook, {bookType: 'xlsx', type: 'array'});

            const blob = new Blob([wbout], {type: 'application/octet-stream'});

            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = `${filename}.xlsx`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);

            console.log(`Excel文件 "${filename}.xlsx" 已生成并下载。`);
        } catch (error) {
            console.error('将JSON保存为Excel时出错:', error);
        }
    };

    // 全局函数:帮助函数,展示使用示例
    window.help = function() {
        console.log('%c=== 浮动上传按钮脚本使用帮助 ===', 'color: green; font-weight: bold;');

        console.group('上传Excel文件并查看JSON输出');
        console.log('1. 点击页面右侧中间的 📁 按钮,选择一个Excel文件(.xlsx 或 .xls)。');
        console.log('2. 上传成功后,解析后的JSON数据会输出到控制台。');
        console.log('   示例输出:');
        console.log('%c解析后的Excel JSON数据:', 'color: blue; font-weight: bold;');
        console.log({
            "Sheet1": [
                ["姓名", "年龄", "城市"],
                ["张三", 28, "北京"],
                ["李四", 34, "上海"],
                ["王五", 23, "广州"]
            ],
            "Sheet2": [
                { "产品": "手机", "价格": 5000 },
                { "产品": "电脑", "价格": 8000 },
                { "产品": "平板", "价格": 3000 }
            ]
        });
        console.groupEnd();

        console.group('将JSON对象保存为Excel文件');
        console.log('1. 在控制台中定义一个JSON对象,结构如下:');
        console.log(`const myData = {
    "Sheet1": [
        ["姓名", "年龄", "城市"],
        ["张三", 28, "北京"],
        ["李四", 34, "上海"],
        ["王五", 23, "广州"]
    ],
    "Sheet2": [
        { "产品": "手机", "价格": 5000 },
        { "产品": "电脑", "价格": 8000 },
        { "产品": "平板", "价格": 3000 }
    ]
};`);
        console.log('2. 调用保存函数:');
        console.log('jsonToExcel(myData);');
        console.log('3. 会弹出提示框,输入文件名(不需要扩展名),例如 "我的数据"。');
        console.log('4. 浏览器将自动下载 "我的数据.xlsx" 文件,包含两个工作表 "Sheet1" 和 "Sheet2"。');
        console.groupEnd();

        console.log('%c=== 结束帮助 ===', 'color: green; font-weight: bold;');
    };

})();