import Utils from "../Script/module/Pay/Utils"; const { ccclass } = cc._decorator; /** 预览和开发版共用的接口面板;独立预览可手动填写登录信息。 */ @ccclass export default class SevenApiPanel extends cc.Component { public preview: (info: any) => void = null; // 独立场景:仅模拟奖励。 public testHome: (info: any) => void = null; // 开发版主页:进入真实活动流程。 private panel: cc.Node; private uid: cc.EditBox; private token: cc.EditBox; private output: cc.Label; private summary: cc.Label; private scroll: cc.ScrollView; private request: XMLHttpRequest = null; private response: any = null; private responseUid = ""; // 仅预览和微信开发版创建面板,提供查询、模拟和主页测试入口。 start() { const fx = (cc as any).fx; if (!CC_PREVIEW && !(fx && fx.GameTool && fx.GameTool.getWechatGameVersion() === "开发版")) return; this.panel = new cc.Node("SevenApiPanel"); this.node.addChild(this.panel); this.panel.zIndex = 2000; this.panel.setContentSize(1040, 1840); const background = this.panel.addComponent(cc.Graphics); background.fillColor = cc.color(22, 29, 43, 255); background.rect(-520, -920, 1040, 1840); background.fill(); this.panel.addComponent(cc.BlockInputEvents); this.label("七日活动 · 测试服接口", 0, 855, 32); this.label("POST " + Utils.testHttpip + "signInActivityInfo", 0, 798, 23); this.label("请求可能更新后端签到记录;预览奖励不增加资产", 0, 749, 24); this.uid = this.input("测试账号 UID", 660, false); this.token = this.input("登录 token(不保存)", 562, true); this.button("读取当前登录", -315, 462, () => this.readLogin()); this.button("发送查询", 0, 462, () => this.send()); this.button("关闭面板", 315, 462, () => { this.panel.active = false; }); this.button(this.testHome ? "测试主页自动弹窗" : "预览返回数据", -250, 380, () => { if (this.testHome) { const fx = (cc as any).fx; const current = fx && fx.GameConfig && fx.GameConfig.GM_INFO; if (!current || !current.uid || !current.token) return this.message("主页尚未登录完成"); if (String(current.uid) !== this.uid.string.trim()) return this.message("主页流程使用当前登录账号,请先点读取当前登录"); if (!this.response || this.responseUid !== String(current.uid)) return this.message("请先发送查询,取得当前登录账号的活动数据"); if (CC_PREVIEW) return this.message("浏览器预览可查询接口;真实资产同步请在微信开发版 HomeScene 测试"); this.panel.active = false; this.testHome(JSON.parse(JSON.stringify(this.response))); return; } if (!this.response || !Array.isArray(this.response.rewards)) return this.message("请先取得有效活动数据"); if (this.preview) { this.preview(JSON.parse(JSON.stringify(this.response))); this.panel.active = false; } }); this.button("清除该账号弹窗记录", 220, 380, () => this.clearPopup()); this.summary = this.label("输入测试服 UID 和 token 后点击发送查询", 0, 265, 24); this.summary.node.setContentSize(960, 140); this.summary.overflow = cc.Label.Overflow.RESIZE_HEIGHT; const viewport = new cc.Node("ResponseScroll"); this.panel.addChild(viewport); viewport.setPosition(0, -320); viewport.setContentSize(970, 1050); viewport.addComponent(cc.Mask); this.scroll = viewport.addComponent(cc.ScrollView); this.scroll.horizontal = false; this.scroll.vertical = true; const content = new cc.Node("Response"); viewport.addChild(content); content.setAnchorPoint(0.5, 1); content.setPosition(0, 525); content.setContentSize(950, 1050); this.scroll.content = content; this.output = content.addComponent(cc.Label); this.output.fontSize = 23; this.output.lineHeight = 30; this.output.horizontalAlign = cc.Label.HorizontalAlign.LEFT; this.output.verticalAlign = cc.Label.VerticalAlign.TOP; this.output.overflow = cc.Label.Overflow.RESIZE_HEIGHT; this.output.string = "响应 JSON 将显示在这里,可上下滚动。"; this.readLogin(); this.panel.active = false; const toggle = new cc.Node("OpenSevenApi"); this.node.addChild(toggle); toggle.zIndex = 1500; toggle.setPosition(0, 925); toggle.setContentSize(280, 60); const title = toggle.addComponent(cc.Label); title.string = "打开接口测试面板"; title.fontSize = 28; toggle.on(cc.Node.EventType.TOUCH_END, () => { this.panel.active = true; this.readLogin(); }); } // 请求测试服并展示原始响应,保留查询时的 UID 供后续校验。 private send() { if (this.request) return this.message("请求中,请等待本次返回"); const uid = this.uid.string.trim(); const token = this.token.string.trim(); if (!uid || !token) return this.message("请填写 UID 和 token"); this.response = null; this.responseUid = ""; const xhr = cc.loader.getXMLHttpRequest(); this.request = xhr; const started = Date.now(); let finished = false; const finish = (error?: string) => { if (finished) return; finished = true; this.request = null; if (!cc.isValid(this.node, true)) return; const text = xhr.responseText || ""; this.output.string = text || error || "空响应"; if (error) this.message(error); else { try { const result = JSON.parse(text); this.output.string = JSON.stringify(result, null, 2); console.log("[testSeven][signInActivityInfo] 回复", this.output.string); this.responseUid = uid; this.response = result.code === 1 && result.data && typeof result.data === "object" ? result.data : null; const data = this.response; this.message("HTTP " + xhr.status + " | " + (Date.now() - started) + " ms | code=" + result.code + "\n" + (data ? "status=" + data.status + " canClaim=" + data.canClaim + " todayClaimed=" + data.todayClaimed + "\n" + this.popupRecord() : String(result.msg || "没有活动数据"))); } catch (_) { this.message("HTTP " + xhr.status + ":响应不是有效 JSON"); } } this.scheduleOnce(() => this.scroll.scrollToTop(0), 0); }; xhr.onload = () => finish(); xhr.onerror = () => finish("网络失败,请检查测试服地址、网络或浏览器 CORS 配置"); xhr.ontimeout = () => finish("请求超时(8 秒),可再次手动请求"); xhr.onabort = () => finish("请求已取消"); try { xhr.open("POST", Utils.testHttpip + "signInActivityInfo", true); xhr.timeout = 8000; xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); this.message("请求中…"); xhr.send("uid=" + encodeURIComponent(uid) + "&token=" + encodeURIComponent(token) + "&isDebug=false"); } catch (error) { finish("请求失败:" + error.message); } } // 读取当前登录信息填入输入框,token 不写入存储。 private readLogin() { const fx = (cc as any).fx; const info = fx && fx.GameConfig && fx.GameConfig.GM_INFO; this.uid.string = info && info.uid || ""; this.token.string = info && info.token || ""; if (!this.uid.string) { try { this.uid.string = String(JSON.parse(cc.sys.localStorage.getItem("uid")) || ""); } catch (_) { } } if (this.summary) this.message(this.token.string ? "已读取当前登录信息,请确认该账号属于测试服" : "未取得登录 token,请从测试账号登录结果复制填入"); } // 使用最近一次查询账号和活动生成弹窗记录键。 private popupKey(): string { return "seven_day_gift_auto_open_v1:" + this.responseUid + ":" + this.response.activityId; } // 展示当前预览环境保存的自动弹窗记录。 private popupRecord(): string { return "本地当天弹窗记录:" + (cc.sys.localStorage.getItem(this.popupKey()) || "无"); } // 仅清除已查询账号的本地弹窗记录,保留奖励和服务端状态。 private clearPopup() { if (!this.response || !this.response.activityId || !this.responseUid) return this.message("请先查询该测试账号的活动"); cc.sys.localStorage.removeItem(this.popupKey()); this.message("已清除查询账号的本地弹窗记录;未清除资产防重记录或后端签到状态"); } // 更新接口操作提示。 private message(text: string) { this.summary.string = text; } // 创建接口面板中的说明文字。 private label(text: string, x: number, y: number, size: number): cc.Label { const node = new cc.Node("Text"); this.panel.addChild(node); node.setPosition(x, y); const label = node.addComponent(cc.Label); label.fontSize = size; label.lineHeight = size + 8; label.string = text; return label; } // 创建单行输入框,token 使用密码样式。 private input(placeholder: string, y: number, password: boolean): cc.EditBox { const node = new cc.Node("Input"); this.panel.addChild(node); node.setPosition(0, y); node.setContentSize(920, 72); const bg = node.addComponent(cc.Graphics); bg.fillColor = cc.color(52, 65, 84); bg.rect(-460, -36, 920, 72); bg.fill(); const input = node.addComponent(cc.EditBox); input.inputMode = cc.EditBox.InputMode.SINGLE_LINE; input.inputFlag = password ? cc.EditBox.InputFlag.PASSWORD : cc.EditBox.InputFlag.DEFAULT; input.fontSize = 26; input.fontColor = cc.Color.WHITE; input.placeholder = placeholder; input.placeholderFontSize = 26; input.maxLength = 4096; return input; } // 创建接口面板的文字按钮。 private button(text: string, x: number, y: number, action: () => void) { const label = this.label(text, x, y, 27); label.node.color = cc.color(100, 220, 255); label.node.on(cc.Node.EventType.TOUCH_END, action); } // 销毁面板时取消请求,防止回调访问已销毁节点。 onDestroy() { if (this.request) { const xhr = this.request; this.request = null; xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onabort = null; xhr.abort(); } } }