import { SevenDayApiTrace } from "../SevenDayGiftApi"; export interface SevenDayGiftApiFeedbackActions { queryAll: () => void; queryLevel: () => void; queryActivity: () => void; queryCoin: () => void; queryProps: () => void; queryPower: () => void; } /** 独立的接口请求/响应查看器,仅用于非正式环境。 */ export default class SevenDayGiftApiFeedbackPanel { private readonly maxTraceCount = 120; private root: cc.Node = null; private panel: cc.Node = null; private content: cc.Node = null; private logLabel: cc.Label = null; private scrollView: cc.ScrollView = null; private traces: string[] = []; constructor(private parent: cc.Node, private actions: SevenDayGiftApiFeedbackActions) { } public build() { this.root = new cc.Node("sevenDayGiftApiFeedback"); this.root.parent = this.parent; this.root.setContentSize(this.parent.getContentSize()); this.root.zIndex = 31000; const toggle = this.createButton("feedbackToggle", "接口反馈", 160, 72, () => this.show()); toggle.parent = this.root; toggle.setPosition(-this.root.width * 0.5 + 95, this.root.height * 0.5 - 190); this.panel = new cc.Node("panel"); this.panel.parent = this.root; this.panel.setContentSize(720, 1180); this.panel.addComponent(cc.BlockInputEvents); this.drawRect(this.panel, cc.color(21, 27, 38, 248), 720, 1180); const scale = Math.min(1, (this.root.width - 20) / 720, (this.root.height - 20) / 1180); this.panel.setScale(scale); this.panel.active = false; this.createLabel(this.panel, "七日活动接口反馈", 36, -40, 540, 540, 56, cc.color(114, 218, 255)); const close = this.createButton("close", "关闭", 120, 58, () => this.hide()); close.parent = this.panel; close.setPosition(290, 540); this.addButton("查询全部", -235, 462, () => this.actions.queryAll(), cc.color(31, 143, 105)); this.addButton("查询服务器等级", 0, 462, () => this.actions.queryLevel(), cc.color(43, 123, 190)); this.addButton("查询签到活动", 235, 462, () => this.actions.queryActivity(), cc.color(43, 123, 190)); this.addButton("查询金币", -235, 384, () => this.actions.queryCoin(), cc.color(154, 107, 34)); this.addButton("查询道具", 0, 384, () => this.actions.queryProps(), cc.color(116, 77, 164)); this.addButton("查询无限体力", 235, 384, () => this.actions.queryPower(), cc.color(174, 70, 96)); this.createLabel(this.panel, "领取和猫皮肤接口会在真实领取时自动记录;下方可上下滚动查看完整字段", 23, -55, 326, 555, 50, cc.color(255, 220, 142)); const clear = this.createButton("clear", "清空记录", 125, 52, () => this.clear()); clear.parent = this.panel; clear.setPosition(290, 326); this.buildScrollView(); this.appendSystemMessage("等待请求。可点击“查询全部”,或分别查询单个接口。"); } public appendTrace(trace: SevenDayApiTrace) { const elapsed = trace.elapsedMs === undefined ? "" : " 耗时=" + trace.elapsedMs + "ms"; const text = "[" + trace.time + "] [#" + trace.requestId + "] [" + trace.phase + "]\n" + "接口:" + trace.endpoint + elapsed + "\n" + "地址:" + trace.url + "\n" + (trace.phase === "REQUEST" ? "请求参数:" : "返回内容:") + "\n" + trace.payload; this.traces.push(text); if (this.traces.length > this.maxTraceCount) this.traces.shift(); this.refreshLog(); } public appendSystemMessage(message: string) { this.traces.push("[提示] " + message); this.refreshLog(); } public show() { if (!this.panel) return; this.panel.active = true; this.refreshLog(); } public hide() { if (this.panel) this.panel.active = false; } public clear() { this.traces = []; this.appendSystemMessage("记录已清空。"); } public destroy() { if (this.root && cc.isValid(this.root)) this.root.destroy(); this.root = null; this.panel = null; this.content = null; this.logLabel = null; this.scrollView = null; } private buildScrollView() { const scrollNode = new cc.Node("scrollView"); scrollNode.parent = this.panel; scrollNode.setContentSize(670, 820); scrollNode.setPosition(0, -110); this.drawRect(scrollNode, cc.color(8, 12, 18, 235), 670, 820); const view = new cc.Node("view"); view.parent = scrollNode; view.setContentSize(650, 800); view.addComponent(cc.Mask); this.content = new cc.Node("content"); this.content.parent = view; this.content.setAnchorPoint(0.5, 1); this.content.setContentSize(630, 800); this.content.setPosition(0, 400); const labelNode = new cc.Node("logLabel"); labelNode.parent = this.content; labelNode.setAnchorPoint(0.5, 1); labelNode.setContentSize(610, 780); labelNode.setPosition(0, -10); labelNode.color = cc.color(224, 235, 244); this.logLabel = labelNode.addComponent(cc.Label); this.logLabel.fontSize = 23; this.logLabel.lineHeight = 29; this.logLabel.horizontalAlign = cc.Label.HorizontalAlign.LEFT; this.logLabel.verticalAlign = cc.Label.VerticalAlign.TOP; this.logLabel.overflow = cc.Label.Overflow.CLAMP; this.scrollView = scrollNode.addComponent(cc.ScrollView); this.scrollView.content = this.content; this.scrollView.horizontal = false; this.scrollView.vertical = true; this.scrollView.inertia = true; this.scrollView.elastic = true; } private refreshLog() { if (!this.logLabel || !this.content) return; const text = this.traces.slice().reverse().join("\n\n────────────────────────\n\n"); const lineCount = this.estimateVisualLines(text, 44); const height = Math.max(800, lineCount * 29 + 35); this.content.setContentSize(630, height); this.logLabel.node.setContentSize(610, height - 20); this.logLabel.node.setPosition(0, -10); this.logLabel.string = text; if (this.scrollView) this.scrollView.scrollToTop(0); } private estimateVisualLines(text: string, charsPerLine: number): number { if (!text) return 1; return text.split("\n").reduce((total, line) => { return total + Math.max(1, Math.ceil(line.length / charsPerLine)); }, 0); } private addButton(text: string, x: number, y: number, callback: () => void, color: cc.Color) { const button = this.createButton(text, text, 210, 64, callback, color); button.parent = this.panel; button.setPosition(x, y); } private createButton(name: string, text: string, width: number, height: number, callback: () => void, color?: cc.Color): cc.Node { const node = new cc.Node(name); node.setContentSize(width, height); node.addComponent(cc.Button); this.drawRect(node, color || cc.color(43, 123, 190), width, height); this.createLabel(node, text, 24, 0, 0, width - 8, height, cc.Color.WHITE); node.on(cc.Node.EventType.TOUCH_END, (event: cc.Event) => { event.stopPropagation(); callback(); }); return node; } private createLabel(parent: cc.Node, text: string, fontSize: number, x: number, y: number, width: number, height: number, color: cc.Color): cc.Label { const node = new cc.Node("label"); node.parent = parent; node.setContentSize(width, height); node.setPosition(x, y); node.color = color; const label = node.addComponent(cc.Label); label.string = text; label.fontSize = fontSize; label.lineHeight = fontSize + 5; label.horizontalAlign = cc.Label.HorizontalAlign.CENTER; label.verticalAlign = cc.Label.VerticalAlign.CENTER; return label; } private drawRect(node: cc.Node, color: cc.Color, width: number, height: number) { const graphics = node.addComponent(cc.Graphics); graphics.fillColor = color; graphics.rect(-width * 0.5, -height * 0.5, width, height); graphics.fill(); } }