feat: 支持关卡配置分享
This commit is contained in:
@@ -3,6 +3,8 @@ import { ViewManager } from './scripts/core/ViewManager';
|
|||||||
import { LevelDataManager } from './scripts/utils/LevelDataManager';
|
import { LevelDataManager } from './scripts/utils/LevelDataManager';
|
||||||
import { AuthManager } from './scripts/utils/AuthManager';
|
import { AuthManager } from './scripts/utils/AuthManager';
|
||||||
import { StorageManager } from './scripts/utils/StorageManager';
|
import { StorageManager } from './scripts/utils/StorageManager';
|
||||||
|
import { ShareManager } from './scripts/utils/ShareManager';
|
||||||
|
import { WxSDK } from './scripts/utils/WxSDK';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +57,27 @@ export class PageLoading extends Component {
|
|||||||
this._syncProgressFromServer();
|
this._syncProgressFromServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预加载 PageHome (80-100%)
|
// 检测分享码:从微信启动参数中获取
|
||||||
|
const shareCode = WxSDK.getShareCodeFromLaunch();
|
||||||
|
if (shareCode && loginSuccess) {
|
||||||
|
this._updateStatusLabel('正在加载挑战关卡...');
|
||||||
|
const joinSuccess = await ShareManager.instance.joinShare(shareCode);
|
||||||
|
if (joinSuccess) {
|
||||||
|
this._updateProgress(1);
|
||||||
|
this._updateStatusLabel('加载完成');
|
||||||
|
// 跳过首页,直接进入分享挑战关卡
|
||||||
|
ViewManager.instance.open('PageLevel', {
|
||||||
|
params: { shareMode: true },
|
||||||
|
onComplete: () => {
|
||||||
|
this.node.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.warn('[PageLoading] 加入分享失败,进入正常模式');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常流程:预加载 PageHome (80-100%)
|
||||||
ViewManager.instance.preload('PageHome',
|
ViewManager.instance.preload('PageHome',
|
||||||
(progress) => {
|
(progress) => {
|
||||||
this._updateProgress(0.8 + progress * 0.2);
|
this._updateProgress(0.8 + progress * 0.2);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
|||||||
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
||||||
import { RuntimeLevelConfig } from 'db://assets/scripts/types/LevelTypes';
|
import { RuntimeLevelConfig } from 'db://assets/scripts/types/LevelTypes';
|
||||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||||
|
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||||
import { PassModal } from 'db://assets/prefabs/PassModal';
|
import { PassModal } from 'db://assets/prefabs/PassModal';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
@@ -104,14 +105,26 @@ export class PageLevel extends BaseView {
|
|||||||
/** 通关弹窗实例 */
|
/** 通关弹窗实例 */
|
||||||
private _passModalNode: Node | null = null;
|
private _passModalNode: Node | null = null;
|
||||||
|
|
||||||
|
/** 是否处于分享挑战模式 */
|
||||||
|
private _isShareMode: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 页面首次加载时调用
|
* 页面首次加载时调用
|
||||||
*/
|
*/
|
||||||
onViewLoad(): void {
|
onViewLoad(): void {
|
||||||
console.log('[PageLevel] onViewLoad');
|
console.log('[PageLevel] onViewLoad');
|
||||||
// 从本地存储恢复关卡进度
|
|
||||||
this.currentLevelIndex = StorageManager.getCurrentLevelIndex();
|
const params = this.getParams();
|
||||||
console.log(`[PageLevel] 恢复关卡进度: 第 ${this.currentLevelIndex + 1} 关`);
|
this._isShareMode = params?.shareMode === true;
|
||||||
|
|
||||||
|
if (this._isShareMode) {
|
||||||
|
this.currentLevelIndex = 0;
|
||||||
|
console.log('[PageLevel] 进入分享挑战模式');
|
||||||
|
} else {
|
||||||
|
// 从本地存储恢复关卡进度
|
||||||
|
this.currentLevelIndex = StorageManager.getCurrentLevelIndex();
|
||||||
|
console.log(`[PageLevel] 恢复关卡进度: 第 ${this.currentLevelIndex + 1} 关`);
|
||||||
|
}
|
||||||
this.updatePointsLabel();
|
this.updatePointsLabel();
|
||||||
this.initIconSetting();
|
this.initIconSetting();
|
||||||
this.initUnlockButtons();
|
this.initUnlockButtons();
|
||||||
@@ -160,13 +173,18 @@ export class PageLevel extends BaseView {
|
|||||||
* 初始化关卡(从 API 数据加载,异步确保资源就绪)
|
* 初始化关卡(从 API 数据加载,异步确保资源就绪)
|
||||||
*/
|
*/
|
||||||
private async initLevel(): Promise<void> {
|
private async initLevel(): Promise<void> {
|
||||||
// 先尝试从缓存获取
|
let config: RuntimeLevelConfig | null = null;
|
||||||
let config = LevelDataManager.instance.getLevelConfig(this.currentLevelIndex);
|
|
||||||
|
|
||||||
if (!config) {
|
if (this._isShareMode) {
|
||||||
// 缓存中没有,异步加载
|
// 分享模式:从 ShareManager 获取关卡
|
||||||
console.log(`[PageLevel] 关卡 ${this.currentLevelIndex + 1} 资源未缓存,开始加载...`);
|
config = await ShareManager.instance.ensureShareLevelReady(this.currentLevelIndex);
|
||||||
config = await LevelDataManager.instance.ensureLevelReady(this.currentLevelIndex);
|
} else {
|
||||||
|
// 正常模式:先尝试缓存,再异步加载
|
||||||
|
config = LevelDataManager.instance.getLevelConfig(this.currentLevelIndex);
|
||||||
|
if (!config) {
|
||||||
|
console.log(`[PageLevel] 关卡 ${this.currentLevelIndex + 1} 资源未缓存,开始加载...`);
|
||||||
|
config = await LevelDataManager.instance.ensureLevelReady(this.currentLevelIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@@ -212,7 +230,14 @@ export class PageLevel extends BaseView {
|
|||||||
this.updateClockLabel();
|
this.updateClockLabel();
|
||||||
|
|
||||||
// 预加载下一关图片(静默加载,不阻塞)
|
// 预加载下一关图片(静默加载,不阻塞)
|
||||||
LevelDataManager.instance.preloadNextLevel(this.currentLevelIndex);
|
if (this._isShareMode) {
|
||||||
|
const nextIndex = this.currentLevelIndex + 1;
|
||||||
|
if (nextIndex < ShareManager.instance.getShareLevelCount()) {
|
||||||
|
ShareManager.instance.ensureShareLevelReady(nextIndex).catch(() => {});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LevelDataManager.instance.preloadNextLevel(this.currentLevelIndex);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}, 答案长度: ${config.answer.length}`);
|
console.log(`[PageLevel] 初始化关卡 ${this.currentLevelIndex + 1}, 答案长度: ${config.answer.length}`);
|
||||||
}
|
}
|
||||||
@@ -356,7 +381,14 @@ export class PageLevel extends BaseView {
|
|||||||
private onIconSettingClick(): void {
|
private onIconSettingClick(): void {
|
||||||
console.log('[PageLevel] IconSetting 点击,返回主页');
|
console.log('[PageLevel] IconSetting 点击,返回主页');
|
||||||
this.playClickSound();
|
this.playClickSound();
|
||||||
ViewManager.instance.back();
|
|
||||||
|
// 分享模式下栈中没有 PageHome,需要清除分享状态并直接打开首页
|
||||||
|
if (this._isShareMode) {
|
||||||
|
ShareManager.instance.clearShareMode();
|
||||||
|
ViewManager.instance.replace('PageHome');
|
||||||
|
} else {
|
||||||
|
ViewManager.instance.back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 线索相关方法 ==========
|
// ========== 线索相关方法 ==========
|
||||||
@@ -656,10 +688,12 @@ export class PageLevel extends BaseView {
|
|||||||
// 播放成功音效
|
// 播放成功音效
|
||||||
this.playSuccessSound();
|
this.playSuccessSound();
|
||||||
|
|
||||||
// 通关奖励:通过服务端增加积分
|
// 通关奖励:分享模式下不增加积分
|
||||||
const levelId = this._currentConfig?.id ?? '';
|
if (!this._isShareMode) {
|
||||||
await UserAssetsManager.instance.earnPoint(levelId);
|
const levelId = this._currentConfig?.id ?? '';
|
||||||
this.updatePointsLabel();
|
await UserAssetsManager.instance.earnPoint(levelId);
|
||||||
|
this.updatePointsLabel();
|
||||||
|
}
|
||||||
|
|
||||||
// 显示通关弹窗
|
// 显示通关弹窗
|
||||||
this._showPassModal();
|
this._showPassModal();
|
||||||
@@ -748,19 +782,29 @@ export class PageLevel extends BaseView {
|
|||||||
* 进入下一关
|
* 进入下一关
|
||||||
*/
|
*/
|
||||||
private async nextLevel(): Promise<void> {
|
private async nextLevel(): Promise<void> {
|
||||||
// 保存当前关卡进度
|
// 分享模式不保存本地进度
|
||||||
StorageManager.onLevelCompleted(this.currentLevelIndex);
|
if (!this._isShareMode) {
|
||||||
|
StorageManager.onLevelCompleted(this.currentLevelIndex);
|
||||||
|
}
|
||||||
|
|
||||||
this.currentLevelIndex++;
|
this.currentLevelIndex++;
|
||||||
|
|
||||||
// 检查是否还有关卡
|
// 检查是否还有关卡
|
||||||
const totalLevels = LevelDataManager.instance.getLevelCount();
|
const totalLevels = this._isShareMode
|
||||||
|
? ShareManager.instance.getShareLevelCount()
|
||||||
|
: LevelDataManager.instance.getLevelCount();
|
||||||
|
|
||||||
if (this.currentLevelIndex >= totalLevels) {
|
if (this.currentLevelIndex >= totalLevels) {
|
||||||
// 所有关卡完成
|
// 所有关卡完成
|
||||||
console.log('[PageLevel] 恭喜通关!');
|
console.log('[PageLevel] 恭喜通关!');
|
||||||
this.stopCountdown();
|
this.stopCountdown();
|
||||||
ViewManager.instance.back();
|
|
||||||
|
if (this._isShareMode) {
|
||||||
|
ShareManager.instance.clearShareMode();
|
||||||
|
ViewManager.instance.replace('PageHome');
|
||||||
|
} else {
|
||||||
|
ViewManager.instance.back();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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 { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager';
|
||||||
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
|
||||||
|
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
|
||||||
const { ccclass, property } = _decorator;
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +79,9 @@ export class PageWriteLevels extends BaseView {
|
|||||||
/** 缓存 view 节点的 UITransform,避免每次 _updateContentSize 重复查找 */
|
/** 缓存 view 节点的 UITransform,避免每次 _updateContentSize 重复查找 */
|
||||||
private _viewTransform: UITransform | null = null;
|
private _viewTransform: UITransform | null = null;
|
||||||
|
|
||||||
|
/** 防止重复提交 */
|
||||||
|
private _isSubmitting: boolean = false;
|
||||||
|
|
||||||
onViewLoad(): void {
|
onViewLoad(): void {
|
||||||
console.log('[PageWriteLevels] onViewLoad');
|
console.log('[PageWriteLevels] onViewLoad');
|
||||||
this._initButtons();
|
this._initButtons();
|
||||||
@@ -92,6 +96,9 @@ export class PageWriteLevels extends BaseView {
|
|||||||
if (this.previewBtn) {
|
if (this.previewBtn) {
|
||||||
this.previewBtn.on(Button.EventType.CLICK, this._onPreviewClick, this);
|
this.previewBtn.on(Button.EventType.CLICK, this._onPreviewClick, this);
|
||||||
}
|
}
|
||||||
|
if (this.completeBtn) {
|
||||||
|
this.completeBtn.on(Button.EventType.CLICK, this._onCompleteClick, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _initScrollView(): void {
|
private _initScrollView(): void {
|
||||||
@@ -156,7 +163,10 @@ export class PageWriteLevels extends BaseView {
|
|||||||
|
|
||||||
onViewShow(): void {
|
onViewShow(): void {
|
||||||
console.log('[PageWriteLevels] onViewShow');
|
console.log('[PageWriteLevels] onViewShow');
|
||||||
this._initLevelList();
|
// 仅首次初始化列表,从预览页返回时保留选中状态
|
||||||
|
if (this._itemNodes.length === 0) {
|
||||||
|
this._initLevelList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _initLevelList(): void {
|
private _initLevelList(): void {
|
||||||
@@ -276,6 +286,8 @@ export class PageWriteLevels extends BaseView {
|
|||||||
const toggle = isSelected.getComponent(Toggle);
|
const toggle = isSelected.getComponent(Toggle);
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
toggle.isChecked = this._selectedIndices.has(index);
|
toggle.isChecked = this._selectedIndices.has(index);
|
||||||
|
// 禁用 Toggle 交互,仅作为视觉指示器,选中逻辑由 item Button 统一处理
|
||||||
|
toggle.interactable = false;
|
||||||
}
|
}
|
||||||
const checkmark = isSelected.getChildByName('Checkmark');
|
const checkmark = isSelected.getChildByName('Checkmark');
|
||||||
if (checkmark) {
|
if (checkmark) {
|
||||||
@@ -428,14 +440,22 @@ export class PageWriteLevels extends BaseView {
|
|||||||
ViewManager.instance.back();
|
ViewManager.instance.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onPreviewClick(): void {
|
/**
|
||||||
|
* 校验是否已选满关卡,未满则 Toast 提示
|
||||||
|
* @returns true 表示校验通过
|
||||||
|
*/
|
||||||
|
private _validateSelection(): boolean {
|
||||||
if (this._selectedIndices.size < MAX_SELECTION) {
|
if (this._selectedIndices.size < MAX_SELECTION) {
|
||||||
const remaining = MAX_SELECTION - this._selectedIndices.size;
|
const remaining = MAX_SELECTION - this._selectedIndices.size;
|
||||||
ToastManager.instance.show(`还需选择${remaining}个关卡`);
|
ToastManager.instance.show(`还需选择${remaining}个关卡`);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onPreviewClick(): void {
|
||||||
|
if (!this._validateSelection()) return;
|
||||||
const shareTitle = this.shareTitleEditBox?.getComponent(EditBox)?.string?.trim() || '';
|
const shareTitle = this.shareTitleEditBox?.getComponent(EditBox)?.string?.trim() || '';
|
||||||
console.log('[PageWriteLevels] 预览按钮点击,标题:', shareTitle, '已选关卡:', Array.from(this._selectedIndices));
|
|
||||||
ViewManager.instance.open('PagePreviewLevels', {
|
ViewManager.instance.open('PagePreviewLevels', {
|
||||||
params: {
|
params: {
|
||||||
selectedIndices: Array.from(this._selectedIndices),
|
selectedIndices: Array.from(this._selectedIndices),
|
||||||
@@ -444,6 +464,57 @@ export class PageWriteLevels extends BaseView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _onCompleteClick(): Promise<void> {
|
||||||
|
if (!this._validateSelection()) return;
|
||||||
|
|
||||||
|
const shareTitle = this.shareTitleEditBox?.getComponent(EditBox)?.string?.trim() || '';
|
||||||
|
if (!shareTitle) {
|
||||||
|
ToastManager.instance.show('请输入分享标题');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isSubmitting) return;
|
||||||
|
this._isSubmitting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const levelIds = this._getSelectedLevelIds();
|
||||||
|
if (levelIds.length !== MAX_SELECTION) {
|
||||||
|
ToastManager.instance.show('获取关卡数据失败,请重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareCode = await ShareManager.instance.createShare(shareTitle, levelIds);
|
||||||
|
if (!shareCode) {
|
||||||
|
ToastManager.instance.show('创建分享失败,请重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[PageWriteLevels] 创建分享成功, code:', shareCode);
|
||||||
|
ShareManager.instance.triggerWxShare(shareTitle, shareCode);
|
||||||
|
ToastManager.instance.show('分享创建成功!');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[PageWriteLevels] 完成按钮异常:', err);
|
||||||
|
ToastManager.instance.show('操作失败,请重试');
|
||||||
|
} finally {
|
||||||
|
this._isSubmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将选中的关卡索引转换为关卡 ID 数组
|
||||||
|
*/
|
||||||
|
private _getSelectedLevelIds(): string[] {
|
||||||
|
const ids: string[] = [];
|
||||||
|
const sortedIndices = Array.from(this._selectedIndices).sort((a, b) => a - b);
|
||||||
|
for (const index of sortedIndices) {
|
||||||
|
const config = LevelDataManager.instance.getLevelConfig(index);
|
||||||
|
if (config) {
|
||||||
|
ids.push(config.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
onViewHide(): void {
|
onViewHide(): void {
|
||||||
console.log('[PageWriteLevels] onViewHide');
|
console.log('[PageWriteLevels] onViewHide');
|
||||||
}
|
}
|
||||||
@@ -456,6 +527,9 @@ export class PageWriteLevels extends BaseView {
|
|||||||
if (this.previewBtn) {
|
if (this.previewBtn) {
|
||||||
this.previewBtn.off(Button.EventType.CLICK, this._onPreviewClick, this);
|
this.previewBtn.off(Button.EventType.CLICK, this._onPreviewClick, this);
|
||||||
}
|
}
|
||||||
|
if (this.completeBtn) {
|
||||||
|
this.completeBtn.off(Button.EventType.CLICK, this._onCompleteClick, this);
|
||||||
|
}
|
||||||
if (this.scrollView) {
|
if (this.scrollView) {
|
||||||
this.scrollView.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
|
this.scrollView.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
|
||||||
this.scrollView.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
|
this.scrollView.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
|
||||||
|
|||||||
@@ -14,8 +14,14 @@ export const API_ENDPOINTS = {
|
|||||||
USER_ASSETS_EARN: `${API_BASE}/user/assets/earn`,
|
USER_ASSETS_EARN: `${API_BASE}/user/assets/earn`,
|
||||||
USER_GAME_DATA: `${API_BASE}/user/game-data`,
|
USER_GAME_DATA: `${API_BASE}/user/game-data`,
|
||||||
LEVELS: `${API_BASE}/wechat-game/levels`,
|
LEVELS: `${API_BASE}/wechat-game/levels`,
|
||||||
|
SHARE_CREATE: `${API_BASE}/share`,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
/** 构建加入分享的 URL */
|
||||||
|
export function getShareJoinUrl(code: string): string {
|
||||||
|
return `${API_BASE}/share/${code}/join`;
|
||||||
|
}
|
||||||
|
|
||||||
/** 积分操作原因 */
|
/** 积分操作原因 */
|
||||||
export const POINT_REASONS = {
|
export const POINT_REASONS = {
|
||||||
HINT_UNLOCK: 'hint_unlock',
|
HINT_UNLOCK: 'hint_unlock',
|
||||||
|
|||||||
@@ -29,3 +29,29 @@ export interface GameData {
|
|||||||
user: { id: string; points: number };
|
user: { id: string; points: number };
|
||||||
completedLevelIds: string[];
|
completedLevelIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 创建分享响应 */
|
||||||
|
export interface CreateShareData {
|
||||||
|
shareCode: string;
|
||||||
|
title: string;
|
||||||
|
levelCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分享关卡数据 */
|
||||||
|
export interface ShareLevelData {
|
||||||
|
id: string;
|
||||||
|
level: number;
|
||||||
|
imageUrl: string;
|
||||||
|
answer: string;
|
||||||
|
hint1: string;
|
||||||
|
hint2: string;
|
||||||
|
hint3: string;
|
||||||
|
sortOrder: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加入分享响应 */
|
||||||
|
export interface JoinShareData {
|
||||||
|
shareCode: string;
|
||||||
|
title: string;
|
||||||
|
levels: ShareLevelData[];
|
||||||
|
}
|
||||||
|
|||||||
167
assets/scripts/utils/ShareManager.ts
Normal file
167
assets/scripts/utils/ShareManager.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
|
||||||
|
import { HttpUtil } from './HttpUtil';
|
||||||
|
import { WxSDK } from './WxSDK';
|
||||||
|
import { API_ENDPOINTS, getShareJoinUrl, API_TIMEOUT } from '../config/ApiConfig';
|
||||||
|
import { ApiEnvelope, CreateShareData, JoinShareData, ShareLevelData } from '../types/ApiTypes';
|
||||||
|
import { RuntimeLevelConfig } from '../types/LevelTypes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享管理器
|
||||||
|
* 负责创建分享、加入分享、缓存分享关卡数据
|
||||||
|
*/
|
||||||
|
export class ShareManager {
|
||||||
|
private static _instance: ShareManager | null = null;
|
||||||
|
|
||||||
|
/** 分享模式的关卡数据(null 表示正常模式) */
|
||||||
|
private _shareLevels: RuntimeLevelConfig[] | null = null;
|
||||||
|
|
||||||
|
/** API 返回的原始关卡数据(保留 imageUrl 用于懒加载) */
|
||||||
|
private _shareApiLevels: ShareLevelData[] = [];
|
||||||
|
|
||||||
|
private _shareTitle: string = '';
|
||||||
|
private _shareCode: string | null = null;
|
||||||
|
|
||||||
|
/** 图片缓存:URL -> SpriteFrame */
|
||||||
|
private _imageCache: Map<string, SpriteFrame> = new Map();
|
||||||
|
|
||||||
|
static get instance(): ShareManager {
|
||||||
|
if (!this._instance) {
|
||||||
|
this._instance = new ShareManager();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
get isShareMode(): boolean {
|
||||||
|
return this._shareLevels !== null && this._shareLevels.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createShare(title: string, levelIds: string[]): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const response = await HttpUtil.post<ApiEnvelope<CreateShareData>>(
|
||||||
|
API_ENDPOINTS.SHARE_CREATE,
|
||||||
|
{ title, levelIds },
|
||||||
|
API_TIMEOUT.DEFAULT,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.success || !response.data) {
|
||||||
|
console.error('[ShareManager] 创建分享失败:', response.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.shareCode;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[ShareManager] 创建分享异常:', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async joinShare(code: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const response = await HttpUtil.post<ApiEnvelope<JoinShareData>>(
|
||||||
|
getShareJoinUrl(code),
|
||||||
|
{},
|
||||||
|
API_TIMEOUT.DEFAULT,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.success || !response.data) {
|
||||||
|
console.error('[ShareManager] 加入分享失败:', response.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { shareCode, title, levels } = response.data;
|
||||||
|
this._shareCode = shareCode;
|
||||||
|
this._shareTitle = title;
|
||||||
|
this._shareApiLevels = levels;
|
||||||
|
|
||||||
|
const runtimeLevels: RuntimeLevelConfig[] = levels.map((level) => ({
|
||||||
|
id: level.id,
|
||||||
|
name: `第${level.level}关`,
|
||||||
|
spriteFrame: null,
|
||||||
|
clue1: level.hint1,
|
||||||
|
clue2: level.hint2,
|
||||||
|
clue3: level.hint3,
|
||||||
|
answer: level.answer,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 预加载首关图片
|
||||||
|
if (levels.length > 0) {
|
||||||
|
const sf = await this._loadImage(levels[0].imageUrl);
|
||||||
|
if (sf) {
|
||||||
|
runtimeLevels[0].spriteFrame = sf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._shareLevels = runtimeLevels;
|
||||||
|
console.log(`[ShareManager] 加入分享成功: ${title}, ${levels.length} 关`);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[ShareManager] 加入分享异常:', err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async ensureShareLevelReady(index: number): Promise<RuntimeLevelConfig | null> {
|
||||||
|
if (!this._shareLevels || index < 0 || index >= this._shareLevels.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = this._shareLevels[index];
|
||||||
|
if (config.spriteFrame) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiLevel = this._shareApiLevels[index];
|
||||||
|
if (apiLevel?.imageUrl) {
|
||||||
|
const sf = await this._loadImage(apiLevel.imageUrl);
|
||||||
|
if (sf) {
|
||||||
|
config.spriteFrame = sf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShareLevelCount(): number {
|
||||||
|
return this._shareLevels?.length ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerWxShare(title: string, shareCode: string): void {
|
||||||
|
WxSDK.shareAppMessage({
|
||||||
|
title: title || '来挑战我出的谐音梗吧!',
|
||||||
|
query: `shareCode=${shareCode}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearShareMode(): void {
|
||||||
|
this._shareLevels = null;
|
||||||
|
this._shareApiLevels = [];
|
||||||
|
this._shareTitle = '';
|
||||||
|
this._shareCode = null;
|
||||||
|
this._imageCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadImage(url: string): Promise<SpriteFrame | null> {
|
||||||
|
const cached = this._imageCache.get(url);
|
||||||
|
if (cached) {
|
||||||
|
return Promise.resolve(cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
assetManager.loadRemote<ImageAsset>(url, (err, imageAsset) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('[ShareManager] 加载图片失败:', url, err);
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const texture = new Texture2D();
|
||||||
|
texture.image = imageAsset;
|
||||||
|
const spriteFrame = new SpriteFrame();
|
||||||
|
spriteFrame.texture = texture;
|
||||||
|
this._imageCache.set(url, spriteFrame);
|
||||||
|
resolve(spriteFrame);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
9
assets/scripts/utils/ShareManager.ts.meta
Normal file
9
assets/scripts/utils/ShareManager.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "6044a8d5-305c-4b68-8abd-6bde3da0505c",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -216,4 +216,24 @@ export class WxSDK {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从启动参数中获取分享码
|
||||||
|
* @returns 分享码,不存在则返回 null
|
||||||
|
*/
|
||||||
|
static getShareCodeFromLaunch(): string | null {
|
||||||
|
const wxApi = WxSDK.getWx();
|
||||||
|
if (!wxApi) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = wxApi.getLaunchOptionsSync();
|
||||||
|
if (options?.query?.shareCode) {
|
||||||
|
console.log('[WxSDK] 检测到分享码:', options.query.shareCode);
|
||||||
|
return options.query.shareCode;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('[WxSDK] 获取启动参数失败:', err);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user