diff --git a/component/sign-in/sign-in.js b/component/sign-in/sign-in.js new file mode 100644 index 0000000..b419508 --- /dev/null +++ b/component/sign-in/sign-in.js @@ -0,0 +1,259 @@ +const signTemplate = document.createElement("template"); +signTemplate.innerHTML = `
0
寄托币
签到规则
`; + +class SignInBox extends HTMLElement { + static get observedAttributes() { + return ["integral", "signnum", "signreward", "todaycount"]; + } + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this.shadowRoot.appendChild(signTemplate.content.cloneNode(true)); + this._state = { + dayOfWeek: 0, + totalDaysInMonth: 0, + currentDay: 0, + list: [], + showList: [], + showPage: 1, + issign: 0, + }; + this._bindUI(); + this._computeMonth(); + this._init(); + this._getList(); + } + connectedCallback() { + window.signInBox = this; + } + attributeChangedCallback(name, oldVal, newVal) { + const el = this.shadowRoot.querySelector(`[data-field="${name}"]`); + if (el) el.textContent = newVal || "0"; + } + setUsers(list) { + const box = this.shadowRoot.querySelector('[data-list="users"]'); + box.innerHTML = list + .map( + (item) => ` +
+
${item.rank || ""}
+ +
+ + +
+
+ ` + ) + .join(""); + } + + _bindUI() { + const ruleBtn = this.shadowRoot.querySelector(".bi-rule"); + const ruleBox = this.shadowRoot.querySelector(".outer-ring"); + ruleBtn.addEventListener("click", () => (ruleBox.hidden = !ruleBox.hidden)); + const ruleClose = this.shadowRoot.querySelector(".rule-close"); + ruleClose.addEventListener("click", () => (ruleBox.hidden = true)); + const signBtn = this.shadowRoot.querySelector('[data-action="sign"]'); + if (signBtn) signBtn.addEventListener("click", () => this._postSign()); + } + + _pad(n) { + return String(n).padStart(2, "0"); + } + + _computeMonth() { + const firstDayOfMonth = new Date(); + firstDayOfMonth.setDate(1); + this._state.dayOfWeek = firstDayOfMonth.getDay(); + + const currentDate = new Date(); + const currentMonth = currentDate.getMonth() + 1; + const currentYear = currentDate.getFullYear(); + this._state.currentDay = currentDate.getDate(); + this._state.totalDaysInMonth = new Date(currentYear, currentMonth, 0).getDate(); + } + + _init() { + this._fetchGet("https://api.gter.net/v2/api/forum/getSignInfo").then((res) => { + if (res.code != 200) return; + const data = res.data || {}; + this._renderCalendar(data.list || {}); + this._setField("integral", Number(data.integral) || 0); + this._setField("signnum", data.signnum || 0); + this._setField("signreward", data.signreward || 0); + this._state.issign = data.issign || 0; + const tipsBox = this.shadowRoot.querySelector('[data-list="tips"]'); + const tips = data.tips || []; + tipsBox.innerHTML = tips + .map( + (t, i) => ` +
+
+ +
+
${t}
+
` + ) + .join(""); + }); + } + + _setField(name, value) { + const el = this.shadowRoot.querySelector(`[data-field="${name}"]`); + if (el) el.textContent = value; + } + + _renderCalendar(list) { + const box = this.shadowRoot.querySelector('[data-list="calendar"]'); + const items = []; + for (let i = 1; i <= this._state.totalDaysInMonth; i++) { + let type = 0; + let name = ""; + const key = this._pad(i); + if (list[key]) { + type = 2; + name = `+${list[key]}`; + } else if (this._state.currentDay > i) type = 1; + else if (this._state.currentDay == i) type = 3; + if (!name) name = this._state.currentDay == i ? "今" : i; + const cls = { 1: "formerly", 2: "already", 3: "today" }[type] || ""; + items.push(`
${name}
`); + } + box.innerHTML = items.join(""); + } + + _getList() { + this._fetchGet("https://api.gter.net/v2/api/forum/getSignRankList").then((res) => { + if (res.code != 200) return; + const data = res.data || {}; + this._state.list = data.list || []; + this._state.showList = this._state.list.slice(0, 10); + this.setUsers(this._state.showList); + this._setField("todaycount", data.todaycount || 0); + const more = this.shadowRoot.querySelector(".sign-in-more"); + const finish = this.shadowRoot.querySelector(".sign-in-finish"); + if (this._state.list.length > this._state.showList.length) { + more.hidden = false; + finish.hidden = true; + more.onclick = () => this._moreList(); + } else { + more.hidden = true; + finish.hidden = false; + } + }); + } + + _moreList() { + const arr = this._state.list.slice(this._state.showPage * 20 - 10, this._state.showPage * 20 + 10); + this._state.showList = this._state.showList.concat(arr); + this._state.showPage++; + this.setUsers(this._state.showList); + const more = this.shadowRoot.querySelector(".sign-in-more"); + const finish = this.shadowRoot.querySelector(".sign-in-finish"); + if (this._state.list.length > this._state.showList.length) { + more.hidden = false; + finish.hidden = true; + } else { + more.hidden = true; + finish.hidden = false; + } + } + + _postSign() { + const user = window.userInfoWin; + if (!user || (user?.uin <= 0 && user?.uid <= 0)) { + creationAlertBox("error", "没有绑定寄托账号"); + showWindow("login", "https://passport.gter.net/login/ajax", "get", -1, { cover: true }); + return; + } + this._fetchPost("https://api.gter.net/v2/api/forum/sign").then((res) => { + if (res.code != 200) { + creationAlertBox("error", res.message); + return; + } + const data = res.data || {}; + const rewardT = data.extra_reward * 1 + data.reward * 1 || 0; + this._setField("integral", (Number(this.shadowRoot.querySelector('[data-field="integral"]').textContent) || 0) + rewardT); + this._setField("signnum", (Number(this.shadowRoot.querySelector('[data-field="signnum"]').textContent) || 0) + 1); + this._setField("signreward", (Number(this.shadowRoot.querySelector('[data-field="signreward"]').textContent) || 0) + rewardT); + const myItem = { avatar: user.avatar || "", uniqid: user.uniqid || "", nickname: user.nickname || "匿名用户", rank: data.rank || 1, reward: rewardT, timestamp: this._currentDateTime() }; + this._state.showList.unshift(myItem); + this.setUsers(this._state.showList); + this._cutSucceed(); + localStorage.setItem("signInState", this._currentDate()); + }); + } + + _cutSucceed() { + const mask = this.shadowRoot.querySelector(".succeed-mask"); + if (!mask) return; + mask.hidden = false; + setTimeout(() => (mask.hidden = true), 1800); + } + + _currentDate() { + const now = new Date(); + return `${now.getFullYear()}-${this._pad(now.getMonth() + 1)}-${this._pad(now.getDate())}`; + } + + _currentDateTime() { + const d = new Date(); + return `${d.getFullYear()}-${this._pad(d.getMonth() + 1)}-${this._pad(d.getDate())} ${this._pad(d.getHours())}:${this._pad(d.getMinutes())}:${this._pad(d.getSeconds())}`; + } + + _fetchPost(url, data = {}) { + return new Promise((resolve) => { + const 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) { + resolve(xhr.response); + } + }; + xhr.send(JSON.stringify(data)); + }); + } + + _fetchGet(url) { + return new Promise((resolve) => { + 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; + try { + response = JSON.parse(response); + } catch {} + resolve(response); + } + }; + xhr.send(); + }); + } +} + +if (!customElements.get("sign-in-box")) customElements.define("sign-in-box", SignInBox); +window.signInBoxClass = SignInBox; +// 工厂:按需创建与打开 +(function(){ + let instance = null; + window.signInBox = { + _init(){ + if (!instance){ instance = document.createElement('sign-in-box'); document.body.appendChild(instance); } + const mask = instance.shadowRoot.querySelector('.signInBox-mask'); + if (mask) mask.hidden = false; + instance._init(); + }, + close(){ if (instance){ const mask = instance.shadowRoot.querySelector('.signInBox-mask'); if (mask) mask.hidden = true; } } + }; +})(); diff --git a/component/sign-in/sign-in.txt b/component/sign-in/sign-in.txt new file mode 100644 index 0000000..56a091f --- /dev/null +++ b/component/sign-in/sign-in.txt @@ -0,0 +1,659 @@ + +
+
+
+ + + +
+
+
+
+ +
0
+
寄托币
+
签到规则
+ +
+ +
+ +
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/css/signIn.css b/css/signIn.css index ed35469..a52abdd 100644 --- a/css/signIn.css +++ b/css/signIn.css @@ -131,6 +131,20 @@ body { padding: 20px; border-radius: 0 0 20px 0; } +.signInBox-mask .signInBox .signInBox-content .left-box .content-header .outer-ring::after { + content: ""; + position: absolute; + top: 26px; + left: -11px; + width: 0; + height: 0; + transform: translateX(-50%); + border: 12px solid #000; + border-top-color: transparent; + border-bottom-color: transparent; + border-left-color: transparent; + border-right-color: #fdda55; +} .signInBox-mask .signInBox .signInBox-content .left-box .content-header .outer-ring .rule-box { background-color: #fff; -moz-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.07058824); diff --git a/css/signIn.less b/css/signIn.less index f7ac4cf..798b29a 100644 --- a/css/signIn.less +++ b/css/signIn.less @@ -149,6 +149,21 @@ body { padding: 20px; border-radius: 0 0 20px 0; + &::after { + content: ""; + position: absolute; + top: 26px; + left: -11px; + width: 0; + height: 0; + transform: translateX(-50%); + border: 12px solid #000; + border-top-color: transparent; + border-bottom-color: transparent; + border-left-color: transparent; + border-right-color: #fdda55; + } + .rule-box { background-color: #fff; -moz-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.0705882352941176); diff --git a/details.html b/details.html index 9b39132..c871089 100644 --- a/details.html +++ b/details.html @@ -17,7 +17,7 @@
-
fi88yrHXiDSj
+
fi88yrHXiDSj
@@ -483,6 +483,7 @@
+ diff --git a/js/details.js b/js/details.js index 64706e7..09c218d 100644 --- a/js/details.js +++ b/js/details.js @@ -15,6 +15,9 @@ const appSectionIndex = createApp({ setup() { onMounted(() => { getUserInfoWin(); + setTimeout(() => { + signInBox._init(); + }, 3000); }); let isLogin = ref(false); @@ -176,8 +179,8 @@ const appSectionIndex = createApp({ let html = formattedText; - html = html.replaceAll('', "[b]"); - html = html.replaceAll('', "[/b]"); + html = html.replaceAll("", "[b]"); + html = html.replaceAll("", "[/b]"); // 1. 还原换行符为
标签 html = html.replace(/\n/g, "
"); diff --git a/js/save.js b/js/save.js index 3ca5e83..f00a368 100644 --- a/js/save.js +++ b/js/save.js @@ -32,6 +32,9 @@ const watchList = { // 监听 item-project.txt,同步到 item-project.js "../component/item-project/item-project.txt": "../component/item-project/item-project.js", + // 监听 sign-in.txt,同步到 sign-in.js + "../component/sign-in/sign-in.txt": "../component/sign-in/sign-in.js", + // 监听 bi.txt,同步到 bi.js "../component/bi/bi.txt": "../component/bi/bi.js", @@ -61,8 +64,7 @@ function syncContent(txtPath, jsPath) { // 匹配 template: `...` 结构,替换反引号内的内容 const templateRegex = /(template:\s*)(`[^`]*`)/; - // 匹配 template.innerHTML = `...` 结构 - const innerHTMLRegex = /(template\.innerHTML\s*=\s*)(`[^`]*`)/; + const innerHTMLRegex = /((?:[A-Za-z_\$][\w\$]*Template|template)\.innerHTML\s*=\s*)(`[^`]*`)/; if (templateRegex.test(jsContent)) { jsContent = jsContent.replace(templateRegex, `$1\`${txtContent}\``);