feat: 完善分享模式
This commit is contained in:
@@ -40,7 +40,7 @@ Git 历史采用 Conventional Commits,且摘要多为中文,例如 `feat:
|
|||||||
<claude-mem-context>
|
<claude-mem-context>
|
||||||
# Memory Context
|
# Memory Context
|
||||||
|
|
||||||
# $CMEM mp-xieyingeng 2026-05-03 10:36pm GMT+8
|
# $CMEM mp-xieyingeng 2026-05-10 9:35pm GMT+8
|
||||||
|
|
||||||
Legend: 🎯session 🔴bugfix 🟣feature 🔄refactor ✅change 🔵discovery ⚖️decision
|
Legend: 🎯session 🔴bugfix 🟣feature 🔄refactor ✅change 🔵discovery ⚖️decision
|
||||||
Format: ID TIME TYPE TITLE
|
Format: ID TIME TYPE TITLE
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
|||||||
import { PassModal } from 'db://assets/prefabs/PassModal';
|
import { PassModal } from 'db://assets/prefabs/PassModal';
|
||||||
import { WrongModal } from 'db://assets/prefabs/WrongModal';
|
import { WrongModal } from 'db://assets/prefabs/WrongModal';
|
||||||
import { TimeoutModal } from 'db://assets/prefabs/TimeoutModal';
|
import { TimeoutModal } from 'db://assets/prefabs/TimeoutModal';
|
||||||
import { StaminaInfo, NextLevelData } from 'db://assets/scripts/types/ApiTypes';
|
import { StaminaInfo, NextLevelData, SubmitShareLevel } from 'db://assets/scripts/types/ApiTypes';
|
||||||
import { AchievementTitleManager } from 'db://assets/scripts/utils/AchievementTitleManager';
|
import { AchievementTitleManager } from 'db://assets/scripts/utils/AchievementTitleManager';
|
||||||
import { applyRoundedCorner } from 'db://assets/scripts/utils/roundedMaterial.utils';
|
import { applyRoundedCorner } from 'db://assets/scripts/utils/roundedMaterial.utils';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
@@ -286,6 +286,12 @@ export class PageLevel extends BaseView {
|
|||||||
/** 分享模式下的关卡索引(仅分享模式使用) */
|
/** 分享模式下的关卡索引(仅分享模式使用) */
|
||||||
private _shareLevelIndex: number = 0;
|
private _shareLevelIndex: number = 0;
|
||||||
|
|
||||||
|
/** 分享模式下每关最终提交内容,等整场结束后一次性提交 */
|
||||||
|
private _shareSubmissions: Map<string, SubmitShareLevel> = new Map();
|
||||||
|
|
||||||
|
/** 是否正在提交分享挑战结果 */
|
||||||
|
private _isSubmittingShareResult: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 页面首次加载时调用
|
* 页面首次加载时调用
|
||||||
*/
|
*/
|
||||||
@@ -300,6 +306,8 @@ export class PageLevel extends BaseView {
|
|||||||
|
|
||||||
if (this._isShareMode) {
|
if (this._isShareMode) {
|
||||||
this._shareLevelIndex = 0;
|
this._shareLevelIndex = 0;
|
||||||
|
this._shareSubmissions.clear();
|
||||||
|
this._isSubmittingShareResult = false;
|
||||||
console.log('[PageLevel] 进入分享挑战模式');
|
console.log('[PageLevel] 进入分享挑战模式');
|
||||||
} else {
|
} else {
|
||||||
// 从 AuthManager 获取首关数据(由 PageLoading → game-data 提供)
|
// 从 AuthManager 获取首关数据(由 PageLoading → game-data 提供)
|
||||||
@@ -703,6 +711,7 @@ export class PageLevel extends BaseView {
|
|||||||
|
|
||||||
private tryAutoSubmitAnswer(): void {
|
private tryAutoSubmitAnswer(): void {
|
||||||
if (!this._currentConfig || this._isTransitioning) return;
|
if (!this._currentConfig || this._isTransitioning) return;
|
||||||
|
if (this._isShareMode) return;
|
||||||
|
|
||||||
const values = this.getInputValues();
|
const values = this.getInputValues();
|
||||||
const isFilled = values.length === Array.from(this._currentConfig.answer ?? '').length && values.every(value => value.length === 1);
|
const isFilled = values.length === Array.from(this._currentConfig.answer ?? '').length && values.every(value => value.length === 1);
|
||||||
@@ -913,8 +922,15 @@ export class PageLevel extends BaseView {
|
|||||||
if (!this._isShareMode) {
|
if (!this._isShareMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this._isSubmittingShareResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._isTransitioning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.playClickSound();
|
this.playClickSound();
|
||||||
|
this._recordCurrentShareSubmission();
|
||||||
void this.goToNextLevel();
|
void this.goToNextLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1639,6 +1655,11 @@ export class PageLevel extends BaseView {
|
|||||||
const userAnswer = this.getAnswer();
|
const userAnswer = this.getAnswer();
|
||||||
console.log(`[PageLevel] 提交答案: ${userAnswer}, 正确答案: ${this._currentConfig.answer}`);
|
console.log(`[PageLevel] 提交答案: ${userAnswer}, 正确答案: ${this._currentConfig.answer}`);
|
||||||
|
|
||||||
|
if (this._isShareMode) {
|
||||||
|
void this._submitShareAnswerAndContinue(userAnswer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (userAnswer === this._currentConfig.answer) {
|
if (userAnswer === this._currentConfig.answer) {
|
||||||
// 答案正确,只播放成功音效(不播放点击音效,避免重合)
|
// 答案正确,只播放成功音效(不播放点击音效,避免重合)
|
||||||
this.showSuccess();
|
this.showSuccess();
|
||||||
@@ -1676,11 +1697,15 @@ export class PageLevel extends BaseView {
|
|||||||
|
|
||||||
this.reportLevelCompleted(levelId, timeSpent);
|
this.reportLevelCompleted(levelId, timeSpent);
|
||||||
|
|
||||||
// 不论是否有谐音梗,都停留固定时间再弹出通关弹窗,保证节奏一致
|
// 不论是否有谐音梗,都停留固定时间,保证玩家能看到答案反馈
|
||||||
await this.delay(PageLevel.PASS_MODAL_DELAY_MS);
|
await this.delay(PageLevel.PASS_MODAL_DELAY_MS);
|
||||||
|
|
||||||
|
if (this._isShareMode) {
|
||||||
if (this._isFinalShareLevel()) {
|
if (this._isFinalShareLevel()) {
|
||||||
this._showShareEndPage();
|
await this._showShareEndPage();
|
||||||
|
} else {
|
||||||
|
await this.goToNextLevel();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1688,6 +1713,24 @@ export class PageLevel extends BaseView {
|
|||||||
this._showPassModal();
|
this._showPassModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _submitShareAnswerAndContinue(userAnswer: string): Promise<void> {
|
||||||
|
if (!this._currentConfig || this._isTransitioning || this._isSubmittingShareResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isTransitioning = true;
|
||||||
|
this.stopCountdown();
|
||||||
|
this.hidePunchline();
|
||||||
|
this._recordCurrentShareSubmission(userAnswer);
|
||||||
|
|
||||||
|
if (this._isFinalShareLevel()) {
|
||||||
|
await this._showShareEndPage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.goToNextLevel();
|
||||||
|
}
|
||||||
|
|
||||||
private getValidPunchline(punchline: string | null): string | null {
|
private getValidPunchline(punchline: string | null): string | null {
|
||||||
if (!punchline?.trim()) {
|
if (!punchline?.trim()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1730,8 +1773,43 @@ export class PageLevel extends BaseView {
|
|||||||
|
|
||||||
this._passModalCompletedLevelCount = null;
|
this._passModalCompletedLevelCount = null;
|
||||||
this._passModalPreviousCompletedLevelCount = null;
|
this._passModalPreviousCompletedLevelCount = null;
|
||||||
// fire-and-forget: errors are logged inside reportLevelProgress
|
this._recordCurrentShareSubmission(undefined, timeSpent);
|
||||||
void ShareManager.instance.reportLevelProgress(levelId, true, timeSpent);
|
}
|
||||||
|
|
||||||
|
private _recordCurrentShareSubmission(answer?: string, timeSpent?: number): void {
|
||||||
|
if (!this._isShareMode || !this._currentConfig?.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elapsedSeconds = Math.max(0, Math.round((Date.now() - this._levelStartTime) / 1000));
|
||||||
|
const finalTimeSpent = Math.max(0, Math.round(timeSpent ?? elapsedSeconds));
|
||||||
|
const finalAnswer = answer ?? this.getAnswer();
|
||||||
|
|
||||||
|
this._shareSubmissions.set(this._currentConfig.id, {
|
||||||
|
levelId: this._currentConfig.id,
|
||||||
|
answer: finalAnswer,
|
||||||
|
timeSpent: finalTimeSpent,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[PageLevel] 记录分享挑战提交: ${this._currentConfig.id}, answer="${finalAnswer}", timeSpent=${finalTimeSpent}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _buildShareSubmissionPayload(): SubmitShareLevel[] {
|
||||||
|
const levelIds = ShareManager.instance.getShareLevelIds();
|
||||||
|
const ids = levelIds.length > 0
|
||||||
|
? levelIds
|
||||||
|
: [...this._shareSubmissions.keys()];
|
||||||
|
|
||||||
|
return ids.map(levelId => {
|
||||||
|
const submission = this._shareSubmissions.get(levelId);
|
||||||
|
return submission ?? {
|
||||||
|
levelId,
|
||||||
|
answer: '',
|
||||||
|
timeSpent: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private delay(ms: number): Promise<void> {
|
private delay(ms: number): Promise<void> {
|
||||||
@@ -2021,7 +2099,7 @@ export class PageLevel extends BaseView {
|
|||||||
if (this._shareLevelIndex >= totalLevels) {
|
if (this._shareLevelIndex >= totalLevels) {
|
||||||
console.log('[PageLevel] 分享关卡全部完成');
|
console.log('[PageLevel] 分享关卡全部完成');
|
||||||
this.stopCountdown();
|
this.stopCountdown();
|
||||||
this._showShareEndPage();
|
await this._showShareEndPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2056,9 +2134,41 @@ export class PageLevel extends BaseView {
|
|||||||
return totalLevels > 0 && this._shareLevelIndex >= totalLevels - 1;
|
return totalLevels > 0 && this._shareLevelIndex >= totalLevels - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showShareEndPage(): void {
|
private async _showShareEndPage(): Promise<void> {
|
||||||
|
if (this._isSubmittingShareResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('[PageLevel] 分享关卡全部完成,进入 PK 结算页');
|
console.log('[PageLevel] 分享关卡全部完成,进入 PK 结算页');
|
||||||
this.stopCountdown();
|
this.stopCountdown();
|
||||||
ViewManager.instance.replace('PagePKEnd');
|
|
||||||
|
if (this._currentConfig?.id && !this._shareSubmissions.has(this._currentConfig.id)) {
|
||||||
|
this._recordCurrentShareSubmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = this._buildShareSubmissionPayload();
|
||||||
|
if (payload.length === 0) {
|
||||||
|
ToastManager.show('挑战数据异常,请重新进入');
|
||||||
|
this._isTransitioning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isSubmittingShareResult = true;
|
||||||
|
ToastManager.show('正在结算挑战...');
|
||||||
|
|
||||||
|
const result = await ShareManager.instance.submitShareChallenge(payload);
|
||||||
|
this._isSubmittingShareResult = false;
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
ToastManager.show('提交挑战结果失败,请稍后重试');
|
||||||
|
this._isTransitioning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewManager.instance.replace('PagePKEnd', {
|
||||||
|
params: {
|
||||||
|
result,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,14 +40,14 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 154
|
"__id__": 160
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 156
|
"__id__": 162
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 158
|
"__id__": 164
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -2258,20 +2258,20 @@
|
|||||||
"__id__": 93
|
"__id__": 93
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 141
|
"__id__": 147
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 149
|
"__id__": 155
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 151
|
"__id__": 157
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 153
|
"__id__": 159
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -2318,17 +2318,17 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 134
|
"__id__": 140
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 136
|
"__id__": 142
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 138
|
"__id__": 144
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 140
|
"__id__": 146
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -2375,11 +2375,11 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 131
|
"__id__": 137
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 133
|
"__id__": 139
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -2424,16 +2424,19 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 108
|
"__id__": 108
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 128
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 128
|
"__id__": 134
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 130
|
"__id__": 136
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -3240,6 +3243,168 @@
|
|||||||
"targetOverrides": null,
|
"targetOverrides": null,
|
||||||
"nestedPrefabInstanceRoots": null
|
"nestedPrefabInstanceRoots": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Node",
|
||||||
|
"_name": "AnswerLabel",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"_parent": {
|
||||||
|
"__id__": 95
|
||||||
|
},
|
||||||
|
"_children": [],
|
||||||
|
"_active": true,
|
||||||
|
"_components": [
|
||||||
|
{
|
||||||
|
"__id__": 129
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__id__": 131
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"_prefab": {
|
||||||
|
"__id__": 133
|
||||||
|
},
|
||||||
|
"_lpos": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 163.106,
|
||||||
|
"y": -13.134,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_lrot": {
|
||||||
|
"__type__": "cc.Quat",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0,
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"_lscale": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0.712,
|
||||||
|
"y": 0.712,
|
||||||
|
"z": 0.712
|
||||||
|
},
|
||||||
|
"_mobility": 0,
|
||||||
|
"_layer": 1073741824,
|
||||||
|
"_euler": {
|
||||||
|
"__type__": "cc.Vec3",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.UITransform",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 128
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 130
|
||||||
|
},
|
||||||
|
"_contentSize": {
|
||||||
|
"__type__": "cc.Size",
|
||||||
|
"width": 170,
|
||||||
|
"height": 136
|
||||||
|
},
|
||||||
|
"_anchorPoint": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 0.5,
|
||||||
|
"y": 0.5
|
||||||
|
},
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "33SRp0SWVHl4kQ1pZ7BQhx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.Label",
|
||||||
|
"_name": "",
|
||||||
|
"_objFlags": 0,
|
||||||
|
"__editorExtras__": {},
|
||||||
|
"node": {
|
||||||
|
"__id__": 128
|
||||||
|
},
|
||||||
|
"_enabled": true,
|
||||||
|
"__prefab": {
|
||||||
|
"__id__": 132
|
||||||
|
},
|
||||||
|
"_customMaterial": null,
|
||||||
|
"_srcBlendFactor": 2,
|
||||||
|
"_dstBlendFactor": 4,
|
||||||
|
"_color": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 255,
|
||||||
|
"g": 255,
|
||||||
|
"b": 255,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_string": "答案",
|
||||||
|
"_horizontalAlign": 1,
|
||||||
|
"_verticalAlign": 1,
|
||||||
|
"_actualFontSize": 80,
|
||||||
|
"_fontSize": 80,
|
||||||
|
"_fontFamily": "Arial",
|
||||||
|
"_lineHeight": 100,
|
||||||
|
"_overflow": 0,
|
||||||
|
"_enableWrapText": true,
|
||||||
|
"_font": {
|
||||||
|
"__uuid__": "fb4acba6-6bc7-4eb3-be34-8f2ac9823a80",
|
||||||
|
"__expectedType__": "cc.TTFFont"
|
||||||
|
},
|
||||||
|
"_isSystemFontUsed": false,
|
||||||
|
"_spacingX": 0,
|
||||||
|
"_isItalic": false,
|
||||||
|
"_isBold": true,
|
||||||
|
"_isUnderline": false,
|
||||||
|
"_underlineHeight": 2,
|
||||||
|
"_cacheMode": 0,
|
||||||
|
"_enableOutline": true,
|
||||||
|
"_outlineColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 191,
|
||||||
|
"g": 127,
|
||||||
|
"b": 2,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_outlineWidth": 5,
|
||||||
|
"_enableShadow": false,
|
||||||
|
"_shadowColor": {
|
||||||
|
"__type__": "cc.Color",
|
||||||
|
"r": 0,
|
||||||
|
"g": 0,
|
||||||
|
"b": 0,
|
||||||
|
"a": 255
|
||||||
|
},
|
||||||
|
"_shadowOffset": {
|
||||||
|
"__type__": "cc.Vec2",
|
||||||
|
"x": 2,
|
||||||
|
"y": 2
|
||||||
|
},
|
||||||
|
"_shadowBlur": 2,
|
||||||
|
"_id": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.CompPrefabInfo",
|
||||||
|
"fileId": "940bZjtkVLIYX8EolxrbQ5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__type__": "cc.PrefabInfo",
|
||||||
|
"root": {
|
||||||
|
"__id__": 1
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"__id__": 0
|
||||||
|
},
|
||||||
|
"fileId": "3aNOwLJv5EJa3QIvuuIt/d",
|
||||||
|
"instance": null,
|
||||||
|
"targetOverrides": null,
|
||||||
|
"nestedPrefabInstanceRoots": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"__type__": "cc.UITransform",
|
"__type__": "cc.UITransform",
|
||||||
"_name": "",
|
"_name": "",
|
||||||
@@ -3250,7 +3415,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 129
|
"__id__": 135
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
@@ -3291,7 +3456,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 132
|
"__id__": 138
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
@@ -3332,7 +3497,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 135
|
"__id__": 141
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
@@ -3360,7 +3525,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 137
|
"__id__": 143
|
||||||
},
|
},
|
||||||
"_type": 0,
|
"_type": 0,
|
||||||
"_inverted": false,
|
"_inverted": false,
|
||||||
@@ -3382,7 +3547,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 139
|
"__id__": 145
|
||||||
},
|
},
|
||||||
"_customMaterial": null,
|
"_customMaterial": null,
|
||||||
"_srcBlendFactor": 2,
|
"_srcBlendFactor": 2,
|
||||||
@@ -3443,17 +3608,17 @@
|
|||||||
"_active": true,
|
"_active": true,
|
||||||
"_components": [
|
"_components": [
|
||||||
{
|
{
|
||||||
"__id__": 142
|
"__id__": 148
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 144
|
"__id__": 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"__id__": 146
|
"__id__": 152
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"_prefab": {
|
"_prefab": {
|
||||||
"__id__": 148
|
"__id__": 154
|
||||||
},
|
},
|
||||||
"_lpos": {
|
"_lpos": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
@@ -3490,11 +3655,11 @@
|
|||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"node": {
|
"node": {
|
||||||
"__id__": 141
|
"__id__": 147
|
||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 143
|
"__id__": 149
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
@@ -3518,11 +3683,11 @@
|
|||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"node": {
|
"node": {
|
||||||
"__id__": 141
|
"__id__": 147
|
||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 145
|
"__id__": 151
|
||||||
},
|
},
|
||||||
"_customMaterial": null,
|
"_customMaterial": null,
|
||||||
"_srcBlendFactor": 2,
|
"_srcBlendFactor": 2,
|
||||||
@@ -3563,11 +3728,11 @@
|
|||||||
"_objFlags": 0,
|
"_objFlags": 0,
|
||||||
"__editorExtras__": {},
|
"__editorExtras__": {},
|
||||||
"node": {
|
"node": {
|
||||||
"__id__": 141
|
"__id__": 147
|
||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 147
|
"__id__": 153
|
||||||
},
|
},
|
||||||
"_alignFlags": 40,
|
"_alignFlags": 40,
|
||||||
"_target": null,
|
"_target": null,
|
||||||
@@ -3616,7 +3781,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 150
|
"__id__": 156
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
@@ -3644,7 +3809,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 152
|
"__id__": 158
|
||||||
},
|
},
|
||||||
"bounceDuration": 0.23,
|
"bounceDuration": 0.23,
|
||||||
"brake": 0.75,
|
"brake": 0.75,
|
||||||
@@ -3688,7 +3853,7 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 155
|
"__id__": 161
|
||||||
},
|
},
|
||||||
"_contentSize": {
|
"_contentSize": {
|
||||||
"__type__": "cc.Size",
|
"__type__": "cc.Size",
|
||||||
@@ -3716,9 +3881,32 @@
|
|||||||
},
|
},
|
||||||
"_enabled": true,
|
"_enabled": true,
|
||||||
"__prefab": {
|
"__prefab": {
|
||||||
"__id__": 157
|
"__id__": 163
|
||||||
|
},
|
||||||
|
"settingButton": {
|
||||||
|
"__id__": 76
|
||||||
|
},
|
||||||
|
"rankLabel": {
|
||||||
|
"__id__": 47
|
||||||
|
},
|
||||||
|
"rightNumberLabel": {
|
||||||
|
"__id__": 53
|
||||||
|
},
|
||||||
|
"rankNumberLabel": {
|
||||||
|
"__id__": 59
|
||||||
|
},
|
||||||
|
"participateNumberLabel": {
|
||||||
|
"__id__": 65
|
||||||
|
},
|
||||||
|
"answerTitleLabel": {
|
||||||
|
"__id__": 89
|
||||||
|
},
|
||||||
|
"answerListContent": {
|
||||||
|
"__id__": 94
|
||||||
|
},
|
||||||
|
"answerItemTemplate": {
|
||||||
|
"__id__": 95
|
||||||
},
|
},
|
||||||
"settingButton": null,
|
|
||||||
"_id": ""
|
"_id": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 { BaseView } from 'db://assets/scripts/core/BaseView';
|
||||||
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
|
||||||
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||||
|
import { SubmitShareData, SubmittedShareLevelData } from 'db://assets/scripts/types/ApiTypes';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
@ccclass('PagePKEnd')
|
@ccclass('PagePKEnd')
|
||||||
export class PagePKEnd extends BaseView {
|
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: '返回首页按钮' })
|
@property({ type: Node, tooltip: '返回首页按钮' })
|
||||||
settingButton: Node | null = null;
|
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 {
|
onViewLoad(): void {
|
||||||
this._resolveNodes();
|
this._resolveNodes();
|
||||||
this._bindEvents();
|
this._bindEvents();
|
||||||
|
this._hideAnswerTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
onViewShow(): void {
|
onViewShow(): void {
|
||||||
console.log('[PagePKEnd] onViewShow');
|
console.log('[PagePKEnd] onViewShow');
|
||||||
|
this._resolveNodes();
|
||||||
|
this._renderResult(this.getParams()?.result ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
onViewDestroy(): void {
|
onViewDestroy(): void {
|
||||||
this._unbindEvents();
|
this._unbindEvents();
|
||||||
|
this._clearAnswerItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resolveNodes(): void {
|
private _resolveNodes(): void {
|
||||||
@@ -27,9 +63,28 @@ export class PagePKEnd extends BaseView {
|
|||||||
this.settingButton = this.node.getChildByName('SettingButton');
|
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) {
|
if (!this.settingButton) {
|
||||||
console.warn('[PagePKEnd] 未找到 SettingButton 节点');
|
console.warn('[PagePKEnd] 未找到 SettingButton 节点');
|
||||||
}
|
}
|
||||||
|
if (!this.answerListContent) {
|
||||||
|
console.warn('[PagePKEnd] 未找到 AnswerList/content 节点');
|
||||||
|
}
|
||||||
|
if (!this.answerItemTemplate) {
|
||||||
|
console.warn('[PagePKEnd] 未找到 AnswerItem 模板节点');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _bindEvents(): void {
|
private _bindEvents(): void {
|
||||||
@@ -46,10 +101,236 @@ export class PagePKEnd extends BaseView {
|
|||||||
if (this.settingButton && this.settingButton.isValid) {
|
if (this.settingButton && this.settingButton.isValid) {
|
||||||
this.settingButton.off(Button.EventType.CLICK, this._onHomeClick, this);
|
this.settingButton.off(Button.EventType.CLICK, this._onHomeClick, this);
|
||||||
}
|
}
|
||||||
|
this._unbindAnswerButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onHomeClick(): void {
|
private _onHomeClick(): void {
|
||||||
ShareManager.instance.clearShareMode();
|
ShareManager.instance.clearShareMode();
|
||||||
ViewManager.instance.replace('PageHome');
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export const API_ENDPOINTS = {
|
|||||||
/** 分享相关 */
|
/** 分享相关 */
|
||||||
SHARE_CREATE: `${API_BASE}/share`,
|
SHARE_CREATE: `${API_BASE}/share`,
|
||||||
SHARE_CREATED: `${API_BASE}/share/created`,
|
SHARE_CREATED: `${API_BASE}/share/created`,
|
||||||
SHARE_PROGRESS: `${API_BASE}/share/progress`,
|
|
||||||
/** 用户信息 */
|
/** 用户信息 */
|
||||||
USER_INFO: `${API_BASE}/user/info`,
|
USER_INFO: `${API_BASE}/user/info`,
|
||||||
/** 用户所有已通关的关卡(成就墙 / 关卡回看) */
|
/** 用户所有已通关的关卡(成就墙 / 关卡回看) */
|
||||||
@@ -38,6 +37,10 @@ export function getShareJoinUrl(code: string): string {
|
|||||||
return `${API_BASE}/share/${code}/join`;
|
return `${API_BASE}/share/${code}/join`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getShareSubmitUrl(code: string): string {
|
||||||
|
return `${API_BASE}/share/${code}/submit`;
|
||||||
|
}
|
||||||
|
|
||||||
export function getGameConfigUrl(key: string): string {
|
export function getGameConfigUrl(key: string): string {
|
||||||
return `${API_BASE}/game-configs/${key}`;
|
return `${API_BASE}/game-configs/${key}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,13 +134,34 @@ export interface JoinShareData {
|
|||||||
levels: ShareLevelData[];
|
levels: ShareLevelData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 上报关卡进度响应 */
|
/** 分享挑战单关提交 */
|
||||||
export interface ReportProgressData {
|
export interface SubmitShareLevel {
|
||||||
passed: boolean;
|
levelId: string;
|
||||||
|
answer: string;
|
||||||
|
timeSpent: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分享挑战提交后的单关结果 */
|
||||||
|
export interface SubmittedShareLevelData extends ShareLevelData {
|
||||||
|
submittedAnswer: string;
|
||||||
|
timeSpent: number;
|
||||||
|
isCorrect: boolean;
|
||||||
timeLimit: number | null;
|
timeLimit: number | null;
|
||||||
withinTimeLimit: boolean;
|
withinTimeLimit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 分享挑战整场提交响应 */
|
||||||
|
export interface SubmitShareData {
|
||||||
|
shareCode: string;
|
||||||
|
title: string;
|
||||||
|
rank: number;
|
||||||
|
correctCount: number;
|
||||||
|
levelCount: number;
|
||||||
|
participantCount: number;
|
||||||
|
totalTimeSpent: number;
|
||||||
|
levels: SubmittedShareLevelData[];
|
||||||
|
}
|
||||||
|
|
||||||
/** 我创建的分享挑战条目 */
|
/** 我创建的分享挑战条目 */
|
||||||
export interface CreatedShareItem {
|
export interface CreatedShareItem {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
|
import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
|
||||||
import { HttpUtil } from './HttpUtil';
|
import { HttpUtil } from './HttpUtil';
|
||||||
import { WxSDK } from './WxSDK';
|
import { WxSDK } from './WxSDK';
|
||||||
import { API_ENDPOINTS, getShareJoinUrl, API_TIMEOUT } from '../config/ApiConfig';
|
import { API_ENDPOINTS, getShareJoinUrl, getShareSubmitUrl, API_TIMEOUT } from '../config/ApiConfig';
|
||||||
import {
|
import {
|
||||||
ApiEnvelope,
|
ApiEnvelope,
|
||||||
CreateShareData,
|
CreateShareData,
|
||||||
JoinShareData,
|
JoinShareData,
|
||||||
ReportProgressData,
|
|
||||||
ShareLevelData,
|
ShareLevelData,
|
||||||
CreatedShareItem,
|
CreatedShareItem,
|
||||||
CreatedShareListData,
|
CreatedShareListData,
|
||||||
|
SubmitShareData,
|
||||||
|
SubmitShareLevel,
|
||||||
} from '../types/ApiTypes';
|
} from '../types/ApiTypes';
|
||||||
import { RuntimeLevelConfig } from '../types/LevelTypes';
|
import { RuntimeLevelConfig } from '../types/LevelTypes';
|
||||||
|
|
||||||
@@ -50,6 +51,14 @@ export class ShareManager {
|
|||||||
return [...this._createdShares];
|
return [...this._createdShares];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get shareCode(): string | null {
|
||||||
|
return this._shareCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shareTitle(): string {
|
||||||
|
return this._shareTitle;
|
||||||
|
}
|
||||||
|
|
||||||
async createShare(title: string, levelIds: string[]): Promise<string | null> {
|
async createShare(title: string, levelIds: string[]): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
const response = await HttpUtil.post<ApiEnvelope<CreateShareData>>(
|
const response = await HttpUtil.post<ApiEnvelope<CreateShareData>>(
|
||||||
@@ -101,6 +110,7 @@ export class ShareManager {
|
|||||||
clue3: level.hint3,
|
clue3: level.hint3,
|
||||||
answer: level.answer,
|
answer: level.answer,
|
||||||
completed: false,
|
completed: false,
|
||||||
|
timeLimit: null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 预加载首关图片(两张并行加载)
|
// 预加载首关图片(两张并行加载)
|
||||||
@@ -178,40 +188,34 @@ export class ShareManager {
|
|||||||
return this._shareLevels?.length ?? 0;
|
return this._shareLevels?.length ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getShareLevelIds(): string[] {
|
||||||
* 上报单关通关进度
|
return this._shareApiLevels.map(level => level.id);
|
||||||
* @param levelId 关卡 ID
|
}
|
||||||
* @param passed 是否通过
|
|
||||||
* @param timeSpent 用时(秒)
|
async submitShareChallenge(levels: SubmitShareLevel[]): Promise<SubmitShareData | null> {
|
||||||
*/
|
|
||||||
async reportLevelProgress(
|
|
||||||
levelId: string,
|
|
||||||
passed: boolean,
|
|
||||||
timeSpent: number,
|
|
||||||
): Promise<ReportProgressData | null> {
|
|
||||||
if (!this._shareCode) {
|
if (!this._shareCode) {
|
||||||
console.warn('[ShareManager] reportLevelProgress: 无分享码,跳过上报');
|
console.warn('[ShareManager] submitShareChallenge: 无分享码,跳过提交');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await HttpUtil.post<ApiEnvelope<ReportProgressData>>(
|
const response = await HttpUtil.post<ApiEnvelope<SubmitShareData>>(
|
||||||
API_ENDPOINTS.SHARE_PROGRESS,
|
getShareSubmitUrl(this._shareCode),
|
||||||
{ shareCode: this._shareCode, levelId, passed, timeSpent },
|
{ levels },
|
||||||
API_TIMEOUT.DEFAULT,
|
API_TIMEOUT.DEFAULT,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.success || !response.data) {
|
if (!response.success || !response.data) {
|
||||||
console.error('[ShareManager] 上报进度失败:', response.message);
|
console.error('[ShareManager] 提交挑战结果失败:', response.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[ShareManager] 上报成功: passed=${response.data.passed}, withinTimeLimit=${response.data.withinTimeLimit}`,
|
`[ShareManager] 提交挑战结果成功: rank=${response.data.rank}, correct=${response.data.correctCount}/${response.data.levelCount}`,
|
||||||
);
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[ShareManager] 上报进度异常:', err);
|
console.error('[ShareManager] 提交挑战结果异常:', err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user