import { _decorator, assetManager, Button, ImageAsset, instantiate, Label, Node, ScrollView, Sprite, SpriteFrame, Texture2D, UITransform } from 'cc'; import { BaseView } from 'db://assets/scripts/core/BaseView'; import { ViewManager } from 'db://assets/scripts/core/ViewManager'; import { ShareManager } from 'db://assets/scripts/utils/ShareManager'; import { SubmitShareData, SubmittedShareLevelData } from 'db://assets/scripts/types/ApiTypes'; const { ccclass, property } = _decorator; @ccclass('PagePKEnd') export class PagePKEnd extends BaseView { private static readonly ANSWER_ITEM_TOP_PADDING = 16; private static readonly ANSWER_ITEM_BOTTOM_PADDING = 16; private static readonly ANSWER_ITEM_SPACING = 16; private static readonly COVER_IMAGE_WIDTH = 1299; private static readonly COVER_IMAGE_HEIGHT = 1004; @property({ type: Node, tooltip: '返回首页按钮' }) settingButton: Node | null = null; @property({ type: Label, tooltip: '顶部排名文案,例如:获得了第1名' }) rankLabel: Label | null = null; @property({ type: Label, tooltip: '答对题数文案,例如:答对了4题' }) rightNumberLabel: Label | null = null; @property({ type: Label, tooltip: '本人排名文案,例如:您获得了第1名' }) rankNumberLabel: Label | null = null; @property({ type: Label, tooltip: '参与人数文案,例如:一共66人参与了挑战' }) participateNumberLabel: Label | null = null; @property({ type: Label, tooltip: '答案列表标题' }) answerTitleLabel: Label | null = null; @property({ type: Node, tooltip: '答案列表 content 节点' }) answerListContent: Node | null = null; @property({ type: Node, tooltip: '答案列表条目模板 AnswerItem' }) answerItemTemplate: Node | null = null; private _answerItemNodes: Node[] = []; private _answerButtonBindings: Array<{ node: Node; handler: () => void }> = []; private _renderVersion: number = 0; onViewLoad(): void { this._resolveNodes(); this._bindEvents(); this._hideAnswerTemplate(); } onViewShow(): void { console.log('[PagePKEnd] onViewShow'); this._resolveNodes(); this._renderResult(this.getParams()?.result ?? null); } onViewDestroy(): void { this._unbindEvents(); this._clearAnswerItems(); } private _resolveNodes(): void { if (!this.settingButton || !this.settingButton.isValid) { this.settingButton = this.node.getChildByName('SettingButton'); } this.rankLabel = this.rankLabel ?? this._findLabel('RankLabel'); this.rightNumberLabel = this.rightNumberLabel ?? this._findLabel('RightNumberLabel'); this.rankNumberLabel = this.rankNumberLabel ?? this._findLabel('RankNumberLabel'); this.participateNumberLabel = this.participateNumberLabel ?? this._findLabel('PartipateNumberLabel'); this.answerTitleLabel = this.answerTitleLabel ?? this._findLabel('AnswerTitle'); const answerList = this.node.getChildByName('AnswerList'); const view = answerList?.getChildByName('view'); this.answerListContent = this.answerListContent ?? view?.getChildByName('content') ?? null; this.answerItemTemplate = this.answerItemTemplate ?? this.answerListContent?.getChildByName('AnswerItem') ?? null; if (!this.settingButton) { console.warn('[PagePKEnd] 未找到 SettingButton 节点'); } if (!this.answerListContent) { console.warn('[PagePKEnd] 未找到 AnswerList/content 节点'); } if (!this.answerItemTemplate) { console.warn('[PagePKEnd] 未找到 AnswerItem 模板节点'); } } private _bindEvents(): void { const button = this.settingButton?.getComponent(Button); if (!button) { console.warn('[PagePKEnd] SettingButton 缺少 Button 组件'); return; } this.settingButton?.on(Button.EventType.CLICK, this._onHomeClick, this); } private _unbindEvents(): void { if (this.settingButton && this.settingButton.isValid) { this.settingButton.off(Button.EventType.CLICK, this._onHomeClick, this); } this._unbindAnswerButtons(); } private _onHomeClick(): void { ShareManager.instance.clearShareMode(); ViewManager.instance.replace('PageHome'); } private _renderResult(result: SubmitShareData | null): void { this._renderVersion++; this._clearAnswerItems(); if (!result) { this._setLabel(this.rankLabel, '暂无排名'); this._setLabel(this.rightNumberLabel, '答对了0题'); this._setLabel(this.rankNumberLabel, '您暂未上榜'); this._setLabel(this.participateNumberLabel, '暂无参与数据'); this._setLabel(this.answerTitleLabel, '暂无挑战结果'); this._hideAnswerTemplate(); return; } this._setLabel(this.rankLabel, `获得了第${result.rank}名`); this._setLabel(this.rightNumberLabel, `答对了${result.correctCount}题`); this._setLabel(this.rankNumberLabel, `您获得了第${result.rank}名`); this._setLabel(this.participateNumberLabel, `一共${result.participantCount}人参与了挑战`); this._setLabel(this.answerTitleLabel, `共用时${result.totalTimeSpent}s,揭晓答案吧`); this._renderAnswerList(result.levels ?? []); } private _renderAnswerList(levels: SubmittedShareLevelData[]): void { if (!this.answerListContent || !this.answerItemTemplate) { return; } this._hideAnswerTemplate(); const version = this._renderVersion; this._layoutAnswerContent(levels.length); levels.forEach((level, index) => { const item = instantiate(this.answerItemTemplate!); item.name = `AnswerItem_${index + 1}`; item.active = true; this.answerListContent!.addChild(item); this._answerItemNodes.push(item); this._positionAnswerItem(item, index); this._applyAnswerState(item, level); const coverSprite = this._findChild(item, 'CoverImage')?.getComponent(Sprite) ?? null; this._prepareCoverSprite(coverSprite); this._loadCoverImage(level.image1Url, coverSprite, version); }); this._scrollAnswerListToTop(); } private _layoutAnswerContent(itemCount: number): void { if (!this.answerListContent || !this.answerItemTemplate) { return; } const contentTransform = this.answerListContent.getComponent(UITransform); const viewTransform = this.answerListContent.parent?.getComponent(UITransform) ?? null; const itemTransform = this.answerItemTemplate.getComponent(UITransform); if (!contentTransform || !viewTransform || !itemTransform) { return; } const itemHeight = itemTransform.height; const contentHeight = Math.max( viewTransform.height, PagePKEnd.ANSWER_ITEM_TOP_PADDING + PagePKEnd.ANSWER_ITEM_BOTTOM_PADDING + itemCount * itemHeight + Math.max(0, itemCount - 1) * PagePKEnd.ANSWER_ITEM_SPACING, ); contentTransform.setContentSize(contentTransform.width, contentHeight); this.answerListContent.setPosition( this.answerListContent.position.x, viewTransform.height / 2, this.answerListContent.position.z, ); } private _positionAnswerItem(item: Node, index: number): void { const itemTransform = item.getComponent(UITransform); if (!itemTransform) { return; } const y = -PagePKEnd.ANSWER_ITEM_TOP_PADDING - itemTransform.height / 2 - index * (itemTransform.height + PagePKEnd.ANSWER_ITEM_SPACING); item.setPosition(0, y, item.position.z); } private _applyAnswerState(item: Node, level: SubmittedShareLevelData): void { const answerButton = this._findChild(item, 'ButtonViewAnswer'); const buttonLabel = answerButton?.getChildByName('Label')?.getComponent(Label) ?? null; const answerLabelNode = this._findChild(item, 'AnswerLabel'); const answerLabel = answerLabelNode?.getComponent(Label) ?? null; this._setLabel(buttonLabel, '查看答案'); this._setLabel(answerLabel, level.answer || '-'); if (level.isCorrect) { if (answerButton) { answerButton.active = false; } if (answerLabelNode) { answerLabelNode.active = true; } return; } if (answerButton) { answerButton.active = true; } if (answerLabelNode) { answerLabelNode.active = false; } const handler = () => { if (answerButton?.isValid) { answerButton.active = false; } if (answerLabelNode?.isValid) { answerLabelNode.active = true; } }; if (answerButton) { answerButton.on(Button.EventType.CLICK, handler, this); this._answerButtonBindings.push({ node: answerButton, handler }); } } private _scrollAnswerListToTop(): void { const scrollView = this.node.getChildByName('AnswerList')?.getComponent(ScrollView); scrollView?.scrollToTop(0); } private _loadCoverImage(url: string, sprite: Sprite | null, version: number): void { if (!url || !sprite) { return; } this._prepareCoverSprite(sprite); assetManager.loadRemote(url, (err, imageAsset) => { if (err || !imageAsset || version !== this._renderVersion || !sprite.node.isValid) { if (err) { console.error('[PagePKEnd] 加载答案封面失败:', url, err); } return; } const texture = new Texture2D(); texture.image = imageAsset; const spriteFrame = new SpriteFrame(); spriteFrame.texture = texture; this._prepareCoverSprite(sprite); sprite.spriteFrame = spriteFrame; this._prepareCoverSprite(sprite); }); } private _prepareCoverSprite(sprite: Sprite | null): void { if (!sprite?.node.isValid) { return; } sprite.sizeMode = Sprite.SizeMode.CUSTOM; const transform = sprite.node.getComponent(UITransform); if (transform) { transform.setContentSize(PagePKEnd.COVER_IMAGE_WIDTH, PagePKEnd.COVER_IMAGE_HEIGHT); } sprite.node.setScale(0.242, 0.242, 0.242); } private _clearAnswerItems(): void { this._unbindAnswerButtons(); for (const item of this._answerItemNodes) { if (item.isValid) { item.removeFromParent(); item.destroy(); } } this._answerItemNodes = []; this._hideAnswerTemplate(); } private _unbindAnswerButtons(): void { for (const binding of this._answerButtonBindings) { if (binding.node.isValid) { binding.node.off(Button.EventType.CLICK, binding.handler, this); } } this._answerButtonBindings = []; } private _hideAnswerTemplate(): void { if (this.answerItemTemplate?.isValid) { this.answerItemTemplate.active = false; } } private _setLabel(label: Label | null, text: string): void { if (label) { label.string = text; } } private _findLabel(nodeName: string): Label | null { return this._findChild(this.node, nodeName)?.getComponent(Label) ?? null; } private _findChild(root: Node, nodeName: string): Node | null { if (root.name === nodeName) { return root; } for (const child of root.children) { const found = this._findChild(child, nodeName); if (found) { return found; } } return null; } }