@reskin/core 0.0.21 → 0.1.0

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.
Files changed (45) hide show
  1. package/bundles/reskin-core-directives.umd.js +303 -163
  2. package/bundles/reskin-core-directives.umd.js.map +1 -1
  3. package/bundles/reskin-core-guards.umd.js +119 -32
  4. package/bundles/reskin-core-guards.umd.js.map +1 -1
  5. package/bundles/reskin-core-interceptors.umd.js +310 -92
  6. package/bundles/reskin-core-interceptors.umd.js.map +1 -1
  7. package/bundles/reskin-core-utils.umd.js +220 -77
  8. package/bundles/reskin-core-utils.umd.js.map +1 -1
  9. package/directives/auth.directive.d.ts +56 -9
  10. package/directives/load.styles.directive.d.ts +45 -5
  11. package/directives/string.template.outlet.directive.d.ts +68 -11
  12. package/esm2015/directives/auth.directive.js +71 -30
  13. package/esm2015/directives/load.styles.directive.js +84 -15
  14. package/esm2015/directives/string.template.outlet.directive.js +118 -60
  15. package/esm2015/guards/auth.guard.js +117 -30
  16. package/esm2015/interceptors/blob.interceptor.js +67 -28
  17. package/esm2015/interceptors/cache.interceptor.js +46 -14
  18. package/esm2015/interceptors/error.interceptor.js +104 -12
  19. package/esm2015/interceptors/public-api.js +2 -1
  20. package/esm2015/interceptors/token.interceptor.js +86 -42
  21. package/esm2015/interceptors/types.js +5 -0
  22. package/esm2015/utils/array.js +42 -22
  23. package/esm2015/utils/dom.js +29 -11
  24. package/esm2015/utils/form.js +44 -13
  25. package/esm2015/utils/store.js +101 -26
  26. package/fesm2015/reskin-core-directives.js +269 -103
  27. package/fesm2015/reskin-core-directives.js.map +1 -1
  28. package/fesm2015/reskin-core-guards.js +116 -29
  29. package/fesm2015/reskin-core-guards.js.map +1 -1
  30. package/fesm2015/reskin-core-interceptors.js +302 -91
  31. package/fesm2015/reskin-core-interceptors.js.map +1 -1
  32. package/fesm2015/reskin-core-utils.js +212 -68
  33. package/fesm2015/reskin-core-utils.js.map +1 -1
  34. package/guards/auth.guard.d.ts +85 -5
  35. package/interceptors/blob.interceptor.d.ts +30 -3
  36. package/interceptors/cache.interceptor.d.ts +28 -4
  37. package/interceptors/error.interceptor.d.ts +43 -2
  38. package/interceptors/public-api.d.ts +1 -0
  39. package/interceptors/token.interceptor.d.ts +41 -12
  40. package/interceptors/types.d.ts +68 -0
  41. package/package.json +1 -1
  42. package/utils/array.d.ts +8 -1
  43. package/utils/dom.d.ts +32 -5
  44. package/utils/form.d.ts +37 -2
  45. package/utils/store.d.ts +56 -15
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs')) :
3
- typeof define === 'function' && define.amd ? define('@reskin/core/utils', ['exports', 'rxjs'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.reskin = global.reskin || {}, global.reskin.core = global.reskin.core || {}, global.reskin.core.utils = {}), global.rxjs));
5
- })(this, (function (exports, rxjs) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('rxjs/operators')) :
3
+ typeof define === 'function' && define.amd ? define('@reskin/core/utils', ['exports', 'rxjs', 'rxjs/operators'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.reskin = global.reskin || {}, global.reskin.core = global.reskin.core || {}, global.reskin.core.utils = {}), global.rxjs, global.rxjs.operators));
5
+ })(this, (function (exports, rxjs, operators) { 'use strict';
6
6
 
7
7
  /**
8
8
  * 默认前缀
@@ -12,160 +12,303 @@
12
12
  * 设置存储前缀
13
13
  *
14
14
  * 该函数用于更改全局存储前缀,影响后续所有存储操作的键名前缀。
15
- * 存储前缀的更改可能会影响已存储的数据被正确识别和访问。
15
+ * 注意:更改前缀后,之前使用旧前缀存储的数据将无法访问。
16
16
  *
17
17
  * @param newPrefix 新的存储前缀字符串
18
+ *
19
+ * @example
20
+ * setStorePrefix('MY_APP_');
18
21
  */
19
22
  function setStorePrefix(newPrefix) {
20
23
  prefix = newPrefix;
21
24
  }
22
25
  /**
23
- * 提供了一个用于缓存值的类,值可以被存储在浏览器的localStorage或sessionStorage中。
24
- * 如果提供了过期时间,存储的值会在过期后自动移除。
26
+ * 提供了一个用于缓存值的类,值可以被存储在浏览器的 localStorage sessionStorage 中。
27
+ * 支持过期时间设置,过期后数据会自动移除。
28
+ *
29
+ * @template T 存储数据的类型
30
+ *
31
+ * @example
32
+ * // 基础用法
33
+ * const userStore = new Store<User>('user', { name: '', age: 0 });
34
+ * userStore.set({ name: '张三', age: 25 });
35
+ * console.log(userStore.get()); // { name: '张三', age: 25 }
36
+ *
37
+ * @example
38
+ * // 使用 localStorage 并设置过期时间
39
+ * const tokenStore = new Store<string>('token', '', {
40
+ * storageEngine: localStorage,
41
+ * expires: 3600000 // 1小时后过期
42
+ * });
25
43
  */
26
44
  var Store = /** @class */ (function () {
27
45
  /**
28
- * 初始化Store类的实例。
29
- * @param key 缓存值的键名。
30
- * @param defaultValue 初始值,默认值会被存储并返回直到设置新的值。
31
- * @param options 配置选项,包括存储引擎和过期时间。
46
+ * 初始化 Store 类的实例
47
+ * @param key 缓存值的键名
48
+ * @param defaultValue 初始值,默认值会被存储并返回直到设置新的值
49
+ * @param options 配置选项,包括存储引擎和过期时间
32
50
  */
33
51
  function Store(key, defaultValue, options) {
52
+ this.key = key;
34
53
  this.value = defaultValue;
35
- // 进行初始设置
36
- this.storageKey = function () { return ("@" + prefix + "_" + key).toLocaleUpperCase(); };
37
- var _a = options !== null && options !== void 0 ? options : {}, _b = _a.storageEngine, storageEngine = _b === void 0 ? sessionStorage : _b, expires = _a.expires;
54
+ var _b = options !== null && options !== void 0 ? options : {}, _c = _b.storageEngine, storageEngine = _c === void 0 ? sessionStorage : _c, expires = _b.expires;
38
55
  this.storage = storageEngine;
39
56
  this.expires = expires;
40
- var storedValue = this.storage.getItem(this.storageKey());
41
- if (storedValue) {
42
- var _c = JSON.parse(storedValue), data = _c.data, expiry = _c.expiry;
43
- // 判断是否过期,过期后删除key
57
+ // 尝试从存储中读取数据
58
+ this.loadFromStorage();
59
+ }
60
+ /**
61
+ * 生成存储键名
62
+ */
63
+ Store.prototype.getStorageKey = function () {
64
+ return ("@" + prefix + "_" + this.key).toLocaleUpperCase();
65
+ };
66
+ /**
67
+ * 从存储中加载数据
68
+ */
69
+ Store.prototype.loadFromStorage = function () {
70
+ try {
71
+ var storedValue = this.storage.getItem(this.getStorageKey());
72
+ if (!storedValue) {
73
+ return;
74
+ }
75
+ var _b = JSON.parse(storedValue), data = _b.data, expiry = _b.expiry;
76
+ // 检查是否过期
44
77
  if (!expiry || expiry > Date.now()) {
45
78
  this.value = data;
46
79
  }
47
80
  else {
48
- this.storage.removeItem(this.storageKey());
81
+ // 过期则删除
82
+ this.clear();
49
83
  }
50
84
  }
51
- }
85
+ catch (error) {
86
+ console.error("\u4ECE\u5B58\u50A8\u4E2D\u8BFB\u53D6\u6570\u636E\u5931\u8D25 (key: " + this.getStorageKey() + "):", error);
87
+ // 数据损坏时清除
88
+ this.clear();
89
+ }
90
+ };
52
91
  /**
53
- * 获取当前缓存的值。
54
- * @returns 缓存的值。
92
+ * 获取当前缓存的值
93
+ * @returns 缓存的值
55
94
  */
56
95
  Store.prototype.get = function () {
57
96
  return this.value;
58
97
  };
59
98
  /**
60
- * 设置新的缓存值。
61
- * @param newValue 新的值。
99
+ * 设置新的缓存值
100
+ * @param newValue 新的值
62
101
  */
63
102
  Store.prototype.set = function (newValue) {
64
103
  this.value = newValue;
65
- // 设置值时进行Storage设置
66
- var value = JSON.stringify({
67
- data: newValue,
68
- expiry: this.expires ? Date.now() + this.expires : null,
69
- });
70
- this.storage.setItem(this.storageKey(), value);
104
+ try {
105
+ var storageData = {
106
+ data: newValue,
107
+ expiry: this.expires ? Date.now() + this.expires : null,
108
+ };
109
+ this.storage.setItem(this.getStorageKey(), JSON.stringify(storageData));
110
+ }
111
+ catch (error) {
112
+ console.error("\u5B58\u50A8\u6570\u636E\u5931\u8D25 (key: " + this.getStorageKey() + "):", error);
113
+ }
71
114
  };
72
115
  /**
73
- * 使用提供的函数更新缓存的值。
74
- * @param updateFn 一个接受当前值并返回新值的函数。
116
+ * 使用提供的函数更新缓存的值
117
+ * @param updateFn 一个接受当前值并返回新值的函数
118
+ *
119
+ * @example
120
+ * const countStore = new Store<number>('count', 0);
121
+ * countStore.update(count => count + 1);
75
122
  */
76
123
  Store.prototype.update = function (updateFn) {
77
124
  if (typeof updateFn === 'function') {
78
125
  this.set(updateFn(this.value));
79
126
  }
80
127
  };
128
+ /**
129
+ * 清除缓存数据
130
+ */
131
+ Store.prototype.clear = function () {
132
+ try {
133
+ this.storage.removeItem(this.getStorageKey());
134
+ }
135
+ catch (error) {
136
+ console.error("\u6E05\u9664\u5B58\u50A8\u6570\u636E\u5931\u8D25 (key: " + this.getStorageKey() + "):", error);
137
+ }
138
+ };
139
+ /**
140
+ * 检查缓存是否存在且未过期
141
+ * @returns 如果缓存存在且未过期返回 true,否则返回 false
142
+ */
143
+ Store.prototype.has = function () {
144
+ try {
145
+ var storedValue = this.storage.getItem(this.getStorageKey());
146
+ if (!storedValue) {
147
+ return false;
148
+ }
149
+ var expiry = JSON.parse(storedValue).expiry;
150
+ return !expiry || expiry > Date.now();
151
+ }
152
+ catch (_a) {
153
+ return false;
154
+ }
155
+ };
81
156
  return Store;
82
157
  }());
83
158
 
84
159
  /**
85
160
  * 深克隆一个对象或数组
86
161
  *
87
- * 该函数通过递归方式深克隆输入的对象或数组,确保与原对象或数组隔离,不会相互影响
162
+ * 该函数通过递归方式深克隆输入的对象或数组,确保与原对象或数组隔离,不会相互影响。
163
+ * 支持克隆:普通对象、数组、Date、Map、Set、RegExp 等类型。
88
164
  *
89
165
  * @param source 需要克隆的源对象或数组
90
166
  * @returns 返回克隆后的对象或数组
167
+ *
168
+ * @example
169
+ * const obj = { a: 1, b: { c: 2 } };
170
+ * const cloned = deepClone(obj);
171
+ * cloned.b.c = 3;
172
+ * console.log(obj.b.c); // 输出: 2
91
173
  */
92
174
  function deepClone(source) {
93
- // 如果源对象为null或不是对象类型,则直接返回源对象
175
+ // 处理 null 和基本类型
94
176
  if (source === null || typeof source !== 'object') {
95
177
  return source;
96
178
  }
97
- // 如果源对象是数组,则创建一个新数组并递归克隆每个元素
179
+ // 处理 Date 对象
180
+ if (source instanceof Date) {
181
+ return new Date(source.getTime());
182
+ }
183
+ // 处理 RegExp 对象
184
+ if (source instanceof RegExp) {
185
+ return new RegExp(source.source, source.flags);
186
+ }
187
+ // 处理 Map 对象
188
+ if (source instanceof Map) {
189
+ var clonedMap_1 = new Map();
190
+ source.forEach(function (value, key) {
191
+ clonedMap_1.set(deepClone(key), deepClone(value));
192
+ });
193
+ return clonedMap_1;
194
+ }
195
+ // 处理 Set 对象
196
+ if (source instanceof Set) {
197
+ var clonedSet_1 = new Set();
198
+ source.forEach(function (value) {
199
+ clonedSet_1.add(deepClone(value));
200
+ });
201
+ return clonedSet_1;
202
+ }
203
+ // 处理数组
98
204
  if (Array.isArray(source)) {
99
- var clonedArray = [];
100
- for (var i = 0; i < source.length; i++) {
101
- clonedArray[i] = deepClone(source[i]);
102
- }
103
- // 忽略类型检查器的警告,确保能够返回泛型T类型的数组
104
- // @ts-ignore
105
- return clonedArray;
205
+ return source.map(function (item) { return deepClone(item); });
106
206
  }
107
- // 如果源对象是普通对象,则创建一个新的空对象并递归克隆每个属性
108
- if (typeof source === 'object') {
109
- var clonedObject = {};
110
- for (var key in source) {
111
- // 确保只处理对象自身的属性,避免原型链上的属性被克隆
112
- // @ts-ignore
113
- if (source.hasOwnProperty(key)) {
114
- clonedObject[key] = deepClone(source[key]);
115
- }
116
- }
117
- // 忽略类型检查器的警告,确保能够返回泛型T类型的对象
118
- return clonedObject;
207
+ // 处理普通对象
208
+ if (Object.prototype.toString.call(source) === '[object Object]') {
209
+ var clonedObject_1 = {};
210
+ Object.keys(source).forEach(function (key) {
211
+ clonedObject_1[key] = deepClone(source[key]);
212
+ });
213
+ return clonedObject_1;
119
214
  }
120
- // 如果源对象是其他非对象类型,则直接返回源对象
215
+ // 其他类型直接返回(如函数、Symbol 等)
121
216
  return source;
122
217
  }
123
218
 
124
219
  /**
125
220
  * 监听元素的大小变化
126
221
  *
127
- * 此函数创建了一个Observable,用于观察给定HTMLElement元素的大小变化它使用了ResizeObserver API来实现
128
- * 当元素的大小发生变化时,会发出一个包含变化记录的数组
222
+ * 此函数创建了一个 Observable,用于观察给定 HTMLElement 元素的大小变化。
223
+ * 它使用了 ResizeObserver API 来实现,当元素的大小发生变化时,会发出一个包含变化记录的数组。
224
+ *
225
+ * @param element 需要观察大小变化的 HTMLElement 元素
226
+ * @param options 配置选项
227
+ * @returns 返回一个 Observable,其中包含 ResizeObserverEntry 的数组,用于记录每次大小变化的信息
129
228
  *
130
- * @param element 需要观察大小变化的HTMLElement元素
131
- * @returns 返回一个Observable,其中包含ResizeObserverEntry的数组,用于记录每次大小变化的信息
229
+ * @example
230
+ * // 基础用法
231
+ * const element = document.querySelector('.container');
232
+ * observeResize(element).subscribe(entries => {
233
+ * const { width, height } = entries[0].contentRect;
234
+ * console.log(`元素尺寸: ${width}x${height}`);
235
+ * });
236
+ *
237
+ * @example
238
+ * // 使用防抖优化性能
239
+ * observeResize(element, { debounce: 300 }).subscribe(entries => {
240
+ * // 300ms 内的多次变化只会触发一次
241
+ * console.log('元素大小已稳定');
242
+ * });
132
243
  */
133
- function observeResize(element) {
134
- return new rxjs.Observable(function (observer) {
135
- // 创建ResizeObserver实例,当观察的目标元素大小发生变化时,会调用回调函数
244
+ function observeResize(element, options) {
245
+ var debounce = (options || {}).debounce;
246
+ var resize$ = new rxjs.Observable(function (observer) {
247
+ // 创建 ResizeObserver 实例,当观察的目标元素大小发生变化时,会调用回调函数
136
248
  var resizeObserver = new ResizeObserver(function (entries) {
137
249
  observer.next(entries);
138
250
  });
139
251
  // 开始观察指定的元素
140
252
  resizeObserver.observe(element);
141
- // 返回一个函数,用于在停止观察时调用,以清理资源
253
+ // 返回清理函数,用于在取消订阅时调用
142
254
  return function () {
143
- // 停止观察指定的元素
144
255
  resizeObserver.unobserve(element);
145
- // 断开与所有目标的观察连接
146
256
  resizeObserver.disconnect();
147
257
  };
148
258
  });
259
+ // 如果设置了防抖,则应用防抖操作符
260
+ return debounce ? resize$.pipe(operators.debounceTime(debounce)) : resize$;
149
261
  }
150
262
 
151
263
  /**
152
264
  * 表单验证
153
- * @param form
265
+ *
266
+ * 遍历表单所有控件,标记为脏状态并触发验证。
267
+ * 如果表单验证通过,返回表单的原始值;否则返回 false。
268
+ *
269
+ * @param form Angular 表单组对象
270
+ * @param options 验证配置选项
271
+ * @returns 验证通过返回表单值,验证失败返回 false
272
+ *
273
+ * @example
274
+ * const result = formVerify(this.form);
275
+ * if (result) {
276
+ * console.log('表单数据:', result);
277
+ * }
278
+ *
279
+ * @example
280
+ * // 自定义错误处理
281
+ * formVerify(this.form, {
282
+ * logError: false,
283
+ * onError: (fields) => {
284
+ * this.message.error(`以下字段验证失败: ${fields.join(', ')}`);
285
+ * }
286
+ * });
154
287
  */
155
- function formVerify(form) {
156
- var errList = [];
157
- for (var key in form.controls) {
158
- form.controls[key].markAsDirty();
159
- form.controls[key].updateValueAndValidity();
160
- if (form.controls[key].status !== 'VALID' && form.controls[key].status !== 'DISABLED') {
161
- errList.push(key);
288
+ function formVerify(form, options) {
289
+ var _a = options || {}, _b = _a.logError, logError = _b === void 0 ? true : _b, onError = _a.onError;
290
+ var invalidFields = [];
291
+ // 遍历所有控件,标记为脏状态并更新验证状态
292
+ Object.keys(form.controls).forEach(function (key) {
293
+ var control = form.controls[key];
294
+ control.markAsDirty();
295
+ control.updateValueAndValidity();
296
+ // 收集验证失败的字段(排除禁用状态)
297
+ if (control.status !== 'VALID' && control.status !== 'DISABLED') {
298
+ invalidFields.push(key);
299
+ }
300
+ });
301
+ // 表单验证失败(排除全部禁用的情况)
302
+ if (form.status !== 'VALID' && form.status !== 'DISABLED') {
303
+ if (logError) {
304
+ console.error("\u8868\u5355\u9A8C\u8BC1\u5931\u8D25\uFF0C\u5B57\u6BB5: " + invalidFields.join(', '));
305
+ }
306
+ if (onError) {
307
+ onError(invalidFields);
162
308
  }
163
- }
164
- // 表单验证状态
165
- if (form.status !== 'VALID') {
166
- console.error("Form validation failed fields\uFF1A" + errList.join(','));
167
309
  return false;
168
310
  }
311
+ // 验证通过,返回表单原始值(包含禁用字段)
169
312
  return form.getRawValue();
170
313
  }
171
314
 
@@ -1 +1 @@
1
- {"version":3,"file":"reskin-core-utils.umd.js","sources":["../../../library/core/utils/store.ts","../../../library/core/utils/array.ts","../../../library/core/utils/dom.ts","../../../library/core/utils/form.ts","../../../library/core/utils/reskin-core-utils.ts"],"sourcesContent":["/**\r\n * 缓存配置信息\r\n */\r\nexport interface StoreOptions {\r\n /**\r\n * 缓存存储方式\r\n */\r\n storageEngine?: Storage;\r\n /**\r\n * 过期时间(毫秒)\r\n */\r\n expires?: number;\r\n}\r\n\r\n/**\r\n * 默认前缀\r\n */\r\nlet prefix: string = 'SK_';\r\n\r\n/**\r\n * 设置存储前缀\r\n *\r\n * 该函数用于更改全局存储前缀,影响后续所有存储操作的键名前缀。\r\n * 存储前缀的更改可能会影响已存储的数据被正确识别和访问。\r\n *\r\n * @param newPrefix 新的存储前缀字符串\r\n */\r\nexport function setStorePrefix(newPrefix: string) {\r\n prefix = newPrefix;\r\n}\r\n\r\n/**\r\n * 提供了一个用于缓存值的类,值可以被存储在浏览器的localStorage或sessionStorage中。\r\n * 如果提供了过期时间,存储的值会在过期后自动移除。\r\n */\r\nexport class Store<T> {\r\n private value: T;\r\n private readonly storage: Storage;\r\n private readonly expires?: number;\r\n private readonly storageKey: () => string;\r\n\r\n /**\r\n * 初始化Store类的实例。\r\n * @param key 缓存值的键名。\r\n * @param defaultValue 初始值,默认值会被存储并返回直到设置新的值。\r\n * @param options 配置选项,包括存储引擎和过期时间。\r\n */\r\n constructor(key: string, defaultValue: T, options?: StoreOptions) {\r\n this.value = defaultValue;\r\n\r\n // 进行初始设置\r\n this.storageKey = () => `@${prefix}_${key}`.toLocaleUpperCase();\r\n const { storageEngine = sessionStorage, expires } = options ?? {};\r\n this.storage = storageEngine;\r\n this.expires = expires;\r\n\r\n const storedValue = this.storage.getItem(this.storageKey());\r\n if (storedValue) {\r\n const { data, expiry } = JSON.parse(storedValue);\r\n // 判断是否过期,过期后删除key\r\n if (!expiry || expiry > Date.now()) {\r\n this.value = data;\r\n } else {\r\n this.storage.removeItem(this.storageKey());\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 获取当前缓存的值。\r\n * @returns 缓存的值。\r\n */\r\n get(): T {\r\n return this.value;\r\n }\r\n\r\n /**\r\n * 设置新的缓存值。\r\n * @param newValue 新的值。\r\n */\r\n set(newValue: T): void {\r\n this.value = newValue;\r\n\r\n // 设置值时进行Storage设置\r\n const value = JSON.stringify({\r\n data: newValue,\r\n expiry: this.expires ? Date.now() + this.expires : null,\r\n });\r\n this.storage.setItem(this.storageKey(), value);\r\n }\r\n\r\n /**\r\n * 使用提供的函数更新缓存的值。\r\n * @param updateFn 一个接受当前值并返回新值的函数。\r\n */\r\n update(updateFn: (currentValue: T) => T): void {\r\n if (typeof updateFn === 'function') {\r\n this.set(updateFn(this.value));\r\n }\r\n }\r\n}\r\n","/**\r\n * 深克隆一个对象或数组\r\n *\r\n * 该函数通过递归方式深克隆输入的对象或数组,确保与原对象或数组隔离,不会相互影响\r\n *\r\n * @param source 需要克隆的源对象或数组\r\n * @returns 返回克隆后的对象或数组\r\n */\r\nexport function deepClone<T>(source: T): T {\r\n // 如果源对象为null或不是对象类型,则直接返回源对象\r\n if (source === null || typeof source !== 'object') {\r\n return source;\r\n }\r\n\r\n // 如果源对象是数组,则创建一个新数组并递归克隆每个元素\r\n if (Array.isArray(source)) {\r\n const clonedArray = [];\r\n for (let i = 0; i < source.length; i++) {\r\n clonedArray[i] = deepClone(source[i]);\r\n }\r\n // 忽略类型检查器的警告,确保能够返回泛型T类型的数组\r\n // @ts-ignore\r\n return clonedArray as T;\r\n }\r\n\r\n // 如果源对象是普通对象,则创建一个新的空对象并递归克隆每个属性\r\n if (typeof source === 'object') {\r\n const clonedObject: Record<string, any> = {};\r\n for (const key in source) {\r\n // 确保只处理对象自身的属性,避免原型链上的属性被克隆\r\n // @ts-ignore\r\n if (source.hasOwnProperty(key)) {\r\n clonedObject[key] = deepClone(source[key]);\r\n }\r\n }\r\n // 忽略类型检查器的警告,确保能够返回泛型T类型的对象\r\n return clonedObject as T;\r\n }\r\n\r\n // 如果源对象是其他非对象类型,则直接返回源对象\r\n return source;\r\n}\r\n","import { Observable } from 'rxjs';\r\n\r\n/**\r\n * 监听元素的大小变化\r\n *\r\n * 此函数创建了一个Observable,用于观察给定HTMLElement元素的大小变化它使用了ResizeObserver API来实现\r\n * 当元素的大小发生变化时,会发出一个包含变化记录的数组\r\n *\r\n * @param element 需要观察大小变化的HTMLElement元素\r\n * @returns 返回一个Observable,其中包含ResizeObserverEntry的数组,用于记录每次大小变化的信息\r\n */\r\nexport function observeResize(element: HTMLElement): Observable<ResizeObserverEntry[]> {\r\n return new Observable((observer) => {\r\n // 创建ResizeObserver实例,当观察的目标元素大小发生变化时,会调用回调函数\r\n const resizeObserver = new ResizeObserver((entries) => {\r\n observer.next(entries);\r\n });\r\n // 开始观察指定的元素\r\n resizeObserver.observe(element);\r\n // 返回一个函数,用于在停止观察时调用,以清理资源\r\n return () => {\r\n // 停止观察指定的元素\r\n resizeObserver.unobserve(element);\r\n // 断开与所有目标的观察连接\r\n resizeObserver.disconnect();\r\n };\r\n });\r\n}\r\n","import { FormGroup } from '@angular/forms';\r\n\r\n/**\r\n * 表单验证\r\n * @param form\r\n */\r\nexport function formVerify<T = Record<string, any>>(form: FormGroup): false | T {\r\n const errList = [];\r\n for (const key in form.controls) {\r\n form.controls[key].markAsDirty();\r\n form.controls[key].updateValueAndValidity();\r\n if (form.controls[key].status !== 'VALID' && form.controls[key].status !== 'DISABLED') {\r\n errList.push(key);\r\n }\r\n }\r\n // 表单验证状态\r\n if (form.status !== 'VALID') {\r\n console.error(`Form validation failed fields:${errList.join(',')}`);\r\n return false;\r\n }\r\n return form.getRawValue();\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["Observable"],"mappings":"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"}
1
+ {"version":3,"file":"reskin-core-utils.umd.js","sources":["../../../library/core/utils/store.ts","../../../library/core/utils/array.ts","../../../library/core/utils/dom.ts","../../../library/core/utils/form.ts","../../../library/core/utils/reskin-core-utils.ts"],"sourcesContent":["/**\r\n * 缓存配置信息\r\n */\r\nexport interface StoreOptions {\r\n /**\r\n * 缓存存储方式\r\n * @default sessionStorage\r\n */\r\n storageEngine?: Storage;\r\n /**\r\n * 过期时间(毫秒)\r\n * @default undefined (永不过期)\r\n */\r\n expires?: number;\r\n}\r\n\r\n/**\r\n * 存储数据结构\r\n */\r\ninterface StorageData<T> {\r\n /**\r\n * 实际存储的数据\r\n */\r\n data: T;\r\n /**\r\n * 过期时间戳(毫秒)\r\n */\r\n expiry: number | null;\r\n}\r\n\r\n/**\r\n * 默认前缀\r\n */\r\nlet prefix: string = 'SK_';\r\n\r\n/**\r\n * 设置存储前缀\r\n *\r\n * 该函数用于更改全局存储前缀,影响后续所有存储操作的键名前缀。\r\n * 注意:更改前缀后,之前使用旧前缀存储的数据将无法访问。\r\n *\r\n * @param newPrefix 新的存储前缀字符串\r\n *\r\n * @example\r\n * setStorePrefix('MY_APP_');\r\n */\r\nexport function setStorePrefix(newPrefix: string): void {\r\n prefix = newPrefix;\r\n}\r\n\r\n/**\r\n * 提供了一个用于缓存值的类,值可以被存储在浏览器的 localStorage 或 sessionStorage 中。\r\n * 支持过期时间设置,过期后数据会自动移除。\r\n *\r\n * @template T 存储数据的类型\r\n *\r\n * @example\r\n * // 基础用法\r\n * const userStore = new Store<User>('user', { name: '', age: 0 });\r\n * userStore.set({ name: '张三', age: 25 });\r\n * console.log(userStore.get()); // { name: '张三', age: 25 }\r\n *\r\n * @example\r\n * // 使用 localStorage 并设置过期时间\r\n * const tokenStore = new Store<string>('token', '', {\r\n * storageEngine: localStorage,\r\n * expires: 3600000 // 1小时后过期\r\n * });\r\n */\r\nexport class Store<T> {\r\n private value: T;\r\n private readonly storage: Storage;\r\n private readonly expires?: number;\r\n private readonly key: string;\r\n\r\n /**\r\n * 初始化 Store 类的实例\r\n * @param key 缓存值的键名\r\n * @param defaultValue 初始值,默认值会被存储并返回直到设置新的值\r\n * @param options 配置选项,包括存储引擎和过期时间\r\n */\r\n constructor(key: string, defaultValue: T, options?: StoreOptions) {\r\n this.key = key;\r\n this.value = defaultValue;\r\n\r\n const { storageEngine = sessionStorage, expires } = options ?? {};\r\n this.storage = storageEngine;\r\n this.expires = expires;\r\n\r\n // 尝试从存储中读取数据\r\n this.loadFromStorage();\r\n }\r\n\r\n /**\r\n * 生成存储键名\r\n */\r\n private getStorageKey(): string {\r\n return `@${prefix}_${this.key}`.toLocaleUpperCase();\r\n }\r\n\r\n /**\r\n * 从存储中加载数据\r\n */\r\n private loadFromStorage(): void {\r\n try {\r\n const storedValue = this.storage.getItem(this.getStorageKey());\r\n if (!storedValue) {\r\n return;\r\n }\r\n\r\n const { data, expiry } = JSON.parse(storedValue) as StorageData<T>;\r\n\r\n // 检查是否过期\r\n if (!expiry || expiry > Date.now()) {\r\n this.value = data;\r\n } else {\r\n // 过期则删除\r\n this.clear();\r\n }\r\n } catch (error) {\r\n console.error(`从存储中读取数据失败 (key: ${this.getStorageKey()}):`, error);\r\n // 数据损坏时清除\r\n this.clear();\r\n }\r\n }\r\n\r\n /**\r\n * 获取当前缓存的值\r\n * @returns 缓存的值\r\n */\r\n get(): T {\r\n return this.value;\r\n }\r\n\r\n /**\r\n * 设置新的缓存值\r\n * @param newValue 新的值\r\n */\r\n set(newValue: T): void {\r\n this.value = newValue;\r\n\r\n try {\r\n const storageData: StorageData<T> = {\r\n data: newValue,\r\n expiry: this.expires ? Date.now() + this.expires : null,\r\n };\r\n this.storage.setItem(this.getStorageKey(), JSON.stringify(storageData));\r\n } catch (error) {\r\n console.error(`存储数据失败 (key: ${this.getStorageKey()}):`, error);\r\n }\r\n }\r\n\r\n /**\r\n * 使用提供的函数更新缓存的值\r\n * @param updateFn 一个接受当前值并返回新值的函数\r\n *\r\n * @example\r\n * const countStore = new Store<number>('count', 0);\r\n * countStore.update(count => count + 1);\r\n */\r\n update(updateFn: (currentValue: T) => T): void {\r\n if (typeof updateFn === 'function') {\r\n this.set(updateFn(this.value));\r\n }\r\n }\r\n\r\n /**\r\n * 清除缓存数据\r\n */\r\n clear(): void {\r\n try {\r\n this.storage.removeItem(this.getStorageKey());\r\n } catch (error) {\r\n console.error(`清除存储数据失败 (key: ${this.getStorageKey()}):`, error);\r\n }\r\n }\r\n\r\n /**\r\n * 检查缓存是否存在且未过期\r\n * @returns 如果缓存存在且未过期返回 true,否则返回 false\r\n */\r\n has(): boolean {\r\n try {\r\n const storedValue = this.storage.getItem(this.getStorageKey());\r\n if (!storedValue) {\r\n return false;\r\n }\r\n\r\n const { expiry } = JSON.parse(storedValue) as StorageData<T>;\r\n return !expiry || expiry > Date.now();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n}\r\n","/**\r\n * 深克隆一个对象或数组\r\n *\r\n * 该函数通过递归方式深克隆输入的对象或数组,确保与原对象或数组隔离,不会相互影响。\r\n * 支持克隆:普通对象、数组、Date、Map、Set、RegExp 等类型。\r\n *\r\n * @param source 需要克隆的源对象或数组\r\n * @returns 返回克隆后的对象或数组\r\n *\r\n * @example\r\n * const obj = { a: 1, b: { c: 2 } };\r\n * const cloned = deepClone(obj);\r\n * cloned.b.c = 3;\r\n * console.log(obj.b.c); // 输出: 2\r\n */\r\nexport function deepClone<T>(source: T): T {\r\n // 处理 null 和基本类型\r\n if (source === null || typeof source !== 'object') {\r\n return source;\r\n }\r\n\r\n // 处理 Date 对象\r\n if (source instanceof Date) {\r\n return new Date(source.getTime()) as unknown as T;\r\n }\r\n\r\n // 处理 RegExp 对象\r\n if (source instanceof RegExp) {\r\n return new RegExp(source.source, source.flags) as unknown as T;\r\n }\r\n\r\n // 处理 Map 对象\r\n if (source instanceof Map) {\r\n const clonedMap = new Map();\r\n source.forEach((value, key) => {\r\n clonedMap.set(deepClone(key), deepClone(value));\r\n });\r\n return clonedMap as unknown as T;\r\n }\r\n\r\n // 处理 Set 对象\r\n if (source instanceof Set) {\r\n const clonedSet = new Set();\r\n source.forEach((value) => {\r\n clonedSet.add(deepClone(value));\r\n });\r\n return clonedSet as unknown as T;\r\n }\r\n\r\n // 处理数组\r\n if (Array.isArray(source)) {\r\n return source.map((item) => deepClone(item)) as unknown as T;\r\n }\r\n\r\n // 处理普通对象\r\n if (Object.prototype.toString.call(source) === '[object Object]') {\r\n const clonedObject = {} as T;\r\n Object.keys(source).forEach((key) => {\r\n (clonedObject as any)[key] = deepClone((source as any)[key]);\r\n });\r\n return clonedObject;\r\n }\r\n\r\n // 其他类型直接返回(如函数、Symbol 等)\r\n return source;\r\n}\r\n","import { Observable } from 'rxjs';\r\nimport { debounceTime } from 'rxjs/operators';\r\n\r\n/**\r\n * 监听元素大小变化的配置选项\r\n */\r\nexport interface ObserveResizeOptions {\r\n /**\r\n * 防抖延迟时间(毫秒)\r\n * 设置后会对频繁的大小变化事件进行防抖处理\r\n * @default undefined (不防抖)\r\n */\r\n debounce?: number;\r\n}\r\n\r\n/**\r\n * 监听元素的大小变化\r\n *\r\n * 此函数创建了一个 Observable,用于观察给定 HTMLElement 元素的大小变化。\r\n * 它使用了 ResizeObserver API 来实现,当元素的大小发生变化时,会发出一个包含变化记录的数组。\r\n *\r\n * @param element 需要观察大小变化的 HTMLElement 元素\r\n * @param options 配置选项\r\n * @returns 返回一个 Observable,其中包含 ResizeObserverEntry 的数组,用于记录每次大小变化的信息\r\n *\r\n * @example\r\n * // 基础用法\r\n * const element = document.querySelector('.container');\r\n * observeResize(element).subscribe(entries => {\r\n * const { width, height } = entries[0].contentRect;\r\n * console.log(`元素尺寸: ${width}x${height}`);\r\n * });\r\n *\r\n * @example\r\n * // 使用防抖优化性能\r\n * observeResize(element, { debounce: 300 }).subscribe(entries => {\r\n * // 300ms 内的多次变化只会触发一次\r\n * console.log('元素大小已稳定');\r\n * });\r\n */\r\nexport function observeResize(element: HTMLElement, options?: ObserveResizeOptions): Observable<ResizeObserverEntry[]> {\r\n const { debounce } = options || {};\r\n\r\n const resize$ = new Observable<ResizeObserverEntry[]>((observer) => {\r\n // 创建 ResizeObserver 实例,当观察的目标元素大小发生变化时,会调用回调函数\r\n const resizeObserver = new ResizeObserver((entries) => {\r\n observer.next(entries);\r\n });\r\n\r\n // 开始观察指定的元素\r\n resizeObserver.observe(element);\r\n\r\n // 返回清理函数,用于在取消订阅时调用\r\n return () => {\r\n resizeObserver.unobserve(element);\r\n resizeObserver.disconnect();\r\n };\r\n });\r\n\r\n // 如果设置了防抖,则应用防抖操作符\r\n return debounce ? resize$.pipe(debounceTime(debounce)) : resize$;\r\n}\r\n","import { FormGroup } from '@angular/forms';\r\n\r\n/**\r\n * 表单验证配置\r\n */\r\nexport interface FormVerifyOptions {\r\n /**\r\n * 是否在控制台输出错误信息\r\n * @default true\r\n */\r\n logError?: boolean;\r\n /**\r\n * 自定义错误处理函数\r\n */\r\n onError?: (invalidFields: string[]) => void;\r\n}\r\n\r\n/**\r\n * 表单验证\r\n *\r\n * 遍历表单所有控件,标记为脏状态并触发验证。\r\n * 如果表单验证通过,返回表单的原始值;否则返回 false。\r\n *\r\n * @param form Angular 表单组对象\r\n * @param options 验证配置选项\r\n * @returns 验证通过返回表单值,验证失败返回 false\r\n *\r\n * @example\r\n * const result = formVerify(this.form);\r\n * if (result) {\r\n * console.log('表单数据:', result);\r\n * }\r\n *\r\n * @example\r\n * // 自定义错误处理\r\n * formVerify(this.form, {\r\n * logError: false,\r\n * onError: (fields) => {\r\n * this.message.error(`以下字段验证失败: ${fields.join(', ')}`);\r\n * }\r\n * });\r\n */\r\nexport function formVerify<T = Record<string, any>>(form: FormGroup, options?: FormVerifyOptions): false | T {\r\n const { logError = true, onError } = options || {};\r\n const invalidFields: string[] = [];\r\n\r\n // 遍历所有控件,标记为脏状态并更新验证状态\r\n Object.keys(form.controls).forEach((key) => {\r\n const control = form.controls[key];\r\n control.markAsDirty();\r\n control.updateValueAndValidity();\r\n\r\n // 收集验证失败的字段(排除禁用状态)\r\n if (control.status !== 'VALID' && control.status !== 'DISABLED') {\r\n invalidFields.push(key);\r\n }\r\n });\r\n\r\n // 表单验证失败(排除全部禁用的情况)\r\n if (form.status !== 'VALID' && form.status !== 'DISABLED') {\r\n if (logError) {\r\n console.error(`表单验证失败,字段: ${invalidFields.join(', ')}`);\r\n }\r\n if (onError) {\r\n onError(invalidFields);\r\n }\r\n return false;\r\n }\r\n\r\n // 验证通过,返回表单原始值(包含禁用字段)\r\n return form.getRawValue();\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["Observable","debounceTime"],"mappings":"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"}
@@ -1,25 +1,72 @@
1
1
  import { OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core';
2
2
  import { RkMenuService } from '@reskin/core/services';
3
3
  import * as i0 from "@angular/core";
4
+ /**
5
+ * 权限指令上下文
6
+ */
7
+ export interface RkAuthContext<T = any> {
8
+ /** 隐式上下文,包含匹配的菜单数据 */
9
+ $implicit: T;
10
+ /** 权限验证状态 */
11
+ status: boolean;
12
+ }
13
+ /**
14
+ * 权限指令
15
+ * 根据权限标识符控制模板的显示与隐藏
16
+ *
17
+ * @example
18
+ * ```html
19
+ * <!-- 基础用法 -->
20
+ * <div *rkAuth="'user:edit'">有编辑权限才显示</div>
21
+ *
22
+ * <!-- 多个权限(或关系) -->
23
+ * <div *rkAuth="['user:edit', 'user:delete']">有任一权限即显示</div>
24
+ *
25
+ * <!-- 使用 then/else 模板 -->
26
+ * <ng-container *rkAuth="'user:edit'; then hasAuth; else noAuth"></ng-container>
27
+ * <ng-template #hasAuth let-menu>
28
+ * <p>有权限,菜单信息:{{ menu | json }}</p>
29
+ * </ng-template>
30
+ * <ng-template #noAuth>
31
+ * <p>无权限</p>
32
+ * </ng-template>
33
+ * ```
34
+ */
4
35
  export declare class RkAuthDirective implements OnDestroy {
5
- private menu;
36
+ private menuService;
6
37
  private viewContainerRef;
7
38
  templateRef: TemplateRef<any>;
39
+ private condition;
40
+ private thenTemplateRef?;
41
+ private elseTemplateRef?;
42
+ private readonly destroy$;
8
43
  /**
9
- * 传递多个标识符时,逗号分隔,匹配按"或者"运算
10
- * @param condition
44
+ * 权限标识符
45
+ * 传递多个标识符时,匹配按"或"运算
11
46
  */
12
47
  set rkAuth(condition: string | string[]);
48
+ /**
49
+ * 有权限时显示的模板
50
+ */
13
51
  set rkAuthThen(templateRef: TemplateRef<any>);
52
+ /**
53
+ * 无权限时显示的模板
54
+ */
14
55
  set rkAuthElse(templateRef: TemplateRef<any>);
15
- constructor(menu: RkMenuService, viewContainerRef: ViewContainerRef, templateRef: TemplateRef<any>);
16
- private condition;
17
- private thenTemplateRef?;
18
- private elseTemplateRef?;
19
- private authSubscription?;
56
+ constructor(menuService: RkMenuService, viewContainerRef: ViewContainerRef, templateRef: TemplateRef<any>);
20
57
  ngOnDestroy(): void;
58
+ /**
59
+ * 更新视图显示
60
+ */
21
61
  private updateView;
22
- private hasAuth;
62
+ /**
63
+ * 渲染视图
64
+ */
65
+ private renderView;
66
+ /**
67
+ * 检查权限
68
+ */
69
+ private checkAuth;
23
70
  static ɵfac: i0.ɵɵFactoryDeclaration<RkAuthDirective, never>;
24
71
  static ɵdir: i0.ɵɵDirectiveDeclaration<RkAuthDirective, "[rkAuth]", never, { "rkAuth": "rkAuth"; "rkAuthThen": "rkAuthThen"; "rkAuthElse": "rkAuthElse"; }, {}, never>;
25
72
  }
@@ -1,15 +1,55 @@
1
1
  import { OnInit, OnDestroy, EventEmitter } from '@angular/core';
2
2
  import { ILoadResult, RkStyleLoaderService } from '@reskin/core/services';
3
3
  import * as i0 from "@angular/core";
4
+ /**
5
+ * 样式按需加载指令
6
+ * 在指令初始化时加载指定的样式文件,销毁时自动卸载
7
+ *
8
+ * @example
9
+ * ```html
10
+ * <!-- 加载单个样式 -->
11
+ * <div [rkLoadStyles]="'assets/styles/theme.css'">内容</div>
12
+ *
13
+ * <!-- 加载多个样式 -->
14
+ * <div [rkLoadStyles]="['assets/styles/theme.css', 'assets/styles/components.css']">内容</div>
15
+ *
16
+ * <!-- 监听加载完成事件 -->
17
+ * <div [rkLoadStyles]="styleUrls" (stylesLoaded)="onLoaded($event)">内容</div>
18
+ * ```
19
+ */
4
20
  export declare class RkLoadStylesDirective implements OnInit, OnDestroy {
5
- private styleLoader;
6
- hrefs: string[] | string;
7
- stylesLoaded: EventEmitter<ILoadResult[]>;
8
- private subscription;
21
+ private styleLoaderService;
22
+ private readonly destroy$;
9
23
  private styleUrls;
10
- constructor(styleLoader: RkStyleLoaderService);
24
+ /**
25
+ * 样式文件路径
26
+ * 可以是单个路径字符串或路径数组
27
+ */
28
+ hrefs: string | string[];
29
+ /**
30
+ * 样式加载完成事件
31
+ * 返回每个样式文件的加载结果
32
+ */
33
+ stylesLoaded: EventEmitter<ILoadResult[]>;
34
+ constructor(styleLoaderService: RkStyleLoaderService);
11
35
  ngOnInit(): void;
12
36
  ngOnDestroy(): void;
37
+ /**
38
+ * 加载样式文件
39
+ */
40
+ private loadStyles;
41
+ /**
42
+ * 卸载样式文件
43
+ */
44
+ private unloadStyles;
45
+ /**
46
+ * 标准化 hrefs 输入为数组
47
+ */
48
+ private normalizeHrefs;
49
+ /**
50
+ * 记录加载结果(仅在开发模式)
51
+ */
52
+ private logLoadResults;
13
53
  static ɵfac: i0.ɵɵFactoryDeclaration<RkLoadStylesDirective, never>;
14
54
  static ɵdir: i0.ɵɵDirectiveDeclaration<RkLoadStylesDirective, "[rkLoadStyles]", never, { "hrefs": "rkLoadStyles"; }, { "stylesLoaded": "stylesLoaded"; }, never>;
15
55
  }