feat: 完善分享模式

This commit is contained in:
richarjiang
2026-05-10 21:38:10 +08:00
parent b68e32ddce
commit c53eac6b24
7 changed files with 677 additions and 70 deletions

View File

@@ -1,25 +1,61 @@
import { _decorator, Button, Node } from 'cc';
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 {
@@ -27,9 +63,28 @@ export class PagePKEnd extends BaseView {
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 {
@@ -46,10 +101,236 @@ export class PagePKEnd extends BaseView {
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<ImageAsset>(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;
}
}