Files
PC-Light-Forum/component/bi/bi.js
DESKTOP-RQ919RC\Pc 51179c3842 refactor: 移除调试日志并优化代码结构
移除多个文件中的console.log调试语句
添加search-tag.css和search-tag.less中的set-hint-box样式
新增recommend.js推荐功能模块
优化ajaxGet调用和用户信息处理逻辑
2025-12-17 19:36:53 +08:00

261 lines
21 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 1. 创建组件模板和样式(内置到 JS 中,无需外部 HTML/Template
const template = document.createElement("template");
template.innerHTML = `<style> .flexflex { display: flex; } .flexcenter { display: flex; justify-content: center; align-items: center; } .flexjcenter { display: flex; justify-content: center; } .flexacenter { display: flex; align-items: center; } .flex1 { flex: 1; } .flexcolumn { display: flex; flex-direction: column; } .one-line-display { word-break: keep-all; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .two-line-display { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; } .coins-area { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 10004; display: none; } .coins-area * { box-sizing: border-box; } .coins-area a { text-decoration: none; } .coins-area .coins-box { width: 624px; background-color: #ffffff; border: 1px solid #e9eef2; border-radius: 11px; flex-direction: column; padding: 40px 30px 35px; position: relative; display: none; max-width: calc(100vw - 10px); } .coins-area .coins-box .fork { width: 12px; height: 12px; position: absolute; top: 20px; right: 20px; cursor: pointer; } .coins-area .coins-box .coins-head { margin-bottom: 32px; } .coins-area .coins-box .coins-head .icon { width: 50px; height: 60px; margin-right: 16px; } .coins-area .coins-box .coins-head .text { font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; font-weight: 400; color: #7f7f7f; font-size: 14px; } .coins-area .coins-box .coins-head .text .sum { font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif; font-weight: 650; font-size: 18px; color: #000000; margin: 0 5px; } .coins-area .coins-box .coins-input { width: 333px; height: 36px; background-color: #ffffff; border: 1px solid #d7d7d7; border-radius: 8px; overflow: hidden; margin-bottom: 31px; } .coins-area .coins-box .coins-input .input { height: 100%; border: none; outline: none; font-size: 14px; padding: 0 10px; } .coins-area .coins-box .coins-input .btn { width: 84px; height: 100%; line-height: 36px; text-align: center; background-color: #50e3c2; cursor: pointer; } .coins-area .coins-box .coins-info { color: #555555; font-size: 14px; margin-bottom: 43px; } .coins-area .coins-box .coins-info .icon { width: 16px; height: 16px; margin-right: 6px; } .coins-area .coins-box .coins-info .sum { font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif; font-weight: 650; color: #000000; margin: 0 5px; } .coins-area .coins-box .coins-info .strategy { margin-left: 5px; color: #026277; cursor: pointer; text-decoration: none; } .coins-area .coins-box .coins-list-area { max-height: 347px; background-color: #fbfbfb; border-radius: 16px; width: 100%; /* padding: 20px 20px 0; */ flex-direction: column; } .coins-area .coins-box .coins-list-area .coins-total { color: #7f7f7f; font-size: 14px; margin: 20px 20px 0; } .coins-area .coins-box .coins-list-area .coins-total .sum { font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif; font-weight: 650; color: #000000; margin: 0 5px; } .coins-area .coins-box .coins-list-area .list { overflow: auto; margin: 0 20px; } .coins-area .coins-box .coins-list-area .list .item { height: 65px; padding: 0 35px; } .coins-area .coins-box .coins-list-area .list .item:not(:last-child) { border-bottom: 1px solid #f2f2f2; } .coins-area .coins-box .coins-list-area .list .item .serial { font-family: "Arial-BoldMT", "Arial Bold", "Arial", sans-serif; font-weight: 700; font-style: normal; color: #ffb600; margin-right: 114px; } .coins-area .coins-box .coins-list-area .list .item .user { color: #555555; font-size: 13px; cursor: pointer; } .coins-area .coins-box .coins-list-area .list .item .user .avatar { width: 32px; height: 32px; margin-right: 10px; border-radius: 50%; } .coins-area .coins-box .coins-list-area .list .item .amount { color: #000000; font-size: 16px; } .coins-area .coins-box .coins-list-area .list .item .amount .text { font-size: 13px; margin-left: 2px; } .unlock-insertcoins-box { width: 520px; border: 1px solid #e5e5e5; background-color: #fff; border-radius: 11px; flex-direction: column; display: flex; -webkit-box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.21); box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.21); display: none; } .unlock-insertcoins-box .unlock-insertcoins-close { width: 16px; height: 16px; padding: 10px; align-self: self-end; box-sizing: content-box; cursor: pointer; } .unlock-insertcoins-box .unlock-insertcoins-head { font-size: 14px; color: #555555; margin: 10px auto 35px; } .unlock-insertcoins-box .unlock-insertcoins-head .bi-icon { width: 40px; height: 48px; } .unlock-insertcoins-box .unlock-insertcoins-btn { width: 175px; height: 43px; border-radius: 45px; background-color: #50e3c2; cursor: pointer; } .unlock-insertcoins-box .in-all { color: #555555; font-size: 13px; margin-top: 17px; margin-bottom: 54px; } .unlock-insertcoins-box .in-all span { font-weight: 650; } .no-jituobi-pop-box { width: 520px; flex-direction: column; border: 1px solid #e5e5e5; background-color: #fff; border-radius: 11px; display: flex; -webkit-box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.21); box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.21); padding-bottom: 55px; display: none; } .no-jituobi-pop-box .no-jituobi-close { width: 16px; height: 16px; padding: 10px; align-self: flex-end; cursor: pointer; box-sizing: content-box; } .no-jituobi-pop-box .no-jituobi-head { font-size: 16px; color: #333; margin: 23px auto 42px; } .no-jituobi-pop-box .no-jituobi-head .bi-icon { width: 50px; height: 60px; } .no-jituobi-pop-box .strategy-btn { width: 198px; height: 43px; color: #000; font-size: 16px; border-radius: 100px; margin: 0 auto; cursor: pointer; } .no-jituobi-pop-box .strategy-btn .strategy-icon { width: 16px; height: 16px; margin-left: 8px; } .greenBj { background-color: #50e3c2; border-color: #50e3c2 !important; }</style> <div class="coins-area flexcenter"> <div class="coins-box flexcenter"> <img class="fork closeCoinBox" src="https://app.gter.net/image/gter/commonCom/bi/img/fork-icon.png" /> <div class="coins-head flexacenter"> <img class="icon" src="https://app.gter.net/image/gter/commonCom/bi/img/bi.png" /> <div class="text flexacenter"> 该<span class="pagetpyeText"></span>已获得 <div class="sum"></div> 个寄托币 </div> </div> <div class="coins-input flexacenter"> <input class="input flex1" type="number" placeholder="输入投币数" /> <div class="btn coinSubmit">投币</div> </div> <div class="coins-info flexacenter"> <img class="icon" src="https://app.gter.net/image/gter/commonCom/bi/img/bi-black-icon.png" /> 你当前共有 <div class="sum"></div> 寄托币 <a class="strategy" target="_blank" href="">[挣币攻略]</a> </div> <div class="coins-list-area flexflex"></div> </div> <!-- <div class="unlock-insertcoins-box flexcenter" v-if="coinMybalance >= defaultcoinnum"> --> <div class="unlock-insertcoins-box flexcenter"> <img class="unlock-insertcoins-close closeCoinBox" src="https://app.gter.net/image/gter/commonCom/bi/img/fork-icon.png" /> <div class="unlock-insertcoins-head flexacenter"> <img class="bi-icon" src="https://app.gter.net/image/gter/commonCom/bi/img/bi.png" style="margin-right: 11px;" /> 作者设置了阅读限制,解锁所有内容仅需 <span style="font-size: 20px;font-weight: 650;margin: 0 4px;" class="coinNum"></span> 寄托币 </div> <div class="unlock-insertcoins-btn flexcenter">投币解锁</div> <div class="in-all">你共有 <span class="balance"></span> 寄托币</div> </div> <!-- <div class="no-jituobi-pop-box" v-else-if="coinMybalance <= 0 || coinMybalance < defaultcoinnum"> --> <div class="no-jituobi-pop-box"> <img class="no-jituobi-close closeCoinBox" src="https://app.gter.net/image/gter/commonCom/bi/img/fork-icon.png" /> <div class="no-jituobi-head flexacenter"> <img class="bi-icon" src="https://app.gter.net/image/gter/commonCom/bi/img/bi.png" style="margin-right: 12px;"> <span style="margin-top: 10px;">你的寄托币不够,快去发帖挣币吧</span> </div> <a href="https://bbs.gter.net/thread-2543548-1-1.html" target="_blank"> <div class="strategy-btn greenBj flexcenter"> 攒币指南 <img class="strategy-icon" src="https://app.gter.net/image/gter/commonCom/bi/img/u1045.svg" /> </div> </a> </div></div>`;
// 2. 定义组件类
class BiCard extends HTMLElement {
constructor() {
super();
this.coins = 0;
this.token = "";
this.pagetpye = "";
this.displaytips = "";
// 创建 Shadow DOM 并挂载模板
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.strategyEl = this.shadowRoot.querySelector(".coins-info .strategy");
this.mybalanceEl = this.shadowRoot.querySelector(".coins-info .sum");
this.coinsAreaEl = this.shadowRoot.querySelector(".coins-area");
this.coinsEl = this.shadowRoot.querySelector(".coins-area .coins-head .sum");
this.coinSubmitEl = this.shadowRoot.querySelector(".coins-input .coinSubmit");
this.closeCoinBoxEl = this.shadowRoot.querySelectorAll(".coins-area .closeCoinBox");
this.closeCoinBoxEl.forEach((item) => {
item.addEventListener("click", () => this.closeCoinBox());
});
this.coinSubmitEl.addEventListener("click", () => this.coinSubmit());
this.input = this.shadowRoot.querySelector(".coins-input .input");
this.coinListAreaEl = this.shadowRoot.querySelector(".coins-list-area");
this.defaultcoinnum = 0;
// 将实例存储到全局变量中,以便外部调用
window.biComponent = this;
this.pagetpyeText = this.shadowRoot.querySelector(".pagetpyeText");
this.coinsBoxEl = this.shadowRoot.querySelector(".coins-box");
// 解锁插入币弹窗
this.coinsUnlock = this.shadowRoot.querySelector(".unlock-insertcoins-box");
this.coinsNoBi = this.shadowRoot.querySelector(".no-jituobi-pop-box");
}
// 监听的属性列表
static get observedAttributes() {
return ["coins", "token", "pagetpye", "displaytips"];
}
// 属性变化回调
attributeChangedCallback(attrName, oldVal, newVal) {
this[attrName] = newVal; // 同步属性值到变量
}
init(type) {
this.fetchGetData(`https://api.gter.net/v2/api/forum/getCoinConfig`).then((res) => {
const data = res.data;
const strategy = data.config.strategy.url || 0;
const mybalance = data.mybalance || 0;
const defaultcoinnum = data.defaultcoinnum || 0;
// 替换策略链接
this.strategyEl.href = "https://bbs.gter.net/account.php?a=credit&op=rule";
this.mybalanceEl.textContent = mybalance;
this.coinsEl.textContent = this.coins;
this.defaultcoinnum = defaultcoinnum;
this.input.placeholder = `输入投币数,默认${defaultcoinnum}个币`;
this.coinsAreaEl.style.display = "flex";
if (type == "unlock") {
if (mybalance >= defaultcoinnum) {
this.coinsBoxEl.style.display = "none";
this.coinsNoBi.style.display = "none";
this.coinsUnlock.style.display = "flex";
this.coinsUnlock.querySelector(".coinNum").textContent = defaultcoinnum;
this.coinsUnlock.querySelector(".balance").textContent = mybalance;
this.coinsUnlock.querySelector(".unlock-insertcoins-btn").addEventListener("click", () => this.coinSubmit(type));
} else {
this.coinsBoxEl.style.display = "none";
this.coinsUnlock.style.display = "none";
this.coinsNoBi.style.display = "flex";
}
} else {
this.coinsUnlock.style.display = "none";
this.coinsNoBi.style.display = "none";
this.coinsBoxEl.style.display = "flex";
}
});
if (!this.coinListAreaEl.querySelector(".list")) this.getCoinRankList();
const obj = {
offer: "Offer",
summary: "总结",
mj: "面经",
thread: "帖子",
vote: "投票",
};
this.pagetpyeText.textContent = obj[this.pagetpye];
}
getCoinRankList() {
this.fetchGetData(`https://api.gter.net/v2/api/forum/getCoinRankList?token=${this.token}&limit=1000`).then((res) => {
const data = res.data;
this.coinListAreaEl.innerHTML = "";
const coinNubmer = data.nubmer;
if (coinNubmer == 0) return;
const total = `<div class="coins-total flexacenter">共<div class="sum">${coinNubmer}</div>人参与投币:</div>`;
const coinList = data.data;
let list = coinList
.map((item) => {
return `<div class="item flexacenter" =>
<div class="serial">${item.rank}</div>
<a class="user flex1 flexacenter" href="https://f.gter.net/u/${item.user.uniqid}" target="_blank">
<img class="avatar" src="${item.user.avatar}" alt="" />
<div class="username one-line-display flex1">${item.user.nickname || "匿名用户"}</div>
</a>
<div class="amount flexacenter">
${item.coins}
<div class="text">币</div>
</div>
</div>`;
})
.join("");
list = `<div class="list flex1">${list}</div>`;
const listHTML = total + list;
this.coinListAreaEl.innerHTML = listHTML;
});
}
coinSubmit() {
const num = Number(this.input.value || this.defaultcoinnum) || 0;
if (num <= 0) {
creationAlertBox("error", "投币数量必须大于0");
return;
}
this.fetchData(`https://api.gter.net/v2/api/forum/postTopicCoin`, {
token: this.token,
num,
}).then((res) => {
if (res.code == 200) creationAlertBox("success", res.message);
else creationAlertBox("error", res.message);
if (res.code != 200) return;
let data = res.data;
if (this.displaytips == "coindisplayuser") {
setTimeout(() => window.location.reload(), 1000);
return;
}
this.mybalanceEl.textContent = Number(this.mybalanceEl.textContent) - num || 0;
this.input.value = "";
const coins = data.coins;
this.coinsEl.textContent = coins || 0;
if (this.pagetpye == "thread") document.querySelector(".action-bar-item.coins .text").textContent = coins || 0;
if (this.pagetpye == "vote") document.querySelector(".coinText").textContent = coins || 0;
if (this.pagetpye == "mj") document.querySelector(".coinText").textContent = coins || 0;
if (this.pagetpye == "offer") document.querySelector(".broadside-text.cursorpointer.coinText").textContent = (coins || 0) + " 寄托币";
if (this.pagetpye == "summary") document.querySelector(".broadside-text.cursorpointer.coinText").textContent = (coins || 0) + " 寄托币";
this.getCoinRankList();
});
}
closeCoinBox() {
this.coinsAreaEl.style.display = "none";
}
// 静态方法,用于在外部调用组件初始化 type normal 展示正常的投币和列表 unlock 解锁插入币弹窗 或者 币不足
static initComponent(type = "normal") {
if (window.biComponent) {
window.biComponent.init(type);
return true;
}
console.warn("bi组件尚未加载或挂载");
return false;
}
// 生命周期:组件挂载
connectedCallback() {}
// 生命周期:组件卸载
disconnectedCallback() {
console.log(`用户卡片 ${this.getAttribute("username")} 已卸载`);
}
getScriptParameter(paramName) {
const currentScript = document.currentScript;
if (currentScript && currentScript.src) {
const url = new URL(currentScript.src, window.location.origin);
return url.searchParams.get(paramName);
}
return null;
}
fetchData(url, data) {
return new Promise((resolve, reject) => {
if (data) data["v"] = this.getScriptParameter("v") || "v2";
var xhr = new XMLHttpRequest();
xhr.responseType = "json";
xhr.withCredentials = true;
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
if (["127.0.0.1", "localhost", "192.168.18.219"].includes(location.hostname)) xhr.setRequestHeader("Authorization", "3b01343c65e3b2fa3ce32ae26feb3a9b");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let response = xhr.response;
resolve(response);
} else if (xhr.status === 401) {
if (typeof ajax_login === "function") ajax_login();
else window.open("https://passport.gter.net/?referer=" + escape(location.href), "_self");
}
};
xhr.send(JSON.stringify(data));
});
}
fetchGetData(url) {
return new Promise((resolve, reject) => {
const paramSymbol = url.includes("?") ? "&" : "?";
url = `${url}${paramSymbol}v=${this.getScriptParameter("v") || "v2"}`;
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("GET", url, true);
if (["127.0.0.1", "localhost", "192.168.18.219"].includes(location.hostname)) xhr.setRequestHeader("Authorization", "3b01343c65e3b2fa3ce32ae26feb3a9b");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
let response = xhr.response;
resolve(JSON.parse(response));
} else if (xhr.status === 401) {
if (typeof ajax_login === "function") ajax_login();
else window.open("https://passport.gter.net/?referer=" + escape(location.href), "_self");
}
};
xhr.send();
});
}
}
// 3. 注册组件(确保只注册一次)
if (!customElements.get("bi-card")) customElements.define("bi-card", BiCard);
// 4. 导出组件类,以便在外部直接调用
window.BiComponent = BiCard;