sculp-js 0.0.2 → 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/LICENSE.md +1 -1
- package/README.md +22 -1
- package/lib/cjs/array.js +32 -55
- package/lib/cjs/async.js +3 -3
- package/lib/cjs/clipboard.js +3 -3
- package/lib/cjs/cookie.js +5 -5
- package/lib/cjs/date.js +142 -24
- package/lib/cjs/dom.js +24 -10
- package/lib/cjs/download.js +9 -9
- package/lib/cjs/easing.js +1 -1
- package/lib/cjs/file.js +5 -4
- package/lib/cjs/func.js +160 -0
- package/lib/cjs/index.js +28 -2
- package/lib/cjs/number.js +82 -0
- package/lib/cjs/object.js +13 -11
- package/lib/cjs/path.js +1 -1
- package/lib/cjs/qs.js +5 -5
- package/lib/cjs/random.js +72 -0
- package/lib/cjs/string.js +40 -7
- package/lib/cjs/type.js +12 -2
- package/lib/cjs/unique.js +83 -0
- package/lib/cjs/url.js +1 -1
- package/lib/cjs/watermark.js +8 -9
- package/lib/es/array.js +33 -55
- package/lib/es/async.js +3 -3
- package/lib/es/clipboard.js +3 -3
- package/lib/es/cookie.js +5 -5
- package/lib/es/date.js +139 -25
- package/lib/es/dom.js +24 -11
- package/lib/es/download.js +9 -9
- package/lib/es/easing.js +1 -1
- package/lib/es/file.js +5 -4
- package/lib/es/func.js +154 -0
- package/lib/es/index.js +10 -6
- package/lib/es/number.js +77 -0
- package/lib/es/object.js +12 -10
- package/lib/es/path.js +1 -1
- package/lib/es/qs.js +5 -5
- package/lib/es/random.js +67 -0
- package/lib/es/string.js +40 -8
- package/lib/es/type.js +12 -3
- package/lib/es/unique.js +79 -0
- package/lib/es/url.js +1 -1
- package/lib/es/watermark.js +8 -9
- package/lib/index.d.ts +254 -80
- package/lib/umd/index.js +637 -132
- package/package.json +36 -12
package/lib/umd/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* sculp-js
|
|
2
|
+
* sculp-js v1.0.0
|
|
3
3
|
* (c) 2023-2023 chandq
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sculpJs = {}, global.bezier));
|
|
11
11
|
})(this, (function (exports, bezier) { 'use strict';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 判断任意值的数据类型
|
|
15
|
+
* @param {unknown} any
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
13
18
|
const typeIs = (any) => Object.prototype.toString.call(any).slice(8, -1);
|
|
14
19
|
// 基本数据类型判断
|
|
15
20
|
const isString = (any) => typeof any === 'string';
|
|
@@ -23,7 +28,11 @@
|
|
|
23
28
|
// 复合数据类型判断
|
|
24
29
|
const isObject = (any) => typeIs(any) === 'Object';
|
|
25
30
|
const isArray = (any) => Array.isArray(any);
|
|
26
|
-
|
|
31
|
+
/**
|
|
32
|
+
* 判断是否为函数
|
|
33
|
+
* @param {unknown} any
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
27
36
|
const isFunction = (any) => typeof any === 'function';
|
|
28
37
|
// 对象类型判断
|
|
29
38
|
const isNaN = (any) => Number.isNaN(any);
|
|
@@ -52,20 +61,22 @@
|
|
|
52
61
|
* @param {string} key
|
|
53
62
|
* @returns {boolean}
|
|
54
63
|
*/
|
|
55
|
-
|
|
64
|
+
function objectHas(obj, key) {
|
|
65
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
66
|
+
}
|
|
56
67
|
/**
|
|
57
68
|
* 遍历对象,返回 false 中断遍历
|
|
58
69
|
* @param {O} obj
|
|
59
70
|
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
60
71
|
*/
|
|
61
|
-
|
|
72
|
+
function objectEach(obj, iterator) {
|
|
62
73
|
for (const key in obj) {
|
|
63
74
|
if (!objectHas(obj, key))
|
|
64
75
|
continue;
|
|
65
76
|
if (iterator(obj[key], key) === false)
|
|
66
77
|
break;
|
|
67
78
|
}
|
|
68
|
-
}
|
|
79
|
+
}
|
|
69
80
|
/**
|
|
70
81
|
* 异步遍历对象,返回 false 中断遍历
|
|
71
82
|
* @param {O} obj
|
|
@@ -171,7 +182,7 @@
|
|
|
171
182
|
* @param {ObjectAssignItem | undefined} targets
|
|
172
183
|
* @returns {R}
|
|
173
184
|
*/
|
|
174
|
-
|
|
185
|
+
function objectAssign(source, ...targets) {
|
|
175
186
|
const map = new Map();
|
|
176
187
|
for (let i = 0; i < targets.length; i++) {
|
|
177
188
|
const target = targets[i];
|
|
@@ -181,7 +192,7 @@
|
|
|
181
192
|
}
|
|
182
193
|
map.clear();
|
|
183
194
|
return source;
|
|
184
|
-
}
|
|
195
|
+
}
|
|
185
196
|
/**
|
|
186
197
|
* 对象填充
|
|
187
198
|
* @param {Partial<R>} source
|
|
@@ -189,7 +200,7 @@
|
|
|
189
200
|
* @param {(s: Partial<R>, t: Partial<R>, key: keyof R) => boolean} fillable
|
|
190
201
|
* @returns {R}
|
|
191
202
|
*/
|
|
192
|
-
|
|
203
|
+
function objectFill(source, target, fillable) {
|
|
193
204
|
const _fillable = fillable || ((source, target, key) => source[key] === undefined);
|
|
194
205
|
objectEach(target, (val, key) => {
|
|
195
206
|
if (_fillable(source, target, key)) {
|
|
@@ -197,7 +208,7 @@
|
|
|
197
208
|
}
|
|
198
209
|
});
|
|
199
210
|
return source;
|
|
200
|
-
}
|
|
211
|
+
}
|
|
201
212
|
function objectGet(obj, path, strict = false) {
|
|
202
213
|
path = path.replace(/\[(\w+)\]/g, '.$1');
|
|
203
214
|
path = path.replace(/^\./, '');
|
|
@@ -230,7 +241,7 @@
|
|
|
230
241
|
* 深拷贝堪称完全体 即:任何类型的数据都会被深拷贝
|
|
231
242
|
* @param {AnyObject | AnyArray} obj
|
|
232
243
|
* @param {WeakMap} map
|
|
233
|
-
* @
|
|
244
|
+
* @returns {AnyObject | AnyArray}
|
|
234
245
|
*/
|
|
235
246
|
function cloneDeep(obj, map = new WeakMap()) {
|
|
236
247
|
if (obj instanceof Date)
|
|
@@ -252,10 +263,11 @@
|
|
|
252
263
|
|
|
253
264
|
/**
|
|
254
265
|
* 判断一个对象是否为类数组
|
|
266
|
+
*
|
|
255
267
|
* @param any
|
|
256
268
|
* @returns {boolean}
|
|
257
269
|
*/
|
|
258
|
-
|
|
270
|
+
function arrayLike(any) {
|
|
259
271
|
if (isArray(any))
|
|
260
272
|
return true;
|
|
261
273
|
if (isString(any))
|
|
@@ -263,34 +275,36 @@
|
|
|
263
275
|
if (!isObject(any))
|
|
264
276
|
return false;
|
|
265
277
|
return objectHas(any, 'length');
|
|
266
|
-
}
|
|
278
|
+
}
|
|
267
279
|
/**
|
|
268
280
|
* 遍历数组,返回 false 中断遍历
|
|
281
|
+
*
|
|
269
282
|
* @param {ArrayLike<V>} array
|
|
270
283
|
* @param {(val: V, idx: number) => any} iterator
|
|
271
284
|
* @param reverse {boolean} 是否倒序
|
|
285
|
+
* @returns {*}
|
|
272
286
|
*/
|
|
273
|
-
|
|
287
|
+
function arrayEach(array, iterator, reverse = false) {
|
|
274
288
|
if (reverse) {
|
|
275
289
|
for (let idx = array.length - 1; idx >= 0; idx--) {
|
|
276
290
|
const val = array[idx];
|
|
277
|
-
if (iterator(val, idx) === false)
|
|
291
|
+
if (iterator(val, idx, array) === false)
|
|
278
292
|
break;
|
|
279
293
|
}
|
|
280
294
|
}
|
|
281
295
|
else {
|
|
282
296
|
for (let idx = 0; idx < array.length; idx++) {
|
|
283
297
|
const val = array[idx];
|
|
284
|
-
if (iterator(val, idx) === false)
|
|
298
|
+
if (iterator(val, idx, array) === false)
|
|
285
299
|
break;
|
|
286
300
|
}
|
|
287
301
|
}
|
|
288
|
-
}
|
|
302
|
+
}
|
|
289
303
|
/**
|
|
290
304
|
* 异步遍历数组,返回 false 中断遍历
|
|
291
|
-
* @param {ArrayLike<V>} array
|
|
292
|
-
* @param {(val: V, idx: number) => Promise<any>} iterator
|
|
293
|
-
* @param {boolean} reverse
|
|
305
|
+
* @param {ArrayLike<V>} array 数组
|
|
306
|
+
* @param {(val: V, idx: number) => Promise<any>} iterator 支持Promise类型的回调函数
|
|
307
|
+
* @param {boolean} reverse 是否反向遍历
|
|
294
308
|
*/
|
|
295
309
|
async function arrayEachAsync(array, iterator, reverse = false) {
|
|
296
310
|
if (reverse) {
|
|
@@ -313,15 +327,16 @@
|
|
|
313
327
|
* @param {AnyArray} array
|
|
314
328
|
* @param {number} start
|
|
315
329
|
* @param {number} to
|
|
330
|
+
* @returns {*}
|
|
316
331
|
*/
|
|
317
|
-
|
|
332
|
+
function arrayInsertBefore(array, start, to) {
|
|
318
333
|
if (start === to || start + 1 === to)
|
|
319
334
|
return;
|
|
320
335
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
321
336
|
const [source] = array.splice(start, 1);
|
|
322
337
|
const insertIndex = to < start ? to : to - 1;
|
|
323
338
|
array.splice(insertIndex, 0, source);
|
|
324
|
-
}
|
|
339
|
+
}
|
|
325
340
|
/**
|
|
326
341
|
* 数组删除指定项目
|
|
327
342
|
* @param {V[]} array
|
|
@@ -342,17 +357,18 @@
|
|
|
342
357
|
}
|
|
343
358
|
/**
|
|
344
359
|
* 自定义深度优先遍历函数(支持continue和break操作)
|
|
345
|
-
* @param {
|
|
346
|
-
* @param {
|
|
347
|
-
* @param {
|
|
348
|
-
* @param {boolean} isReverse
|
|
360
|
+
* @param {ArrayLike<V>} tree 树形数据
|
|
361
|
+
* @param {Function} iterator 迭代函数
|
|
362
|
+
* @param {string} children 定制子元素的key
|
|
363
|
+
* @param {boolean} isReverse 是否反向遍历
|
|
364
|
+
* @returns {*}
|
|
349
365
|
*/
|
|
350
|
-
|
|
366
|
+
function deepTraversal(tree, iterator, children = 'children', isReverse = false) {
|
|
351
367
|
let level = 0;
|
|
352
368
|
const walk = (arr, parent) => {
|
|
353
369
|
if (isReverse) {
|
|
354
370
|
for (let i = arr.length - 1; i >= 0; i--) {
|
|
355
|
-
const re = iterator(arr[i], i,
|
|
371
|
+
const re = iterator(arr[i], i, tree, parent, level);
|
|
356
372
|
if (re === 'break') {
|
|
357
373
|
break;
|
|
358
374
|
}
|
|
@@ -369,7 +385,7 @@
|
|
|
369
385
|
}
|
|
370
386
|
else {
|
|
371
387
|
for (let i = 0; i < arr.length; i++) {
|
|
372
|
-
const re = iterator(arr[i], i,
|
|
388
|
+
const re = iterator(arr[i], i, tree, parent, level);
|
|
373
389
|
if (re === 'break') {
|
|
374
390
|
break;
|
|
375
391
|
}
|
|
@@ -385,14 +401,15 @@
|
|
|
385
401
|
}
|
|
386
402
|
}
|
|
387
403
|
};
|
|
388
|
-
walk(
|
|
389
|
-
}
|
|
404
|
+
walk(tree, null);
|
|
405
|
+
}
|
|
390
406
|
/**
|
|
391
407
|
* 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
|
|
392
|
-
*
|
|
393
|
-
* @param {
|
|
394
|
-
* @param {
|
|
395
|
-
* @
|
|
408
|
+
*
|
|
409
|
+
* @param {ArrayLike<T>} tree - 树形数据
|
|
410
|
+
* @param {IdLike} nodeId - 元素ID
|
|
411
|
+
* @param {ITreeConf} config - 迭代配置项
|
|
412
|
+
* @returns {[IdLike[], ITreeItem<V>[]]} - 由parentId...childId, parentObject-childObject组成的二维数组
|
|
396
413
|
*/
|
|
397
414
|
function getTreeIds(tree, nodeId, config) {
|
|
398
415
|
const { children = 'children', id = 'id' } = config || {};
|
|
@@ -413,40 +430,12 @@
|
|
|
413
430
|
while (child && child.parentId) {
|
|
414
431
|
ids = [child.parentId, ...ids];
|
|
415
432
|
nodes = [child.parent, ...nodes];
|
|
416
|
-
child = flatArray.find(_ => _[id] === child.parentId);
|
|
433
|
+
child = flatArray.find(_ => _[id] === child.parentId); // eslint-disable-line
|
|
417
434
|
}
|
|
418
435
|
return [ids, nodes];
|
|
419
436
|
};
|
|
420
437
|
return getIds(toFlatArray(tree));
|
|
421
438
|
}
|
|
422
|
-
/**
|
|
423
|
-
* 异步ForEach函数
|
|
424
|
-
* @param {array} array
|
|
425
|
-
* @param {asyncFuntion} callback
|
|
426
|
-
* // asyncForEach 使用范例如下
|
|
427
|
-
// const start = async () => {
|
|
428
|
-
// await asyncForEach(result, async (item) => {
|
|
429
|
-
// await request(item);
|
|
430
|
-
// count++;
|
|
431
|
-
// });
|
|
432
|
-
|
|
433
|
-
// console.log('发送次数', count);
|
|
434
|
-
// }
|
|
435
|
-
|
|
436
|
-
// for await...of 使用范例如下
|
|
437
|
-
// const loadImages = async (images) => {
|
|
438
|
-
// for await (const item of images) {
|
|
439
|
-
// await request(item);
|
|
440
|
-
// count++;
|
|
441
|
-
// }
|
|
442
|
-
// }
|
|
443
|
-
* @return {*}
|
|
444
|
-
*/
|
|
445
|
-
async function asyncForEach(array, callback) {
|
|
446
|
-
for (let index = 0, len = array.length; index < len; index++) {
|
|
447
|
-
await callback(array[index], index, array);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
439
|
|
|
451
440
|
// @ref https://cubic-bezier.com/
|
|
452
441
|
const easingDefines = {
|
|
@@ -482,24 +471,24 @@
|
|
|
482
471
|
* @param {boolean} [bigger] 是否大写第一个字母
|
|
483
472
|
* @returns {string}
|
|
484
473
|
*/
|
|
485
|
-
|
|
474
|
+
function stringCamelCase(string, bigger) {
|
|
486
475
|
let string2 = string;
|
|
487
476
|
if (bigger) {
|
|
488
477
|
string2 = string.replace(/^./, origin => origin.toUpperCase());
|
|
489
478
|
}
|
|
490
479
|
const HUMP_RE = /[\s_-](.)/g;
|
|
491
480
|
return string2.replace(HUMP_RE, (orign, char) => char.toUpperCase());
|
|
492
|
-
}
|
|
481
|
+
}
|
|
493
482
|
/**
|
|
494
483
|
* 将字符串转换为连字格式
|
|
495
484
|
* @param {string} string
|
|
496
485
|
* @param {string} [separator] 分隔符,默认是"-"(短横线)
|
|
497
486
|
* @returns {string}
|
|
498
487
|
*/
|
|
499
|
-
|
|
488
|
+
function stringKebabCase(string, separator = '-') {
|
|
500
489
|
const string2 = string.replace(/^./, origin => origin.toLowerCase());
|
|
501
490
|
return string2.replace(/[A-Z]/g, origin => `${separator}${origin.toLowerCase()}`);
|
|
502
|
-
}
|
|
491
|
+
}
|
|
503
492
|
const STRING_ARABIC_NUMERALS = '0123456789';
|
|
504
493
|
const STRING_LOWERCASE_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
|
|
505
494
|
const STRING_UPPERCASE_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
@@ -515,7 +504,7 @@
|
|
|
515
504
|
* @param args
|
|
516
505
|
* @returns {string}
|
|
517
506
|
*/
|
|
518
|
-
|
|
507
|
+
function stringFormat(string, ...args) {
|
|
519
508
|
let index = 0;
|
|
520
509
|
const result = string.replace(placeholderRE, (origin) => {
|
|
521
510
|
const arg = args[index++];
|
|
@@ -533,7 +522,7 @@
|
|
|
533
522
|
}
|
|
534
523
|
});
|
|
535
524
|
return [result, ...args.splice(index).map(String)].join(' ');
|
|
536
|
-
}
|
|
525
|
+
}
|
|
537
526
|
const ev = (expression, data) => {
|
|
538
527
|
try {
|
|
539
528
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval,@typescript-eslint/no-unsafe-return
|
|
@@ -592,6 +581,36 @@
|
|
|
592
581
|
* @returns {string}
|
|
593
582
|
*/
|
|
594
583
|
const stringFill = (length, value = ' ') => new Array(length).fill(value).join('');
|
|
584
|
+
/**
|
|
585
|
+
* 字符串的像素宽度
|
|
586
|
+
* @param {string} str 目标字符串
|
|
587
|
+
* @param {number} fontSize 字符串字体大小
|
|
588
|
+
* @param {boolean} isRemoveDom 计算后是否移除中间dom元素
|
|
589
|
+
* @returns {*}
|
|
590
|
+
*/
|
|
591
|
+
function getStrWidthPx(str, fontSize = 14, isRemoveDom = false) {
|
|
592
|
+
let strWidth = 0;
|
|
593
|
+
console.assert(isString(str), `${str} 不是有效的字符串`);
|
|
594
|
+
if (isString(str) && str.length > 0) {
|
|
595
|
+
let getEle = document.querySelector('#getStrWidth1494304949567');
|
|
596
|
+
if (!getEle) {
|
|
597
|
+
const _ele = document.createElement('span');
|
|
598
|
+
_ele.id = 'getStrWidth1494304949567';
|
|
599
|
+
_ele.style.fontSize = fontSize + 'px';
|
|
600
|
+
_ele.style.whiteSpace = 'nowrap';
|
|
601
|
+
_ele.style.visibility = 'hidden';
|
|
602
|
+
_ele.textContent = str;
|
|
603
|
+
document.body.appendChild(_ele);
|
|
604
|
+
getEle = _ele;
|
|
605
|
+
}
|
|
606
|
+
getEle.textContent = str;
|
|
607
|
+
strWidth = getEle.offsetWidth;
|
|
608
|
+
if (isRemoveDom) {
|
|
609
|
+
document.body.appendChild(getEle);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return strWidth;
|
|
613
|
+
}
|
|
595
614
|
|
|
596
615
|
/**
|
|
597
616
|
* 判断元素是否包含某个样式名
|
|
@@ -599,11 +618,11 @@
|
|
|
599
618
|
* @param {string} className
|
|
600
619
|
* @returns {boolean}
|
|
601
620
|
*/
|
|
602
|
-
|
|
621
|
+
function hasClass(el, className) {
|
|
603
622
|
if (className.indexOf(' ') !== -1)
|
|
604
623
|
throw new Error('className should not contain space.');
|
|
605
624
|
return el.classList.contains(className);
|
|
606
|
-
}
|
|
625
|
+
}
|
|
607
626
|
const eachClassName = (classNames, func) => {
|
|
608
627
|
const classNameList = classNames.split(/\s+/g);
|
|
609
628
|
classNameList.forEach(func);
|
|
@@ -613,17 +632,17 @@
|
|
|
613
632
|
* @param {HTMLElement} el
|
|
614
633
|
* @param {string} classNames
|
|
615
634
|
*/
|
|
616
|
-
|
|
635
|
+
function addClass(el, classNames) {
|
|
617
636
|
eachClassName(classNames, className => el.classList.add(className));
|
|
618
|
-
}
|
|
637
|
+
}
|
|
619
638
|
/**
|
|
620
639
|
* 给元素移除样式名
|
|
621
640
|
* @param {HTMLElement} el
|
|
622
641
|
* @param {string} classNames
|
|
623
642
|
*/
|
|
624
|
-
|
|
643
|
+
function removeClass(el, classNames) {
|
|
625
644
|
eachClassName(classNames, className => el.classList.remove(className));
|
|
626
|
-
}
|
|
645
|
+
}
|
|
627
646
|
/**
|
|
628
647
|
* 设置元素样式
|
|
629
648
|
* @param {HTMLElement} el
|
|
@@ -642,12 +661,14 @@
|
|
|
642
661
|
};
|
|
643
662
|
/**
|
|
644
663
|
* 获取元素样式
|
|
645
|
-
* @param {HTMLElement} el
|
|
664
|
+
* @param {HTMLElement} el 元素
|
|
646
665
|
* @param {string} key
|
|
647
666
|
* @returns {string}
|
|
648
667
|
*/
|
|
649
|
-
|
|
650
|
-
|
|
668
|
+
function getStyle(el, key) {
|
|
669
|
+
return getComputedStyle(el).getPropertyValue(key);
|
|
670
|
+
}
|
|
671
|
+
function smoothScroll(options) {
|
|
651
672
|
return new Promise(resolve => {
|
|
652
673
|
const defaults = {
|
|
653
674
|
el: document,
|
|
@@ -722,6 +743,17 @@
|
|
|
722
743
|
domReadyCallbacks.push(callback);
|
|
723
744
|
}
|
|
724
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* 获取元素样式属性的计算值
|
|
748
|
+
* @param {HTMLElement} el
|
|
749
|
+
* @param {string} property
|
|
750
|
+
* @param {boolean} reNumber
|
|
751
|
+
* @returns {string|number}
|
|
752
|
+
*/
|
|
753
|
+
function getComputedCssVal(el, property, reNumber = true) {
|
|
754
|
+
const originVal = getComputedStyle(el).getPropertyValue(property) ?? '';
|
|
755
|
+
return reNumber ? Number(originVal.replace(/([0-9]*)(.*)/g, '$1')) : originVal;
|
|
756
|
+
}
|
|
725
757
|
|
|
726
758
|
const textEl = document.createElement('textarea');
|
|
727
759
|
setStyle(textEl, {
|
|
@@ -735,7 +767,7 @@
|
|
|
735
767
|
* 复制文本
|
|
736
768
|
* @param {string} text
|
|
737
769
|
*/
|
|
738
|
-
|
|
770
|
+
function copyText(text) {
|
|
739
771
|
textEl.value = text;
|
|
740
772
|
textEl.focus({ preventScroll: true });
|
|
741
773
|
textEl.select();
|
|
@@ -746,14 +778,14 @@
|
|
|
746
778
|
catch (err) {
|
|
747
779
|
// ignore
|
|
748
780
|
}
|
|
749
|
-
}
|
|
781
|
+
}
|
|
750
782
|
|
|
751
783
|
/**
|
|
752
784
|
* 获取cookie
|
|
753
785
|
* @param {string} name
|
|
754
786
|
* @returns {string}
|
|
755
787
|
*/
|
|
756
|
-
|
|
788
|
+
function cookieGet(name) {
|
|
757
789
|
const { cookie } = document;
|
|
758
790
|
if (!cookie)
|
|
759
791
|
return '';
|
|
@@ -765,14 +797,14 @@
|
|
|
765
797
|
return decodeURIComponent(val);
|
|
766
798
|
}
|
|
767
799
|
return '';
|
|
768
|
-
}
|
|
800
|
+
}
|
|
769
801
|
/**
|
|
770
802
|
* 设置 cookie
|
|
771
803
|
* @param {string} name
|
|
772
804
|
* @param {string} value
|
|
773
805
|
* @param {number | Date} [maxAge]
|
|
774
806
|
*/
|
|
775
|
-
|
|
807
|
+
function cookieSet(name, value, maxAge) {
|
|
776
808
|
const metas = [];
|
|
777
809
|
const EXPIRES = 'expires';
|
|
778
810
|
metas.push([name, encodeURIComponent(value)]);
|
|
@@ -791,13 +823,66 @@
|
|
|
791
823
|
return `${key}=${val}`;
|
|
792
824
|
})
|
|
793
825
|
.join(';');
|
|
794
|
-
}
|
|
826
|
+
}
|
|
795
827
|
/**
|
|
796
828
|
* 删除单个 cookie
|
|
797
829
|
* @param name cookie 名称
|
|
798
830
|
*/
|
|
799
831
|
const cookieDel = (name) => cookieSet(name, '', -1);
|
|
800
832
|
|
|
833
|
+
const isValidDate = (any) => isDate(any) && !isNaN(any.getTime());
|
|
834
|
+
/* istanbul ignore next */
|
|
835
|
+
const guessDateSeparator = (value) => {
|
|
836
|
+
if (!isString(value))
|
|
837
|
+
return;
|
|
838
|
+
const value2 = value.replace(/-/g, '/');
|
|
839
|
+
return new Date(value2);
|
|
840
|
+
};
|
|
841
|
+
/* istanbul ignore next */
|
|
842
|
+
const guessDateTimezone = (value) => {
|
|
843
|
+
if (!isString(value))
|
|
844
|
+
return;
|
|
845
|
+
const re = /([+-])(\d\d)(\d\d)$/;
|
|
846
|
+
const matches = re.exec(value);
|
|
847
|
+
if (!matches)
|
|
848
|
+
return;
|
|
849
|
+
const value2 = value.replace(re, 'Z');
|
|
850
|
+
const d = new Date(value2);
|
|
851
|
+
if (!isValidDate(d))
|
|
852
|
+
return;
|
|
853
|
+
const [, flag, hours, minutes] = matches;
|
|
854
|
+
const hours2 = parseInt(hours, 10);
|
|
855
|
+
const minutes2 = parseInt(minutes, 10);
|
|
856
|
+
const offset = (a, b) => (flag === '+' ? a - b : a + b);
|
|
857
|
+
d.setHours(offset(d.getHours(), hours2));
|
|
858
|
+
d.setMinutes(offset(d.getMinutes(), minutes2));
|
|
859
|
+
return d;
|
|
860
|
+
};
|
|
861
|
+
/**
|
|
862
|
+
* 解析为Date对象
|
|
863
|
+
* @param {DateValue} value - 可以是数值、字符串或 Date 对象
|
|
864
|
+
* @returns {Date} - 转换后的目标Date
|
|
865
|
+
*/
|
|
866
|
+
function dateParse(value) {
|
|
867
|
+
const d1 = new Date(value);
|
|
868
|
+
if (isValidDate(d1))
|
|
869
|
+
return d1;
|
|
870
|
+
// safari 浏览器的日期解析有问题
|
|
871
|
+
// new Date('2020-06-26 18:06:15') 返回值是一个非法日期对象
|
|
872
|
+
/* istanbul ignore next */
|
|
873
|
+
const d2 = guessDateSeparator(value);
|
|
874
|
+
/* istanbul ignore next */
|
|
875
|
+
if (isValidDate(d2))
|
|
876
|
+
return d2;
|
|
877
|
+
// safari 浏览器的日期解析有问题
|
|
878
|
+
// new Date('2020-06-26T18:06:15.000+0800') 返回值是一个非法日期对象
|
|
879
|
+
/* istanbul ignore next */
|
|
880
|
+
const d3 = guessDateTimezone(value);
|
|
881
|
+
/* istanbul ignore next */
|
|
882
|
+
if (isValidDate(d3))
|
|
883
|
+
return d3;
|
|
884
|
+
throw new SyntaxError(`${value.toString()} 不是一个合法的日期描述`);
|
|
885
|
+
}
|
|
801
886
|
/**
|
|
802
887
|
* 格式化为日期对象(带自定义格式化模板)
|
|
803
888
|
* @param {DateValue} value 可以是数值、字符串或 Date 对象
|
|
@@ -812,10 +897,69 @@
|
|
|
812
897
|
* - mm:分
|
|
813
898
|
* - ss:秒
|
|
814
899
|
* - SSS:毫秒
|
|
815
|
-
* - ww: 周
|
|
816
900
|
* @returns {string}
|
|
817
901
|
*/
|
|
818
|
-
const
|
|
902
|
+
// export const dateStringify = (value: DateValue, format = 'YYYY-MM-DD HH:mm:ss'): string => {
|
|
903
|
+
// const date = dateParse(value);
|
|
904
|
+
// let fmt = format;
|
|
905
|
+
// let ret;
|
|
906
|
+
// const opt: DateObj = {
|
|
907
|
+
// 'Y+': `${date.getFullYear()}`, // 年
|
|
908
|
+
// 'y+': `${date.getFullYear()}`, // 年
|
|
909
|
+
// 'M+': `${date.getMonth() + 1}`, // 月
|
|
910
|
+
// 'D+': `${date.getDate()}`, // 日
|
|
911
|
+
// 'd+': `${date.getDate()}`, // 日
|
|
912
|
+
// 'H+': `${date.getHours()}`, // 时
|
|
913
|
+
// 'm+': `${date.getMinutes()}`, // 分
|
|
914
|
+
// 's+': `${date.getSeconds()}`, // 秒
|
|
915
|
+
// 'S+': `${date.getMilliseconds()}` // 豪秒
|
|
916
|
+
// };
|
|
917
|
+
// for (const k in opt) {
|
|
918
|
+
// ret = new RegExp(`(${k})`).exec(fmt);
|
|
919
|
+
// if (ret) {
|
|
920
|
+
// fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
|
|
921
|
+
// }
|
|
922
|
+
// }
|
|
923
|
+
// return fmt;
|
|
924
|
+
// };
|
|
925
|
+
/**
|
|
926
|
+
* 将日期转换为一天的开始时间,即0点0分0秒0毫秒
|
|
927
|
+
* @param {DateValue} value
|
|
928
|
+
* @returns {Date}
|
|
929
|
+
*/
|
|
930
|
+
function dateToStart(value) {
|
|
931
|
+
const d = dateParse(value);
|
|
932
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* 将日期转换为一天的结束时间,即23点59分59秒999毫秒
|
|
936
|
+
* @param {DateValue} value
|
|
937
|
+
* @returns {Date}
|
|
938
|
+
*/
|
|
939
|
+
function dateToEnd(value) {
|
|
940
|
+
const d = dateToStart(value);
|
|
941
|
+
d.setDate(d.getDate() + 1);
|
|
942
|
+
return dateParse(d.getTime() - 1);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* 格式化为日期对象(带自定义格式化模板)
|
|
946
|
+
* @param {Date} value - 可以是数值、字符串或 Date 对象
|
|
947
|
+
* @param {string} [format] - 模板,默认是 YYYY-MM-DD HH:mm:ss,模板字符:
|
|
948
|
+
* - YYYY:年
|
|
949
|
+
* - yyyy: 年
|
|
950
|
+
* - MM:月
|
|
951
|
+
* - DD:日
|
|
952
|
+
* - dd: 日
|
|
953
|
+
* - HH:时(24 小时制)
|
|
954
|
+
* - hh:时(12 小时制)
|
|
955
|
+
* - mm:分
|
|
956
|
+
* - ss:秒
|
|
957
|
+
* - SSS:毫秒
|
|
958
|
+
* - ww: 周
|
|
959
|
+
* @returns {string} 格式化后的日期字符串
|
|
960
|
+
*/
|
|
961
|
+
function formatDate(value, format = 'YYYY-MM-DD HH:mm:ss') {
|
|
962
|
+
const date = dateParse(value);
|
|
819
963
|
let fmt = format;
|
|
820
964
|
let ret;
|
|
821
965
|
const opt = {
|
|
@@ -838,39 +982,39 @@
|
|
|
838
982
|
}
|
|
839
983
|
}
|
|
840
984
|
return fmt;
|
|
841
|
-
}
|
|
985
|
+
}
|
|
842
986
|
/**
|
|
843
987
|
* 计算向前或向后N天的具体日期
|
|
844
|
-
* @param {string} strDate 参考日期
|
|
845
|
-
* @param {number} n 正数:向后推算;负数:向前推算
|
|
846
|
-
* @param {string} sep 日期格式的分隔符
|
|
847
|
-
* @
|
|
988
|
+
* @param {string} strDate - 参考日期
|
|
989
|
+
* @param {number} n - 正数:向后推算;负数:向前推算
|
|
990
|
+
* @param {string} sep - 日期格式的分隔符
|
|
991
|
+
* @returns {string} 计算后的目标日期
|
|
848
992
|
*/
|
|
849
993
|
function calculateDate(strDate, n, sep = '-') {
|
|
850
994
|
//strDate 为字符串日期 如:'2019-01-01' n为你要传入的参数,当前为0,前一天为-1,后一天为1
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
995
|
+
const dateArr = strDate.split(sep); //这边给定一个特定时间
|
|
996
|
+
const newDate = new Date(+dateArr[0], +dateArr[1] - 1, +dateArr[2]);
|
|
997
|
+
const befminuts = newDate.getTime() + 1000 * 60 * 60 * 24 * parseInt(String(n)); //计算前几天用减,计算后几天用加,最后一个就是多少天的数量
|
|
998
|
+
const beforeDat = new Date();
|
|
855
999
|
beforeDat.setTime(befminuts);
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1000
|
+
const befMonth = beforeDat.getMonth() + 1;
|
|
1001
|
+
const mon = befMonth >= 10 ? befMonth : '0' + befMonth;
|
|
1002
|
+
const befDate = beforeDat.getDate();
|
|
1003
|
+
const da = befDate >= 10 ? befDate : '0' + befDate;
|
|
1004
|
+
const finalNewDate = beforeDat.getFullYear() + '-' + mon + '-' + da;
|
|
861
1005
|
return finalNewDate;
|
|
862
1006
|
}
|
|
863
1007
|
/**
|
|
864
1008
|
* 计算向前或向后N天的具体时间日期
|
|
865
|
-
* @param {number} n 正数:向后推算;负数:向前推算
|
|
866
|
-
* @param {string} dateSep 日期分隔符
|
|
867
|
-
* @param {string} timeSep 时间分隔符
|
|
868
|
-
* @
|
|
1009
|
+
* @param {number} n - 正数:向后推算;负数:向前推算
|
|
1010
|
+
* @param {string} dateSep - 日期分隔符
|
|
1011
|
+
* @param {string} timeSep - 时间分隔符
|
|
1012
|
+
* @returns {string} 转换后的目标日期时间
|
|
869
1013
|
*/
|
|
870
1014
|
function calculateDateTime(n, dateSep = '-', timeSep = ':') {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1015
|
+
const date = new Date();
|
|
1016
|
+
const separator1 = '-';
|
|
1017
|
+
const separator2 = ':';
|
|
874
1018
|
let year = date.getFullYear();
|
|
875
1019
|
let month = date.getMonth() + 1;
|
|
876
1020
|
let strDate = date.getDate() + n;
|
|
@@ -962,7 +1106,7 @@
|
|
|
962
1106
|
* @param {string} queryString
|
|
963
1107
|
* @returns {Params}
|
|
964
1108
|
*/
|
|
965
|
-
|
|
1109
|
+
function qsParse(queryString) {
|
|
966
1110
|
const params = new URLSearchParams(queryString);
|
|
967
1111
|
const result = {};
|
|
968
1112
|
for (const [key, val] of params.entries()) {
|
|
@@ -976,7 +1120,7 @@
|
|
|
976
1120
|
result[key] = params.getAll(key);
|
|
977
1121
|
}
|
|
978
1122
|
return result;
|
|
979
|
-
}
|
|
1123
|
+
}
|
|
980
1124
|
const defaultReplacer = (val) => {
|
|
981
1125
|
if (isString(val))
|
|
982
1126
|
return val;
|
|
@@ -994,7 +1138,7 @@
|
|
|
994
1138
|
* @param {Replacer} replacer
|
|
995
1139
|
* @returns {string}
|
|
996
1140
|
*/
|
|
997
|
-
|
|
1141
|
+
function qsStringify(query, replacer = defaultReplacer) {
|
|
998
1142
|
const params = new URLSearchParams();
|
|
999
1143
|
objectEach(query, (val, key) => {
|
|
1000
1144
|
if (isArray(val)) {
|
|
@@ -1013,7 +1157,7 @@
|
|
|
1013
1157
|
}
|
|
1014
1158
|
});
|
|
1015
1159
|
return params.toString();
|
|
1016
|
-
}
|
|
1160
|
+
}
|
|
1017
1161
|
|
|
1018
1162
|
const anchorEl = document.createElement('a');
|
|
1019
1163
|
/**
|
|
@@ -1089,15 +1233,15 @@
|
|
|
1089
1233
|
* @param {string} url
|
|
1090
1234
|
* @param {LooseParams} params
|
|
1091
1235
|
*/
|
|
1092
|
-
|
|
1236
|
+
function downloadURL(url, params) {
|
|
1093
1237
|
window.open(params ? urlSetParams(url, params) : url);
|
|
1094
|
-
}
|
|
1238
|
+
}
|
|
1095
1239
|
/**
|
|
1096
1240
|
* 通过 A 链接的方式下载
|
|
1097
1241
|
* @param {string} href
|
|
1098
1242
|
* @param {string} filename
|
|
1099
1243
|
*/
|
|
1100
|
-
|
|
1244
|
+
function downloadHref(href, filename) {
|
|
1101
1245
|
const eleLink = document.createElement('a');
|
|
1102
1246
|
eleLink.download = filename;
|
|
1103
1247
|
eleLink.style.display = 'none';
|
|
@@ -1105,17 +1249,17 @@
|
|
|
1105
1249
|
document.body.appendChild(eleLink);
|
|
1106
1250
|
eleLink.click();
|
|
1107
1251
|
setTimeout(() => document.body.removeChild(eleLink));
|
|
1108
|
-
}
|
|
1252
|
+
}
|
|
1109
1253
|
/**
|
|
1110
1254
|
* 将大文件对象通过 A 链接的方式下载
|
|
1111
1255
|
* @param {Blob} blob
|
|
1112
1256
|
* @param {string} filename
|
|
1113
1257
|
*/
|
|
1114
|
-
|
|
1258
|
+
function downloadBlob(blob, filename) {
|
|
1115
1259
|
const objURL = URL.createObjectURL(blob);
|
|
1116
1260
|
downloadHref(objURL, filename);
|
|
1117
1261
|
setTimeout(() => URL.revokeObjectURL(objURL));
|
|
1118
|
-
}
|
|
1262
|
+
}
|
|
1119
1263
|
/**
|
|
1120
1264
|
* 将指定数据格式通过 A 链接的方式下载
|
|
1121
1265
|
* @param {AnyObject | AnyObject[]} data
|
|
@@ -1123,7 +1267,7 @@
|
|
|
1123
1267
|
* @param {string} filename
|
|
1124
1268
|
* @param {string[]} [headers]
|
|
1125
1269
|
*/
|
|
1126
|
-
|
|
1270
|
+
function downloadData(data, fileType, filename, headers) {
|
|
1127
1271
|
filename = filename.replace(`.${fileType}`, '') + `.${fileType}`;
|
|
1128
1272
|
if (fileType === 'json') {
|
|
1129
1273
|
const blob = new Blob([JSON.stringify(data, null, 4)]);
|
|
@@ -1150,14 +1294,14 @@
|
|
|
1150
1294
|
const href = 'data:' + MIMETypes[fileType] + ';charset=utf-8,\ufeff' + encodeURIComponent(headerStr + bodyStr);
|
|
1151
1295
|
downloadHref(href, filename);
|
|
1152
1296
|
}
|
|
1153
|
-
}
|
|
1297
|
+
}
|
|
1154
1298
|
|
|
1155
1299
|
/**
|
|
1156
1300
|
* 等待一段时间
|
|
1157
1301
|
* @param {number} timeout 等待时间,单位毫秒
|
|
1158
1302
|
* @returns {Promise<void>}
|
|
1159
1303
|
*/
|
|
1160
|
-
|
|
1304
|
+
function wait(timeout = 1) {
|
|
1161
1305
|
return new Promise(resolve => setTimeout(resolve, timeout));
|
|
1162
1306
|
}
|
|
1163
1307
|
/**
|
|
@@ -1169,7 +1313,7 @@
|
|
|
1169
1313
|
* @param {number} concurrency 并发数量,默认无限
|
|
1170
1314
|
* @returns {Promise<R[]>}
|
|
1171
1315
|
*/
|
|
1172
|
-
|
|
1316
|
+
function asyncMap(list, mapper, concurrency = Infinity) {
|
|
1173
1317
|
return new Promise((resolve, reject) => {
|
|
1174
1318
|
const iterator = list[Symbol.iterator]();
|
|
1175
1319
|
const limit = Math.min(list.length, concurrency);
|
|
@@ -1207,10 +1351,11 @@
|
|
|
1207
1351
|
|
|
1208
1352
|
/**
|
|
1209
1353
|
* 选择本地文件
|
|
1210
|
-
* @param {
|
|
1211
|
-
* @
|
|
1354
|
+
* @param {string} accept 上传的文件类型,用于过滤
|
|
1355
|
+
* @param {Function} changeCb 选择文件回调
|
|
1356
|
+
* @returns {HTMLInputElement}
|
|
1212
1357
|
*/
|
|
1213
|
-
function chooseLocalFile(
|
|
1358
|
+
function chooseLocalFile(accept, changeCb) {
|
|
1214
1359
|
const inputObj = document.createElement('input');
|
|
1215
1360
|
inputObj.setAttribute('id', String(Date.now()));
|
|
1216
1361
|
inputObj.setAttribute('type', 'file');
|
|
@@ -1233,17 +1378,18 @@
|
|
|
1233
1378
|
* @desc 网页加水印的工具类
|
|
1234
1379
|
*/
|
|
1235
1380
|
/**
|
|
1236
|
-
* canvas 实现
|
|
1381
|
+
* canvas 实现 水印, 具备防删除功能
|
|
1237
1382
|
* @param {ICanvasWM} canvasWM
|
|
1383
|
+
* @example genCanvasWM({ content: 'QQMusicFE' })
|
|
1238
1384
|
*/
|
|
1239
|
-
|
|
1385
|
+
function genCanvasWM(canvasWM) {
|
|
1240
1386
|
const { container = document.body, width = '300px', height = '200px', textAlign = 'center', textBaseline = 'middle', font = '20px PingFangSC-Medium,PingFang SC',
|
|
1241
1387
|
// fontWeight = 500,
|
|
1242
1388
|
fillStyle = 'rgba(189, 177, 167, .3)', content = '请勿外传', rotate = 30, zIndex = 2147483647 } = canvasWM;
|
|
1243
1389
|
// 仅限主页面添加水印
|
|
1244
|
-
if (!location.pathname.includes('/home')) {
|
|
1245
|
-
|
|
1246
|
-
}
|
|
1390
|
+
// if (!location.pathname.includes('/home')) {
|
|
1391
|
+
// return;
|
|
1392
|
+
// }
|
|
1247
1393
|
const args = canvasWM;
|
|
1248
1394
|
const canvas = document.createElement('canvas');
|
|
1249
1395
|
canvas.setAttribute('width', width);
|
|
@@ -1304,20 +1450,359 @@
|
|
|
1304
1450
|
});
|
|
1305
1451
|
mo.observe(container, { attributes: true, subtree: true, childList: true });
|
|
1306
1452
|
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/**
|
|
1456
|
+
* 防抖函数
|
|
1457
|
+
* 当函数被连续调用时,该函数并不执行,只有当其全部停止调用超过一定时间后才执行1次。
|
|
1458
|
+
* 例如:上电梯的时候,大家陆陆续续进来,电梯的门不会关上,只有当一段时间都没有人上来,电梯才会关门。
|
|
1459
|
+
* @param {F} func
|
|
1460
|
+
* @param {number} wait
|
|
1461
|
+
* @returns {DebounceFunc<F>}
|
|
1462
|
+
*/
|
|
1463
|
+
const debounce = (func, wait) => {
|
|
1464
|
+
let timeout;
|
|
1465
|
+
let canceled = false;
|
|
1466
|
+
const f = function (...args) {
|
|
1467
|
+
if (canceled)
|
|
1468
|
+
return;
|
|
1469
|
+
clearTimeout(timeout);
|
|
1470
|
+
timeout = setTimeout(() => {
|
|
1471
|
+
func.call(this, ...args);
|
|
1472
|
+
}, wait);
|
|
1473
|
+
};
|
|
1474
|
+
f.cancel = () => {
|
|
1475
|
+
clearTimeout(timeout);
|
|
1476
|
+
canceled = true;
|
|
1477
|
+
};
|
|
1478
|
+
return f;
|
|
1479
|
+
};
|
|
1480
|
+
/**
|
|
1481
|
+
* 节流函数
|
|
1482
|
+
* 节流就是节约流量,将连续触发的事件稀释成预设评率。 比如每间隔1秒执行一次函数,无论这期间触发多少次事件。
|
|
1483
|
+
* 这有点像公交车,无论在站点等车的人多不多,公交车只会按时来一班,不会来一个人就来一辆公交车。
|
|
1484
|
+
* @param {F} func
|
|
1485
|
+
* @param {number} wait
|
|
1486
|
+
* @param {boolean} immediate
|
|
1487
|
+
* @returns {ThrottleFunc<F>}
|
|
1488
|
+
*/
|
|
1489
|
+
const throttle = (func, wait, immediate) => {
|
|
1490
|
+
let timeout;
|
|
1491
|
+
let canceled = false;
|
|
1492
|
+
let lastCalledTime = 0;
|
|
1493
|
+
const f = function (...args) {
|
|
1494
|
+
if (canceled)
|
|
1495
|
+
return;
|
|
1496
|
+
const now = Date.now();
|
|
1497
|
+
const call = () => {
|
|
1498
|
+
lastCalledTime = now;
|
|
1499
|
+
func.call(this, ...args);
|
|
1500
|
+
};
|
|
1501
|
+
// 第一次执行
|
|
1502
|
+
if (lastCalledTime === 0) {
|
|
1503
|
+
if (immediate) {
|
|
1504
|
+
return call();
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
lastCalledTime = now;
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
const remain = lastCalledTime + wait - now;
|
|
1512
|
+
if (remain > 0) {
|
|
1513
|
+
clearTimeout(timeout);
|
|
1514
|
+
timeout = setTimeout(() => call(), wait);
|
|
1515
|
+
}
|
|
1516
|
+
else {
|
|
1517
|
+
call();
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
f.cancel = () => {
|
|
1521
|
+
clearTimeout(timeout);
|
|
1522
|
+
canceled = true;
|
|
1523
|
+
};
|
|
1524
|
+
return f;
|
|
1525
|
+
};
|
|
1526
|
+
/**
|
|
1527
|
+
* 单次函数
|
|
1528
|
+
* @param {AnyFunc} func
|
|
1529
|
+
* @returns {AnyFunc}
|
|
1530
|
+
*/
|
|
1531
|
+
const once = (func) => {
|
|
1532
|
+
let called = false;
|
|
1533
|
+
let result;
|
|
1534
|
+
return function (...args) {
|
|
1535
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
1536
|
+
if (called)
|
|
1537
|
+
return result;
|
|
1538
|
+
called = true;
|
|
1539
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1540
|
+
result = func.call(this, ...args);
|
|
1541
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
1542
|
+
return result;
|
|
1543
|
+
};
|
|
1544
|
+
};
|
|
1545
|
+
/**
|
|
1546
|
+
* 设置全局变量
|
|
1547
|
+
* @param {string | number | symbol} key
|
|
1548
|
+
* @param val
|
|
1549
|
+
*/
|
|
1550
|
+
function setGlobal(key, val) {
|
|
1551
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1552
|
+
// @ts-ignore
|
|
1553
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1554
|
+
if (typeof globalThis !== 'undefined')
|
|
1555
|
+
globalThis[key] = val;
|
|
1556
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1557
|
+
// @ts-ignore
|
|
1558
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1559
|
+
else if (typeof window !== 'undefined')
|
|
1560
|
+
window[key] = val;
|
|
1561
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1562
|
+
// @ts-ignore
|
|
1563
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1564
|
+
else if (typeof global !== 'undefined')
|
|
1565
|
+
global[key] = val;
|
|
1566
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1567
|
+
// @ts-ignore
|
|
1568
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1569
|
+
else if (typeof self !== 'undefined')
|
|
1570
|
+
self[key] = val;
|
|
1571
|
+
else
|
|
1572
|
+
throw new SyntaxError('当前环境下无法设置全局属性');
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* 设置全局变量
|
|
1576
|
+
* @param {string | number | symbol} key
|
|
1577
|
+
* @param val
|
|
1578
|
+
*/
|
|
1579
|
+
function getGlobal(key) {
|
|
1580
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1581
|
+
// @ts-ignore
|
|
1582
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1583
|
+
if (typeof globalThis !== 'undefined')
|
|
1584
|
+
return globalThis[key];
|
|
1585
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1586
|
+
// @ts-ignore
|
|
1587
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1588
|
+
else if (typeof window !== 'undefined')
|
|
1589
|
+
return window[key];
|
|
1590
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1591
|
+
// @ts-ignore
|
|
1592
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1593
|
+
else if (typeof global !== 'undefined')
|
|
1594
|
+
return global[key];
|
|
1595
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1596
|
+
// @ts-ignore
|
|
1597
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1598
|
+
else if (typeof self !== 'undefined')
|
|
1599
|
+
return self[key];
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/**
|
|
1603
|
+
* 随机整数
|
|
1604
|
+
* @param {number} min
|
|
1605
|
+
* @param {number} max
|
|
1606
|
+
* @returns {number}
|
|
1607
|
+
*/
|
|
1608
|
+
const randomNumber = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
|
|
1609
|
+
const STRING_POOL = `${STRING_ARABIC_NUMERALS}${STRING_UPPERCASE_ALPHA}${STRING_LOWERCASE_ALPHA}`;
|
|
1610
|
+
/**
|
|
1611
|
+
* 随机字符串
|
|
1612
|
+
* @param {number | string} length
|
|
1613
|
+
* @param {string} pool
|
|
1614
|
+
* @returns {string}
|
|
1615
|
+
*/
|
|
1616
|
+
const randomString = (length, pool) => {
|
|
1617
|
+
let _length = 0;
|
|
1618
|
+
let _pool = STRING_POOL;
|
|
1619
|
+
if (isString(pool)) {
|
|
1620
|
+
_length = length;
|
|
1621
|
+
_pool = pool;
|
|
1622
|
+
}
|
|
1623
|
+
else if (isNumber(length)) {
|
|
1624
|
+
_length = length;
|
|
1625
|
+
}
|
|
1626
|
+
else if (isString(length)) {
|
|
1627
|
+
_pool = length;
|
|
1628
|
+
}
|
|
1629
|
+
let times = Math.max(_length, 1);
|
|
1630
|
+
let result = '';
|
|
1631
|
+
const min = 0;
|
|
1632
|
+
const max = _pool.length - 1;
|
|
1633
|
+
if (max < 2)
|
|
1634
|
+
throw new Error('字符串池长度不能少于 2');
|
|
1635
|
+
while (times--) {
|
|
1636
|
+
const index = randomNumber(min, max);
|
|
1637
|
+
result += _pool[index];
|
|
1638
|
+
}
|
|
1639
|
+
return result;
|
|
1640
|
+
};
|
|
1641
|
+
/**
|
|
1642
|
+
* 优先浏览器原生能力获取 UUID v4
|
|
1643
|
+
* @returns {string}
|
|
1644
|
+
*/
|
|
1645
|
+
function randomUuid() {
|
|
1646
|
+
if (typeof URL === 'undefined' || !URL.createObjectURL || typeof Blob === 'undefined') {
|
|
1647
|
+
const hex = '0123456789abcdef';
|
|
1648
|
+
const model = 'xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx';
|
|
1649
|
+
let str = '';
|
|
1650
|
+
for (let i = 0; i < model.length; i++) {
|
|
1651
|
+
const rnd = randomNumber(0, 15);
|
|
1652
|
+
str += model[i] == '-' || model[i] == '4' ? model[i] : hex[rnd];
|
|
1653
|
+
}
|
|
1654
|
+
return str;
|
|
1655
|
+
}
|
|
1656
|
+
return /[^/]+$/.exec(URL.createObjectURL(new Blob()).slice())[0];
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
const HEX_POOL = `${STRING_ARABIC_NUMERALS}${STRING_UPPERCASE_ALPHA}${STRING_LOWERCASE_ALPHA}`;
|
|
1660
|
+
const supportBigInt = typeof BigInt !== 'undefined';
|
|
1661
|
+
const jsbi = () => getGlobal('JSBI');
|
|
1662
|
+
const toBigInt = (n) => (supportBigInt ? BigInt(n) : jsbi().BigInt(n));
|
|
1663
|
+
const divide = (x, y) => (supportBigInt ? x / y : jsbi().divide(x, y));
|
|
1664
|
+
const remainder = (x, y) => (supportBigInt ? x % y : jsbi().remainder(x, y));
|
|
1665
|
+
/**
|
|
1666
|
+
* 将十进制转换成任意进制
|
|
1667
|
+
* @param {number | string} decimal 十进制数值或字符串,可以是任意长度,会使用大数进行计算
|
|
1668
|
+
* @param {string} [hexPool] 进制池,默认 62 进制
|
|
1669
|
+
* @returns {string}
|
|
1670
|
+
*/
|
|
1671
|
+
function numberToHex(decimal, hexPool = HEX_POOL) {
|
|
1672
|
+
if (hexPool.length < 2)
|
|
1673
|
+
throw new Error('进制池长度不能少于 2');
|
|
1674
|
+
if (!supportBigInt) {
|
|
1675
|
+
throw new Error('需要安装 jsbi 模块并将 JSBI 设置为全局变量:\nimport JSBI from "jsbi"; window.JSBI = JSBI;');
|
|
1676
|
+
}
|
|
1677
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1678
|
+
let bigInt = toBigInt(decimal);
|
|
1679
|
+
const ret = [];
|
|
1680
|
+
const { length } = hexPool;
|
|
1681
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1682
|
+
const bigLength = toBigInt(length);
|
|
1683
|
+
const execute = () => {
|
|
1684
|
+
const y = Number(remainder(bigInt, bigLength));
|
|
1685
|
+
bigInt = divide(bigInt, bigLength);
|
|
1686
|
+
ret.unshift(hexPool[y]);
|
|
1687
|
+
if (bigInt > 0) {
|
|
1688
|
+
execute();
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
execute();
|
|
1692
|
+
return ret.join('');
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* 缩写
|
|
1696
|
+
* @param {number | string} num
|
|
1697
|
+
* @param {Array<string>} units
|
|
1698
|
+
* @param {number} ratio
|
|
1699
|
+
* @param {number} exponent
|
|
1700
|
+
* @returns {string}
|
|
1701
|
+
*/
|
|
1702
|
+
const numberAbbr = (num, units, ratio = 1000, exponent) => {
|
|
1703
|
+
const { length } = units;
|
|
1704
|
+
if (length === 0)
|
|
1705
|
+
throw new Error('至少需要一个单位');
|
|
1706
|
+
let num2 = Number(num);
|
|
1707
|
+
let times = 0;
|
|
1708
|
+
while (num2 >= ratio && times < length - 1) {
|
|
1709
|
+
num2 = num2 / ratio;
|
|
1710
|
+
times++;
|
|
1711
|
+
}
|
|
1712
|
+
const value = num2.toFixed(exponent);
|
|
1713
|
+
const unit = units[times];
|
|
1714
|
+
return value.toString() + '' + unit;
|
|
1715
|
+
};
|
|
1716
|
+
/**
|
|
1717
|
+
* 将数字格式化成千位分隔符显示的字符串
|
|
1718
|
+
* @param {number} val 数字
|
|
1719
|
+
* @param {'int' | 'float'} type 展示分段显示的类型 int:整型 | float:浮点型
|
|
1720
|
+
* @returns {string}
|
|
1721
|
+
*/
|
|
1722
|
+
function formatNumber(val, type = 'int') {
|
|
1723
|
+
return type === 'int' ? parseInt(String(val)).toLocaleString() : Number(val).toLocaleString('en-US');
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
const padStartWithZero = (str, maxLength = 2) => String(str).padStart(maxLength, '0');
|
|
1727
|
+
let safeNo = 0;
|
|
1728
|
+
let lastTimestamp = 0;
|
|
1729
|
+
// 安全后缀长度,按 1 毫秒运算 99999 次 JS 代码来进行估算
|
|
1730
|
+
// 那么,补足长度为 5
|
|
1731
|
+
// 时间戳模式长度为 13
|
|
1732
|
+
// 取最长 5 + 13 = 18
|
|
1733
|
+
const UNIQUE_NUMBER_SAFE_LENGTH = 18;
|
|
1734
|
+
const FIX_SAFE_LENGTH = 5;
|
|
1735
|
+
const TIMESTAMP_LENGTH = 13;
|
|
1736
|
+
/**
|
|
1737
|
+
* 生成唯一不重复数值
|
|
1738
|
+
* @param {number} length
|
|
1739
|
+
* @returns {string}
|
|
1740
|
+
*/
|
|
1741
|
+
const uniqueNumber = (length = UNIQUE_NUMBER_SAFE_LENGTH) => {
|
|
1742
|
+
const now = Date.now();
|
|
1743
|
+
length = Math.max(length, UNIQUE_NUMBER_SAFE_LENGTH);
|
|
1744
|
+
if (now !== lastTimestamp) {
|
|
1745
|
+
lastTimestamp = now;
|
|
1746
|
+
safeNo = 0;
|
|
1747
|
+
}
|
|
1748
|
+
const timestamp = `${now}`;
|
|
1749
|
+
let random = '';
|
|
1750
|
+
const rndLength = length - FIX_SAFE_LENGTH - TIMESTAMP_LENGTH;
|
|
1751
|
+
if (rndLength > 0) {
|
|
1752
|
+
const rndMin = 10 ** (rndLength - 1);
|
|
1753
|
+
const rndMax = 10 ** rndLength - 1;
|
|
1754
|
+
const rnd = randomNumber(rndMin, rndMax);
|
|
1755
|
+
random = `${rnd}`;
|
|
1756
|
+
}
|
|
1757
|
+
const safe = padStartWithZero(safeNo, FIX_SAFE_LENGTH);
|
|
1758
|
+
safeNo++;
|
|
1759
|
+
return `${timestamp}${random}${safe}`;
|
|
1760
|
+
};
|
|
1761
|
+
const randomFromPool = (pool) => {
|
|
1762
|
+
const poolIndex = randomNumber(0, pool.length - 1);
|
|
1763
|
+
return pool[poolIndex];
|
|
1764
|
+
};
|
|
1765
|
+
/**
|
|
1766
|
+
* 生成唯一不重复字符串
|
|
1767
|
+
* @param {number | string} length
|
|
1768
|
+
* @param {string} pool
|
|
1769
|
+
* @returns {string}
|
|
1770
|
+
*/
|
|
1771
|
+
const uniqueString = (length, pool) => {
|
|
1772
|
+
let _length = 0;
|
|
1773
|
+
let _pool = HEX_POOL;
|
|
1774
|
+
if (isString(pool)) {
|
|
1775
|
+
_length = length;
|
|
1776
|
+
_pool = pool;
|
|
1777
|
+
}
|
|
1778
|
+
else if (isNumber(length)) {
|
|
1779
|
+
_length = length;
|
|
1780
|
+
}
|
|
1781
|
+
else if (isString(length)) {
|
|
1782
|
+
_pool = length;
|
|
1783
|
+
}
|
|
1784
|
+
let uniqueString = numberToHex(uniqueNumber(), _pool);
|
|
1785
|
+
let insertLength = _length - uniqueString.length;
|
|
1786
|
+
if (insertLength <= 0)
|
|
1787
|
+
return uniqueString;
|
|
1788
|
+
while (insertLength--) {
|
|
1789
|
+
uniqueString += randomFromPool(_pool);
|
|
1790
|
+
}
|
|
1791
|
+
return uniqueString;
|
|
1307
1792
|
};
|
|
1308
|
-
// 调用
|
|
1309
|
-
// __canvasWM({ content: 'QQMusicFE' })
|
|
1310
1793
|
|
|
1794
|
+
exports.HEX_POOL = HEX_POOL;
|
|
1311
1795
|
exports.STRING_ARABIC_NUMERALS = STRING_ARABIC_NUMERALS;
|
|
1312
1796
|
exports.STRING_LOWERCASE_ALPHA = STRING_LOWERCASE_ALPHA;
|
|
1797
|
+
exports.STRING_POOL = STRING_POOL;
|
|
1313
1798
|
exports.STRING_UPPERCASE_ALPHA = STRING_UPPERCASE_ALPHA;
|
|
1799
|
+
exports.UNIQUE_NUMBER_SAFE_LENGTH = UNIQUE_NUMBER_SAFE_LENGTH;
|
|
1314
1800
|
exports.addClass = addClass;
|
|
1315
1801
|
exports.arrayEach = arrayEach;
|
|
1316
1802
|
exports.arrayEachAsync = arrayEachAsync;
|
|
1317
1803
|
exports.arrayInsertBefore = arrayInsertBefore;
|
|
1318
1804
|
exports.arrayLike = arrayLike;
|
|
1319
1805
|
exports.arrayRemove = arrayRemove;
|
|
1320
|
-
exports.asyncForEach = asyncForEach;
|
|
1321
1806
|
exports.asyncMap = asyncMap;
|
|
1322
1807
|
exports.calculateDate = calculateDate;
|
|
1323
1808
|
exports.calculateDateTime = calculateDateTime;
|
|
@@ -1327,13 +1812,21 @@
|
|
|
1327
1812
|
exports.cookieGet = cookieGet;
|
|
1328
1813
|
exports.cookieSet = cookieSet;
|
|
1329
1814
|
exports.copyText = copyText;
|
|
1815
|
+
exports.dateParse = dateParse;
|
|
1816
|
+
exports.dateToEnd = dateToEnd;
|
|
1817
|
+
exports.dateToStart = dateToStart;
|
|
1818
|
+
exports.debounce = debounce;
|
|
1330
1819
|
exports.deepTraversal = deepTraversal;
|
|
1331
1820
|
exports.downloadBlob = downloadBlob;
|
|
1332
1821
|
exports.downloadData = downloadData;
|
|
1333
1822
|
exports.downloadHref = downloadHref;
|
|
1334
1823
|
exports.downloadURL = downloadURL;
|
|
1335
1824
|
exports.formatDate = formatDate;
|
|
1825
|
+
exports.formatNumber = formatNumber;
|
|
1336
1826
|
exports.genCanvasWM = genCanvasWM;
|
|
1827
|
+
exports.getComputedCssVal = getComputedCssVal;
|
|
1828
|
+
exports.getGlobal = getGlobal;
|
|
1829
|
+
exports.getStrWidthPx = getStrWidthPx;
|
|
1337
1830
|
exports.getStyle = getStyle;
|
|
1338
1831
|
exports.getTreeIds = getTreeIds;
|
|
1339
1832
|
exports.hasClass = hasClass;
|
|
@@ -1354,6 +1847,9 @@
|
|
|
1354
1847
|
exports.isString = isString;
|
|
1355
1848
|
exports.isSymbol = isSymbol;
|
|
1356
1849
|
exports.isUndefined = isUndefined;
|
|
1850
|
+
exports.isValidDate = isValidDate;
|
|
1851
|
+
exports.numberAbbr = numberAbbr;
|
|
1852
|
+
exports.numberToHex = numberToHex;
|
|
1357
1853
|
exports.objectAssign = objectAssign;
|
|
1358
1854
|
exports.objectEach = objectEach;
|
|
1359
1855
|
exports.objectEachAsync = objectEachAsync;
|
|
@@ -1365,11 +1861,16 @@
|
|
|
1365
1861
|
exports.objectOmit = objectOmit;
|
|
1366
1862
|
exports.objectPick = objectPick;
|
|
1367
1863
|
exports.onDomReady = onDomReady;
|
|
1864
|
+
exports.once = once;
|
|
1368
1865
|
exports.pathJoin = pathJoin;
|
|
1369
1866
|
exports.pathNormalize = pathNormalize;
|
|
1370
1867
|
exports.qsParse = qsParse;
|
|
1371
1868
|
exports.qsStringify = qsStringify;
|
|
1869
|
+
exports.randomNumber = randomNumber;
|
|
1870
|
+
exports.randomString = randomString;
|
|
1871
|
+
exports.randomUuid = randomUuid;
|
|
1372
1872
|
exports.removeClass = removeClass;
|
|
1873
|
+
exports.setGlobal = setGlobal;
|
|
1373
1874
|
exports.setStyle = setStyle;
|
|
1374
1875
|
exports.smoothScroll = smoothScroll;
|
|
1375
1876
|
exports.stringAssign = stringAssign;
|
|
@@ -1378,6 +1879,10 @@
|
|
|
1378
1879
|
exports.stringFill = stringFill;
|
|
1379
1880
|
exports.stringFormat = stringFormat;
|
|
1380
1881
|
exports.stringKebabCase = stringKebabCase;
|
|
1882
|
+
exports.throttle = throttle;
|
|
1883
|
+
exports.typeIs = typeIs;
|
|
1884
|
+
exports.uniqueNumber = uniqueNumber;
|
|
1885
|
+
exports.uniqueString = uniqueString;
|
|
1381
1886
|
exports.urlDelParams = urlDelParams;
|
|
1382
1887
|
exports.urlParse = urlParse;
|
|
1383
1888
|
exports.urlSetParams = urlSetParams;
|