dzkcc-mflow 0.0.18 → 0.0.20

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.
package/README.md CHANGED
@@ -12,6 +12,7 @@ Cocos模块化流程框架(Modular Flow Framework)是一个为Cocos Creator
12
12
  - **UI管理系统**:完整的UI界面管理方案
13
13
  - **事件系统**:强大的事件广播和监听机制
14
14
  - **资源加载系统**:统一的资源加载和释放管理
15
+ - **HTTP网络请求系统**:简洁易用的HTTP客户端
15
16
  - **开发工具**:配套的Cocos Creator编辑器插件
16
17
 
17
18
  ### 1.3 安装说明
@@ -176,7 +177,87 @@ const spineData = await mf.res.loadSpine(spineComponent, 'path/to/spine');
176
177
  mf.res.release('path/to/asset');
177
178
  ```
178
179
 
179
- ## 6. 开发工具
180
+ ## 6. HTTP网络请求系统
181
+
182
+ ### 6.1 HttpManager HTTP管理器
183
+
184
+ HttpManager提供了简洁易用的HTTP客户端功能,支持常见的HTTP方法:
185
+
186
+ ```typescript
187
+ // GET 请求
188
+ const userData = await mf.http.get('/api/users/123', { includeProfile: true });
189
+
190
+ // POST 请求
191
+ const newUser = await mf.http.post('/api/users', {
192
+ name: 'John',
193
+ email: 'john@example.com'
194
+ });
195
+
196
+ // PUT 请求
197
+ const updatedUser = await mf.http.put('/api/users/123', {
198
+ name: 'John Doe'
199
+ });
200
+
201
+ // DELETE 请求
202
+ await mf.http.delete('/api/users/123');
203
+
204
+ // 自定义请求
205
+ const result = await mf.http.request({
206
+ url: '/api/upload',
207
+ method: 'POST',
208
+ data: formData,
209
+ headers: {
210
+ 'Authorization': 'Bearer token'
211
+ },
212
+ timeout: 30000
213
+ });
214
+ ```
215
+
216
+ ### 6.2 功能特性
217
+
218
+ 1. **Promise-based API**:所有请求都返回Promise,支持async/await
219
+ 2. **超时控制**:默认10秒超时,可自定义
220
+ 3. **自动JSON解析**:自动解析JSON响应
221
+ 4. **错误处理**:统一的错误处理机制
222
+ 5. **请求拦截**:支持自定义请求头
223
+ 6. **URL参数处理**:自动处理GET请求的查询参数
224
+
225
+ ### 6.3 使用示例
226
+
227
+ ```typescript
228
+ // 在Manager中使用
229
+ @manager()
230
+ export class UserManager extends AbstractManager {
231
+ async getUserProfile(userId: string): Promise<any> {
232
+ try {
233
+ const profile = await mf.http.get(`/api/users/${userId}/profile`);
234
+ return profile;
235
+ } catch (error) {
236
+ console.error('Failed to fetch user profile:', error);
237
+ throw error;
238
+ }
239
+ }
240
+
241
+ async updateUser(userId: string, data: any): Promise<any> {
242
+ try {
243
+ const result = await mf.http.put(`/api/users/${userId}`, data, {
244
+ 'Authorization': `Bearer ${this.getAuthToken()}`
245
+ });
246
+ return result;
247
+ } catch (error) {
248
+ console.error('Failed to update user:', error);
249
+ throw error;
250
+ }
251
+ }
252
+
253
+ private getAuthToken(): string {
254
+ // 获取认证令牌的逻辑
255
+ return 'your-auth-token';
256
+ }
257
+ }
258
+ ```
259
+
260
+ ## 7. 开发工具
180
261
 
181
262
  框架配套了Cocos Creator编辑器插件`mflow-tools`,可以:
182
263
 
@@ -184,7 +265,7 @@ mf.res.release('path/to/asset');
184
265
  2. 自动引用Prefab上需要操作的元素
185
266
  3. 自动挂载脚本组件
186
267
 
187
- ### 6.1 使用方法
268
+ ### 7.1 使用方法
188
269
 
189
270
  1. 在Prefab中,将需要引用的节点重命名为`#属性名#组件类型`格式,例如:
190
271
  - `#titleLabel#Label` 表示引用Label组件
@@ -195,9 +276,9 @@ mf.res.release('path/to/asset');
195
276
 
196
277
  3. 插件会自动生成基础脚本和业务脚本,并自动设置引用关系
197
278
 
198
- ## 7. 完整示例
279
+ ## 8. 完整示例
199
280
 
200
- ### 7.1 创建Manager
281
+ ### 8.1 创建Manager
201
282
 
202
283
  ```typescript
203
284
  @manager()
@@ -220,7 +301,7 @@ export class GameManager extends AbstractManager {
220
301
  }
221
302
  ```
222
303
 
223
- ### 7.2 创建Model
304
+ ### 8.2 创建Model
224
305
 
225
306
  ```typescript
226
307
  @model()
@@ -241,7 +322,7 @@ export class GameModel implements IModel {
241
322
  }
242
323
  ```
243
324
 
244
- ### 7.3 创建UI界面
325
+ ### 8.3 创建UI界面
245
326
 
246
327
  ```typescript
247
328
  // BaseHomeView.ts (由工具自动生成)
@@ -299,7 +380,7 @@ export class HomeView extends BaseHomeView {
299
380
  }
300
381
  ```
301
382
 
302
- ### 7.4 在场景中使用
383
+ ### 8.4 在场景中使用
303
384
 
304
385
  ```typescript
305
386
  // 在游戏启动时
@@ -311,18 +392,20 @@ export class GameApp extends Component {
311
392
  }
312
393
  ```
313
394
 
314
- ## 8. 最佳实践
395
+ ## 9. 最佳实践
315
396
 
316
397
  1. **模块化设计**:将相关的业务逻辑封装在对应的Manager中
317
398
  2. **数据驱动**:使用Model管理数据状态
318
399
  3. **事件解耦**:通过事件系统实现模块间通信
319
400
  4. **资源管理**:使用BaseView自动管理资源加载和释放
320
401
  5. **依赖注入**:使用装饰器简化依赖管理
321
- 6. **工具辅助**:使用mflow-tools提高开发效率
402
+ 6. **网络请求**:使用HttpManager统一管理网络请求
403
+ 7. **工具辅助**:使用mflow-tools提高开发效率
322
404
 
323
- ## 9. 注意事项
405
+ ## 10. 注意事项
324
406
 
325
407
  1. 确保在使用框架功能前Core已经初始化
326
408
  2. 注意资源的正确加载和释放,避免内存泄漏
327
409
  3. 合理使用事件系统,避免事件监听过多影响性能
328
- 4. 使用BaseView的子类时,确保正确实现所有抽象方法
410
+ 4. 使用BaseView的子类时,确保正确实现所有抽象方法
411
+ 5. 网络请求时注意错误处理和超时设置
package/dist/App.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ICore, IUIManager, IEventManager, ICocosResManager } from "./core";
1
+ import { ICore, IUIManager, IEventManager, ICocosResManager, IHttpManager } from "./core";
2
2
  /**
3
3
  * 对外暴露的全局app对像,用于访问基础能力,为上层业务提供了简洁的访问方式
4
4
  *
@@ -9,7 +9,7 @@ export declare class App {
9
9
  static readonly log: any;
10
10
  static readonly config: any;
11
11
  static get gui(): IUIManager;
12
- static readonly http: any;
12
+ static get http(): IHttpManager;
13
13
  static readonly socket: any;
14
14
  static get res(): ICocosResManager;
15
15
  static get event(): IEventManager;
package/dist/App.js CHANGED
@@ -13,6 +13,9 @@ class App {
13
13
  static get gui() {
14
14
  return ServiceLocator.getService('UIManager');
15
15
  }
16
+ static get http() {
17
+ return ServiceLocator.getService('HttpManager');
18
+ }
16
19
  static get res() {
17
20
  return ServiceLocator.getService('ResLoader');
18
21
  }
@@ -22,7 +25,6 @@ class App {
22
25
  }
23
26
  App.log = null;
24
27
  App.config = null;
25
- App.http = null;
26
28
  App.socket = null;
27
29
  App.storage = null;
28
30
  App.audio = null;
@@ -28,6 +28,21 @@ export interface IUIManager {
28
28
  }
29
29
  export interface IResManager {
30
30
  }
31
+ export interface IHttpManager extends IManager {
32
+ get<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>): Promise<T>;
33
+ post<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T>;
34
+ put<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T>;
35
+ delete<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>): Promise<T>;
36
+ request<T = any>(options: HttpRequestOptions): Promise<T>;
37
+ }
38
+ export interface HttpRequestOptions {
39
+ url: string;
40
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
41
+ headers?: Record<string, string>;
42
+ params?: Record<string, any>;
43
+ data?: any;
44
+ timeout?: number;
45
+ }
31
46
  export interface IEventMsgKey {
32
47
  }
33
48
  export type ToAnyIndexKey<IndexKey, AnyType> = IndexKey extends keyof AnyType ? IndexKey : keyof AnyType;
@@ -1,4 +1,4 @@
1
- import { ICore, IEventManager, IManager, IModel } from "./Api";
1
+ import { ICore, IEventManager, IManager, IModel, IHttpManager } from "./Api";
2
2
  export declare abstract class AbstractCore<T extends AbstractCore<T>> implements ICore {
3
3
  private readonly container;
4
4
  constructor();
@@ -14,5 +14,6 @@ export declare abstract class AbstractManager implements IManager {
14
14
  dispose(): void;
15
15
  protected getModel<T extends IModel>(ctor: new () => T): T;
16
16
  protected getEventManager(): IEventManager;
17
+ protected getHttpManager(): IHttpManager;
17
18
  private releaseEventManager;
18
19
  }
package/dist/core/Core.js CHANGED
@@ -71,6 +71,10 @@ class AbstractManager {
71
71
  }
72
72
  return this.eventManager;
73
73
  }
74
+ // HTTP 管理器获取
75
+ getHttpManager() {
76
+ return ServiceLocator.getService('HttpManager');
77
+ }
74
78
  releaseEventManager() {
75
79
  var _a, _b;
76
80
  if (this.eventManager) {
@@ -6,6 +6,6 @@ export interface ICocosResManager extends IResManager {
6
6
  loadPrefab(path: string, nameOrUrl?: string): Promise<Prefab>;
7
7
  loadSpriteFrame(ref: Sprite, path: string, nameOrUrl?: string): Promise<SpriteFrame>;
8
8
  loadSpine(ref: sp.Skeleton, path: string, nameOrUrl?: string): Promise<sp.SkeletonData>;
9
- release(asset: Asset): void;
10
- release(path: string, type?: AssetType<Asset>, nameOrUrl?: string): void;
9
+ release(asset: Asset, force?: boolean): void;
10
+ release(path: string, type?: AssetType<Asset>, nameOrUrl?: string, force?: boolean): void;
11
11
  }
@@ -65,8 +65,7 @@ class BaseView extends Component {
65
65
  onDestroy() {
66
66
  // 自动清理加载的资源
67
67
  this._loaderHandlers.forEach(({ path, asset }) => {
68
- mf.res.release(path, asset.constructor);
69
- // mf.res.release(asset);
68
+ mf.res.release(asset);
70
69
  });
71
70
  this._loaderHandlers = [];
72
71
  }
@@ -5,6 +5,7 @@ import { ServiceLocator } from '../core/ServiceLocator.js';
5
5
  import { UIManager } from './UIManager.js';
6
6
  import { ResLoader } from './ResLoader.js';
7
7
  import { Broadcaster } from './Broadcaster.js';
8
+ import { HttpManager } from './HttpManager.js';
8
9
  import '../App.js';
9
10
 
10
11
  class Core extends AbstractCore {
@@ -14,6 +15,7 @@ class Core extends AbstractCore {
14
15
  ServiceLocator.regService('EventManager', new Broadcaster());
15
16
  ServiceLocator.regService('ResLoader', new ResLoader());
16
17
  ServiceLocator.regService('UIManager', new UIManager());
18
+ ServiceLocator.regService('HttpManager', new HttpManager());
17
19
  // 注册业务模块(通过装饰器自动注册)
18
20
  // 推迟到构造函数执行完毕
19
21
  queueMicrotask(() => autoRegister(this));
@@ -0,0 +1,72 @@
1
+ import { IHttpManager, HttpRequestOptions } from "../core";
2
+ /**
3
+ * HTTP 管理器实现类
4
+ *
5
+ * 发起请求
6
+
7
+ 【请求拦截器】修改请求参数(如添加 token、签名等)
8
+
9
+ 发送到服务器
10
+
11
+ 收到响应
12
+
13
+ 【响应拦截器】处理响应数据(如提取数据、转换格式等)
14
+
15
+ 返回结果 ✅
16
+
17
+ 如果出错 ❌
18
+
19
+ 【错误拦截器】统一处理错误(如显示提示、记录日志等)
20
+ */
21
+ export declare class HttpManager implements IHttpManager {
22
+ private baseURL;
23
+ private defaultTimeout;
24
+ private defaultHeaders;
25
+ private requestInterceptors;
26
+ private responseInterceptors;
27
+ private errorInterceptors;
28
+ private pendingRequests;
29
+ initialize(): void;
30
+ dispose(): void;
31
+ /**
32
+ * 设置基础 URL
33
+ */
34
+ setBaseURL(url: string): void;
35
+ /**
36
+ * 设置默认请求头
37
+ */
38
+ setDefaultHeader(key: string, value: string): void;
39
+ /**
40
+ * 添加请求拦截器
41
+ */
42
+ addRequestInterceptor(interceptor: (options: HttpRequestOptions) => HttpRequestOptions | Promise<HttpRequestOptions>): void;
43
+ /**
44
+ * 添加响应拦截器
45
+ */
46
+ addResponseInterceptor(interceptor: (response: any) => any | Promise<any>): void;
47
+ /**
48
+ * 添加错误拦截器
49
+ */
50
+ addErrorInterceptor(interceptor: (error: Error) => void | Promise<void>): void;
51
+ /**
52
+ * 取消指定请求
53
+ */
54
+ cancelRequest(requestKey: string): void;
55
+ /**
56
+ * 取消所有请求
57
+ */
58
+ cancelAllRequests(): void;
59
+ get<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>): Promise<T>;
60
+ post<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T>;
61
+ put<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T>;
62
+ delete<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>): Promise<T>;
63
+ request<T = any>(options: HttpRequestOptions): Promise<T>;
64
+ /**
65
+ * 构建带参数的 URL
66
+ */
67
+ private _buildUrlWithParams;
68
+ /**
69
+ * 标准化请求头
70
+ */
71
+ private _normalizeHeaders;
72
+ }
@@ -0,0 +1,278 @@
1
+ import { __awaiter } from '../_virtual/_tslib.js';
2
+
3
+ /**
4
+ * HTTP 管理器实现类
5
+ *
6
+ * 发起请求
7
+
8
+ 【请求拦截器】修改请求参数(如添加 token、签名等)
9
+
10
+ 发送到服务器
11
+
12
+ 收到响应
13
+
14
+ 【响应拦截器】处理响应数据(如提取数据、转换格式等)
15
+
16
+ 返回结果 ✅
17
+
18
+ 如果出错 ❌
19
+
20
+ 【错误拦截器】统一处理错误(如显示提示、记录日志等)
21
+ */
22
+ class HttpManager {
23
+ constructor() {
24
+ this.baseURL = ''; // 基础 URL
25
+ this.defaultTimeout = 10000; // 默认超时时间 10 秒
26
+ this.defaultHeaders = {
27
+ 'Content-Type': 'application/json',
28
+ };
29
+ // 请求拦截器
30
+ // 在请求发送之前修改请求参数,可以:
31
+ // 添加认证信息(Token)
32
+ // 添加通用参数(时间戳、版本号、签名等)
33
+ // 修改请求头
34
+ // 记录请求日志
35
+ // 加密请求数据
36
+ // 等等
37
+ this.requestInterceptors = [];
38
+ // 响应拦截器
39
+ // 在收到响应后处理数据,可以:
40
+ // 提取实际数据(去除包装层)
41
+ // 统一处理错误码
42
+ // 转换数据格式
43
+ // 缓存响应结果
44
+ // 等等
45
+ this.responseInterceptors = [];
46
+ // 错误拦截器
47
+ // 在请求出错时处理错误,可以:
48
+ // 显示错误提示
49
+ // 记录错误日志
50
+ // 重试请求
51
+ // 等等
52
+ this.errorInterceptors = [];
53
+ // 正在进行的请求管理
54
+ this.pendingRequests = new Map();
55
+ }
56
+ initialize() {
57
+ // 初始化逻辑(如果需要)
58
+ }
59
+ dispose() {
60
+ // 取消所有正在进行的请求
61
+ this.cancelAllRequests();
62
+ // 清空拦截器
63
+ this.requestInterceptors = [];
64
+ this.responseInterceptors = [];
65
+ this.errorInterceptors = [];
66
+ }
67
+ /**
68
+ * 设置基础 URL
69
+ */
70
+ setBaseURL(url) {
71
+ this.baseURL = url;
72
+ }
73
+ /**
74
+ * 设置默认请求头
75
+ */
76
+ setDefaultHeader(key, value) {
77
+ this.defaultHeaders[key] = value;
78
+ }
79
+ /**
80
+ * 添加请求拦截器
81
+ */
82
+ addRequestInterceptor(interceptor) {
83
+ this.requestInterceptors.push(interceptor);
84
+ }
85
+ /**
86
+ * 添加响应拦截器
87
+ */
88
+ addResponseInterceptor(interceptor) {
89
+ this.responseInterceptors.push(interceptor);
90
+ }
91
+ /**
92
+ * 添加错误拦截器
93
+ */
94
+ addErrorInterceptor(interceptor) {
95
+ this.errorInterceptors.push(interceptor);
96
+ }
97
+ /**
98
+ * 取消指定请求
99
+ */
100
+ cancelRequest(requestKey) {
101
+ const controller = this.pendingRequests.get(requestKey);
102
+ if (controller) {
103
+ controller.abort();
104
+ this.pendingRequests.delete(requestKey);
105
+ }
106
+ }
107
+ /**
108
+ * 取消所有请求
109
+ */
110
+ cancelAllRequests() {
111
+ this.pendingRequests.forEach(controller => controller.abort());
112
+ this.pendingRequests.clear();
113
+ }
114
+ get(url, params, headers) {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ const fullUrl = this._buildUrlWithParams(url, params);
117
+ return this.request({
118
+ url: fullUrl,
119
+ method: 'GET',
120
+ headers: Object.assign(Object.assign({}, this.defaultHeaders), headers)
121
+ });
122
+ });
123
+ }
124
+ post(url, data, headers) {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ return this.request({
127
+ url,
128
+ method: 'POST',
129
+ headers: Object.assign(Object.assign({}, this.defaultHeaders), headers),
130
+ data
131
+ });
132
+ });
133
+ }
134
+ put(url, data, headers) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ return this.request({
137
+ url,
138
+ method: 'PUT',
139
+ headers: Object.assign(Object.assign({}, this.defaultHeaders), headers),
140
+ data
141
+ });
142
+ });
143
+ }
144
+ delete(url, params, headers) {
145
+ return __awaiter(this, void 0, void 0, function* () {
146
+ const fullUrl = this._buildUrlWithParams(url, params);
147
+ return this.request({
148
+ url: fullUrl,
149
+ method: 'DELETE',
150
+ headers: Object.assign(Object.assign({}, this.defaultHeaders), headers)
151
+ });
152
+ });
153
+ }
154
+ request(options) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ let { url, method, headers = {}, params, data, timeout = this.defaultTimeout } = options;
157
+ // 应用请求拦截器
158
+ let requestOptions = Object.assign({}, options);
159
+ for (const interceptor of this.requestInterceptors) {
160
+ requestOptions = yield interceptor(requestOptions);
161
+ }
162
+ // 更新变量(拦截器可能修改了请求参数)
163
+ ({ url, method, headers = {}, params, data, timeout = this.defaultTimeout } = requestOptions);
164
+ // 处理 baseURL
165
+ let fullUrl = url;
166
+ if (this.baseURL && !url.startsWith('http')) {
167
+ fullUrl = this.baseURL + (url.startsWith('/') ? url : '/' + url);
168
+ }
169
+ // 构建完整的 URL(如果还有未处理的 params)
170
+ if (params && (method === 'GET' || method === 'DELETE')) {
171
+ fullUrl = this._buildUrlWithParams(fullUrl, params);
172
+ }
173
+ // 生成请求唯一标识(用于取消请求)
174
+ const requestKey = `${method}:${fullUrl}`;
175
+ // 构建 fetch 选项
176
+ const fetchOptions = {
177
+ method,
178
+ headers: this._normalizeHeaders(Object.assign(Object.assign({}, this.defaultHeaders), headers)),
179
+ };
180
+ // 添加请求体(仅适用于允许请求体的方法)
181
+ if (data && ['POST', 'PUT', 'PATCH'].includes(method)) {
182
+ if (data instanceof FormData) {
183
+ // FormData 不需要设置 Content-Type,浏览器会自动设置
184
+ fetchOptions.body = data;
185
+ // 移除 Content-Type,让浏览器自动设置正确的 boundary
186
+ delete fetchOptions.headers['content-type'];
187
+ }
188
+ else if (typeof data === 'object') {
189
+ fetchOptions.body = JSON.stringify(data);
190
+ }
191
+ else {
192
+ fetchOptions.body = data.toString();
193
+ }
194
+ }
195
+ try {
196
+ // 创建 AbortController(每个请求都需要独立的 controller)
197
+ const controller = new AbortController();
198
+ this.pendingRequests.set(requestKey, controller);
199
+ const timeoutId = setTimeout(() => {
200
+ controller.abort();
201
+ this.pendingRequests.delete(requestKey);
202
+ }, timeout);
203
+ fetchOptions.signal = controller.signal;
204
+ // 发起请求
205
+ const response = yield fetch(fullUrl, fetchOptions);
206
+ clearTimeout(timeoutId);
207
+ this.pendingRequests.delete(requestKey);
208
+ // 检查响应状态
209
+ if (!response.ok) {
210
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
211
+ }
212
+ // 尝试解析 JSON,如果失败则返回文本
213
+ let result;
214
+ try {
215
+ result = yield response.json();
216
+ }
217
+ catch (_a) {
218
+ result = yield response.text();
219
+ }
220
+ // 应用响应拦截器
221
+ for (const interceptor of this.responseInterceptors) {
222
+ result = yield interceptor(result);
223
+ }
224
+ return result;
225
+ }
226
+ catch (error) {
227
+ this.pendingRequests.delete(requestKey);
228
+ let finalError;
229
+ if (error instanceof Error) {
230
+ if (error.name === 'AbortError') {
231
+ finalError = new Error(`Request timeout after ${timeout}ms`);
232
+ }
233
+ else {
234
+ finalError = new Error(`Network error: ${error.message}`);
235
+ }
236
+ }
237
+ else {
238
+ finalError = new Error('Unknown network error');
239
+ }
240
+ // 应用错误拦截器
241
+ for (const interceptor of this.errorInterceptors) {
242
+ yield interceptor(finalError);
243
+ }
244
+ throw finalError;
245
+ }
246
+ });
247
+ }
248
+ /**
249
+ * 构建带参数的 URL
250
+ */
251
+ _buildUrlWithParams(url, params) {
252
+ if (!params)
253
+ return url;
254
+ const urlObj = new URL(url, typeof window !== 'undefined' ? window.location.href : 'http://localhost');
255
+ Object.keys(params).forEach(key => {
256
+ if (params[key] !== undefined && params[key] !== null) {
257
+ urlObj.searchParams.append(key, String(params[key]));
258
+ }
259
+ });
260
+ // 如果原始 URL 没有协议和主机,只返回路径和查询参数
261
+ if (!url.startsWith('http')) {
262
+ return urlObj.pathname + urlObj.search;
263
+ }
264
+ return urlObj.toString();
265
+ }
266
+ /**
267
+ * 标准化请求头
268
+ */
269
+ _normalizeHeaders(headers) {
270
+ const normalized = {};
271
+ Object.keys(headers).forEach(key => {
272
+ normalized[key.toLowerCase()] = headers[key];
273
+ });
274
+ return normalized;
275
+ }
276
+ }
277
+
278
+ export { HttpManager };
@@ -5,6 +5,6 @@ export declare class ResLoader implements ICocosResManager {
5
5
  loadPrefab(path: string, nameOrUrl?: string): Promise<Prefab>;
6
6
  loadSpriteFrame(ref: Sprite, path: string, nameOrUrl?: string): Promise<SpriteFrame>;
7
7
  loadSpine(ref: sp.Skeleton, path: string, nameOrUrl?: string): Promise<sp.SkeletonData>;
8
- release(asset: Asset): void;
9
- release(path: string, type?: AssetType<Asset>, nameOrUrl?: string): void;
8
+ release(asset: Asset, force?: boolean): void;
9
+ release(path: string, type?: AssetType<Asset>, nameOrUrl?: string, force?: boolean): void;
10
10
  }
@@ -4,8 +4,6 @@ import { assetManager, Prefab, Asset, SpriteFrame, sp } from 'cc';
4
4
  const DefaultBundle = "resources";
5
5
  class ResLoader {
6
6
  loadAsset(path, type, nameOrUrl = DefaultBundle) {
7
- //TODO: bundle.release和assetManager.releaseAsset的区别?
8
- //TODO: prefab是否需要addRef,prefab被克隆出来的节点被销毁时,对应的prefab如何处理?
9
7
  if (assetManager.assets.has(path)) {
10
8
  const asset = assetManager.assets.get(path);
11
9
  asset.addRef();
@@ -31,6 +29,7 @@ class ResLoader {
31
29
  });
32
30
  }
33
31
  loadPrefab(path, nameOrUrl = DefaultBundle) {
32
+ //refCount 记录的是持有者数量,不是资源份数,所以prefab也需要addRef
34
33
  return this.loadAsset(path, Prefab, nameOrUrl);
35
34
  }
36
35
  loadSpriteFrame(ref, path, nameOrUrl = DefaultBundle) {
@@ -41,7 +40,7 @@ class ResLoader {
41
40
  return Promise.resolve(sf);
42
41
  }
43
42
  else {
44
- // 没有引用到的资源,释放掉
43
+ // 没有引用对象,释放掉资源
45
44
  this.release(path, SpriteFrame, nameOrUrl);
46
45
  return Promise.reject(new Error("Sprite is not valid"));
47
46
  }
@@ -55,26 +54,37 @@ class ResLoader {
55
54
  return Promise.resolve(spine);
56
55
  }
57
56
  else {
58
- // 没有引用到的资源,释放掉
57
+ // 没有引用对象,释放掉资源
59
58
  this.release(path, sp.SkeletonData, nameOrUrl);
60
59
  return Promise.reject(new Error("Spine is not valid"));
61
60
  }
62
61
  });
63
62
  }
64
- release(pathOrAsset, type, nameOrUrl = DefaultBundle) {
63
+ release(pathOrAsset, typeOrForce, nameOrUrl = DefaultBundle, forceParam = false) {
64
+ var _a;
65
+ let asset;
66
+ let force = false;
65
67
  if (typeof pathOrAsset === "string") {
66
- const bundle = assetManager.getBundle(nameOrUrl);
67
- const asset = bundle === null || bundle === void 0 ? void 0 : bundle.get(pathOrAsset, type);
68
- asset === null || asset === void 0 ? void 0 : asset.decRef();
69
- if ((asset === null || asset === void 0 ? void 0 : asset.refCount) === 0) {
70
- bundle === null || bundle === void 0 ? void 0 : bundle.release(pathOrAsset, type);
68
+ if (!typeOrForce || typeof typeOrForce === "boolean") {
69
+ throw new Error('typeOrForce is undefined, or typeOrForce is boolean! typeOrForce must be AssetType<Asset>!');
71
70
  }
71
+ force = forceParam;
72
+ asset = (_a = assetManager.getBundle(nameOrUrl)) === null || _a === void 0 ? void 0 : _a.get(pathOrAsset, typeOrForce);
72
73
  }
73
74
  else if (pathOrAsset instanceof Asset) {
74
- pathOrAsset.decRef();
75
- if (pathOrAsset.refCount === 0) {
76
- assetManager.releaseAsset(pathOrAsset);
77
- }
75
+ asset = pathOrAsset;
76
+ force = typeof typeOrForce === 'boolean' ? typeOrForce : forceParam;
77
+ }
78
+ if (!asset) {
79
+ console.warn(`${pathOrAsset} Release asset failed, asset is null or undefined`);
80
+ return;
81
+ }
82
+ if (force) {
83
+ assetManager.releaseAsset(asset);
84
+ }
85
+ else {
86
+ // decRef原型:decRef (autoRelease = true),所以引用数量为 0,则将自动释放该资源。
87
+ asset.decRef();
78
88
  }
79
89
  }
80
90
  }
@@ -1,5 +1,5 @@
1
1
  import { __awaiter } from '../_virtual/_tslib.js';
2
- import { director, Node, Sprite, Widget, Input, input, 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 'reflect-metadata';
5
5
 
@@ -144,6 +144,7 @@ class UIManager extends CcocosUIManager {
144
144
  target = instantiate(prefab);
145
145
  this._cache.set(viewType.name, target);
146
146
  }
147
+ target.active = true;
147
148
  return target.getComponent(viewType);
148
149
  });
149
150
  }
@@ -163,10 +164,21 @@ class UIManager extends CcocosUIManager {
163
164
  }
164
165
  viewortype.onExit();
165
166
  viewortype.node.removeFromParent();
167
+ viewortype.node.active = false;
166
168
  if (destroy) {
167
169
  let cacheKey = viewortype.constructor.name;
168
170
  (_a = this._cache.get(cacheKey)) === null || _a === void 0 ? void 0 : _a.destroy();
169
171
  this._cache.delete(cacheKey);
172
+ // 销毁被克隆出的UI后Node后,尝试释放 Prefab 资源
173
+ try {
174
+ const viewType = viewortype.constructor;
175
+ const prefabPath = this._getPrefabPath(viewType);
176
+ const ResMgr = ServiceLocator.getService('ResLoader');
177
+ ResMgr.release(prefabPath, Prefab);
178
+ }
179
+ catch (error) {
180
+ console.error(`Failed to release prefab for ${cacheKey}:`, error);
181
+ }
170
182
  }
171
183
  }
172
184
  internalGetTopView() {
@@ -4,3 +4,4 @@ export * from './CocosCore';
4
4
  export * from './ResLoader';
5
5
  export * from './UIManager';
6
6
  export * from './UIRoot';
7
+ export * from './HttpManager';
@@ -4,3 +4,4 @@ export { CocosCore } from './CocosCore.js';
4
4
  export { ResLoader } from './ResLoader.js';
5
5
  export { UIManager } from './UIManager.js';
6
6
  export { UIRoot } from './UIRoot.js';
7
+ export { HttpManager } from './HttpManager.js';
Binary file
@@ -1,3 +1,4 @@
1
+ export * from './ArrayExt';
1
2
  export * from './ArrayUtil';
2
3
  export * from './CameraUtil';
3
4
  export * from './ImageUtil';
@@ -1,3 +1,4 @@
1
+ import './ArrayExt.js';
1
2
  export { ArrayUtil } from './ArrayUtil.js';
2
3
  export { CameraUtil } from './CameraUtil.js';
3
4
  export { ImageUtil } from './ImageUtil.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dzkcc-mflow",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
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",