feat: 完善关卡创作页面
This commit is contained in:
138
assets/scripts/utils/CompletedLevelsManager.ts
Normal file
138
assets/scripts/utils/CompletedLevelsManager.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
|
||||
import { HttpUtil } from './HttpUtil';
|
||||
import { API_ENDPOINTS, API_TIMEOUT } from '../config/ApiConfig';
|
||||
import { ApiEnvelope, CompletedLevel } from '../types/ApiTypes';
|
||||
|
||||
/**
|
||||
* 已通关关卡管理器
|
||||
* 单例模式,负责拉取当前用户所有已通关关卡 + 封面图缓存
|
||||
* 适用于「成就墙」「关卡回看」「出题 / 预览」等场景
|
||||
*/
|
||||
export class CompletedLevelsManager {
|
||||
private static _instance: CompletedLevelsManager | null = null;
|
||||
|
||||
/** 关卡数据按服务端返回顺序缓存 */
|
||||
private _levels: CompletedLevel[] = [];
|
||||
|
||||
/** 是否已经成功拉取过一次 */
|
||||
private _loaded: boolean = false;
|
||||
|
||||
/** 图片缓存:URL -> SpriteFrame */
|
||||
private _imageCache: Map<string, SpriteFrame> = new Map();
|
||||
|
||||
/** 正在进行中的请求,用于去重并发调用 */
|
||||
private _inflight: Promise<CompletedLevel[] | null> | null = null;
|
||||
|
||||
static get instance(): CompletedLevelsManager {
|
||||
if (!this._instance) {
|
||||
this._instance = new CompletedLevelsManager();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* 获取已缓存的关卡列表(需先 fetch 或 ensureLoaded)
|
||||
*/
|
||||
get levels(): CompletedLevel[] {
|
||||
return this._levels;
|
||||
}
|
||||
|
||||
get count(): number {
|
||||
return this._levels.length;
|
||||
}
|
||||
|
||||
isLoaded(): boolean {
|
||||
return this._loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按索引(0-based)获取关卡,越界返回 null
|
||||
*/
|
||||
getByIndex(index: number): CompletedLevel | null {
|
||||
if (index < 0 || index >= this._levels.length) return null;
|
||||
return this._levels[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取并缓存已通关关卡列表
|
||||
* - forceRefresh=true 强制重新请求
|
||||
* - 并发调用会共享同一次请求
|
||||
*/
|
||||
async fetch(forceRefresh: boolean = false): Promise<CompletedLevel[] | null> {
|
||||
if (!forceRefresh && this._loaded) {
|
||||
return this._levels;
|
||||
}
|
||||
|
||||
if (this._inflight) {
|
||||
return this._inflight;
|
||||
}
|
||||
|
||||
this._inflight = this._doFetch();
|
||||
try {
|
||||
return await this._inflight;
|
||||
} finally {
|
||||
this._inflight = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async _doFetch(): Promise<CompletedLevel[] | null> {
|
||||
try {
|
||||
const response = await HttpUtil.get<ApiEnvelope<CompletedLevel[]>>(
|
||||
API_ENDPOINTS.COMPLETED_LEVELS,
|
||||
API_TIMEOUT.DEFAULT,
|
||||
);
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
console.error('[CompletedLevelsManager] 拉取失败:', response.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
this._levels = response.data;
|
||||
this._loaded = true;
|
||||
console.log(`[CompletedLevelsManager] 拉取成功,共 ${this._levels.length} 关`);
|
||||
return this._levels;
|
||||
} catch (err) {
|
||||
console.error('[CompletedLevelsManager] 拉取异常:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按图片 URL 加载并缓存 SpriteFrame
|
||||
* 已缓存直接返回
|
||||
*/
|
||||
loadImage(url: string): Promise<SpriteFrame | null> {
|
||||
if (!url) return Promise.resolve(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('[CompletedLevelsManager] 加载图片失败:', 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 清除缓存(登出等场景) */
|
||||
clear(): void {
|
||||
this._levels = [];
|
||||
this._loaded = false;
|
||||
this._imageCache.clear();
|
||||
this._inflight = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user