// ==UserScript==
// @name TagBangumiUserPro 班固米用户备注
// @version 2.8
// @description Based on AttachHowOldtoUserinPosts, to tag users in Bangumi. Now with clickable badges to edit notes.
// @author age_anime
// @match https://bgm.tv/*
// @match https://chii.in/*
// @match https://bangumi.tv/*
// @grant none
// @license MIT
// @namespace https://gf.qytechs.cn/users/1426310
// ==/UserScript==
(function () {
'use strict';
// 默认颜色配置
const defaultNoteColors = {
"BGMER": "#40E0D0",
"Wiki管理": "#BB44BB",
"官方管理": "#000000",
"大佬": "#DD6D22",
"巨魔": "#CC3333",
"大明星": "#DD6D22",
"知名人士": "#3C3CC4"
};
let noteColors = JSON.parse(localStorage.getItem('userNoteColors_')) || defaultNoteColors;
const defaultNote = "bgmer";
const defaultColor = "rgba(102, 170, 85, 0.2)";
function displayUserNote(userId, note) {
const userAnchor = $("strong a.l[href='/user/" + userId + "']");
if (userAnchor.length > 0 && userAnchor.next(".note-badge").length === 0) {
const badgeColor = noteColors[note] || defaultColor;
const badge = $(`
<span class="note-badge" style="
background-color: ${badgeColor};
font-size: 11px;
padding: 2px 5px;
color: #FFF;
border-radius: 100px;
line-height: 150%;
display: inline-block;
cursor: pointer;
" title="点击编辑备注">${note}</span>`);
badge.click(function () {
const newNote = prompt("请输入新的备注:", note);
if (newNote !== null) {
const trimmedNote = newNote.trim();
if (trimmedNote === "") {
// 清空备注时移除缓存
localStorage.removeItem("userNote_" + userId);
badge.remove();
} else if (trimmedNote !== note) {
// 保存新备注到缓存
note = trimmedNote;
localStorage.setItem("userNote_" + userId, note);
badge.text(note);
const newBadgeColor = noteColors[note] || defaultColor;
badge.css("background-color", newBadgeColor);
}
}
});
userAnchor.after(badge);
}
}
function markUserNotes() {
$("strong a.l:not(.avatar)").each(function () {
const userLink = $(this).attr("href");
const userId = userLink.split("/").pop();
// 仅在缓存存在时使用,否则使用默认值(但不自动保存)
const note = localStorage.getItem("userNote_" + userId) || defaultNote;
displayUserNote(userId, note);
});
}
function importUserNotes() {
const input = document.createElement("input");
input.type = "file";
input.accept = "application/json";
input.onchange = function (event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function (e) {
try {
const userNotes = JSON.parse(e.target.result);
Object.keys(userNotes).forEach(key => localStorage.setItem(key, userNotes[key]));
alert("导入成功");
} catch (error) {
alert("无效的JSON文件:" + error);
}
};
reader.readAsText(file);
}
};
input.click();
}
function exportUserNotes() {
const userNotes = {};
Object.keys(localStorage).forEach(key => {
if (key.startsWith("userNote_")) {
userNotes[key] = localStorage.getItem(key);
}
});
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const entryCount = Object.keys(userNotes).length;
const filename = `userNotes_${timestamp}_${entryCount}entries.json`;
const blob = new Blob([JSON.stringify(userNotes, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
function openColorManager() {
const modal = document.createElement('div');
Object.assign(modal.style, {
position: 'fixed',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: 'white',
padding: '20px',
borderRadius: '5px',
boxShadow: '0 0 10px rgba(0,0,0,0.5)',
zIndex: '9999',
width: '450px'
});
// 添加颜色表单
const form = document.createElement('div');
form.style.marginBottom = '5px';
const nameLabel = document.createElement('label');
nameLabel.textContent = '名称:';
nameLabel.style.color = '#333333';
nameLabel.style.marginRight = '5px';
const nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.style.minWidth = '240px';
nameInput.style.width = 'auto';
nameInput.style.height = '22px';
nameInput.style.marginRight = '5px';
nameInput.style.position = 'relative';
nameInput.style.top = '-5px';
const colorLabel = document.createElement('label');
colorLabel.textContent = '颜色:';
colorLabel.style.color = '#333333';
colorLabel.style.marginRight = '5px';
const colorInput = document.createElement('input');
colorInput.type = 'color';
colorInput.style.marginRight = '5px';
const addButton = document.createElement('button');
addButton.textContent = '添加';
addButton.onclick = () => {
const name = nameInput.value.trim();
const color = colorInput.value;
if (name && color) {
noteColors[name] = color;
textarea.value = JSON.stringify(noteColors, null, 4);
nameInput.value = '';
colorInput.value = '#000000';
} else {
alert('名称和颜色不能为空!');
}
};
form.append(nameLabel, nameInput, colorLabel, colorInput, addButton);
// JSON 编辑区域
const textarea = document.createElement('textarea');
textarea.style.cssText = 'width: 100%; height: 200px; margin-bottom: 10px;';
textarea.value = JSON.stringify(noteColors, null, 4);
// 按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = 'text-align: right;';
const cancelBtn = document.createElement('button');
cancelBtn.textContent = '取消';
cancelBtn.onclick = () => modal.remove();
const saveBtn = document.createElement('button');
saveBtn.textContent = '保存';
saveBtn.style.marginLeft = '8px';
saveBtn.onclick = () => {
try {
const newColors = JSON.parse(textarea.value);
if (typeof newColors !== 'object' || newColors === null) {
throw new Error('请输入有效的JSON对象');
}
localStorage.setItem('userNoteColors_', JSON.stringify(newColors));
noteColors = newColors;
document.querySelectorAll('.note-badge').forEach(badge => {
const note = badge.textContent;
badge.style.backgroundColor = noteColors[note] || defaultColor;
});
modal.remove();
} catch (error) {
alert('错误: 请检查JSON格式(其他行的末尾是有英文逗号的,最后一行的末尾是没有逗号的!),还不会就把错误代码和JSON内容放在AI里面问问: ' + error.message);
}
};
buttonContainer.append(cancelBtn, saveBtn);
modal.append(form, textarea, buttonContainer);
document.body.appendChild(modal);
}
function addButtonFunctions() {
const badgeUserPanel = document.querySelector("ul#badgeUserPanel");
if (badgeUserPanel) {
// 导入按钮
const importLi = document.createElement('li');
const importBtn = document.createElement('a');
importBtn.href = '#';
importBtn.textContent = '导入用户数据';
importBtn.onclick = (e) => { e.preventDefault(); importUserNotes(); };
importLi.appendChild(importBtn);
// 导出按钮
const exportLi = document.createElement('li');
const exportBtn = document.createElement('a');
exportBtn.href = '#';
exportBtn.textContent = '导出用户数据';
exportBtn.onclick = (e) => { e.preventDefault(); exportUserNotes(); };
exportLi.appendChild(exportBtn);
// 颜色管理按钮
const colorLi = document.createElement('li');
const colorBtn = document.createElement('a');
colorBtn.href = '#';
colorBtn.textContent = '管理颜色配置';
colorBtn.onclick = (e) => { e.preventDefault(); openColorManager(); };
colorLi.appendChild(colorBtn);
badgeUserPanel.append(importLi, exportLi, colorLi);
}
}
if (document.body) {
markUserNotes();
} else {
window.onload = markUserNotes;
}
setTimeout(addButtonFunctions, 1000);
})();