import { _decorator, Node, Button, instantiate, Label, ScrollView, UITransform, Sprite, SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc'; import { BaseView } from 'db://assets/scripts/core/BaseView'; import { ViewManager } from 'db://assets/scripts/core/ViewManager'; import { CreatedShareItem, ShareParticipantRankSummary } from 'db://assets/scripts/types/ApiTypes'; import { ShareManager } from 'db://assets/scripts/utils/ShareManager'; import { ToastManager } from 'db://assets/scripts/utils/ToastManager'; const { ccclass, property } = _decorator; @ccclass('PagePKData') export class PagePKData extends BaseView { private static readonly CREATED_ITEM_TOP_PADDING = 20; private static readonly CREATED_ITEM_BOTTOM_PADDING = 20; private static readonly CREATED_ITEM_SPACING = 20; @property({ type: Node, tooltip: '返回按钮' }) backBtn: Node | null = null; @property({ type: Node, tooltip: '我创建的挑战列表 content 节点' }) createdListContent: Node | null = null; @property({ type: Node, tooltip: '我创建的挑战列表条目模板 FriendsPKRankListItem' }) createdListItemTemplate: Node | null = null; private _createdShares: CreatedShareItem[] = []; private _createdItemNodes: Node[] = []; private _createdButtonBindings: Array<{ node: Node; handler: () => void }> = []; private _isLoading: boolean = false; private _renderVersion: number = 0; onViewLoad(): void { this._resolveNodes(); if (this.backBtn) { this.backBtn.on(Button.EventType.CLICK, this._onBackClick, this); } this._hideCreatedItemTemplate(); } onViewShow(): void { this._resolveNodes(); void this._loadCreatedShares(); } onViewHide(): void { } private _onBackClick(): void { ViewManager.instance.back(); } private async _loadCreatedShares(): Promise { if (this._isLoading) { return; } this._isLoading = true; try { const items = await ShareManager.instance.fetchCreatedShares(); if (!items) { ToastManager.instance.show('获取挑战列表失败,请稍后重试'); return; } this._createdShares = items; console.log('[PagePKData] 我创建的挑战列表:', this._createdShares); this._renderCreatedShares(); } finally { this._isLoading = false; } } onViewDestroy(): void { if (this.backBtn) { this.backBtn.off(Button.EventType.CLICK, this._onBackClick, this); } this._clearCreatedItems(); } private _resolveNodes(): void { if (!this.backBtn || !this.backBtn.isValid) { this.backBtn = this.node.getChildByName('BtnBack'); } const createdList = this.node.getChildByName('FriendsPKRankList'); const view = createdList?.getChildByName('view'); this.createdListContent = this.createdListContent ?? view?.getChildByName('content') ?? null; this.createdListItemTemplate = this.createdListItemTemplate ?? this.createdListContent?.getChildByName('FriendsPKRankListItem') ?? null; if (!this.createdListContent) { console.warn('[PagePKData] 未找到 FriendsPKRankList/content 节点'); } if (!this.createdListItemTemplate) { console.warn('[PagePKData] 未找到 FriendsPKRankListItem 模板节点'); } } private _renderCreatedShares(): void { this._renderVersion++; this._clearCreatedItems(); if (!this.createdListContent || !this.createdListItemTemplate) { return; } this._layoutCreatedList(this._createdShares.length); this._hideCreatedItemTemplate(); this._createdShares.forEach((share, index) => { const item = instantiate(this.createdListItemTemplate!); item.name = `FriendsPKRankListItem_${index + 1}`; item.active = true; this.createdListContent!.addChild(item); this._createdItemNodes.push(item); this._positionCreatedItem(item, index); this._applyCreatedShare(item, share, this._renderVersion); }); this._scrollCreatedListToTop(); } private _applyCreatedShare(item: Node, share: CreatedShareItem, version: number): void { this._setLabel(this._findLabelIn(item, 'Name'), share.title || '未命名挑战'); this._setLabel(this._findLabelIn(item, 'ParticipateLabel'), `共${share.participantCount ?? 0}人参与`); const firstParticipant = this._getFirstParticipant(share); const firstName = this._getParticipantName(firstParticipant); const rankNumber = firstParticipant?.rank ?? 1; this._setLabel(this._findLabelIn(item, 'NameLabel'), firstName || (share.participantCount > 0 ? '微信用户' : '暂无参与')); this._setLabel(this._findLabelIn(item, 'RankNumberLabel'), firstParticipant ? `第 ${rankNumber} 名` : '暂无排名'); this._loadAvatar(firstParticipant?.avatarUrl ?? '', this._findAvatarSprite(item), version); const shareButton = this._findChild(item, 'ShareButton'); if (shareButton) { const handler = () => ShareManager.instance.triggerWxShare(share.title, share.shareCode); shareButton.on(Button.EventType.CLICK, handler, this); this._createdButtonBindings.push({ node: shareButton, handler }); } } private _getFirstParticipant(share: CreatedShareItem): ShareParticipantRankSummary | null { return share.firstPlaceUser ?? share.topParticipant ?? share.firstParticipant ?? share.champion ?? null; } private _getParticipantName(participant: ShareParticipantRankSummary | null): string { return participant?.nickname || participant?.nickName || ''; } private _layoutCreatedList(itemCount: number): void { if (!this.createdListContent || !this.createdListItemTemplate) { return; } const contentTransform = this.createdListContent.getComponent(UITransform); const viewTransform = this.createdListContent.parent?.getComponent(UITransform) ?? null; const itemTransform = this.createdListItemTemplate.getComponent(UITransform); if (!contentTransform || !viewTransform || !itemTransform) { return; } const contentHeight = Math.max( viewTransform.height, PagePKData.CREATED_ITEM_TOP_PADDING + PagePKData.CREATED_ITEM_BOTTOM_PADDING + itemCount * itemTransform.height + Math.max(0, itemCount - 1) * PagePKData.CREATED_ITEM_SPACING, ); contentTransform.setContentSize(contentTransform.width, contentHeight); this.createdListContent.setPosition( this.createdListContent.position.x, viewTransform.height / 2, this.createdListContent.position.z, ); } private _positionCreatedItem(item: Node, index: number): void { const itemTransform = item.getComponent(UITransform); if (!itemTransform) { return; } const y = -PagePKData.CREATED_ITEM_TOP_PADDING - itemTransform.height / 2 - index * (itemTransform.height + PagePKData.CREATED_ITEM_SPACING); item.setPosition(0, y, item.position.z); } private _scrollCreatedListToTop(): void { const scrollView = this.node.getChildByName('FriendsPKRankList')?.getComponent(ScrollView); scrollView?.scrollToTop(0); } private _findAvatarSprite(item: Node): Sprite | null { const headNode = this._findChild(item, 'Head'); return headNode?.children[0]?.getComponent(Sprite) ?? headNode?.getComponent(Sprite) ?? null; } private _loadAvatar(url: string, sprite: Sprite | null, version: number): void { if (!url || !sprite) { return; } assetManager.loadRemote(url, (err, imageAsset) => { if (err || !imageAsset || version !== this._renderVersion || !sprite.node.isValid) { if (err) { console.error('[PagePKData] 加载第一名头像失败:', url, err); } return; } const texture = new Texture2D(); texture.image = imageAsset; const spriteFrame = new SpriteFrame(); spriteFrame.texture = texture; sprite.spriteFrame = spriteFrame; }); } private _clearCreatedItems(): void { this._unbindCreatedButtons(); for (const item of this._createdItemNodes) { if (item.isValid) { item.removeFromParent(); item.destroy(); } } this._createdItemNodes = []; this._hideCreatedItemTemplate(); } private _unbindCreatedButtons(): void { for (const binding of this._createdButtonBindings) { if (binding.node.isValid) { binding.node.off(Button.EventType.CLICK, binding.handler, this); } } this._createdButtonBindings = []; } private _hideCreatedItemTemplate(): void { if (this.createdListItemTemplate?.isValid) { this.createdListItemTemplate.active = false; } } private _setLabel(label: Label | null, text: string): void { if (label) { label.string = text; } } private _findLabelIn(root: Node, nodeName: string): Label | null { return this._findChild(root, 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; } }