// ==UserScript== // @name Table 导出 CSV 右键菜单 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 为页面中的table元素添加右键菜单,支持导出为CSV文件 // @author newbieking // @match *://*/* // @grant GM_registerMenuCommand // @grant GM_addStyle // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/564514/Table%20%E5%AF%BC%E5%87%BA%20CSV%20%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95.user.js // @updateURL https://update.greasyfork.icu/scripts/564514/Table%20%E5%AF%BC%E5%87%BA%20CSV%20%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95.meta.js // ==/UserScript== (function() { 'use strict'; // 1. 定义全局变量,存储右键点击的table元素 let targetTable = null; // 2. 添加右键菜单的样式,保证菜单美观且不被页面样式干扰 GM_addStyle(` #newbieking-table-csv-menu { position: fixed; z-index: 999999; background: #fff; border: 1px solid #ccc; border-radius: 4px; box-shadow: 2px 2px 8px rgba(0,0,0,0.2); padding: 5px 0; display: none; min-width: 120px; font-size: 14px; } #newbieking-table-csv-menu li { list-style: none; padding: 6px 15px; cursor: pointer; } #newbieking-table-csv-menu li:hover { background-color: #f0f0f0; } `); // 3. 创建右键菜单DOM元素 function createContextMenu() { // 避免重复创建菜单 if (document.getElementById('newbieking-table-csv-menu')) return; const menu = document.createElement('ul'); menu.id = 'newbieking-table-csv-menu'; menu.innerHTML = `
  • 导出为CSV文件
  • `; document.body.appendChild(menu); // 绑定菜单点击事件 document.getElementById('export-table-csv').addEventListener('click', exportTableToCSV); // 点击页面其他区域关闭菜单 document.addEventListener('click', (e) => { if (!menu.contains(e.target)) { menu.style.display = 'none'; } }); } // 4. 提取Table数据并转换为CSV格式 function tableToCSV(table) { const csvRows = []; const rows = table.querySelectorAll('tr'); // 遍历每一行 for (const row of rows) { const csvCells = []; const cells = row.querySelectorAll('th, td'); // 遍历每个单元格,处理特殊字符(逗号、换行、引号) for (const cell of cells) { // 获取单元格纯文本,去除多余空格和换行 let cellText = cell.textContent.trim().replace(/\s+/g, ' '); // 处理CSV特殊字符:包含逗号/引号/换行时,用双引号包裹,内部双引号转义为两个 if (cellText.includes(',') || cellText.includes('"') || cellText.includes('\n')) { cellText = `"${cellText.replace(/"/g, '""')}"`; } csvCells.push(cellText); } // 单元格用逗号分隔,组成一行 csvRows.push(csvCells.join(',')); } // 行之间用换行分隔,生成完整CSV内容 return csvRows.join('\n'); } // 5. 导出CSV文件的核心函数 function exportTableToCSV() { if (!targetTable) return; // 提取CSV数据 const csvContent = tableToCSV(targetTable); // 创建Blob对象(UTF-8编码,解决中文乱码) const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }); // 创建下载链接 const url = URL.createObjectURL(blob); const link = document.createElement('a'); // 设置文件名(用页面标题+时间戳,避免重复) const fileName = `${document.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_')}_${new Date().getTime()}.csv`; link.setAttribute('href', url); link.setAttribute('download', fileName); link.style.display = 'none'; document.body.appendChild(link); // 触发下载 link.click(); // 清理资源 document.body.removeChild(link); URL.revokeObjectURL(url); // 关闭菜单 document.getElementById('newbieking-table-csv-menu').style.display = 'none'; } // 6. 监听页面右键事件,只在table上显示自定义菜单 document.addEventListener('contextmenu', (e) => { // 判断右键目标是否是table,或table的子元素(td/th/tr) const table = e.target.closest('table'); if (table) { // 阻止默认右键菜单 e.preventDefault(); // 记录当前目标table targetTable = table; // 创建菜单(首次执行) createContextMenu(); // 显示菜单,定位到鼠标位置 const menu = document.getElementById('newbieking-table-csv-menu'); menu.style.display = 'block'; menu.style.left = `${e.clientX}px`; menu.style.top = `${e.clientY}px`; // 处理菜单超出页面边界的情况(向右/向下超出时调整位置) const menuRect = menu.getBoundingClientRect(); const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; if (menuRect.right > windowWidth) { menu.style.left = `${e.clientX - menuRect.width}px`; } if (menuRect.bottom > windowHeight) { menu.style.top = `${e.clientY - menuRect.height}px`; } } else { // 非table区域,隐藏自定义菜单 const menu = document.getElementById('newbieking-table-csv-menu'); if (menu) menu.style.display = 'none'; } }); })();