feat: 更新 PagePKData 组件,添加挑战参与者排行摘要支持

This commit is contained in:
richarjiang
2026-05-12 21:37:13 +08:00
parent b7dec68dd6
commit 1cbc25481b
4 changed files with 297 additions and 78 deletions

View File

@@ -1612,7 +1612,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -264.639,
"x": -336.301,
"y": 152.257,
"z": 0
},
@@ -1653,7 +1653,7 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 560,
"width": 400,
"height": 88.2
},
"_anchorPoint": {
@@ -1689,15 +1689,15 @@
"b": 29,
"a": 255
},
"_string": "地名谐音梗大挑战",
"_horizontalAlign": 1,
"_string": "地名谐音梗",
"_horizontalAlign": 0,
"_verticalAlign": 1,
"_actualFontSize": 70,
"_fontSize": 70,
"_fontFamily": "Arial",
"_lineHeight": 70,
"_overflow": 0,
"_enableWrapText": true,
"_overflow": 1,
"_enableWrapText": false,
"_font": {
"__uuid__": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80",
"__expectedType__": "cc.TTFFont"
@@ -1988,7 +1988,7 @@
"a": 255
},
"_string": "共66人参与",
"_horizontalAlign": 1,
"_horizontalAlign": 0,
"_verticalAlign": 1,
"_actualFontSize": 50,
"_fontSize": 50,
@@ -2848,7 +2848,7 @@
"a": 255
},
"_string": "第 1 名",
"_horizontalAlign": 1,
"_horizontalAlign": 0,
"_verticalAlign": 1,
"_actualFontSize": 60,
"_fontSize": 60,
@@ -6764,6 +6764,12 @@
"backBtn": {
"__id__": 266
},
"createdListContent": {
"__id__": 54
},
"createdListItemTemplate": {
"__id__": 63
},
"_id": ""
},
{

View File

@@ -1,26 +1,42 @@
import { _decorator, Node, Button } from 'cc';
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 } from 'db://assets/scripts/types/ApiTypes';
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();
}
@@ -46,6 +62,7 @@ export class PagePKData extends BaseView {
this._createdShares = items;
console.log('[PagePKData] 我创建的挑战列表:', this._createdShares);
this._renderCreatedShares();
} finally {
this._isLoading = false;
}
@@ -55,5 +72,202 @@ export class PagePKData extends BaseView {
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<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;
}
}

View File

@@ -162,6 +162,17 @@ export interface SubmitShareData {
levels: SubmittedShareLevelData[];
}
/** 分享挑战参与者排行摘要 */
export interface ShareParticipantRankSummary {
userId?: string | null;
nickname?: string | null;
nickName?: string | null;
avatarUrl?: string | null;
rank?: number | null;
correctCount?: number | null;
totalTimeSpent?: number | null;
}
/** 我创建的分享挑战条目 */
export interface CreatedShareItem {
id: string;
@@ -171,6 +182,10 @@ export interface CreatedShareItem {
participantCount: number;
userRank: number | null;
createdAt: string;
firstPlaceUser?: ShareParticipantRankSummary | null;
topParticipant?: ShareParticipantRankSummary | null;
firstParticipant?: ShareParticipantRankSummary | null;
champion?: ShareParticipantRankSummary | null;
}
/** 我创建的分享挑战列表响应 */