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