299 lines
11 KiB
TypeScript
299 lines
11 KiB
TypeScript
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<void> {
|
|
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 viewButton = this._findChild(item, 'ViewButton');
|
|
if (viewButton) {
|
|
const handler = () => this._openShareDetail(share);
|
|
viewButton.on(Button.EventType.CLICK, handler, this);
|
|
this._createdButtonBindings.push({ node: viewButton, handler });
|
|
}
|
|
|
|
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 _openShareDetail(share: CreatedShareItem): void {
|
|
if (!share.shareCode) {
|
|
ToastManager.instance.show('挑战数据异常,请稍后重试');
|
|
return;
|
|
}
|
|
|
|
ViewManager.instance.open('PagePKDetail', {
|
|
params: {
|
|
share,
|
|
shareCode: share.shareCode,
|
|
},
|
|
onError: (err) => {
|
|
console.error('[PagePKData] 打开挑战详情失败:', err);
|
|
ToastManager.instance.show('打开挑战详情失败,请稍后重试');
|
|
},
|
|
});
|
|
}
|
|
|
|
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<ImageAsset>(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;
|
|
}
|
|
}
|