nhanh-pure-function 1.0.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.
package/lib/Index.js ADDED
@@ -0,0 +1,5 @@
1
+ import * as Utility from "./Utility.js";
2
+ import * as User from "./User.js";
3
+
4
+ const _PureFunction = { Utility, User };
5
+ export default _PureFunction;
package/lib/User.d.ts ADDED
@@ -0,0 +1,185 @@
1
+ /**
2
+ * 添加滚动触底事件
3
+ * @param {滚动标签} element
4
+ * @param {触底事件} callback
5
+ */
6
+ export function _AddScrollBottomListener(element: HTMLElement, callback: Function): void;
7
+
8
+ /**
9
+ * 自动处理 currentPage * pageSize > total
10
+ * @param ask 请求方法
11
+ * @param config 请求参数
12
+ */
13
+ export function _PagingQuery<Ask extends Function>(
14
+ ask: Ask,
15
+ config: {
16
+ currentPage: number;
17
+ pageSize: number;
18
+ [key: string]: any;
19
+ }
20
+ ): ReturnType<Ask> & { currentPage?: number };
21
+
22
+ /**
23
+ * 纯数字转 数字加单位
24
+ * @param number 数字或字符串数字
25
+ * @param config : {
26
+ * join 拼接起来吗
27
+ * suffix 后缀
28
+ * integer 不超过万位的数字时保持整数吗
29
+ * }
30
+ * @returns 123456 --> 12.34万 | [ 12.34 , 万 ]
31
+ */
32
+ export function _FormatNumberWithUnit(
33
+ number: string | number,
34
+ config?: {
35
+ join?: boolean;
36
+ suffix?: string;
37
+ integer?: boolean;
38
+ }
39
+ ): string | [number, string];
40
+
41
+ /**
42
+ * 设置量词属性
43
+ * @param {*} data 需修改对象
44
+ * @param {*} options 配置
45
+ * @returns data
46
+ */
47
+ export function _SetQuantifierAttribute<T>(
48
+ data: T,
49
+ options?: (
50
+ | keyof T
51
+ | [
52
+ keyof T,
53
+ {
54
+ join?: boolean;
55
+ suffix?: string;
56
+ integer?: boolean;
57
+ }
58
+ ]
59
+ )[]
60
+ ): T;
61
+
62
+ /**
63
+ * 为属性值为null | undefined的属性设置默认值
64
+ * @param data 需修改对象
65
+ * @param options 配置
66
+ * @returns
67
+ */
68
+ export function _SetDefaultValue<T>(
69
+ data: T,
70
+ options: {
71
+ defaultValue?: string;
72
+ fieldsNotRequiringAction?: (string | number | symbol)[];
73
+ } = {}
74
+ ): T;
75
+
76
+ /**
77
+ * 将字典value转为对应label
78
+ * @param data 需修改对象
79
+ * @param options 配置
80
+ * @returns
81
+ */
82
+ export function _SetDictionary<T>(
83
+ data: T,
84
+ options: {
85
+ dictionaryOptions?: { [key in keyof T]: { [key: string | number]: any } };
86
+ dictionaryLabel?: (keyof T)[];
87
+ dictionaryLabelJoin?: (keyof T)[];
88
+ defaultValue?: string;
89
+ }
90
+ ): T;
91
+
92
+ /**
93
+ * 将字符串拼接的图片地址转为数组
94
+ * @param data 需修改对象
95
+ * @param options 配置
96
+ * @returns
97
+ */
98
+ export function _SetPhoto<T>(
99
+ data: T,
100
+ options: {
101
+ label?: (keyof T)[];
102
+ defaultUrl?: {
103
+ [key in keyof T]: string[];
104
+ };
105
+ }
106
+ ): T;
107
+
108
+ /**
109
+ * 将接口返回的数据进行处理,得到展示数据
110
+ * @param data object 类型的数据
111
+ * @param options 配置
112
+ * @returns exhibit_data
113
+ */
114
+ export function _Exhibit_details<T>(
115
+ data: T,
116
+ options: {
117
+ dictionaryOptions?: { [key in keyof T]: { [key: string | number]: any } };
118
+ dictionaryLabel?: (keyof T)[];
119
+ dictionaryLabelJoin?: (keyof T)[];
120
+
121
+ photoLabel?: (keyof T)[];
122
+ photoDefaultUrl?: {
123
+ [key in keyof T]: string[];
124
+ };
125
+
126
+ quantifierLabel?: (
127
+ | keyof T
128
+ | [
129
+ keyof T,
130
+ {
131
+ join?: boolean;
132
+ suffix?: string;
133
+ integer?: boolean;
134
+ }
135
+ ]
136
+ )[];
137
+
138
+ filterLabel?: (keyof T)[];
139
+
140
+ defaultValue?: string;
141
+ } = {}
142
+ ): T;
143
+
144
+ // 定义加载状态更新函数类型
145
+ type LoadingStateUpdater = (newState: boolean) => void;
146
+ // 定义加载控制器的类型
147
+ interface LoadingController {
148
+ invokers: Set<any>; // 假设invoker可以是任何类型
149
+ timer: NodeJS.Timeout | null;
150
+ startTime: number;
151
+ loadingState: LoadingStateUpdater;
152
+ delayTime: number;
153
+ minDisplayTime: number;
154
+ }
155
+ /** 多组loading控制器 */
156
+ export class _LoadingController {
157
+ #controllersCollection: Map<string, LoadingController>;
158
+
159
+ constructor() {}
160
+
161
+ // addController方法的类型定义
162
+ addController(
163
+ key?: string,
164
+ config: {
165
+ loadingState: LoadingStateUpdater;
166
+ delayTime?: number;
167
+ minDisplayTime?: number;
168
+ }
169
+ ): void;
170
+
171
+ // deleteController方法的类型定义
172
+ deleteController(key: string): void;
173
+
174
+ // getController方法的类型定义
175
+ getController(key?: string): LoadingController;
176
+
177
+ // resetController方法的类型定义
178
+ resetController(key: string): void;
179
+
180
+ // startLoading方法的类型定义
181
+ startLoading(invoker: any, key?: string): void;
182
+
183
+ // stopLoading方法的类型定义
184
+ stopLoading(invoker: any, key?: string): void;
185
+ }
package/lib/User.js ADDED
@@ -0,0 +1,387 @@
1
+ import { _IsObject, _IsWithinErrorMargin, _NotNull } from './Utility';
2
+
3
+ /**
4
+ * 添加滚动触底事件
5
+ * @param {滚动标签} element
6
+ * @param {触底事件} callback
7
+ */
8
+ export function _AddScrollBottomListener(element, callback) {
9
+ element.addEventListener('scroll', function () {
10
+ if (_IsWithinErrorMargin(element.scrollTop + element.clientHeight, element.scrollHeight, 2)) {
11
+ callback();
12
+ }
13
+ });
14
+ }
15
+
16
+ /**
17
+ * 自动处理 currentPage * pageSize > total
18
+ * @param ask 请求方法
19
+ * @param config 请求参数
20
+ */
21
+ export function _PagingQuery(ask, config) {
22
+ return new Promise(function (resolve, reject) {
23
+ ask(config)
24
+ .then((data) => {
25
+ const { rows, total } = data;
26
+ if (rows.length == 0 && total > 0) {
27
+ config.currentPage = Math.ceil(total / config.pageSize);
28
+ ask(config)
29
+ .then((data) => {
30
+ data.currentPage = config.currentPage;
31
+ resolve(data);
32
+ })
33
+ .catch(reject);
34
+ } else {
35
+ resolve(data);
36
+ }
37
+ })
38
+ .catch(reject);
39
+ });
40
+ }
41
+
42
+ /**
43
+ * 纯数字转 数字加单位
44
+ * @param number 数字或字符串数字
45
+ * @param config : {
46
+ * join 拼接起来吗
47
+ * suffix 后缀
48
+ * integer 不超过万位的数字时保持整数吗
49
+ * }
50
+ * @returns 123456 --> 12.34万 | [ 12.34 , 万 ]
51
+ */
52
+ export function _FormatNumberWithUnit(number, config = {}) {
53
+ const { join, suffix, integer } = Object.assign(
54
+ {
55
+ join: true,
56
+ suffix: '',
57
+ integer: false
58
+ },
59
+ config
60
+ );
61
+
62
+ function _join(value, suffix, plus = true) {
63
+ value = (plus ? '' : '-') + value;
64
+ if (join) return value + suffix;
65
+ else return [value, suffix];
66
+ }
67
+
68
+ if (typeof number == 'string') {
69
+ if (!/^\d+$/.test(number.trim())) {
70
+ console.error('错误输入:', number);
71
+ return _join(0, suffix);
72
+ }
73
+ } else if (typeof number != 'number') {
74
+ console.error('错误输入:', number);
75
+ return _join(0, suffix);
76
+ }
77
+
78
+ if (Math.abs(number) == Infinity || number == 0) {
79
+ return _join(0, suffix);
80
+ }
81
+
82
+ number = Number(number);
83
+ const plus = number >= 0;
84
+ number = Math.abs(number);
85
+
86
+ const units = ['', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极'];
87
+ const digits = Math.floor(Math.log10(number) / 4); // 计算位数
88
+
89
+ // 不超过万位的数字直接返回
90
+ if (digits === 0) {
91
+ if (integer) {
92
+ return _join(number, suffix, plus);
93
+ } else {
94
+ return _join(number.toFixed(2), suffix, plus);
95
+ }
96
+ }
97
+
98
+ const dividedNumber = number / Math.pow(10000, digits);
99
+ const formattedNumber = dividedNumber.toFixed(2);
100
+
101
+ return _join(formattedNumber, units[digits] + suffix, plus);
102
+ }
103
+
104
+ /**
105
+ * 设置量词属性
106
+ * @param {*} data 需修改对象
107
+ * @param {*} options 配置
108
+ * @returns data
109
+ */
110
+ export function _SetQuantifierAttribute(data, options = []) {
111
+ if (!_IsObject(data)) {
112
+ console.error('异常输入:', data);
113
+ return data;
114
+ }
115
+
116
+ options.forEach((item) => {
117
+ if (typeof item === 'string') {
118
+ data[item] = _FormatNumberWithUnit(data[item]);
119
+ } else if (Array.isArray(item)) {
120
+ const [label, config] = data[item];
121
+ if (_NotNull(label) && _IsObject(config)) data[label] = _FormatNumberWithUnit(label, config);
122
+ }
123
+ });
124
+ return data;
125
+ }
126
+
127
+ /**
128
+ * 为属性值为null | undefined的属性设置默认值
129
+ * @param data 需修改对象
130
+ * @param options 配置
131
+ * @returns
132
+ */
133
+ export function _SetDefaultValue(data, options = {}) {
134
+ if (!_IsObject(data)) {
135
+ console.error('异常输入:', data);
136
+ return data;
137
+ }
138
+
139
+ const { defaultValue = '--', fieldsNotRequiringAction } = options;
140
+
141
+ for (const key in data) {
142
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
143
+ const element = data[key];
144
+ if (fieldsNotRequiringAction) {
145
+ if (!fieldsNotRequiringAction.includes(key) && !_NotNull(element)) {
146
+ data[key] = defaultValue;
147
+ }
148
+ } else if (!_NotNull(element)) {
149
+ data[key] = defaultValue;
150
+ }
151
+ }
152
+ }
153
+
154
+ return data;
155
+ }
156
+
157
+ /**
158
+ * 将字典value转为对应label
159
+ * @param data 需修改对象
160
+ * @param options 配置
161
+ * @returns
162
+ */
163
+ export function _SetDictionary(data, options = {}) {
164
+ if (!_IsObject(data)) {
165
+ console.error('异常输入:', data);
166
+ return data;
167
+ }
168
+
169
+ const {
170
+ dictionaryLabel = [],
171
+ dictionaryLabelJoin = [],
172
+ dictionaryOptions,
173
+ defaultValue = '--'
174
+ } = options;
175
+
176
+ if (dictionaryOptions) {
177
+ dictionaryLabel.forEach((label) => {
178
+ if (_NotNull(data[label])) {
179
+ const options = dictionaryOptions[label];
180
+
181
+ if (options) {
182
+ data[label] = options[data[label]];
183
+ } else {
184
+ data[label] = defaultValue;
185
+ }
186
+ } else {
187
+ data[label] = defaultValue;
188
+ }
189
+ });
190
+ dictionaryLabelJoin.forEach((label) => {
191
+ if (_NotNull(data[label]) && data[label] != '') {
192
+ const options = dictionaryOptions[label];
193
+ if (options) {
194
+ const oldvalue = data[label].split(',');
195
+ data[label] = '';
196
+ oldvalue.forEach((_label) => {
197
+ data[label] += options[_label];
198
+ });
199
+ } else {
200
+ data[label] = defaultValue;
201
+ }
202
+ } else {
203
+ data[label] = defaultValue;
204
+ }
205
+ });
206
+ }
207
+
208
+ return data;
209
+ }
210
+
211
+ /**
212
+ * 将字符串拼接的图片地址转为数组
213
+ * @param data 需修改对象
214
+ * @param options 配置
215
+ * @returns
216
+ */
217
+ export function _SetPhoto(data, options = {}) {
218
+ if (!_IsObject(data)) {
219
+ console.error('异常输入:', data);
220
+ return data;
221
+ }
222
+
223
+ const { label, defaultUrl } = options;
224
+
225
+ if (label) {
226
+ label.forEach((label) => {
227
+ const defaultValue = (defaultUrl && defaultUrl[label]) || [];
228
+ const value = data[label];
229
+ if (typeof value === 'string') {
230
+ data[label] = value.split(',').filter(Boolean);
231
+ } else {
232
+ data[label] = defaultValue;
233
+ }
234
+ });
235
+ }
236
+
237
+ return data;
238
+ }
239
+
240
+ /**
241
+ * 将接口返回的数据进行处理,得到展示数据
242
+ * @param data object 类型的数据
243
+ * @param options 配置
244
+ * @returns exhibit_data
245
+ */
246
+ export function _Exhibit_details(data, options = {}) {
247
+ if (!_IsObject(data)) {
248
+ console.error('异常输入:', data);
249
+ return {};
250
+ }
251
+
252
+ data = JSON.parse(JSON.stringify(data));
253
+
254
+ const {
255
+ dictionaryLabel = [],
256
+ dictionaryLabelJoin = [],
257
+ dictionaryOptions,
258
+
259
+ photoLabel = [],
260
+ photoDefaultUrl,
261
+
262
+ quantifierLabel = [],
263
+
264
+ filterLabel = [],
265
+
266
+ defaultValue = '--'
267
+ } = options;
268
+
269
+ _SetDictionary(data, {
270
+ dictionaryLabel,
271
+ dictionaryLabelJoin,
272
+ dictionaryOptions,
273
+ defaultValue
274
+ });
275
+
276
+ _SetPhoto(data, {
277
+ label: photoLabel,
278
+ defaultUrl: photoDefaultUrl
279
+ });
280
+
281
+ _SetQuantifierAttribute(data, quantifierLabel);
282
+
283
+ _SetDefaultValue(data, {
284
+ defaultValue,
285
+ fieldsNotRequiringAction: dictionaryLabel
286
+ .concat(dictionaryLabelJoin)
287
+ .concat(photoLabel)
288
+ .concat(
289
+ quantifierLabel
290
+ .map((item) => {
291
+ if (typeof item == 'string') return item;
292
+ if (Array.isArray(item)) return item[0];
293
+ })
294
+ .filter(Boolean)
295
+ )
296
+ .concat(filterLabel)
297
+ });
298
+
299
+ return data;
300
+ }
301
+
302
+ /** 多组loading控制器 */
303
+ export class _LoadingController {
304
+ #controllersCollection = new Map();
305
+ constructor() {}
306
+
307
+ addController(key = 'default', config) {
308
+ if (this.#controllersCollection.has(key))
309
+ throw new Error('key为: ' + key + ' 的loading控制器已存在, 请重命名。');
310
+
311
+ const {
312
+ loadingState /** 更新/获取 loading 状态的方法 */,
313
+ delayTime = 200 /** 延迟时间 */,
314
+ minDisplayTime = 400 /** 最少显示时间 */
315
+ } = config;
316
+ this.#controllersCollection.set(key, {
317
+ invokers: new Set(),
318
+ timer: null,
319
+ startTime: 0,
320
+ loadingState,
321
+ delayTime,
322
+ minDisplayTime
323
+ });
324
+ }
325
+
326
+ deleteController(key) {
327
+ this.#controllersCollection.delete(key);
328
+ }
329
+
330
+ getController(key = 'default') {
331
+ const controller = this.#controllersCollection.get(key);
332
+ if (!controller) throw new Error('还未添加key为: ' + key + ' 的loading控制器');
333
+ return controller;
334
+ }
335
+
336
+ resetController(key) {
337
+ const controller = this.getController(key);
338
+ const { invokers, loadingState, timer } = controller;
339
+ invokers.clear();
340
+ loadingState(false);
341
+ controller.startTime = 0;
342
+ if (timer) clearTimeout(timer);
343
+ controller.timer = null;
344
+ }
345
+
346
+ startLoading(invoker, key) {
347
+ const controller = this.getController(key);
348
+ const { invokers, timer, loadingState, delayTime } = controller;
349
+
350
+ invokers.add(invoker);
351
+
352
+ if (invokers.size > 1) return;
353
+
354
+ if (delayTime) {
355
+ if (timer) {
356
+ return;
357
+ } else if (!loadingState()) {
358
+ controller.timer = setTimeout(() => {
359
+ loadingState(true);
360
+ controller.startTime = +new Date();
361
+ controller.timer = null;
362
+ }, delayTime);
363
+ }
364
+ } else {
365
+ loadingState(true);
366
+ controller.startTime = +new Date();
367
+ }
368
+ }
369
+
370
+ stopLoading(invoker, key) {
371
+ const controller = this.getController(key);
372
+ const { invokers, startTime, minDisplayTime } = controller;
373
+
374
+ const isFinished = invokers.has(invoker) && invokers.size == 1;
375
+
376
+ if (isFinished) {
377
+ const displayTime = +new Date() - startTime;
378
+ if (displayTime >= minDisplayTime) {
379
+ this.resetController(key);
380
+ } else {
381
+ setTimeout(() => this.stopLoading(invoker, key), displayTime - minDisplayTime);
382
+ }
383
+ } else {
384
+ invokers.delete(invoker);
385
+ }
386
+ }
387
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * 非null | undefined判断
3
+ * @param value any
4
+ * @returns boolean
5
+ */
6
+ export function _NotNull(value: any): boolean;
7
+
8
+ /**
9
+ * 是正常对象吗
10
+ * @param {} value
11
+ * @returns boolean
12
+ */
13
+ export function _IsObject(value: any): boolean;
14
+
15
+ /**
16
+ * 寻找空闲时机执行传入方法
17
+ * @param callback 需执行的方法
18
+ */
19
+ export function _ExecuteWhenIdle(callback: Function);
20
+
21
+ /**
22
+ * 转为百分比字符串
23
+ * @param value 分子
24
+ * @param totalValue 分母
25
+ * @param decimalPlaces 保留小数位
26
+ * @returns 10.00%
27
+ */
28
+ export function _ConvertToPercentage(
29
+ value: number,
30
+ totalValue: number,
31
+ decimalPlaces?: number
32
+ ): number;
33
+
34
+ /**
35
+ * 等待条件满足
36
+ * @param conditionChecker 条件检查器
37
+ * @param timeoutMillis 超时毫秒数
38
+ * @returns Promise<unknown>
39
+ */
40
+ export function _WaitForCondition(
41
+ conditionChecker: () => boolean,
42
+ timeoutMillis: number
43
+ ): Promise;
44
+
45
+ /**
46
+ * 排除子串
47
+ * @param inputString 需裁剪字符串
48
+ * @param substringToDelete 被裁减字符串
49
+ * @param delimiter 分隔符
50
+ * @returns 裁减后的字符串
51
+ */
52
+ export function _ExcludeSubstring(
53
+ inputString: string,
54
+ substringToDelete: string,
55
+ delimiter?: string
56
+ ): string;
57
+
58
+ /**
59
+ * 首字母大写
60
+ * @param string
61
+ * @returns string
62
+ */
63
+ export function _CapitalizeFirstLetter(string: string): string;
64
+
65
+ /**
66
+ * 合并对象 注意: 本函数会直接操作 A
67
+ * @param {Object | Array} A
68
+ * @param {Object | Array} B
69
+ * @returns A&B || B
70
+ */
71
+ export function _MergeObjects<T, T1>(A: T, B: T1): T & T1;
72
+
73
+ /**
74
+ * 时间戳转换字符串
75
+ * @param {Number | Date} time 时间戳或Date对象
76
+ * @param {String} template 完整模板 --> yyyy MM DD hh mm ss ms
77
+ * @param {Boolean} pad 补0
78
+ */
79
+ export function _TimeTransition(
80
+ time: number | Date,
81
+ template: string,
82
+ pad: boolean
83
+ ): string;
84
+
85
+ /**
86
+ * 误差范围
87
+ * @param value 需要判断的数字
88
+ * @param target 目标数字
89
+ * @param errorMargin 正负误差范围
90
+ * @returns 是否在误差内
91
+ */
92
+ export function _IsWithinErrorMargin(
93
+ value: number,
94
+ target: number,
95
+ errorMargin: number
96
+ ): boolean;
97
+
98
+ /**
99
+ * 读取文件
100
+ * @param src 文件地址
101
+ * @returns 文件的字符串内容
102
+ */
103
+ export function _ReadFile(src: string): Promise<string>;
104
+
105
+ /**
106
+ * 下载文件
107
+ * @param {文件路径} href
108
+ * @param {导出文件名} fileName
109
+ */
110
+ export function _DownloadFile(href: string, fileName?: string): void;
111
+
112
+ /**
113
+ * 获取帧率
114
+ * @param {Function} callback callback( 帧率 , 每帧时间 )
115
+ * @param {Number} referenceNode 参考节点数量
116
+ */
117
+ export function _GetFrameRate(
118
+ callback: Function,
119
+ referenceNode: number = 10
120
+ ): void;
121
+
122
+ /**
123
+ * 进度
124
+ * @param {Function} callback callback( 进度百分比 )
125
+ * @param {Number} TIME 总时长
126
+ */
127
+ export function _Schedule(callback: Function, TIME: number = 500): void;
128
+
129
+ /**
130
+ * 格式化数字,给数字加上千位分隔符。
131
+ * @param {number} number - 要格式化的数字。
132
+ * @returns {string} - 格式化后的字符串。
133
+ */
134
+ export function _FormatNumber(number: number): string;
135
+
136
+ /**
137
+ * 单位转换 12** -> **px
138
+ * @param {string} width
139
+ * @returns 对应的单位为px的宽
140
+ */
141
+ export function _GetOtherSizeInPixels(width: string): string;
142
+
143
+ /**
144
+ * 驼峰命名
145
+ * @param {字符串} str
146
+ * @param {是否删除分割字符} isRemoveDelimiter
147
+ * @returns 'wq1wqw-qw2qw' -> 'wq1Wqw-Qw2Qw' / 'wqWqwQwQw'
148
+ */
149
+ export function _ConvertToCamelCase(
150
+ str: string,
151
+ isRemoveDelimiter?: boolean
152
+ ): string;
package/lib/Utility.js ADDED
@@ -0,0 +1,304 @@
1
+ /**
2
+ * 非null | undefined判断
3
+ * @param value any
4
+ * @returns boolean
5
+ */
6
+ export function _NotNull(value) {
7
+ return value !== null && value !== undefined;
8
+ }
9
+
10
+ /**
11
+ * 是正常对象吗
12
+ * @param {} value
13
+ * @returns boolean
14
+ */
15
+ export function _IsObject(value) {
16
+ return !(value === null || typeof value !== "object" || Array.isArray(value));
17
+ }
18
+
19
+ /**
20
+ * 寻找空闲时机执行传入方法
21
+ * @param callback 需执行的方法
22
+ */
23
+ export function _ExecuteWhenIdle(callback) {
24
+ if (typeof callback !== "function")
25
+ return console.error("非函数:", callback);
26
+ const loop = function (deadline) {
27
+ if (deadline.didTimeout || deadline.timeRemaining() <= 0)
28
+ requestIdleCallback(loop);
29
+ else callback();
30
+ };
31
+ requestIdleCallback(loop);
32
+ }
33
+
34
+ /**
35
+ * 转为百分比字符串
36
+ * @param value 分子
37
+ * @param totalValue 分母
38
+ * @param decimalPlaces 保留小数位
39
+ * @returns 10.00%
40
+ */
41
+ export function _ConvertToPercentage(value, totalValue, decimalPlaces = 2) {
42
+ if (
43
+ typeof value !== "number" ||
44
+ typeof totalValue !== "number" ||
45
+ typeof decimalPlaces !== "number" ||
46
+ totalValue == 0
47
+ ) {
48
+ console.error("异常输入:", arguments);
49
+ return "0.00%";
50
+ }
51
+ return (
52
+ Number(
53
+ parseInt((value / totalValue) * Math.pow(10, 2 + decimalPlaces)) /
54
+ Math.pow(10, decimalPlaces)
55
+ ) || 0
56
+ );
57
+ }
58
+
59
+ /**
60
+ * 等待条件满足
61
+ * @param conditionChecker 条件检查器
62
+ * @param timeoutMillis 超时毫秒数
63
+ * @returns Promise<unknown>
64
+ */
65
+ export function _WaitForCondition(conditionChecker, timeoutMillis) {
66
+ const startTime = new Date() - 0;
67
+ return new Promise((resolve, reject) => {
68
+ const checkCondition = () => {
69
+ const nowTime = new Date() - 0;
70
+ if (nowTime - startTime >= timeoutMillis) return reject("超时");
71
+ if (conditionChecker()) return resolve("完成");
72
+ requestIdleCallback(checkCondition);
73
+ };
74
+ checkCondition();
75
+ });
76
+ }
77
+
78
+ /**
79
+ * 排除子串
80
+ * @param inputString 需裁剪字符串
81
+ * @param substringToDelete 被裁减字符串
82
+ * @param delimiter 分隔符
83
+ * @returns 裁减后的字符串
84
+ */
85
+ export function _ExcludeSubstring(
86
+ inputString,
87
+ substringToDelete,
88
+ delimiter = ","
89
+ ) {
90
+ const regex = new RegExp(
91
+ `(^|${delimiter})${substringToDelete}(${delimiter}|$)`,
92
+ "g"
93
+ );
94
+ return inputString.replace(regex, function ($0, $1, $2) {
95
+ return $1 === $2 ? delimiter : "";
96
+ });
97
+ }
98
+
99
+ /**
100
+ * 首字母大写
101
+ * @param str
102
+ * @returns string
103
+ */
104
+ export function _CapitalizeFirstLetter(string) {
105
+ return string.charAt(0).toUpperCase() + string.slice(1);
106
+ }
107
+
108
+ /**
109
+ * 合并对象 注意: 本函数会直接操作 A
110
+ * @param {Object | Array} A
111
+ * @param {Object | Array} B
112
+ * @returns A&B || B
113
+ */
114
+ export function _MergeObjects(A, B, visitedObjects = []) {
115
+ const getType = (v) => (Array.isArray(v) ? "array" : typeof v);
116
+ const TA = getType(A);
117
+ const TB = getType(B);
118
+
119
+ if (TA != TB) return B;
120
+ if (visitedObjects.some((item) => item == B)) return B;
121
+
122
+ if (TA == "object") {
123
+ visitedObjects.push(A, B);
124
+ for (const key in B) {
125
+ if (Object.prototype.hasOwnProperty.call(B, key)) {
126
+ const BC = B[key];
127
+ const AC = A[key];
128
+ const fianlValue = _MergeObjects(AC, BC, visitedObjects);
129
+ A[key] = fianlValue;
130
+ }
131
+ }
132
+ return A;
133
+ } else if (TA == "array") {
134
+ visitedObjects.push(A, B);
135
+ B.forEach((item, index) => {
136
+ const BC = item;
137
+ const AC = A[index];
138
+ const fianlValue = _MergeObjects(AC, BC, visitedObjects);
139
+ A[index] = fianlValue;
140
+ });
141
+ return A;
142
+ } else return B;
143
+ }
144
+
145
+ /**
146
+ * 时间戳转换字符串
147
+ * @param {Number | Date} time 时间戳或Date对象
148
+ * @param {String} template 完整模板 --> yyyy MM DD hh mm ss ms
149
+ * @param {Boolean} pad 补0
150
+ */
151
+ export function _TimeTransition(time, template, pad = true) {
152
+ try {
153
+ time = new Date(time);
154
+ } catch (error) {
155
+ console.error(error);
156
+ return "";
157
+ }
158
+ const dictionary = {
159
+ yyyy: "getFullYear",
160
+ MM: "getMonth",
161
+ DD: "getDate",
162
+ hh: "getHours",
163
+ mm: "getMinutes",
164
+ ss: "getSeconds",
165
+ ms: (num) => +num % 1000,
166
+ };
167
+ for (const key in dictionary) {
168
+ if (Object.hasOwnProperty.call(dictionary, key)) {
169
+ if (new RegExp(key).test(template)) {
170
+ let value,
171
+ fun = dictionary[key];
172
+
173
+ if (typeof fun == "function") value = fun(time);
174
+ else value = time[fun]();
175
+
176
+ if (key == "MM") value++;
177
+
178
+ if (pad) value = String(value).padStart(2, "0");
179
+
180
+ template = template.replace(key, value);
181
+ }
182
+ }
183
+ }
184
+ return template;
185
+ }
186
+
187
+ /**
188
+ * 误差范围
189
+ * @param value 需要判断的数字
190
+ * @param target 目标数字
191
+ * @param errorMargin 正负误差范围
192
+ * @returns 是否在误差内
193
+ */
194
+ export function _IsWithinErrorMargin(value, target, errorMargin) {
195
+ return Math.abs(value - target) <= errorMargin;
196
+ }
197
+
198
+ /**
199
+ * 读取文件
200
+ * @param src 文件地址
201
+ * @returns 文件的字符串内容
202
+ */
203
+ export function _ReadFile(src) {
204
+ return new Promise((resolve, reject) => {
205
+ fetch(src)
206
+ .then((response) => resolve(response.text()))
207
+ .catch((error) => {
208
+ console.error("Error fetching :", error);
209
+ reject(error);
210
+ });
211
+ });
212
+ }
213
+
214
+ /**
215
+ * 下载文件
216
+ * @param {文件路径} href
217
+ * @param {导出文件名} download
218
+ */
219
+ export function _DownloadFile(href, fileName) {
220
+ const a = document.createElement("a");
221
+ a.href = href;
222
+ if (fileName) a.download = fileName;
223
+ a.style.display = "none";
224
+ document.body.appendChild(a);
225
+ a.click();
226
+ a.remove();
227
+ }
228
+
229
+ /**
230
+ * 获取帧率
231
+ * @param {Function} callback callback( 帧率 , 每帧时间 )
232
+ * @param {Number} referenceNode 参考节点数量
233
+ */
234
+ export function _GetFrameRate(callback, referenceNode = 10) {
235
+ let t,
236
+ arr = [];
237
+ function loop(time) {
238
+ if (t) {
239
+ arr.push(time - t);
240
+ let l = arr.length;
241
+ if (l >= referenceNode) {
242
+ let num = arr.reduce((a, b) => a + b, 0);
243
+ num /= l;
244
+ callback(1000 / num, num);
245
+ return;
246
+ }
247
+ }
248
+ t = time;
249
+ requestAnimationFrame(loop);
250
+ }
251
+ requestAnimationFrame(loop);
252
+ }
253
+
254
+ /**
255
+ * 进度
256
+ * @param {Function} callback callback( 进度百分比 )
257
+ * @param {Number} TIME 总时长
258
+ */
259
+ export function _Schedule(callback, TIME = 500) {
260
+ let t;
261
+ function loop(time) {
262
+ if (!t) t = time;
263
+ let percentage = Math.min((time - t) / TIME, 1);
264
+ callback(percentage);
265
+ if (time - t < TIME) requestAnimationFrame(loop);
266
+ }
267
+ requestAnimationFrame(loop);
268
+ }
269
+
270
+ /**
271
+ * 格式化数字,给数字加上千位分隔符。
272
+ * @param {number} number - 要格式化的数字。
273
+ * @returns {string} - 格式化后的字符串。
274
+ */
275
+ export function _FormatNumber(number) {
276
+ return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
277
+ }
278
+
279
+ /**
280
+ * 单位转换 12** -> **px
281
+ * @param {string} width
282
+ * @returns 对应的单位为px的宽
283
+ */
284
+ export function _GetOtherSizeInPixels(width) {
285
+ if (/px/.test(width)) return width;
286
+ const dom = document.createElement("div");
287
+ dom.style.width = width;
288
+ document.body.appendChild(dom);
289
+ width = parseFloat(window.getComputedStyle(dom).width);
290
+ document.body.removeChild(dom);
291
+ return width;
292
+ }
293
+
294
+ /**
295
+ * 驼峰命名
296
+ * @param {字符串} str
297
+ * @param {是否删除分割字符} isRemoveDelimiter
298
+ * @returns 'wq1wqw-qw2qw' -> 'wq1Wqw-Qw2Qw' / 'wqWqwQwQw'
299
+ */
300
+ export function _ConvertToCamelCase(str, isRemoveDelimiter) {
301
+ str = str.replace(/([^a-zA-Z][a-z])/g, (match) => match.toUpperCase());
302
+ if (isRemoveDelimiter) return str.replace(/[^a-zA-Z]+/g, "");
303
+ return str;
304
+ }
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "nhanh-pure-function",
3
+ "version": "1.0.0",
4
+ "description": "纯函数工具",
5
+ "main": "lib/Index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "pure-function",
11
+ "format"
12
+ ],
13
+ "author": "nhanh",
14
+ "license": "MIT"
15
+ }