import { _decorator, Node, Prefab, instantiate, error } from 'cc'; import { BaseView, ViewConfig, ViewOptions } from './BaseView'; const { ccclass } = _decorator; /** * 已注册的页面配置映射 */ interface RegisteredView { config: ViewConfig; } /** * 页面管理器 * 单例模式,统一管理页面的注册、打开、关闭、返回等操作 */ @ccclass('ViewManager') export class ViewManager { /** 单例实例 */ private static _instance: ViewManager | null = null; /** 获取单例 */ public static get instance(): ViewManager { if (!ViewManager._instance) { ViewManager._instance = new ViewManager(); } return ViewManager._instance; } /** 页面容器节点 */ private _container: Node | null = null; /** 已注册的页面配置 */ private _registeredViews: Map = new Map(); /** 页面栈 */ private _viewStack: BaseView[] = []; /** 页面实例缓存(用于缓存模式的页面) */ private _viewCache: Map = new Map(); /** * 初始化管理器 * @param container 页面容器节点(通常是 Canvas) */ init(container: Node): void { this._container = container; } /** * 获取当前容器 */ getContainer(): Node | null { return this._container; } /** * 注册页面 * @param viewId 页面唯一标识 * @param config 页面配置 */ register(viewId: string, config: ViewConfig): void { if (this._registeredViews.has(viewId)) { error(`ViewManager: 页面 "${viewId}" 已注册`); return; } this._registeredViews.set(viewId, { config: { cache: true, // 默认缓存 zIndex: 0, // 默认层级 ...config } }); } /** * 批量注册页面 * @param views 页面配置映射 */ registerAll(views: Record): void { for (const [viewId, config] of Object.entries(views)) { this.register(viewId, config); } } /** * 打开页面 * @param viewId 页面唯一标识 * @param options 打开选项 */ open(viewId: string, options?: ViewOptions): void { if (!this._container) { const err = new Error('ViewManager: 未初始化,请先调用 init()'); options?.onError?.(err); return; } const registered = this._registeredViews.get(viewId); if (!registered) { const err = new Error(`ViewManager: 页面 "${viewId}" 未注册`); options?.onError?.(err); return; } // 检查是否有缓存的实例 const cachedView = this._viewCache.get(viewId); if (cachedView && cachedView.node.isValid) { this._showView(cachedView, options); return; } // 直接使用预制体引用实例化 this._instantiateView(viewId, registered.config.prefab, options); } /** * 实例化视图 */ private _instantiateView(viewId: string, prefab: Prefab, options?: ViewOptions): void { if (!this._container) return; const registered = this._registeredViews.get(viewId); if (!registered) return; const node = instantiate(prefab); const view = node.getComponent(BaseView); if (!view) { error(`ViewManager: 预制体 "${viewId}" 缺少 BaseView 组件`); node.destroy(); options?.onError?.(new Error('缺少 BaseView 组件')); return; } // 设置视图属性 view.viewId = viewId; view.config = registered.config; view.setParams(options?.params); // 设置层级 node.setSiblingIndex(registered.config.zIndex || 0); // 添加到容器 this._container.addChild(node); // 调用加载回调 view.onViewLoad(); // 缓存视图实例 if (registered.config.cache) { this._viewCache.set(viewId, view); } // 显示视图 this._showView(view, options); } /** * 显示视图 */ private _showView(view: BaseView, options?: ViewOptions): void { // 隐藏当前页面 const currentView = this.getCurrentView(); if (currentView && currentView !== view) { currentView._doHide(); } // 设置参数 if (options?.params !== undefined) { view.setParams(options?.params); } // 入栈 if (!this._viewStack.includes(view)) { this._viewStack.push(view); } // 显示 view._doShow(); // 回调 options?.onComplete?.(view); } /** * 关闭当前页面 * @param options 关闭选项 */ close(options?: { destroy?: boolean }): void { const currentView = this._viewStack.pop(); if (!currentView) return; const shouldDestroy = options?.destroy ?? !currentView.config?.cache; this._hideAndDestroyView(currentView, shouldDestroy); // 显示上一页 const prevView = this.getCurrentView(); if (prevView) { prevView._doShow(); } } /** * 返回上一页(close 的别名) */ back(): void { this.close(); } /** * 替换当前页面 * @param viewId 新页面标识 * @param options 打开选项 */ replace(viewId: string, options?: ViewOptions): void { const currentView = this.getCurrentView(); if (currentView) { this._viewStack.pop(); const shouldDestroy = !currentView.config?.cache; this._hideAndDestroyView(currentView, shouldDestroy); } this.open(viewId, options); } /** * 隐藏并销毁视图(内部方法) */ private _hideAndDestroyView(view: BaseView, shouldDestroy: boolean): void { view._doHide(); if (shouldDestroy) { this._viewCache.delete(view.viewId); view._doDestroy(); } } /** * 获取当前页面 */ getCurrentView(): BaseView | null { return this._viewStack.length > 0 ? this._viewStack[this._viewStack.length - 1] : null; } /** * 获取页面栈 */ getViewStack(): BaseView[] { return [...this._viewStack]; } /** * 清空所有页面 */ clearAll(): void { // 从栈顶开始销毁 while (this._viewStack.length > 0) { const view = this._viewStack.pop()!; const shouldDestroy = !view.config?.cache; this._hideAndDestroyView(view, shouldDestroy); } // 销毁缓存的页面 for (const view of this._viewCache.values()) { if (view.node.isValid) { view._doDestroy(); } } this._viewCache.clear(); } /** * 预加载页面预制体(主包资源已随游戏加载,此方法仅为兼容性保留) * @param viewId 页面标识 * @param onProgress 进度回调 * @param onComplete 完成回调 */ preload(viewId: string, onProgress?: (progress: number) => void, onComplete?: () => void): void { const registered = this._registeredViews.get(viewId); if (!registered) { error(`ViewManager: 页面 "${viewId}" 未注册`); onComplete?.(); return; } // 主包资源已加载,直接回调 onProgress?.(1); onComplete?.(); } /** * 批量预加载页面 * @param viewIds 页面标识数组 * @param onProgress 总进度回调 (0-1) * @param onComplete 完成回调 */ preloadAll(viewIds: string[], onProgress?: (progress: number) => void, onComplete?: () => void): void { if (viewIds.length === 0) { onProgress?.(1); onComplete?.(); return; } let completed = 0; const total = viewIds.length; for (const viewId of viewIds) { this.preload(viewId, () => { completed++; onProgress?.(completed / total); if (completed === total) { onComplete?.(); } }); } } }