您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在右下角添加悬浮球,点击对应图床按钮弹出上传表单对话框(目前支持 imgURL/SMMS 图床)
当前为
// ==UserScript== // @name 图床上传脚本 // @namespace http://21zys.com/ // @version 1.3.4 // @description 在右下角添加悬浮球,点击对应图床按钮弹出上传表单对话框(目前支持 imgURL/SMMS 图床) // @match *://*/* // @author 21zys // @grant GM_setValue // @grant GM_getValue // @grant GM_setClipboard // @require https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.13/dayjs.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js // @connect sm.ms // @connect smms.app // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function() { 'use strict'; // 加载时从localStorage中获取悬浮球和对话框的位置 var savedPosition = JSON.parse(localStorage.getItem('floatingBallPosition')) || { right: '20px', bottom: '20px' }; const myJson = JSON; const imgUrlDialog = initImgUrlDialog(); const smmsDialog = initSmmsDialog(); // 创建悬浮球容器 var floatingContainer = document.createElement('div'); floatingContainer.style.position = 'fixed'; floatingContainer.style.right = savedPosition.right; floatingContainer.style.bottom = savedPosition.bottom; floatingContainer.style.left = savedPosition.left || 'auto'; floatingContainer.style.top = savedPosition.top || 'auto'; floatingContainer.style.padding = '25px'; floatingContainer.style.zIndex = '99999'; // 创建悬浮球 var floatingBall = document.createElement('div'); 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 = '99999'; floatingBall.innerHTML = '+'; floatingBall.style.userSelect = 'none'; floatingBall.style.webkitUserSelect = 'none'; // Chrome, Safari, Edge floatingBall.style.mozUserSelect = 'none'; // Firefox floatingBall.style.msUserSelect = 'none'; // Internet Explorer/Edge floatingContainer.appendChild(floatingBall); document.body.appendChild(floatingContainer); let isDraggingBall = false; let startX, startY; // 添加拖拽功能并保存悬浮球位置 floatingContainer.addEventListener('mousedown', function(e) { startX = e.clientX; startY = e.clientY; var offsetX = e.clientX - floatingContainer.getBoundingClientRect().left; var offsetY = e.clientY - floatingContainer.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; floatingContainer.style.left = newLeft + 'px'; floatingContainer.style.top = newTop + 'px'; floatingContainer.style.right = 'auto'; floatingContainer.style.bottom = 'auto'; // 保存位置到localStorage localStorage.setItem('floatingBallPosition', JSON.stringify({ left: floatingContainer.style.left, top: floatingContainer.style.top })); } } function onMouseUp() { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); setTimeout(() => isDraggingBall = false, 100); // 延迟100ms重置拖拽状态 } document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }); // 创建imgUrl按钮 var imgUrlButton = document.createElement('div'); imgUrlButton.style.position = 'relative'; imgUrlButton.style.bottom = '90px'; imgUrlButton.style.left = '35px'; imgUrlButton.style.width = '50px'; imgUrlButton.style.height = '50px'; imgUrlButton.style.background = "url('https://www.imgurl.org/favicon.ico') no-repeat center center"; imgUrlButton.style.backgroundSize = "50% 50%"; imgUrlButton.style.backgroundColor = 'rgba(0, 0, 0, 0)'; imgUrlButton.style.cursor = 'pointer'; imgUrlButton.style.display = 'none'; imgUrlButton.style.zIndex = '99999'; floatingBall.appendChild(imgUrlButton); var smmsButton = document.createElement('div'); smmsButton.style.position = 'relative'; smmsButton.style.bottom = '90px'; smmsButton.style.left = '50px'; smmsButton.style.width = '50px'; smmsButton.style.height = '50px'; smmsButton.style.background = "url('https://smms.app/favicon-32x32.png') no-repeat center center"; smmsButton.style.backgroundSize = "50% 50%"; smmsButton.style.backgroundColor = 'rgba(0, 0, 0, 0)'; smmsButton.style.cursor = 'pointer'; smmsButton.style.display = 'none'; imgUrlButton.style.zIndex = '99999'; floatingBall.appendChild(smmsButton); // 鼠标移入悬浮球时,显示小按钮 floatingContainer.addEventListener('mouseenter', function() { imgUrlButton.style.display = 'block'; smmsButton.style.display = 'block'; }); // 鼠标移出悬浮球时,隐藏小按钮 floatingContainer.addEventListener('mouseleave', function() { imgUrlButton.style.display = 'none' smmsButton.style.display = 'none' }); // 点击小悬浮球按钮,打开对话框 imgUrlButton.addEventListener('click', function() { openDialog(imgUrlDialog); }); // 点击小悬浮球按钮,打开对话框 smmsButton.addEventListener('click', function() { openDialog(smmsDialog); }); // 打开对话框的函数,带淡入效果 function openDialog(dialog) { dialog.style.display = 'block'; setTimeout(function() { dialog.style.opacity = '1'; }, 10); } // 关闭对话框的函数,带淡出效果 function closeDialog(dialog) { dialog.style.opacity = '0'; setTimeout(function() { dialog.style.display = 'none'; }, 300); } // 4.1 imgURL 图床对话框元素初始化 function initImgUrlDialog() { // 使用统一的存储键名,确保在所有域名下都共享数据 const GLOBAL_STORAGE_KEY = 'globalImgUrlUploadData'; const DIALOG_POSITION = 'imgUrlDialogPosition'; // 从localStorage中获取全局持久化数据 const globalData = JSON.parse(GM_getValue(GLOBAL_STORAGE_KEY, null) || localStorage.getItem(GLOBAL_STORAGE_KEY)) || { uid: '您的UID', token: '您的Token', uploadDate: new Date().toISOString().split('T')[0], uploadCount: 0, selectedTab: 'imgURL', selectedAlbumId: 'default', albumList: [] }; const today = new Date().toISOString().split('T')[0]; // 如果日期已变,重置上传数量 if (globalData.uploadDate !== today) { globalData.uploadDate = today; globalData.uploadCount = 0; saveGlobalData(); } function saveGlobalData() { GM_setValue(GLOBAL_STORAGE_KEY, JSON.stringify(globalData)); localStorage.setItem(GLOBAL_STORAGE_KEY, JSON.stringify(globalData)); } var savedDialogPosition = JSON.parse(localStorage.getItem(DIALOG_POSITION)) || null; // 创建对话框主体,绑定拖拽功能 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 = '999999'; 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 = 25; 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(DIALOG_POSITION, 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 = '×'; 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); }); 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'; uidLabel.style.display = 'inline-block'; uidLabel.style.minHeight = 'none'; uidLabel.style.padding = 'none'; var uidInput = document.createElement('input'); uidInput.type = 'text'; uidInput.name = 'uid'; uidInput.value = globalData.uid ? globalData.uid : ''; uidInput.style.padding = '8px'; uidInput.style.border = '1px solid #ccc'; uidInput.style.borderRadius = '4px'; uidInput.style.width = 'auto'; uidInput.style.maxWidth = 'none'; form.appendChild(uidLabel); form.appendChild(uidInput); // 创建Token字段 var tokenLabel = document.createElement('label'); tokenLabel.innerText = 'Token:'; tokenLabel.style.fontWeight = 'bold'; tokenLabel.style.color = '#333'; tokenLabel.style.display = 'inline-block'; tokenLabel.style.minHeight = 'none'; tokenLabel.style.padding = 'none'; var tokenInput = document.createElement('input'); tokenInput.type = 'text'; tokenInput.name = 'token'; tokenInput.value = globalData.token ? globalData.token : ''; tokenInput.style.padding = '8px'; tokenInput.style.border = '1px solid #ccc'; tokenInput.style.borderRadius = '4px'; tokenInput.style.width = 'auto'; tokenInput.style.maxWidth = 'none'; 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= '120px'; albumSelect.style.height = '34px'; albumSelect.style.marginTop = '-3px'; albumSelect.style.marginRight = '3px'; albumSelect.style.textAlign = 'center'; albumSelect.style.border = '1px solid #ccc'; albumSelect.style.borderRadius = '4px'; albumSelect.style.overflow = 'hidden'; albumSelectContainer.appendChild(albumSelect); // 创建水印文本输入框 var waterInput = document.createElement('input'); waterInput.type = 'text'; waterInput.name = 'water'; waterInput.style.width= '120px'; waterInput.style.height = '34px'; waterInput.style.marginTop = '-3px'; waterInput.style.textAlign = 'left'; waterInput.style.border = '1px solid #ccc'; waterInput.style.borderRadius = '4px'; waterInput.placeholder = '请输入水印文本'; waterInput.value = globalData.water ? globalData.water : ''; albumSelectContainer.appendChild(waterInput); dialog.appendChild(albumSelectContainer); // 将相册下拉列表放在表单外 // 加载持久化相册列表和选择 loadAlbumList(); // 刷新相册列表 albumBtn.addEventListener('click', function() { fetchAlbums(); }); albumSelect.addEventListener('change', function() { globalData.selectedAlbumId = albumSelect.value; saveGlobalData() }); function loadAlbumList() { albumSelect.innerHTML = ''; // 清空下拉列表 var defaultOption = document.createElement('option'); defaultOption.value = 'default'; defaultOption.textContent = '默认相册'; albumSelect.appendChild(defaultOption); if (globalData.albumList.length > 0) { globalData.albumList.forEach(function(album) { var option = document.createElement('option'); option.value = album.album_id; option.textContent = album.name; albumSelect.appendChild(option); }); } // 选择持久化的相册 albumSelect.value = globalData.selectedAlbumId; } 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) { globalData.albumList = response.data; saveGlobalData() // 持久化保存相册列表 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'; fileLabel.style.display = 'inline-block'; fileLabel.style.minHeight = 'none'; fileLabel.style.padding = 'none'; 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'; fileInput.style.width = 'auto'; fileInput.style.maxWidth = 'none'; 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 = `今日已上传 ${globalData.uploadCount} 张图片`; uploadCountLabel.style.fontSize = '1rem'; uploadCountLabel.style.fontWeight = 'bold'; uploadCountLabel.style.color = 'rgb(51, 51, 51)'; uploadCountLabel.style.marginRight = '10px'; uploadCountLabel.style.display = 'inline-block'; uploadCountLabel.style.minHeight = 'none'; uploadCountLabel.style.padding = 'none'; buttonContainer.appendChild(uploadCountLabel); // 创建上传按钮 var uploadBtn = document.createElement('input'); uploadBtn.type = 'submit'; uploadBtn.id = 'upload-btn'; uploadBtn.value = '开始上传'; uploadBtn.className = 'upload-btn'; uploadBtn.style.width = 'auto'; uploadBtn.style.maxWidth = 'none'; 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.width = 'auto'; clearBtn.style.maxWidth = 'none'; 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%'; // 创建进度条 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'; // 创建选项卡 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 === globalData.selectedTab ? '#007bff' : '#f8f9fa'; tabButton.style.color = tab === globalData.selectedTab ? '#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.maxWidth = 'none'; 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() { GM_setClipboard(resultInput.value); }); // 将表单、选项卡容器、进度条容器、结果容器添加到对话框中 dialog.appendChild(form); dialog.appendChild(tabContainer); dialog.appendChild(progressContainer); dialog.appendChild(resultContainer); // 处理选项卡切换 function selectTab(tab) { globalData.selectedTab = tab; saveGlobalData(); // 持久化选项卡选择 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 = ``; break; case 'BBCode': formattedText = `[img]${url}[/img]`; break; } if (resultInput.value !== formattedText) { resultInput.value = formattedText; // 只有在内容不匹配时才更新文本框内容 } } // 初始化选项卡UI和结果格式 updateTabUI(globalData.selectedTab); // 处理表单提交逻辑 form.addEventListener('submit', function(event) { event.preventDefault(); // 阻止表单默认提交行为 // 获取UID和Token var uid = uidInput.value.trim(); var token = tokenInput.value.trim(); var water = waterInput.value.trim(); // 检查UID和Token是否为空 if (!uid || !token) { resultInput.value = '请填写UID和Token后再试。'; resultInput.style.color = 'red'; return; } // 保存UID和Token到全局持久化数据中 globalData.uid = uid; globalData.token = token; globalData.selectedAlbumId = albumSelect.value; globalData.water = water; saveGlobalData(); // 检查是否选择了文件 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; } // 添加水印并生成新图片 addWatermark(file, 'res.21zys.com', function(watermarkedBlob) { // 显示进度条 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(globalData.selectedTab); // 更新结果格式 resultInput.style.color = 'green'; // 更新并保存上传数量 globalData.uploadCount++; saveGlobalData(); uploadCountLabel.textContent = `今日已上传 ${globalData.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'; }); // 将带有水印的Blob作为文件上传 var formData = new FormData(); formData.append('file', watermarkedBlob, file.name); formData.append('uid', globalData.uid); formData.append('token', globalData.token); if (albumSelect.value !== 'default') { // 仅当选择了具体相册时才附加album_id formData.append('album_id', albumSelect.value); } xhr.send(formData); }); }); return dialog; } // sm.ms 图床对话框初始化 function initSmmsDialog() { const GLOBAL_STORAGE_KEY = 'globalSmmsUploadData'; const DIALOG_POSITION = 'smmsDialogPosition'; // 从localStorage中获取全局持久化数据 const globalData = JSON.parse(GM_getValue(GLOBAL_STORAGE_KEY, null) || localStorage.getItem(GLOBAL_STORAGE_KEY)) || { token: '您的Token', uploadDate: new Date().toISOString().split('T')[0], uploadCount: 0, selectedTab: 'imgURL', water: '', renamePattern: '', autoIncrement: 0 }; const today = new Date().toISOString().split('T')[0]; if (globalData.uploadDate !== today) { globalData.uploadDate = today; globalData.uploadCount = 0; saveGlobalData(); } function saveGlobalData() { GM_setValue(GLOBAL_STORAGE_KEY, JSON.stringify(globalData)); localStorage.setItem(GLOBAL_STORAGE_KEY, JSON.stringify(globalData)); } var savedDialogPosition = JSON.parse(localStorage.getItem(DIALOG_POSITION)) || null; // 创建对话框主体,绑定拖拽功能 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 = '999999'; 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; const edgeThreshold = 25; 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.setItem(DIALOG_POSITION, JSON.stringify({ left: dialog.style.left, top: dialog.style.top })); } function onMouseUp() { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); setTimeout(() => isDraggingDialog = false, 100); } document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); } }); var closeButton = document.createElement('span'); closeButton.innerHTML = '×'; 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); }); dialog.appendChild(closeButton); // 创建表单 var form = document.createElement('form'); form.enctype = 'multipart/form-data'; form.method = 'post'; form.id = 'smms-upform'; form.style.display = 'grid'; form.style.gap = '10px'; // 创建水印字段 var waterLabel = document.createElement('label'); waterLabel.innerText = '文本水印:'; waterLabel.style.fontWeight = 'bold'; waterLabel.style.color = '#333'; waterLabel.style.display = 'inline-block'; waterLabel.style.minHeight = 'none'; waterLabel.style.padding = 'none'; var waterInput = document.createElement('input'); waterInput.type = 'text'; waterInput.name = 'smms-water'; waterInput.value = globalData.water ? globalData.water : ''; waterInput.placeholder = '请输入需要添加的文本水印'; waterInput.style.padding = '8px'; waterInput.style.border = '1px solid #ccc'; waterInput.style.borderRadius = '4px'; waterInput.style.width = 'auto'; waterInput.style.maxWidth = 'none'; form.appendChild(waterLabel); form.appendChild(waterInput); // 创建高级文件重命名字段 var renameLabel = document.createElement('label'); renameLabel.innerText = '高级文件重命名:'; renameLabel.style.fontWeight = 'bold'; renameLabel.style.color = '#333'; renameLabel.style.display = 'inline-block'; renameLabel.style.minHeight = 'none'; renameLabel.style.padding = 'none'; var renameInput = document.createElement('input'); renameInput.type = 'text'; renameInput.name = 'smms-rename'; renameInput.value = globalData.renamePattern ? globalData.renamePattern : ''; renameInput.placeholder = '请输入重命名格式(忽略请留空)'; renameInput.style.padding = '8px'; renameInput.style.border = '1px solid #ccc'; renameInput.style.borderRadius = '4px'; renameInput.style.width = 'auto'; renameInput.style.maxWidth = 'none'; form.appendChild(renameLabel); form.appendChild(renameInput); // 创建Token字段 var tokenLabel = document.createElement('label'); tokenLabel.innerText = 'Token:'; tokenLabel.style.fontWeight = 'bold'; tokenLabel.style.color = '#333'; tokenLabel.style.display = 'inline-block'; tokenLabel.style.minHeight = 'none'; tokenLabel.style.padding = 'none'; var tokenInput = document.createElement('input'); tokenInput.type = 'text'; tokenInput.name = 'token'; tokenInput.value = globalData.token ? globalData.token : ''; tokenInput.style.padding = '8px'; tokenInput.style.border = '1px solid #ccc'; tokenInput.style.borderRadius = '4px'; tokenInput.style.width = 'auto'; tokenInput.style.maxWidth = 'none'; form.appendChild(tokenLabel); form.appendChild(tokenInput); // 创建文件上传输入框 var fileLabel = document.createElement('label'); fileLabel.innerText = '选择文件:'; fileLabel.style.fontWeight = 'bold'; fileLabel.style.color = '#333'; fileLabel.style.display = 'inline-block'; fileLabel.style.minHeight = 'none'; fileLabel.style.padding = 'none'; 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'; fileInput.style.width = 'auto'; fileInput.style.maxWidth = 'none'; 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 = `今日已上传 ${globalData.uploadCount} 张图片`; uploadCountLabel.style.fontSize = '1rem'; uploadCountLabel.style.fontWeight = 'bold'; uploadCountLabel.style.color = 'rgb(51, 51, 51)'; uploadCountLabel.style.marginRight = '10px'; uploadCountLabel.style.display = 'inline-block'; uploadCountLabel.style.minHeight = 'none'; uploadCountLabel.style.padding = 'none'; buttonContainer.appendChild(uploadCountLabel); // 创建上传按钮 var uploadBtn = document.createElement('input'); uploadBtn.type = 'submit'; uploadBtn.id = 'upload-btn'; uploadBtn.value = '开始上传'; uploadBtn.className = 'upload-btn'; uploadBtn.style.width = 'auto'; uploadBtn.style.maxWidth = 'none'; 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.width = 'auto'; clearBtn.style.maxWidth = 'none'; 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%'; // 创建进度条 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'; tabContainer.style.marginBottom = '3px'; // 创建选项卡 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 === globalData.selectedTab ? '#007bff' : '#f8f9fa'; tabButton.style.color = tab === globalData.selectedTab ? '#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.maxWidth = 'none'; 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() { GM_setClipboard(resultInput.value); }); // 将表单、选项卡容器、进度条容器、结果容器添加到对话框中 dialog.appendChild(form); dialog.appendChild(tabContainer); dialog.appendChild(progressContainer); dialog.appendChild(resultContainer); // 处理选项卡切换 function selectTab(tab) { globalData.selectedTab = tab; saveGlobalData(); // 持久化选项卡选择 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 = ``; break; case 'BBCode': formattedText = `[URL=${url}][IMG]${url}[/IMG][/URL]`; break; } if (resultInput.value !== formattedText) { resultInput.value = formattedText; // 只有在内容不匹配时才更新文本框内容 } } // 初始化选项卡UI和结果格式 updateTabUI(globalData.selectedTab); // 处理表单提交逻辑 form.addEventListener('submit', function(event) { event.preventDefault(); var token = tokenInput.value.trim(); var water = waterInput.value.trim(); var renamePattern = renameInput.value.trim(); var autoIncrement = globalData.autoIncrement; autoIncrement++; if (!token) { resultInput.value = '请填写Token后再试。'; resultInput.style.color = 'red'; return; } globalData.token = token; globalData.water = water; globalData.renamePattern = renamePattern; globalData.autoIncrement = autoIncrement; saveGlobalData(); if (!fileInput.files.length) { resultInput.value = '请选择要上传的文件。'; resultInput.style.color = 'red'; return; } var file = fileInput.files[0]; var allowedFormats = ['jpg', 'jpeg', 'png', 'gif', 'bmp']; var fileExtension = file.name.split('.').pop().toLowerCase(); if (!allowedFormats.includes(fileExtension)) { resultInput.value = '仅支持格式: ' + allowedFormats.join(', '); resultInput.style.color = 'red'; return; } // 添加水印并生成新图片 addWatermark(file, water, function(watermarkedBlob) { // 显示进度条 progressContainer.style.display = 'block'; var formData = new FormData(); formData.append('smfile', watermarkedBlob, superPictureRename(file.name, renamePattern, autoIncrement)); formData.append('format', 'json'); progressBar.value = 30; GM_xmlhttpRequest({ method: 'POST', url: 'https://sm.ms/api/v2/upload', headers: { 'Authorization': `${globalData.token}` }, data: formData, onprogress: function(e) { if (e.lengthComputable) { var percentComplete = (e.loaded / e.total) * 100; progressBar.value = percentComplete; } }, onload: function(response) { var responseData = myJson.parse(response.responseText); if (responseData.success && responseData.data && responseData.data.url) { progressBar.value = 80; resultInput.dataset.url = responseData.data.url; // 保存上传成功后的URL updateResultFormat(globalData.selectedTab); // 更新结果格式 resultInput.style.color = 'green'; // 更新并保存上传数量 globalData.uploadCount++; saveGlobalData(); uploadCountLabel.textContent = `今日已上传 ${globalData.uploadCount} 张图片`; } else { resultInput.value = '上传失败: ' + (responseData.message || '未知错误'); resultInput.style.color = 'red'; } progressBar.value = 100; progressContainer.style.display = 'none'; }, onerror: function() { resultInput.value = '上传失败: 网络错误'; resultInput.style.color = 'red'; progressContainer.style.display = 'none'; } }); }); }); return dialog; } // 添加水印功能 function addWatermark(file, text, callback) { const reader = new FileReader(); reader.onload = function(event) { const img = new Image(); img.src = event.target.result; img.onload = function() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置canvas尺寸与图片一致 canvas.width = img.width; canvas.height = img.height; // 绘制原始图片 ctx.drawImage(img, 0, 0); // 字体大小,根据图片大小改变水印文案字体大小 var fontSize = img.width * 0.1; ctx.font = `${fontSize}px Arial`; ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'; // 黑色,透明度0.4 ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; // 计算水印位置并倾斜45度 ctx.translate(canvas.width / 2, canvas.height / 2); ctx.rotate(-Math.PI / 4); // 添加水印 ctx.fillText(text, 0, 0); // 恢复canvas状态 ctx.rotate(Math.PI / 4); ctx.translate(-canvas.width / 2, -canvas.height / 2); // 导出带水印的新图片 canvas.toBlob(callback, file.type); }; }; reader.readAsDataURL(file); } // 图片高级重命名 function superPictureRename(filename, pattern, autoIncrement) { if (!pattern) return filename; // 去除文件后缀名 const extIndex = filename.lastIndexOf('.'); const ext = extIndex > -1 ? filename.substring(extIndex) : ''; const baseFilename = extIndex > -1 ? filename.substring(0, extIndex) : filename; // 生成当前日期时间 const now = dayjs(); // 生成随机字符串 function randomString(length) { let result = ''; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const charactersLength = characters.length; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } // 替换占位符 const formatted = pattern.replace(/{Y}/g, now.format('YYYY')) .replace(/{y}/g, now.format('YY')) .replace(/{m}/g, now.format('MM')) .replace(/{d}/g, now.format('DD')) .replace(/{h}/g, now.format('HH')) .replace(/{i}/g, now.format('mm')) .replace(/{s}/g, now.format('ss')) .replace(/{ms}/g, now.format('SSS')) .replace(/{timestamp}/g, now.valueOf()) .replace(/{md5}/g, CryptoJS.MD5(randomString(32)).toString()) .replace(/{md5-16}/g, CryptoJS.MD5(randomString(16)).toString().substring(0, 16)) .replace(/{uuid}/g, uuid.v4()) .replace(/{str-(\d+)}/g, (match, p1) => randomString(parseInt(p1))) .replace(/{filename}/g, baseFilename) .replace(/{auto}/g, () => autoIncrement); // 返回格式化后的文件名 return formatted + ext; } })();