dzkcc-mflow 0.0.37 → 0.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,6 +65,10 @@ export interface IView {
65
65
  onPause(): void;
66
66
  /** 恢复视图(从暂停状态恢复) */
67
67
  onResume(): void;
68
+ /** 进入动画(可选) */
69
+ onEnterAnimation?(): Promise<void>;
70
+ /** 退出动画(可选) */
71
+ onExitAnimation?(): Promise<void>;
68
72
  }
69
73
  /**
70
74
  * UI 管理器接口 - 管理视图的打开、关闭和栈操作
@@ -75,15 +79,17 @@ export interface IUIManager {
75
79
  /** 打开视图 */
76
80
  open<T extends keyof UIRegistry>(viewClass: T, args?: any): Promise<InstanceType<UIRegistry[T]>>;
77
81
  /** 关闭视图 */
78
- close<T extends keyof UIRegistry>(viewClass: T): void;
82
+ close<T extends keyof UIRegistry>(viewClass: T): Promise<void>;
79
83
  /** 打开视图并入栈 */
80
84
  openAndPush<T extends keyof UIRegistry>(viewClass: T, group: string, args?: any): Promise<InstanceType<UIRegistry[T]>>;
81
85
  /** 关闭栈顶视图并弹出 */
82
- closeAndPop(group: string, destroy?: boolean): void;
86
+ closeAndPop(group: string, destroy?: boolean): Promise<void>;
83
87
  /** 获取栈顶视图 */
84
88
  getTopView(): IView | undefined;
85
89
  /** 清空视图栈 */
86
90
  clearStack(group: string, destroy?: boolean): void;
91
+ /** 关闭所有视图 */
92
+ closeAll(destroy?: boolean): void;
87
93
  }
88
94
  /**
89
95
  * 资源管理器接口
package/dist/core/Core.js CHANGED
@@ -33,7 +33,7 @@ class AbstractCore {
33
33
  * ```
34
34
  */
35
35
  getModel(modelClass) {
36
- const className = modelClass.name;
36
+ const className = modelClass;
37
37
  // 如果已存在实例,直接返回
38
38
  if (this.container.has(className)) {
39
39
  return this.container.get(className);
@@ -55,7 +55,7 @@ class AbstractCore {
55
55
  * ```
56
56
  */
57
57
  getManager(managerClass) {
58
- const className = managerClass.name;
58
+ const className = managerClass;
59
59
  // 如果已存在实例,直接返回
60
60
  if (this.container.has(className)) {
61
61
  return this.container.get(className);
@@ -36,4 +36,40 @@ export declare abstract class BaseView extends Component implements IView {
36
36
  * ```
37
37
  */
38
38
  protected getManager<T extends keyof ManagerRegistry>(managerClass: T): InstanceType<ManagerRegistry[T]>;
39
+ /**
40
+ * 进入动画(可被子类覆盖以实现自定义动画)
41
+ * 默认实现:缩放+淡入效果
42
+ * @returns 返回 Promise 以支持异步动画
43
+ * @example
44
+ * ```typescript
45
+ * async onEnterAnimation(): Promise<void> {
46
+ * // 自定义动画实现
47
+ * return new Promise<void>((resolve) => {
48
+ * tween(this.node)
49
+ * .to(0.5, { scale: new Vec3(1, 1, 1) })
50
+ * .call(() => resolve())
51
+ * .start();
52
+ * });
53
+ * }
54
+ * ```
55
+ */
56
+ onEnterAnimation(): Promise<void>;
57
+ /**
58
+ * 退出动画(可被子类覆盖以实现自定义动画)
59
+ * 默认实现:缩放+淡出效果
60
+ * @returns 返回 Promise 以支持异步动画
61
+ * @example
62
+ * ```typescript
63
+ * async onExitAnimation(): Promise<void> {
64
+ * // 自定义动画实现
65
+ * return new Promise<void>((resolve) => {
66
+ * tween(this.node)
67
+ * .to(0.3, { scale: new Vec3(0, 0, 1) })
68
+ * .call(() => resolve())
69
+ * .start();
70
+ * });
71
+ * }
72
+ * ```
73
+ */
74
+ onExitAnimation(): Promise<void>;
39
75
  }
@@ -1,4 +1,5 @@
1
- import { _decorator, Component } from 'cc';
1
+ import { __awaiter } from '../_virtual/_tslib.js';
2
+ import { _decorator, Component, UIOpacity, tween, Vec3 } from 'cc';
2
3
 
3
4
  const { ccclass, property } = _decorator;
4
5
  class BaseView extends Component {
@@ -93,6 +94,78 @@ class BaseView extends Component {
93
94
  getManager(managerClass) {
94
95
  return mf.core.getManager(managerClass);
95
96
  }
97
+ /**
98
+ * 进入动画(可被子类覆盖以实现自定义动画)
99
+ * 默认实现:缩放+淡入效果
100
+ * @returns 返回 Promise 以支持异步动画
101
+ * @example
102
+ * ```typescript
103
+ * async onEnterAnimation(): Promise<void> {
104
+ * // 自定义动画实现
105
+ * return new Promise<void>((resolve) => {
106
+ * tween(this.node)
107
+ * .to(0.5, { scale: new Vec3(1, 1, 1) })
108
+ * .call(() => resolve())
109
+ * .start();
110
+ * });
111
+ * }
112
+ * ```
113
+ */
114
+ onEnterAnimation() {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ const node = this.node;
117
+ node.setScale(0.8, 0.8, 1);
118
+ let uiOpacity = node.getComponent(UIOpacity);
119
+ if (uiOpacity) {
120
+ uiOpacity.opacity = 0;
121
+ }
122
+ return new Promise((resolve) => {
123
+ const tweenNode = tween(node).to(0.3, { scale: new Vec3(1, 1, 1) }, { easing: 'backOut' });
124
+ if (uiOpacity) {
125
+ tween(uiOpacity)
126
+ .to(0.3, { opacity: 255 })
127
+ .start();
128
+ }
129
+ tweenNode
130
+ .call(() => resolve())
131
+ .start();
132
+ });
133
+ });
134
+ }
135
+ /**
136
+ * 退出动画(可被子类覆盖以实现自定义动画)
137
+ * 默认实现:缩放+淡出效果
138
+ * @returns 返回 Promise 以支持异步动画
139
+ * @example
140
+ * ```typescript
141
+ * async onExitAnimation(): Promise<void> {
142
+ * // 自定义动画实现
143
+ * return new Promise<void>((resolve) => {
144
+ * tween(this.node)
145
+ * .to(0.3, { scale: new Vec3(0, 0, 1) })
146
+ * .call(() => resolve())
147
+ * .start();
148
+ * });
149
+ * }
150
+ * ```
151
+ */
152
+ onExitAnimation() {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ const node = this.node;
155
+ const uiOpacity = node.getComponent(UIOpacity);
156
+ return new Promise((resolve) => {
157
+ const tweenNode = tween(node).to(0.2, { scale: new Vec3(0.8, 0.8, 1) }, { easing: 'backIn' });
158
+ if (uiOpacity) {
159
+ tween(uiOpacity)
160
+ .to(0.2, { opacity: 0 })
161
+ .start();
162
+ }
163
+ tweenNode
164
+ .call(() => resolve())
165
+ .start();
166
+ });
167
+ });
168
+ }
96
169
  }
97
170
 
98
171
  export { BaseView };
@@ -1,10 +1,24 @@
1
1
  import { Asset, Prefab, SpriteFrame, Sprite, sp } from "cc";
2
2
  import { ICocosResManager, AssetType } from "../core";
3
3
  export declare class ResLoader implements ICocosResManager {
4
+ /**
5
+ *
6
+ * @param nameOrUrl 资源包名称或路径
7
+ * @returns Promise<AssetManager.Bundle>
8
+ */
9
+ private _loadBundle;
10
+ /**
11
+ *
12
+ * @param path
13
+ * @param type
14
+ * @param nameOrUrl
15
+ * @returns
16
+ */
4
17
  loadAsset<T extends Asset>(path: string, type: AssetType<T>, nameOrUrl?: string): Promise<T>;
5
18
  loadPrefab(path: string, nameOrUrl?: string): Promise<Prefab>;
6
19
  loadSpriteFrame(ref: Sprite, path: string, nameOrUrl?: string): Promise<SpriteFrame>;
7
20
  loadSpine(ref: sp.Skeleton, path: string, nameOrUrl?: string): Promise<sp.SkeletonData>;
21
+ preloadAsset(paths: string | string[], type: AssetType<Asset>, nameOrUrl?: string): Promise<void>;
8
22
  release(asset: Asset, force?: boolean): void;
9
23
  release(path: string, type?: AssetType<Asset>, nameOrUrl?: string, force?: boolean): void;
10
24
  }
@@ -1,13 +1,18 @@
1
1
  import { __awaiter } from '../_virtual/_tslib.js';
2
- import { assetManager, Prefab, Asset, SpriteFrame, sp } from 'cc';
2
+ import { path, assetManager, Prefab, SpriteFrame, sp, Asset } from 'cc';
3
3
 
4
4
  const DefaultBundle = "resources";
5
5
  class ResLoader {
6
- loadAsset(path, type, nameOrUrl = DefaultBundle) {
7
- if (assetManager.assets.has(path)) {
8
- const asset = assetManager.assets.get(path);
9
- asset.addRef();
10
- return Promise.resolve(asset);
6
+ /**
7
+ *
8
+ * @param nameOrUrl 资源包名称或路径
9
+ * @returns Promise<AssetManager.Bundle>
10
+ */
11
+ _loadBundle(nameOrUrl = DefaultBundle) {
12
+ const bundleName = path.basename(nameOrUrl);
13
+ const bundle = assetManager.getBundle(bundleName);
14
+ if (bundle) {
15
+ return Promise.resolve(bundle);
11
16
  }
12
17
  return new Promise((resolve, reject) => {
13
18
  assetManager.loadBundle(nameOrUrl, (err, bundle) => {
@@ -15,22 +20,52 @@ class ResLoader {
15
20
  reject(err);
16
21
  }
17
22
  else {
18
- bundle.load(path, type, (err, data) => {
19
- if (err) {
20
- reject(err);
21
- }
22
- else {
23
- data.addRef();
24
- resolve(data);
25
- }
26
- });
23
+ resolve(bundle);
27
24
  }
28
25
  });
29
26
  });
30
27
  }
28
+ /**
29
+ *
30
+ * @param path
31
+ * @param type
32
+ * @param nameOrUrl
33
+ * @returns
34
+ */
35
+ loadAsset(path, type, nameOrUrl = DefaultBundle) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ // 如果是在resources路径下,则截取resources之后的路径,否则直接使用路径
38
+ const _path = (nameOrUrl == DefaultBundle && path.includes('resources')) ? path.split('resources/')[1] : path;
39
+ // 加载资源的通用逻辑
40
+ const loadFromBundle = (bundle) => {
41
+ const cachedAsset = bundle.get(_path, type);
42
+ if (cachedAsset) {
43
+ cachedAsset.addRef();
44
+ return Promise.resolve(cachedAsset);
45
+ }
46
+ else {
47
+ return new Promise((resolve, reject) => {
48
+ bundle.load(_path, type, (err, data) => {
49
+ if (err) {
50
+ reject(err);
51
+ }
52
+ else {
53
+ data.addRef();
54
+ resolve(data);
55
+ }
56
+ });
57
+ });
58
+ }
59
+ };
60
+ const bundle = yield this._loadBundle(nameOrUrl);
61
+ return loadFromBundle(bundle);
62
+ });
63
+ }
31
64
  loadPrefab(path, nameOrUrl = DefaultBundle) {
32
- //refCount 记录的是持有者数量,不是资源份数,所以prefab也需要addRef
33
- return this.loadAsset(path, Prefab, nameOrUrl);
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ //refCount 记录的是持有者数量,不是资源份数,所以prefab也需要addRef
67
+ return yield this.loadAsset(path, Prefab, nameOrUrl);
68
+ });
34
69
  }
35
70
  loadSpriteFrame(ref, path, nameOrUrl = DefaultBundle) {
36
71
  return __awaiter(this, void 0, void 0, function* () {
@@ -60,6 +95,21 @@ class ResLoader {
60
95
  }
61
96
  });
62
97
  }
98
+ preloadAsset(paths, type, nameOrUrl = DefaultBundle) {
99
+ return __awaiter(this, void 0, void 0, function* () {
100
+ const bundle = yield this._loadBundle(nameOrUrl);
101
+ new Promise((resolve, reject) => {
102
+ bundle.preload(paths, type, (err, data) => {
103
+ if (err) {
104
+ reject(err);
105
+ }
106
+ else {
107
+ resolve(data);
108
+ }
109
+ });
110
+ });
111
+ });
112
+ }
63
113
  release(pathOrAsset, typeOrForce, nameOrUrl = DefaultBundle, forceParam = false) {
64
114
  var _a;
65
115
  let asset;
@@ -1,34 +1,179 @@
1
- import { Component } from "cc";
1
+ import { Component, Node, Color } from "cc";
2
2
  import { IUIManager, IView } from "../core";
3
+ /**
4
+ * UI遮罩配置选项
5
+ */
6
+ export interface UIMaskOptions {
7
+ /** 遮罩颜色 */
8
+ color?: Color;
9
+ /** 是否可点击关闭顶层UI */
10
+ clickToClose?: boolean;
11
+ }
12
+ /**
13
+ * UI打开选项
14
+ */
15
+ export interface UIOpenOptions {
16
+ /** 是否显示等待视图 */
17
+ showLoading?: boolean;
18
+ /** 是否可点击遮罩关闭 */
19
+ clickToClose?: boolean;
20
+ /** 自定义参数 */
21
+ args?: any;
22
+ }
23
+ /**
24
+ * 等待视图配置
25
+ */
26
+ export interface UILoadingConfig {
27
+ /** 是否全局启用等待视图 */
28
+ enabled?: boolean;
29
+ /** 等待视图预制体路径 */
30
+ prefabPath?: string;
31
+ /** 等待视图显示延迟(毫秒) */
32
+ delay?: number;
33
+ /** 最小显示时间(毫秒) */
34
+ minShowTime?: number;
35
+ /** 自定义创建函数(高级用法) */
36
+ createCustomLoading?: () => Node | Promise<Node>;
37
+ }
38
+ /**
39
+ * LRU缓存配置
40
+ */
41
+ export interface UICacheConfig {
42
+ /** 最大缓存数量 */
43
+ maxSize?: number;
44
+ /** 是否启用LRU策略 */
45
+ enableLRU?: boolean;
46
+ }
47
+ /**
48
+ * 预加载配置
49
+ */
50
+ export interface UIPreloadConfig {
51
+ /** 预加载的视图列表 */
52
+ views?: (keyof UIRegistry)[];
53
+ /** 预加载延迟(毫秒) */
54
+ delay?: number;
55
+ }
3
56
  type ICocosView = IView & Component;
4
57
  declare abstract class CcocosUIManager implements IUIManager {
5
58
  getTopView(): IView | undefined;
6
- open<T extends keyof UIRegistry>(viewClass: T, args?: any): Promise<InstanceType<UIRegistry[T]>>;
7
- close<T extends keyof UIRegistry>(viewClass: T): void;
8
- openAndPush<T extends keyof UIRegistry>(viewClass: T, group: string, args?: any): Promise<InstanceType<UIRegistry[T]>>;
9
- closeAndPop(group: string, destroy?: boolean): void;
59
+ open<T extends keyof UIRegistry>(viewClass: T, args?: UIOpenOptions): Promise<InstanceType<UIRegistry[T]>>;
60
+ close<T extends keyof UIRegistry>(viewClass: T): Promise<void>;
61
+ openAndPush<T extends keyof UIRegistry>(viewClass: T, group: string, args?: UIOpenOptions): Promise<InstanceType<UIRegistry[T]>>;
62
+ closeAndPop(group: string, destroy?: boolean): Promise<void>;
10
63
  clearStack(group: string, destroy?: boolean): void;
11
- protected abstract internalOpen(viewKey: string, args?: any): Promise<ICocosView>;
12
- protected abstract internalClose(viewKey: string | IView, destory?: boolean): void;
13
- protected abstract internalOpenAndPush(viewKey: string, group: string, args?: any): Promise<ICocosView>;
14
- protected abstract internalCloseAndPop(group: string, destroy?: boolean): void;
15
- protected abstract internalClearStack(group: string, destroy?: boolean): void;
16
- protected abstract internalGetTopView(): ICocosView | undefined;
64
+ closeAll(destroy?: boolean): void;
65
+ protected abstract _internalOpen(viewKey: string, args?: UIOpenOptions): Promise<ICocosView>;
66
+ protected abstract _internalClose(viewKey: string | IView, destory?: boolean): Promise<void>;
67
+ protected abstract _internalOpenAndPush(viewKey: string, group: string, args?: UIOpenOptions): Promise<ICocosView>;
68
+ protected abstract _internalCloseAndPop(group: string, destroy?: boolean): Promise<void>;
69
+ protected abstract _internalClearStack(group: string, destroy?: boolean): void;
70
+ protected abstract _internalGetTopView(): ICocosView | undefined;
71
+ protected abstract _internalCloseAll(destroy?: boolean): void;
17
72
  }
18
73
  export declare class UIManager extends CcocosUIManager {
19
74
  private _cache;
20
75
  private _groupStacks;
76
+ private _inputBlocker;
77
+ private _loadingView;
78
+ private _loadingPromises;
79
+ private _lruOrder;
80
+ private _preloadedViews;
81
+ private _maskOptions;
82
+ private _loadingConfig;
83
+ private _cacheConfig;
84
+ private _preloadConfig;
85
+ private _openOptions;
21
86
  constructor();
87
+ /**
88
+ * 设置遮罩配置
89
+ */
90
+ setMaskConfig(options: UIMaskOptions): void;
91
+ /**
92
+ * 设置等待视图配置
93
+ */
94
+ setLoadingConfig(config: UILoadingConfig): void;
95
+ /**
96
+ * 设置缓存配置
97
+ */
98
+ setCacheConfig(config: UICacheConfig): void;
99
+ /**
100
+ * 设置预加载配置
101
+ */
102
+ setPreloadConfig(config: UIPreloadConfig): void;
103
+ /**
104
+ * 检查指定视图是否已打开
105
+ */
106
+ contains<T extends keyof UIRegistry>(viewKey: T): boolean;
107
+ /**
108
+ * 检查视图是否正在加载
109
+ */
110
+ isLoading<T extends keyof UIRegistry>(viewKey: T): boolean;
111
+ /**
112
+ * 预加载视图(支持单个或多个)
113
+ */
114
+ preload<T extends keyof UIRegistry>(viewKeyOrKeys: T | T[]): Promise<void>;
22
115
  private _getPrefabPath;
116
+ /**
117
+ * 获取所有活跃的视图节点(排除遮罩节点)
118
+ */
119
+ private _getActiveViews;
120
+ /**
121
+ * 通过prefab创建Node对象
122
+ * @param args
123
+ * @returns Node对象
124
+ */
125
+ private _generateNode;
126
+ /**
127
+ * 调整遮罩层级:始终保持在最顶层UI的下一层
128
+ */
23
129
  private _adjustMaskLayer;
130
+ /**
131
+ * 更新LRU顺序
132
+ */
133
+ private _updateLRUOrder;
134
+ /**
135
+ * 阻塞/解除输入事件
136
+ */
24
137
  private _blockInput;
138
+ /**
139
+ * 设置遮罩点击处理器
140
+ */
141
+ private _setupMaskClickHandler;
142
+ /**
143
+ * 显示等待视图
144
+ */
145
+ private _showLoading;
146
+ /**
147
+ * 隐藏等待视图
148
+ */
149
+ private _hideLoading;
150
+ /**
151
+ * 预加载视图
152
+ */
153
+ private _startPreload;
154
+ /**
155
+ * 预加载单个视图
156
+ */
157
+ private _preloadView;
158
+ /**
159
+ * 获取当前最顶层的视图
160
+ */
161
+ protected _internalGetTopView(): ICocosView | undefined;
25
162
  private _load;
163
+ private _loadInternal;
164
+ protected _internalOpen(viewKey: string, options?: UIOpenOptions): Promise<ICocosView>;
165
+ protected _internalClose(viewKeyOrInstance: string | IView, destroy?: boolean): Promise<void>;
166
+ protected _internalOpenAndPush(viewKey: string, group: string, options?: UIOpenOptions): Promise<ICocosView>;
167
+ protected _internalCloseAndPop(group: string, destroy?: boolean): Promise<void>;
168
+ /**
169
+ * 移除视图
170
+ */
26
171
  private _remove;
27
- protected internalGetTopView(): ICocosView | undefined;
28
- protected internalOpen(viewKey: string, args?: any): Promise<ICocosView>;
29
- protected internalClose(viewKey: string | IView, destroy?: boolean): void;
30
- protected internalOpenAndPush(viewKey: string, group: string, args?: any): Promise<ICocosView>;
31
- protected internalCloseAndPop(group: string, destroy?: boolean): void;
32
- protected internalClearStack(group: string, destroy?: boolean): void;
172
+ private _releasePrefab;
173
+ protected _internalClearStack(group: string, destroy?: boolean): void;
174
+ /**
175
+ * 关闭所有视图,不播放动画
176
+ */
177
+ protected _internalCloseAll(destroy?: boolean): void;
33
178
  }
34
179
  export {};
@@ -1,8 +1,14 @@
1
1
  import { __awaiter } from '../_virtual/_tslib.js';
2
- import { director, Node, Sprite, Widget, Input, input, Prefab, instantiate } from 'cc';
2
+ import { director, Node, Sprite, Widget, input, Input, Prefab, instantiate } from 'cc';
3
3
  import { ServiceLocator } from '../core/ServiceLocator.js';
4
4
  import { getViewClass } from '../core/Decorators.js';
5
+ import '../utils/ArrayExt.js';
6
+ import { ImageUtil } from '../utils/ImageUtil.js';
7
+ import '../utils/MathUtil.js';
5
8
 
9
+ /**
10
+ * 为节点添加全屏Widget组件
11
+ */
6
12
  function addWidget(node) {
7
13
  const widget = node.getComponent(Widget) || node.addComponent(Widget);
8
14
  widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true;
@@ -33,19 +39,21 @@ function addChild(node) {
33
39
  setLayer(node);
34
40
  }
35
41
  let _uiMask;
42
+ /**
43
+ * UI遮罩节点代理
44
+ */
36
45
  const UIMask = new Proxy({}, {
37
46
  get(target, prop) {
38
47
  if (!_uiMask) {
39
48
  _uiMask = new Node('__UIMask__');
40
49
  addChild(_uiMask);
41
- addWidget(_uiMask);
42
50
  _uiMask.setPosition(0, 0);
43
- _uiMask.addComponent(Sprite).color.set(0, 0, 0, 0.5);
51
+ _uiMask.addComponent(Sprite).spriteFrame = ImageUtil.createSolidColorSpriteFrame();
52
+ addWidget(_uiMask);
44
53
  }
45
54
  const value = Reflect.get(_uiMask, prop);
46
- // 如果是放的话,可能要绑定原始实例上下文
55
+ // 绑定方法到原始实例
47
56
  return typeof value === 'function' ? value.bind(_uiMask) : value;
48
- // return Reflect.get(_uiMask, prop)
49
57
  },
50
58
  set(target, p, newValue, receiver) {
51
59
  if (p === 'active') {
@@ -58,25 +66,25 @@ const UIMask = new Proxy({}, {
58
66
  // 接口隔离,实现具体的CcocosUIManager
59
67
  class CcocosUIManager {
60
68
  getTopView() {
61
- return this.internalGetTopView();
69
+ return this._internalGetTopView();
62
70
  }
63
71
  open(viewClass, args) {
64
- const className = viewClass.name;
65
- return this.internalOpen(className, args);
72
+ return this._internalOpen(viewClass, args);
66
73
  }
67
74
  close(viewClass) {
68
- const className = viewClass.name;
69
- this.internalClose(className);
75
+ return this._internalClose(viewClass);
70
76
  }
71
77
  openAndPush(viewClass, group, args) {
72
- const className = viewClass.name;
73
- return this.internalOpenAndPush(className, group, args);
78
+ return this._internalOpenAndPush(viewClass, group, args);
74
79
  }
75
80
  closeAndPop(group, destroy) {
76
- this.internalCloseAndPop(group, destroy);
81
+ return this._internalCloseAndPop(group, destroy);
77
82
  }
78
83
  clearStack(group, destroy) {
79
- this.internalClearStack(group, destroy);
84
+ this._internalClearStack(group, destroy);
85
+ }
86
+ closeAll(destroy) {
87
+ this._internalCloseAll(destroy);
80
88
  }
81
89
  }
82
90
  class UIManager extends CcocosUIManager {
@@ -84,19 +92,80 @@ class UIManager extends CcocosUIManager {
84
92
  super();
85
93
  this._cache = new Map();
86
94
  this._groupStacks = new Map();
87
- UIMask.on(Node.EventType.TOUCH_END, (event) => {
88
- let view = this.getTopView();
89
- if ('__group__' in view) {
90
- if (view.__group__ != undefined) {
91
- this.closeAndPop(view.__group__, false);
92
- }
93
- else {
94
- // 对于直接关闭视图,我们需要通过内部方法处理
95
- this.internalClose(view, false);
96
- }
95
+ this._inputBlocker = null;
96
+ this._loadingView = null;
97
+ this._loadingPromises = new Map();
98
+ this._lruOrder = [];
99
+ this._preloadedViews = new Set();
100
+ this._maskOptions = { clickToClose: true };
101
+ this._loadingConfig = { enabled: true, delay: 200, minShowTime: 500 };
102
+ this._cacheConfig = { maxSize: 10, enableLRU: true };
103
+ this._preloadConfig = { views: [], delay: 1000 };
104
+ this._openOptions = { showLoading: true, clickToClose: true };
105
+ this._setupMaskClickHandler();
106
+ this._startPreload();
107
+ }
108
+ /**
109
+ * 设置遮罩配置
110
+ */
111
+ setMaskConfig(options) {
112
+ this._maskOptions = Object.assign(Object.assign({}, this._maskOptions), options);
113
+ if (options.color && UIMask) {
114
+ const sprite = UIMask.getComponent(Sprite);
115
+ if (sprite) {
116
+ sprite.color = options.color;
117
+ }
118
+ }
119
+ }
120
+ /**
121
+ * 设置等待视图配置
122
+ */
123
+ setLoadingConfig(config) {
124
+ this._loadingConfig = Object.assign(Object.assign({}, this._loadingConfig), config);
125
+ }
126
+ /**
127
+ * 设置缓存配置
128
+ */
129
+ setCacheConfig(config) {
130
+ this._cacheConfig = Object.assign(Object.assign({}, this._cacheConfig), config);
131
+ }
132
+ /**
133
+ * 设置预加载配置
134
+ */
135
+ setPreloadConfig(config) {
136
+ this._preloadConfig = Object.assign(Object.assign({}, this._preloadConfig), config);
137
+ this._startPreload();
138
+ }
139
+ /**
140
+ * 检查指定视图是否已打开
141
+ */
142
+ contains(viewKey) {
143
+ const viewType = getViewClass(viewKey);
144
+ return this._cache.has(viewType.name) && this._cache.get(viewType.name).parent !== null;
145
+ }
146
+ /**
147
+ * 检查视图是否正在加载
148
+ */
149
+ isLoading(viewKey) {
150
+ return this._loadingPromises.has(viewKey);
151
+ }
152
+ /**
153
+ * 预加载视图(支持单个或多个)
154
+ */
155
+ preload(viewKeyOrKeys) {
156
+ return __awaiter(this, void 0, void 0, function* () {
157
+ if (Array.isArray(viewKeyOrKeys)) {
158
+ const promises = viewKeyOrKeys.map(key => this._preloadView(key));
159
+ yield Promise.all(promises);
160
+ }
161
+ else {
162
+ yield this._preloadView(viewKeyOrKeys);
97
163
  }
98
164
  });
99
165
  }
166
+ //----------------------------------------------------------
167
+ // ⬇️⬇️⬇️⬇️⬇️ 内部私有方法 ⬇️⬇️⬇️⬇️⬇️
168
+ //----------------------------------------------------------
100
169
  _getPrefabPath(viewType) {
101
170
  let prototype = Object.getPrototypeOf(viewType);
102
171
  // 沿着原型链向上查找直到找到定义__path__的基类。注意通过类只能找到静态属性。
@@ -108,175 +177,474 @@ class UIManager extends CcocosUIManager {
108
177
  }
109
178
  throw new Error(`Prefab path not found for ${viewType.name}`);
110
179
  }
111
- // 调整Mask层级
180
+ /**
181
+ * 获取所有活跃的视图节点(排除遮罩节点)
182
+ */
183
+ _getActiveViews() {
184
+ return UIRoot.children.filter(child => child !== _uiMask && child.name !== '__UIMask__');
185
+ }
186
+ /**
187
+ * 通过prefab创建Node对象
188
+ * @param args
189
+ * @returns Node对象
190
+ */
191
+ _generateNode(args) {
192
+ return __awaiter(this, void 0, void 0, function* () {
193
+ let prefabPath = args.prefabPath;
194
+ if (!prefabPath) {
195
+ prefabPath = this._getPrefabPath(getViewClass(args.viewKey));
196
+ }
197
+ const ResMgr = ServiceLocator.getService('ResLoader');
198
+ const prefab = yield ResMgr.loadPrefab(prefabPath);
199
+ return instantiate(prefab);
200
+ });
201
+ }
202
+ /**
203
+ * 调整遮罩层级:始终保持在最顶层UI的下一层
204
+ */
112
205
  _adjustMaskLayer() {
113
- let children = UIRoot.children;
114
- if (children.length == 1) {
206
+ const activeViews = this._getActiveViews();
207
+ // 没有活跃视图时隐藏遮罩
208
+ if (activeViews.length === 0) {
115
209
  UIMask.active = false;
116
210
  return;
117
211
  }
212
+ // 有视图时显示遮罩并调整到倒数第二层
118
213
  UIMask.active = true;
119
- UIMask.setSiblingIndex(Math.max(children.length - 2, 0));
214
+ const targetIndex = Math.max(UIRoot.children.length - 2, 0);
215
+ UIMask.setSiblingIndex(targetIndex);
120
216
  }
121
- _blockInput(block) {
122
- function blocker(event) {
123
- event.propagationImmediateStopped = true;
217
+ /**
218
+ * 更新LRU顺序
219
+ */
220
+ _updateLRUOrder(viewKey) {
221
+ if (!this._cacheConfig.enableLRU)
222
+ return;
223
+ const index = this._lruOrder.indexOf(viewKey);
224
+ if (index > -1) {
225
+ this._lruOrder.splice(index, 1);
226
+ }
227
+ this._lruOrder.push(viewKey);
228
+ // 检查缓存大小限制
229
+ if (this._lruOrder.length > this._cacheConfig.maxSize) {
230
+ const oldestKey = this._lruOrder.shift();
231
+ const oldestNode = this._cache.get(oldestKey);
232
+ // 只清理未激活的视图(不在场景树中的)
233
+ if (oldestNode && !oldestNode.parent) {
234
+ this._cache.delete(oldestKey);
235
+ // LRU清理不需要走框架的remove机制,因为:
236
+ // 1. 这些节点已经不在场景树中(已通过_remove移除)
237
+ // 2. onExit等生命周期已在_remove中调用过
238
+ // 3. 直接销毁节点释放内存即可
239
+ oldestNode.destroy();
240
+ // 尝试释放对应的Prefab资源
241
+ this._releasePrefab(oldestKey);
242
+ }
124
243
  }
244
+ }
245
+ /**
246
+ * 阻塞/解除输入事件
247
+ */
248
+ _blockInput(block) {
125
249
  if (block) {
126
- for (const eventType in Input.EventType) {
127
- input.on(Input.EventType[eventType], blocker);
250
+ // 创建并保存阻塞器引用
251
+ if (!this._inputBlocker) {
252
+ this._inputBlocker = (event) => {
253
+ event.propagationImmediateStopped = true;
254
+ };
128
255
  }
256
+ // 只监听常用的触摸和鼠标事件
257
+ input.on(Input.EventType.TOUCH_START, this._inputBlocker, this);
258
+ input.on(Input.EventType.TOUCH_MOVE, this._inputBlocker, this);
259
+ input.on(Input.EventType.TOUCH_END, this._inputBlocker, this);
260
+ input.on(Input.EventType.TOUCH_CANCEL, this._inputBlocker, this);
129
261
  }
130
262
  else {
131
- for (const eventType in Input.EventType) {
132
- input.off(Input.EventType[eventType], blocker);
263
+ // 使用保存的引用解除监听
264
+ if (this._inputBlocker) {
265
+ input.off(Input.EventType.TOUCH_START, this._inputBlocker, this);
266
+ input.off(Input.EventType.TOUCH_MOVE, this._inputBlocker, this);
267
+ input.off(Input.EventType.TOUCH_END, this._inputBlocker, this);
268
+ input.off(Input.EventType.TOUCH_CANCEL, this._inputBlocker, this);
133
269
  }
134
270
  }
135
271
  }
136
- _load(viewKey, args) {
137
- return __awaiter(this, void 0, void 0, function* () {
138
- const viewType = getViewClass(viewKey);
139
- let target;
140
- if (this._cache.has(viewType.name)) {
141
- target = this._cache.get(viewType.name);
272
+ /**
273
+ * 设置遮罩点击处理器
274
+ */
275
+ _setupMaskClickHandler() {
276
+ UIMask.on(Node.EventType.TOUCH_END, (event) => {
277
+ if (!this._maskOptions.clickToClose) {
278
+ return;
279
+ }
280
+ const view = this.getTopView();
281
+ if (!view) {
282
+ return;
283
+ }
284
+ // 区分两种情况处理:
285
+ // 1. 如果视图有 __group__ 属性且不为 undefined,说明是通过 openAndPush 打开的栈式UI
286
+ // 2. 否则是通过 open 打开的普通 UI
287
+ if ('__group__' in view && view.__group__ !== undefined) {
288
+ // 栈式UI:调用 _internalCloseAndPop 来处理返回逻辑
289
+ this._internalCloseAndPop(view.__group__, false);
142
290
  }
143
291
  else {
144
- let prefabPath = this._getPrefabPath(viewType);
145
- const ResMgr = ServiceLocator.getService('ResLoader');
146
- const prefab = yield ResMgr.loadPrefab(prefabPath);
147
- target = instantiate(prefab);
148
- this._cache.set(viewType.name, target);
292
+ // 普通UI:直接关闭该视图
293
+ this._internalClose(view, false);
149
294
  }
150
- target.active = true;
151
- return target.getComponent(viewType);
152
295
  });
153
296
  }
154
- _remove(viewKeyOrInstance, destroy) {
155
- var _a;
156
- // 如果是 string,从缓存中获取视图实例
157
- if (typeof viewKeyOrInstance === 'string') {
158
- const viewType = getViewClass(viewKeyOrInstance);
159
- const cached = this._cache.get(viewType.name);
160
- if (!cached) {
161
- console.warn(`No cached view found for ${viewType.name}`);
162
- return;
297
+ /**
298
+ * 显示等待视图
299
+ */
300
+ _showLoading() {
301
+ return __awaiter(this, void 0, void 0, function* () {
302
+ if (!this._loadingConfig.enabled) {
303
+ return Promise.resolve();
163
304
  }
164
- const viewInstance = cached.getComponent(viewType);
165
- if (!viewInstance) {
166
- console.warn(`No view component found on node ${cached.name}`);
167
- return;
305
+ // 如果已经显示了等待视图,直接返回
306
+ if (this._loadingView && this._loadingView.activeInHierarchy) {
307
+ return Promise.resolve();
308
+ }
309
+ // 首次加载或创建等待视图
310
+ if (!this._loadingView) {
311
+ if (this._loadingConfig.createCustomLoading) {
312
+ // 使用自定义创建函数
313
+ this._loadingView = yield this._loadingConfig.createCustomLoading();
314
+ }
315
+ else if (this._loadingConfig.prefabPath) {
316
+ try {
317
+ // 从 prefab 创建等待视图
318
+ this._loadingView = yield this._generateNode({ prefabPath: this._loadingConfig.prefabPath });
319
+ }
320
+ catch (error) {
321
+ throw error;
322
+ }
323
+ }
324
+ else {
325
+ // 没有配置等待视图
326
+ return Promise.resolve();
327
+ }
328
+ }
329
+ // 激活并添加到场景,调整到最顶层
330
+ this._loadingView.active = true;
331
+ UIRoot.parent.addChild(this._loadingView);
332
+ });
333
+ }
334
+ /**
335
+ * 隐藏等待视图
336
+ */
337
+ _hideLoading() {
338
+ if (this._loadingView && this._loadingView.activeInHierarchy) {
339
+ this._loadingView.active = false;
340
+ if (this._loadingView.parent) {
341
+ this._loadingView.removeFromParent();
168
342
  }
169
- this._remove(viewInstance, destroy);
170
- return;
171
343
  }
172
- // 处理视图实例
173
- const viewInstance = viewKeyOrInstance;
174
- if ('__group__' in viewInstance) {
175
- viewInstance.__group__ = undefined;
344
+ }
345
+ /**
346
+ * 预加载视图
347
+ */
348
+ _startPreload() {
349
+ if (!this._preloadConfig.views || this._preloadConfig.views.length === 0) {
350
+ return;
176
351
  }
177
- viewInstance.onExit();
178
- viewInstance.node.removeFromParent();
179
- viewInstance.node.active = false;
180
- if (destroy) {
181
- let cacheKey = viewInstance.constructor.name;
182
- (_a = this._cache.get(cacheKey)) === null || _a === void 0 ? void 0 : _a.destroy();
183
- this._cache.delete(cacheKey);
184
- // 销毁被克隆出的UI后Node后,尝试释放 Prefab 资源
185
- try {
186
- const viewType = viewInstance.constructor;
187
- const prefabPath = this._getPrefabPath(viewType);
188
- const ResMgr = ServiceLocator.getService('ResLoader');
189
- ResMgr.release(prefabPath, Prefab);
352
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () {
353
+ for (const viewKey of this._preloadConfig.views) {
354
+ if (!this._preloadedViews.has(viewKey)) {
355
+ try {
356
+ yield this._preloadView(viewKey);
357
+ this._preloadedViews.add(viewKey);
358
+ }
359
+ catch (error) {
360
+ console.warn(`Failed to preload view ${viewKey}:`, error);
361
+ }
362
+ }
190
363
  }
191
- catch (error) {
192
- console.error(`Failed to release prefab for ${cacheKey}:`, error);
364
+ }), this._preloadConfig.delay || 1000);
365
+ }
366
+ /**
367
+ * 预加载单个视图
368
+ */
369
+ _preloadView(viewKey) {
370
+ return __awaiter(this, void 0, void 0, function* () {
371
+ const viewType = getViewClass(viewKey);
372
+ if (this._cache.has(viewType.name)) {
373
+ return; // 已经缓存
193
374
  }
194
- }
375
+ const target = yield this._generateNode({ viewKey: viewKey });
376
+ target.active = false; // 预加载的视图保持不激活状态
377
+ this._cache.set(viewType.name, target);
378
+ this._updateLRUOrder(viewType.name);
379
+ });
195
380
  }
196
- internalGetTopView() {
197
- let target = UIRoot.children.reverse()[0];
198
- if (!target) {
381
+ /**
382
+ * 获取当前最顶层的视图
383
+ */
384
+ _internalGetTopView() {
385
+ const activeViews = this._getActiveViews();
386
+ if (activeViews.length === 0) {
199
387
  return undefined;
200
388
  }
389
+ // 获取最后一个视图节点(最顶层)
390
+ const target = activeViews[activeViews.length - 1];
201
391
  const comps = target.components;
202
392
  for (let i = 0; i < comps.length; i++) {
203
393
  const comp = comps[i];
204
- if ("__isIView__" in comp) {
205
- if (comp.__isIView__) {
206
- return comp;
207
- }
394
+ if ("__isIView__" in comp && comp.__isIView__) {
395
+ return comp;
208
396
  }
209
397
  }
210
398
  console.warn(`No view found in ${target.name}`);
211
399
  return undefined;
212
400
  }
213
- internalOpen(viewKey, args) {
401
+ //----------------------------------------------------------
402
+ _load(viewKey) {
403
+ return __awaiter(this, void 0, void 0, function* () {
404
+ // 并发控制:如果正在加载同一个视图,返回现有的Promise
405
+ if (this._loadingPromises.has(viewKey)) {
406
+ return this._loadingPromises.get(viewKey);
407
+ }
408
+ const loadPromise = this._loadInternal(viewKey);
409
+ this._loadingPromises.set(viewKey, loadPromise);
410
+ try {
411
+ return yield loadPromise;
412
+ }
413
+ finally {
414
+ this._loadingPromises.delete(viewKey);
415
+ }
416
+ });
417
+ }
418
+ _loadInternal(viewKey) {
419
+ return __awaiter(this, void 0, void 0, function* () {
420
+ const viewType = getViewClass(viewKey);
421
+ let target;
422
+ if (this._cache.has(viewType.name)) {
423
+ target = this._cache.get(viewType.name);
424
+ }
425
+ else {
426
+ target = yield this._generateNode({ viewKey: viewKey });
427
+ this._cache.set(viewType.name, target);
428
+ }
429
+ // 更新LRU顺序
430
+ this._updateLRUOrder(viewType.name);
431
+ target.active = true;
432
+ return target.getComponent(viewType);
433
+ });
434
+ }
435
+ _internalOpen(viewKey, options) {
436
+ var _a;
437
+ return __awaiter(this, void 0, void 0, function* () {
438
+ const op = Object.assign(Object.assign({}, this._openOptions), options);
439
+ // 显示等待视图
440
+ if (op.showLoading) {
441
+ yield this._showLoading();
442
+ }
443
+ // 打开UI前,阻塞输入事件
444
+ this._blockInput(true);
445
+ try {
446
+ const view = yield this._load(viewKey);
447
+ addChild(view.node);
448
+ this._adjustMaskLayer();
449
+ // 先执行onEnter初始化,再播放动画
450
+ view.onEnter(op.args);
451
+ // 播放打开动画
452
+ yield ((_a = view.onEnterAnimation) === null || _a === void 0 ? void 0 : _a.call(view));
453
+ return view;
454
+ }
455
+ finally {
456
+ // 隐藏等待视图
457
+ if (op.showLoading) {
458
+ this._hideLoading();
459
+ }
460
+ // 打开UI后,解除输入事件阻塞
461
+ this._blockInput(false);
462
+ }
463
+ });
464
+ }
465
+ _internalClose(viewKeyOrInstance, destroy) {
214
466
  return __awaiter(this, void 0, void 0, function* () {
215
467
  this._blockInput(true);
216
- let view = yield this._load(viewKey, args);
217
- addChild(view.node);
218
- this._adjustMaskLayer();
219
- view.onEnter(args);
220
- this._blockInput(false);
221
- return view;
468
+ try {
469
+ yield this._remove(viewKeyOrInstance, destroy);
470
+ this._adjustMaskLayer();
471
+ }
472
+ finally {
473
+ this._blockInput(false);
474
+ }
222
475
  });
223
476
  }
224
- internalClose(viewKey, destroy) {
225
- this._remove(viewKey, destroy);
226
- this._adjustMaskLayer();
477
+ _internalOpenAndPush(viewKey, group, options) {
478
+ var _a, _b;
479
+ return __awaiter(this, void 0, void 0, function* () {
480
+ const op = Object.assign(Object.assign({}, this._openOptions), options);
481
+ // 显示等待视图
482
+ if (op.showLoading) {
483
+ yield this._showLoading();
484
+ }
485
+ // 打开UI前,阻塞输入事件
486
+ this._blockInput(true);
487
+ try {
488
+ const view = yield this._load(viewKey);
489
+ let stack = this._groupStacks.get(group);
490
+ if (!stack) {
491
+ stack = [];
492
+ this._groupStacks.set(group, stack);
493
+ }
494
+ // 暂停并移除当前栈顶视图
495
+ const top = stack[stack.length - 1];
496
+ if (top) {
497
+ // 先执行onExit,再播放退出动画
498
+ yield ((_a = top.onExitAnimation) === null || _a === void 0 ? void 0 : _a.call(top));
499
+ top.onPause();
500
+ top.node.removeFromParent();
501
+ }
502
+ // 标记视图所属组并入栈
503
+ if ('__group__' in view) {
504
+ view.__group__ = group;
505
+ }
506
+ stack.push(view);
507
+ addChild(view.node);
508
+ this._adjustMaskLayer();
509
+ // 先执行onEnter初始化,再播放动画
510
+ view.onEnter(op.args);
511
+ // 播放打开动画
512
+ yield ((_b = view.onEnterAnimation) === null || _b === void 0 ? void 0 : _b.call(view));
513
+ return view;
514
+ }
515
+ finally {
516
+ // 隐藏等待视图
517
+ if (op.showLoading) {
518
+ this._hideLoading();
519
+ }
520
+ // 打开UI后,解除输入事件阻塞
521
+ this._blockInput(false);
522
+ }
523
+ });
227
524
  }
228
- internalOpenAndPush(viewKey, group, args) {
525
+ _internalCloseAndPop(group, destroy) {
526
+ var _a;
229
527
  return __awaiter(this, void 0, void 0, function* () {
528
+ const stack = this._groupStacks.get(group);
529
+ if (!stack || stack.length === 0) {
530
+ console.warn(`No stack or empty stack for group ${group}`);
531
+ return;
532
+ }
230
533
  this._blockInput(true);
231
- let view = yield this._load(viewKey, args);
232
- let stack = this._groupStacks.get(group) || [];
233
- this._groupStacks.set(group, stack);
234
- let top = stack[stack.length - 1];
235
- if (top) {
236
- top.onPause();
237
- top.node.removeFromParent();
238
- }
239
- if ('__group__' in view) {
240
- view.__group__ = group;
241
- }
242
- stack.push(view);
243
- addChild(view.node);
244
- this._adjustMaskLayer();
245
- view.onEnter(args);
246
- this._blockInput(false);
247
- return view;
534
+ try {
535
+ // 移除当前栈顶视图
536
+ yield this._remove(stack.pop(), destroy);
537
+ // 恢复上一个视图
538
+ const top = stack[stack.length - 1];
539
+ if (top) {
540
+ top.onResume();
541
+ addChild(top.node);
542
+ // 播放恢复动画
543
+ yield ((_a = top.onEnterAnimation) === null || _a === void 0 ? void 0 : _a.call(top));
544
+ }
545
+ // 调整遮罩层级
546
+ this._adjustMaskLayer();
547
+ }
548
+ finally {
549
+ this._blockInput(false);
550
+ }
248
551
  });
249
552
  }
250
- internalCloseAndPop(group, destroy) {
251
- let stack = this._groupStacks.get(group);
252
- if (!stack) {
253
- console.warn(`No stack found for group ${group}`);
254
- return;
255
- }
256
- if (stack.length == 0) {
257
- console.warn(`Stack is empty for group ${group}`);
258
- return;
553
+ /**
554
+ * 移除视图
555
+ */
556
+ _remove(viewKeyOrInstance, destroy, skipAnimation) {
557
+ var _a, _b;
558
+ return __awaiter(this, void 0, void 0, function* () {
559
+ // 如果是 string,从缓存中获取视图实例
560
+ if (typeof viewKeyOrInstance === 'string') {
561
+ const viewType = getViewClass(viewKeyOrInstance);
562
+ const cached = this._cache.get(viewType.name);
563
+ if (!cached) {
564
+ console.warn(`No cached view found for ${viewType.name}`);
565
+ return;
566
+ }
567
+ const viewInstance = cached.getComponent(viewType);
568
+ if (!viewInstance) {
569
+ console.warn(`No view component found on node ${cached.name}`);
570
+ return;
571
+ }
572
+ yield this._remove(viewInstance, destroy);
573
+ return;
574
+ }
575
+ // 处理视图实例
576
+ const viewInstance = viewKeyOrInstance;
577
+ if ('__group__' in viewInstance) {
578
+ viewInstance.__group__ = undefined;
579
+ }
580
+ if (!skipAnimation) {
581
+ // * 播放关闭动画,使用async是必要的,因为:
582
+ // * 确保动画播放完成后再执行onExit和节点移除,不然还没播放动画了,UI就已经没了
583
+ yield ((_a = viewInstance.onExitAnimation) === null || _a === void 0 ? void 0 : _a.call(viewInstance));
584
+ }
585
+ viewInstance.onExit();
586
+ viewInstance.node.removeFromParent();
587
+ viewInstance.node.active = false;
588
+ if (destroy) {
589
+ let cacheKey = viewInstance.constructor.name;
590
+ (_b = this._cache.get(cacheKey)) === null || _b === void 0 ? void 0 : _b.destroy();
591
+ this._cache.delete(cacheKey);
592
+ // 销毁被克隆出的UI后Node后,尝试释放 Prefab 资源
593
+ this._releasePrefab(viewInstance);
594
+ }
595
+ });
596
+ }
597
+ _releasePrefab(viewKey) {
598
+ try {
599
+ let prefabPath;
600
+ if (typeof viewKey === 'string') {
601
+ prefabPath = this._getPrefabPath(getViewClass(viewKey));
602
+ }
603
+ else {
604
+ prefabPath = this._getPrefabPath(viewKey.constructor);
605
+ }
606
+ const ResMgr = ServiceLocator.getService('ResLoader');
607
+ ResMgr.release(prefabPath, Prefab);
259
608
  }
260
- this._remove(stack.pop(), destroy);
261
- let top = stack[stack.length - 1];
262
- if (top) {
263
- top.onResume();
264
- addChild(top.node);
609
+ catch (error) {
610
+ console.error(`Failed to release prefab for ${viewKey}:`, error);
265
611
  }
266
- this._adjustMaskLayer();
267
612
  }
268
- internalClearStack(group, destroy) {
269
- let stack = this._groupStacks.get(group);
613
+ _internalClearStack(group, destroy) {
614
+ const stack = this._groupStacks.get(group);
270
615
  if (!stack) {
271
616
  console.warn(`No stack found for group ${group}`);
272
617
  return;
273
618
  }
619
+ // 清空栈中所有视图,不播放动画
274
620
  while (stack.length > 0) {
275
- let view = stack.pop();
621
+ const view = stack.pop();
276
622
  if (view) {
277
- this._remove(view, destroy);
623
+ this._remove(view, destroy, true);
278
624
  }
279
625
  }
626
+ // 调整遮罩层级
627
+ this._adjustMaskLayer();
628
+ }
629
+ /**
630
+ * 关闭所有视图,不播放动画
631
+ */
632
+ _internalCloseAll(destroy) {
633
+ const activeViews = this._getActiveViews();
634
+ for (const node of activeViews) {
635
+ const comps = node.components;
636
+ for (let i = 0; i < comps.length; i++) {
637
+ const comp = comps[i];
638
+ if ("__isIView__" in comp && comp.__isIView__) {
639
+ this._remove(comp, destroy, true);
640
+ break;
641
+ }
642
+ }
643
+ }
644
+ // 清空所有栈
645
+ this._groupStacks.clear();
646
+ // 调整遮罩
647
+ this._adjustMaskLayer();
280
648
  }
281
649
  }
282
650
 
Binary file
@@ -1,4 +1,4 @@
1
- import { Color, Texture2D } from "cc";
1
+ import { Color, Texture2D, SpriteFrame } from "cc";
2
2
  /**
3
3
  * 图像工具
4
4
  */
@@ -30,4 +30,8 @@ export declare class ImageUtil {
30
30
  * @param base64 Base64 字符
31
31
  */
32
32
  static base64ToBlob(base64: string): Blob;
33
+ /**
34
+ * 创建纯色SpriteFrame用于遮罩
35
+ */
36
+ static createSolidColorSpriteFrame(color?: Color): SpriteFrame;
33
37
  }
@@ -1,4 +1,4 @@
1
- import { Color, Texture2D } from 'cc';
1
+ import { Color, Texture2D, SpriteFrame, Rect } from 'cc';
2
2
 
3
3
  /**
4
4
  * 图像工具
@@ -92,6 +92,30 @@ class ImageUtil {
92
92
  }
93
93
  return new Blob([uint8Array], { type: type });
94
94
  }
95
+ /**
96
+ * 创建纯色SpriteFrame用于遮罩
97
+ */
98
+ static createSolidColorSpriteFrame(color = new Color(0, 0, 0, 125)) {
99
+ // 创建一个1x1像素的纯色纹理
100
+ const texture = new Texture2D();
101
+ texture.reset({
102
+ width: 1,
103
+ height: 1,
104
+ format: Texture2D.PixelFormat.RGBA8888
105
+ });
106
+ // 设置像素数据
107
+ const pixelData = new Uint8Array(4);
108
+ pixelData[0] = color.r;
109
+ pixelData[1] = color.g;
110
+ pixelData[2] = color.b;
111
+ pixelData[3] = color.a;
112
+ texture.uploadData(pixelData);
113
+ // 创建SpriteFrame
114
+ const spriteFrame = new SpriteFrame();
115
+ spriteFrame.texture = texture;
116
+ spriteFrame.rect = new Rect(0, 0, 1, 1);
117
+ return spriteFrame;
118
+ }
95
119
  }
96
120
 
97
121
  export { ImageUtil };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dzkcc-mflow",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "A modular design and process management framework developed for the cocos engine, suitable for decoupling and dependency injection.",
5
5
  "author": "duanzhk",
6
6
  "license": "MIT",