Files
PC-Light-Forum/component/bi/bi.js
DESKTOP-RQ919RC\Pc dc871d80c0 feat: 添加BI组件并优化多个页面样式和功能
refactor: 重构slideshow-box组件,移除帖子标签

fix: 修复item-bottom组件中QR码加载状态显示问题

style: 调整多个页面的CSS样式,包括字体大小和间距

perf: 优化save.js文件同步逻辑,支持更多模板格式

docs: 更新组件文档和注释

chore: 添加bi组件相关文件并配置监听同步

test: 更新测试用例以适应组件变更
2025-11-07 19:42:19 +08:00

208 lines
14 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: 2; display: none; } .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; } .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; }</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"> 该帖子已获得 <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>`;
// 2. 定义组件类
class BiCard extends HTMLElement {
constructor() {
super();
this.coins = 0;
this.token = "";
this.pagetpye = "";
// 创建 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.querySelector(".coins-area .closeCoinBox");
this.coinSubmitEl.addEventListener("click", () => this.coinSubmit());
this.closeCoinBoxEl.addEventListener("click", () => this.closeCoinBox());
this.input = this.shadowRoot.querySelector(".coins-input .input");
this.coinListAreaEl = this.shadowRoot.querySelector(".coins-list-area");
this.defaultcoinnum = 0;
// 将实例存储到全局变量中,以便外部调用
window.biComponent = this;
}
// 监听的属性列表
static get observedAttributes() {
return ["coins", "token", "pagetpye"];
}
// 属性变化回调
attributeChangedCallback(attrName, oldVal, newVal) {
this[attrName] = newVal; // 同步属性值到变量
}
init() {
this.getCoinConfig();
if (!this.coinListAreaEl.querySelector(".list")) this.getCoinRankList();
}
getCoinConfig() {
this.$ajaxget(`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 = strategy;
this.mybalanceEl.textContent = mybalance;
this.coinsEl.textContent = this.coins;
this.defaultcoinnum = defaultcoinnum;
this.coinsAreaEl.style.display = "flex";
});
}
getCoinRankList() {
this.$ajaxget(`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://bbs.gter.net/home.php?mod=space&uin=${item.user.uin}" 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;
this.$ajax(`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;
this.mybalanceEl.textContent = Number(this.mybalanceEl.textContent) - num || 0;
this.input.value = "";
const coins = data.coins;
this.coinsEl.textContent = coins || 0;
if (this.pagetpye == "forum") document.querySelector(".action-bar-item.coins .text").textContent = coins || 0;
this.getCoinRankList();
});
}
closeCoinBox() {
this.coinsAreaEl.style.display = "none";
}
// 静态方法,用于在外部调用组件初始化
static initComponent() {
if (window.biComponent) {
window.biComponent.init();
return true;
}
console.warn("bi组件尚未加载或挂载");
return false;
}
// 生命周期:组件挂载
connectedCallback() {}
// 生命周期:组件卸载
disconnectedCallback() {
console.log(`用户卡片 ${this.getAttribute("username")} 已卸载`);
}
$ajax(url) {
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return new Promise(function (resolve, reject) {
axios
.post(url, data, {
emulateJSON: true,
withCredentials: true,
})
.then(function (res) {
var data = typeof res.data == "string" ? JSON.parse(res.data) : res.data;
if (data.code == 401) {
// 需要登录
showWindow("login", "https://passport.gter.net/login/ajax", "get", -1, {
cover: true,
});
reject();
}
resolve(data);
})
.catch((err) => {
if (err.response.status == 401)
showWindow("login", "https://passport.gter.net/login/ajax", "get", -1, {
cover: true,
});
});
});
}
$ajaxget(url) {
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return new Promise(function (resolve, reject) {
axios
.get(
url,
{},
{
emulateJSON: true,
withCredentials: true,
}
)
.then(function (res) {
var data = typeof res.data == "string" ? JSON.parse(res.data) : res.data;
if (data.code == 401) {
// 需要登录
showWindow("login", "https://passport.gter.net/login/ajax", "get", -1, {
cover: true,
});
reject();
}
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
}
// 3. 注册组件(确保只注册一次)
if (!customElements.get("bi-card")) customElements.define("bi-card", BiCard);
// 4. 导出组件类,以便在外部直接调用
window.BiComponent = BiCard;