多主页支持,全局导入导出,紧凑悬浮滑动菜单,适合多域名管理备份!
// ==UserScript==
// @name 漫画助手
// @namespace http://tampermonkey.net/
// @version 3.6.1
// @description 多主页支持,全局导入导出,紧凑悬浮滑动菜单,适合多域名管理备份!
// @author おかゆ&ChatGPT
// @license MIT
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// ==/UserScript==
(function() {
'use strict';
const domain = location.hostname;
let allLists = {};
let mangaList = [];
let ui;
function loadData() {
allLists = GM_getValue('mangaLists', {});
mangaList = allLists[domain] || [];
}
function saveData() {
allLists[domain] = mangaList;
GM_setValue('mangaLists', allLists);
}
// 读取主页域名数组,兼容旧版字符串
function getHomepageDomains() {
let domains = GM_getValue('homepageDomains', []);
if (!Array.isArray(domains)) {
if (typeof domains === 'string' && domains) {
domains = [domains];
} else {
domains = [];
}
}
return domains;
}
// 判断当前域名是否主页之一
function isHomepageDomain() {
const domains = getHomepageDomains();
return domains.includes(domain);
}
// 添加当前域名到主页数组(如果未包含)
function addHomepage() {
let domains = getHomepageDomains();
if (!domains.includes(domain)) {
domains.push(domain);
GM_setValue('homepageDomains', domains);
alert(`添加主页成功:${domain}`);
location.reload();
} else {
alert(`主页已存在:${domain}`);
}
}
// 从主页数组中删除当前域名,并删除该域名漫画数据
function deleteHomepage() {
let domains = getHomepageDomains();
if (confirm(`确定要删除当前主页 ${domain} 吗?这将删除该域名所有漫画记录。`)) {
domains = domains.filter(d => d !== domain);
GM_setValue('homepageDomains', domains);
loadData();
delete allLists[domain];
GM_setValue('mangaLists', allLists);
alert('主页设置及漫画记录已清除');
if (ui && ui.parentNode) ui.parentNode.removeChild(ui);
location.reload();
}
}
// 导出所有数据(含主页数组)
function exportAllData() {
const exportData = {
homepageDomains: getHomepageDomains(),
mangaLists: GM_getValue('mangaLists', {})
};
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'MangaAssistantBackup.json';
a.click();
URL.revokeObjectURL(url);
}
// 导入数据,兼容新版数组和旧版字符串
function importAllData() {
let txt = prompt('粘贴JSON(全局数据)');
if (!txt) return;
try {
let data = JSON.parse(txt);
if (typeof data !== 'object' || Array.isArray(data)) throw new Error('格式错误:顶层应为对象');
if (!data.mangaLists || typeof data.mangaLists !== 'object' || Array.isArray(data.mangaLists)) {
throw new Error('格式错误:缺少或错误的 mangaLists');
}
let homepageDomains = [];
if ('homepageDomains' in data) {
if (!Array.isArray(data.homepageDomains)) {
throw new Error('格式错误:homepageDomains 应为数组');
}
homepageDomains = data.homepageDomains.filter(d => typeof d === 'string');
} else if ('homepageDomain' in data) {
if (typeof data.homepageDomain === 'string') {
homepageDomains = [data.homepageDomain];
} else {
throw new Error('格式错误:homepageDomain 应为字符串');
}
}
GM_setValue('mangaLists', data.mangaLists);
GM_setValue('homepageDomains', homepageDomains);
alert(`成功导入,主页:${homepageDomains.length ? homepageDomains.join(', ') : '无'}`);
location.reload();
} catch (e) {
alert(`导入失败,${e.message || '格式错误'}`);
}
}
// 添加当前页面漫画到列表
function addCurrentManga() {
loadData();
let def = document.title.replace(/ -.*$/, '').slice(0, 16);
let name = prompt('漫画名(最多18字符)', def);
if (!name) return;
mangaList.unshift({
id: Date.now() + '',
name: name.slice(0, 18),
url: location.href,
added: new Date().toISOString()
});
saveData();
updateMenu();
}
// 注册右键菜单命令
function registerMenu() {
loadData();
const isHomepage = isHomepageDomain();
GM_registerMenuCommand('📥 导入全部(恢复数据)', importAllData);
if (isHomepage) {
GM_registerMenuCommand('📤 导出全部(备份数据)', exportAllData);
GM_registerMenuCommand('➖ 删除主页', deleteHomepage);
} else {
GM_registerMenuCommand('⚙️ 添加为主页', addHomepage);
}
GM_registerMenuCommand('🔄 刷新', () => location.reload());
}
// UI 渲染
function renderUI() {
if (!isHomepageDomain()) return;
loadData();
if (!ui) {
ui = document.createElement('div');
ui.id = 'manga-assistant-ui';
// 悬浮按钮
const btn = document.createElement('div');
btn.textContent = '📚';
btn.style.cssText = `
position:fixed;bottom:20px;right:20px;width:44px;height:44px;
background:#06c;color:#fff;display:flex;
align-items:center;justify-content:center;font-size:22px;
cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.25);z-index:9999;
`;
btn.onclick = toggleMenu;
ui.appendChild(btn);
// 菜单
const menu = document.createElement('div');
menu.id = 'manga-menu';
menu.style.cssText = `
position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);
width:280px;height:360px;background:#fff;box-shadow:0 3px 8px rgba(0,0,0,0.25);
padding:8px;font-family:sans-serif;display:none;z-index:9999;
flex-direction:column;justify-content:flex-start;align-items:center;
`;
// 标题
const title = document.createElement('div');
title.textContent = '📚 漫画助手/漫画收藏';
title.style.cssText = 'font-size:16px; margin-bottom:6px; font-weight:600;';
menu.appendChild(title);
// 内容区域(可滑动)
const listC = document.createElement('div');
listC.id = 'manga-list';
listC.style.cssText = `
flex:1; width:100%; overflow-y:auto; overflow-x:hidden;
display:flex; flex-direction:column; justify-content:flex-start;
margin-bottom:8px; font-size:12px;
`;
menu.appendChild(listC);
// 添加漫画按钮
const control = document.createElement('div');
control.style.cssText = 'width:100%; text-align:center;';
const addBtn = document.createElement('button');
addBtn.textContent = '➕ 添加漫画';
addBtn.style.cssText = 'margin:4px 0; font-size:13px; padding:4px 8px; cursor:pointer;';
addBtn.onclick = addCurrentManga;
control.appendChild(addBtn);
menu.appendChild(control);
ui.appendChild(menu);
document.body.appendChild(ui);
}
updateMenu();
}
// 显示/隐藏菜单
function toggleMenu() {
const menu = document.getElementById('manga-menu');
if (menu.style.display === 'none' || menu.style.display === '') {
updateMenu();
menu.style.display = 'flex';
} else {
menu.style.display = 'none';
}
}
// 更新菜单内容
function updateMenu() {
const listC = document.getElementById('manga-list');
listC.innerHTML = '';
if (mangaList.length === 0) {
listC.textContent = '📭 暂无收藏漫画';
return;
}
mangaList.forEach((m, index) => {
const itemDiv = document.createElement('div');
itemDiv.style.cssText = 'margin:4px 0; display:flex; align-items:center;';
const renameBtn = document.createElement('button');
renameBtn.textContent = '✏️';
renameBtn.style.cssText = 'font-size:11px; border:none; background:none; cursor:pointer; margin-right:4px; padding:0;';
renameBtn.onclick = () => {
const newName = prompt('输入新名称(最多18字符)', m.name);
if (newName) {
mangaList[index].name = newName.slice(0, 18);
saveData();
updateMenu();
}
};
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '❌';
deleteBtn.style.cssText = 'font-size:11px; border:none; background:none; cursor:pointer; margin-right:6px; padding:0;';
deleteBtn.onclick = () => {
if (confirm(`确定删除 "${m.name}" 吗?`)) {
mangaList.splice(index, 1);
saveData();
updateMenu();
}
};
const a = document.createElement('a');
a.textContent = m.name;
a.href = m.url;
a.target = '_blank';
a.style.cssText = 'color:#06c; text-decoration:none; flex:1; text-align:left; font-size:13px;';
itemDiv.appendChild(renameBtn);
itemDiv.appendChild(deleteBtn);
itemDiv.appendChild(a);
listC.appendChild(itemDiv);
});
}
// 主流程
loadData();
registerMenu();
renderUI();
})();