B站粉丝、关注列表备注名功能

用于给B站好友添加备注名,可导入导出

// ==UserScript==
// @name         B站粉丝、关注列表备注名功能
// @namespace    http://nsfjgf.cn/
// @version      0.1
// @description  用于给B站好友添加备注名,可导入导出
// @author       nsfjgf
// @run-at       document-end
// @grant        none
// @match        https://space.bilibili.com/*
// @license      GPL-3.0
// ==/UserScript==

(function () {
  "use strict";

  let name_arr = [];
  if (localStorage.getItem("name_arr")) {
    name_arr = JSON.parse(localStorage.getItem("name_arr"));
  }

  function createBtn(params) {
    let fix_dom = document.createElement("div");
    fix_dom.classList.add("my__fix");
    fix_dom.innerHTML =
      '<div class="export">导出</div><div class="import"><input id="my_export" type="file" accept=".json"><label for="my_export">导入</label></div>';
    document.body.appendChild(fix_dom);

    const dragBox = document.querySelector(".my__fix");

    let isDragging = false;
    let offsetY = 0;

    dragBox.addEventListener("mousedown", (e) => {
      isDragging = true;
      offsetY = e.clientY - dragBox.offsetTop;
      dragBox.style.transition = "none"; // 取消过渡动画以避免延迟
    });

    document.addEventListener("mousemove", (e) => {
      if (isDragging) {
        dragBox.style.top = e.clientY - offsetY + "px";
      }
    });

    document.addEventListener("mouseup", () => {
      isDragging = false;
    });
  }

  createBtn();

  const exportFileJSON = (data = {}, filename = "dataJSON.json") => {
    if (typeof data === "object") {
      data = JSON.stringify(data, null, 4);
    }
    // 导出数据
    const blob = new Blob([data], { type: "text/json" }),
      e = new MouseEvent("click"),
      a = document.createElement("a");

    a.download = filename;
    a.href = window.URL.createObjectURL(blob);
    a.dataset.downloadurl = ["text/json", a.download, a.href].join(":");
    a.dispatchEvent(e);
  };

  function addExportListener() {
    $(".export").addEventListener("click", () => {
      exportFileJSON(name_arr, "name_arr.json");
    });
  }

  addExportListener();

  const importFileJSON = (ev) => {
    return new Promise((resolve, reject) => {
      const fileDom = ev.target,
        file = fileDom.files[0];

      // 格式判断
      if (file.type !== "application/json") {
        reject("仅允许上传json文件");
      }
      // 检验是否支持FileRender
      if (typeof FileReader === "undefined") {
        reject("当前浏览器不支持FileReader");
      }

      // 执行后清空input的值,防止下次选择同一个文件不会触发onchange事件
      ev.target.value = "";

      // 执行读取json数据操作
      const reader = new FileReader();
      reader.readAsText(file); // 读取的结果还有其他读取方式,我认为text最为方便

      reader.onerror = (err) => {
        reject("json文件解析失败", err);
      };

      reader.onload = () => {
        const resultData = reader.result;
        if (resultData) {
          try {
            const importData = JSON.parse(resultData);
            resolve(importData);
          } catch (error) {
            reject("读取数据解析失败", error);
          }
        } else {
          reject("读取数据解析失败", error);
        }
      };
    });
  };

  function mergeName_arr(data) {
    data.forEach((item) => {
      if (name_arr.find((item2) => item2.id === item.id)) return;
      name_arr.push(item);
    });

    localStorage.setItem("name_arr", JSON.stringify(name_arr));
    alert("导入合并成功,刷新后生效");
  }

  function addImportListenr() {
    $("my_export").addEventListener("change", (event) => {
      // exportFileJSON(name_arr, "name_arr.json");
      importFileJSON(event)
        .then((res) => {
          // 回显数据
          // showImportData(res);
          mergeName_arr(res);
        })
        .catch((err) => {
          alert(err);
        });
    });
  }

  addImportListenr();

  function addDynamicStyles() {
    // 检查是否已经添加过,避免重复添加

    const style = document.createElement("style");
    style.id = "dynamic-input-styles";

    style.textContent = `
        /* 基础输入框样式 */
        .edit_input {
         color: #999999;
         font-size:14px;
         line-height:20px;
         outline:none;
         width: 200px;
         margin:4px 0 4px 4px;
         text-indent:4px;
  
        }
  
        /* 焦点状态 */
        .edit_input:focus {
          border-color: #2196F3;
          border-radius:4px;
          box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2);
        }

        .my__fix {
            position: fixed;
            top: 50%;
            left: 0;
            background: #fff;
            border: 1px solid #ccc;
            cursor: pointer;
            padding: 6px;
            z-index:99999999;
        }
        .my__fix:hover {
            background: #f5f5f5;
        }

        .my__fix .import{
            margin-top:2px;
        }

        #my_export{
            width: 0;
            height: 0;
        }
  
  
      `;

    document.head.appendChild(style);
  }

  addDynamicStyles();

  function $(select) {
    if (select.includes(".")) return document.querySelector(select);
    return document.getElementById(select);
  }

  function updateUserName(id, name) {
    let index = name_arr.findIndex((item) => item.id === id);
    name = name.replace(/[\r\n]/g, "");

    if (name != "") {
      if (index !== -1) {
        name_arr[index].name = name;
      } else {
        name_arr.push({ id, name });
      }
    } else {
      name_arr.splice(index, 1);
    }

    localStorage.setItem("name_arr", JSON.stringify(name_arr));
  }

  // 创建一个新的 <div> 元素

  function init() {
    let listBox = document.querySelector(".relation-content");
    if (listBox.querySelector(".edit_input")) return;
    let userList = listBox.querySelectorAll(".item");
    // window.hasCall = true;
    for (let i = 0; i < userList.length; i++) {
      let userid = userList[i]
        .querySelector("[data-user-profile-id]")
        .getAttribute("data-user-profile-id");
      let nameDom = userList[i].querySelector(".relation-card-info__uname div");
      nameDom.style.webkitLineClamp = 3;
      let div = document.createElement("div");
      div.setAttribute("contenteditable", "plaintext-only");
      div.innerText = name_arr.find((item) => item.id === userid)
        ? name_arr.find((item) => item.id === userid).name
        : "备注名";
      div.classList.add("edit_input");
      div.addEventListener("click", (e) => {
        e.stopPropagation();
        e.preventDefault();
      });

      div.addEventListener("focusout", (e) => {
        let name = e.target.innerText;
        updateUserName(userid, name);
      });
      nameDom.appendChild(div);
    }
  }

  function callInit() {
    let listBox = document.querySelector(".relation-content");
    if (!!listBox) {
      let userList = listBox.querySelectorAll(".item");
      if (userList.length > 0) {
        initAndClick();
        return;
      } else {
        setTimeout(() => {
          callInit();
        }, 200);
      }
    } else {
      setTimeout(() => {
        callInit();
      }, 200);
    }
  }

  callInit(); //这样子调用没用

  function throttle(fn, delay) {
    let timer = null;
    return function () {
      if (timer) {
        return;
      }
      timer = setTimeout(() => {
        fn.apply(this);
        timer = null;
      }, delay);
    };
  }

  let initThrottle = throttle(init, 200);

  const observer1 = new MutationObserver((mutationsList, obs) => {
    for (let mutation of mutationsList) {
      // console.log("这里执行吧0000", mutation);
      if (mutation.type === "childList" && mutation.removedNodes.length > 0) {
        initThrottle();
      } else if (mutation.type === "attributes") {
        console.log(
          "The " + mutation.attributeName + " attribute was modified."
        );
      }
    }
  });

  // 保存原生方法
  const nativePushState = history.pushState;
  const nativeReplaceState = history.replaceState;

  // 重写 pushState
  history.pushState = function (state, title, url) {
    nativePushState.apply(this, arguments);
    dispatchUrlChangeEvent(url); // 触发自定义事件
  };

  // 重写 replaceState
  history.replaceState = function (state, title, url) {
    nativeReplaceState.apply(this, arguments);
    dispatchUrlChangeEvent(url);
  };

  // 创建自定义事件
  function dispatchUrlChangeEvent(url) {
    const event = new CustomEvent("urlchange", {
      detail: { url },
    });
    window.dispatchEvent(event);
  }

  let self_url = "";

  function initAndClick() {
    init();
    const targetNode1 = document.querySelector(".relation-content");
    observer1.observe(targetNode1, { childList: true, subtree: true });
    // 监听自定义事件
    window.addEventListener("urlchange", (e) => {
      if (self_url !== e.detail.url) {
        setTimeout(() => {
          if (self_url !== "") {
            observer1.disconnect();
          }
          const targetNode = document.querySelector(".relation-content");
          observer1.observe(targetNode, { childList: true, subtree: true });
        }, 200);
      }
      self_url = e.detail.url;
    });
  }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址