TagBangumiUserPro 班固米用户备注

Based on AttachHowOldtoUserinPosts, to tag users in Bangumi. Now with clickable badges to edit notes.

目前為 2025-02-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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://greasyfork.org/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);
})();