sculp-js 0.1.1 → 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/umd/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * sculp-js v0.1.0
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
- // eslint-disable-next-line @typescript-eslint/ban-types
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
- const objectHas = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
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
- const objectEach = (obj, iterator) => {
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
- const objectAssign = (source, ...targets) => {
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
- const objectFill = (source, target, fillable) => {
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
- * @return {AnyObject | AnyArray}
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
- const arrayLike = (any) => {
270
+ function arrayLike(any) {
259
271
  if (isArray(any))
260
272
  return true;
261
273
  if (isString(any))
@@ -263,14 +275,16 @@
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
- const arrayEach = (array, iterator, reverse = false) => {
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];
@@ -285,12 +299,12 @@
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
- const arrayInsertBefore = (array, start, to) => {
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 {array} deepList
346
- * @param {function} iterator
347
- * @param {array} children
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
- const deepTraversal = (deepList, iterator, children = 'children', isReverse = false) => {
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, deepList, parent, level);
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, deepList, parent, level);
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(deepList, null);
389
- };
404
+ walk(tree, null);
405
+ }
390
406
  /**
391
407
  * 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
392
- * @param {ArrayLike<T>} tree
393
- * @param {IdLike} nodeId
394
- * @param {ITreeConf} config
395
- * @return {[IdLike[], ITreeItem<V>[]]}
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 || {};
@@ -454,24 +471,24 @@
454
471
  * @param {boolean} [bigger] 是否大写第一个字母
455
472
  * @returns {string}
456
473
  */
457
- const stringCamelCase = (string, bigger) => {
474
+ function stringCamelCase(string, bigger) {
458
475
  let string2 = string;
459
476
  if (bigger) {
460
477
  string2 = string.replace(/^./, origin => origin.toUpperCase());
461
478
  }
462
479
  const HUMP_RE = /[\s_-](.)/g;
463
480
  return string2.replace(HUMP_RE, (orign, char) => char.toUpperCase());
464
- };
481
+ }
465
482
  /**
466
483
  * 将字符串转换为连字格式
467
484
  * @param {string} string
468
485
  * @param {string} [separator] 分隔符,默认是"-"(短横线)
469
486
  * @returns {string}
470
487
  */
471
- const stringKebabCase = (string, separator = '-') => {
488
+ function stringKebabCase(string, separator = '-') {
472
489
  const string2 = string.replace(/^./, origin => origin.toLowerCase());
473
490
  return string2.replace(/[A-Z]/g, origin => `${separator}${origin.toLowerCase()}`);
474
- };
491
+ }
475
492
  const STRING_ARABIC_NUMERALS = '0123456789';
476
493
  const STRING_LOWERCASE_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
477
494
  const STRING_UPPERCASE_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
@@ -487,7 +504,7 @@
487
504
  * @param args
488
505
  * @returns {string}
489
506
  */
490
- const stringFormat = (string, ...args) => {
507
+ function stringFormat(string, ...args) {
491
508
  let index = 0;
492
509
  const result = string.replace(placeholderRE, (origin) => {
493
510
  const arg = args[index++];
@@ -505,7 +522,7 @@
505
522
  }
506
523
  });
507
524
  return [result, ...args.splice(index).map(String)].join(' ');
508
- };
525
+ }
509
526
  const ev = (expression, data) => {
510
527
  try {
511
528
  // eslint-disable-next-line @typescript-eslint/no-implied-eval,@typescript-eslint/no-unsafe-return
@@ -569,7 +586,7 @@
569
586
  * @param {string} str 目标字符串
570
587
  * @param {number} fontSize 字符串字体大小
571
588
  * @param {boolean} isRemoveDom 计算后是否移除中间dom元素
572
- * @return {*}
589
+ * @returns {*}
573
590
  */
574
591
  function getStrWidthPx(str, fontSize = 14, isRemoveDom = false) {
575
592
  let strWidth = 0;
@@ -601,11 +618,11 @@
601
618
  * @param {string} className
602
619
  * @returns {boolean}
603
620
  */
604
- const hasClass = (el, className) => {
621
+ function hasClass(el, className) {
605
622
  if (className.indexOf(' ') !== -1)
606
623
  throw new Error('className should not contain space.');
607
624
  return el.classList.contains(className);
608
- };
625
+ }
609
626
  const eachClassName = (classNames, func) => {
610
627
  const classNameList = classNames.split(/\s+/g);
611
628
  classNameList.forEach(func);
@@ -615,17 +632,17 @@
615
632
  * @param {HTMLElement} el
616
633
  * @param {string} classNames
617
634
  */
618
- const addClass = (el, classNames) => {
635
+ function addClass(el, classNames) {
619
636
  eachClassName(classNames, className => el.classList.add(className));
620
- };
637
+ }
621
638
  /**
622
639
  * 给元素移除样式名
623
640
  * @param {HTMLElement} el
624
641
  * @param {string} classNames
625
642
  */
626
- const removeClass = (el, classNames) => {
643
+ function removeClass(el, classNames) {
627
644
  eachClassName(classNames, className => el.classList.remove(className));
628
- };
645
+ }
629
646
  /**
630
647
  * 设置元素样式
631
648
  * @param {HTMLElement} el
@@ -644,12 +661,14 @@
644
661
  };
645
662
  /**
646
663
  * 获取元素样式
647
- * @param {HTMLElement} el
664
+ * @param {HTMLElement} el 元素
648
665
  * @param {string} key
649
666
  * @returns {string}
650
667
  */
651
- const getStyle = (el, key) => getComputedStyle(el).getPropertyValue(key);
652
- async function smoothScroll(options) {
668
+ function getStyle(el, key) {
669
+ return getComputedStyle(el).getPropertyValue(key);
670
+ }
671
+ function smoothScroll(options) {
653
672
  return new Promise(resolve => {
654
673
  const defaults = {
655
674
  el: document,
@@ -729,7 +748,7 @@
729
748
  * @param {HTMLElement} el
730
749
  * @param {string} property
731
750
  * @param {boolean} reNumber
732
- * @return {string|number}
751
+ * @returns {string|number}
733
752
  */
734
753
  function getComputedCssVal(el, property, reNumber = true) {
735
754
  const originVal = getComputedStyle(el).getPropertyValue(property) ?? '';
@@ -748,7 +767,7 @@
748
767
  * 复制文本
749
768
  * @param {string} text
750
769
  */
751
- const copyText = (text) => {
770
+ function copyText(text) {
752
771
  textEl.value = text;
753
772
  textEl.focus({ preventScroll: true });
754
773
  textEl.select();
@@ -759,14 +778,14 @@
759
778
  catch (err) {
760
779
  // ignore
761
780
  }
762
- };
781
+ }
763
782
 
764
783
  /**
765
784
  * 获取cookie
766
785
  * @param {string} name
767
786
  * @returns {string}
768
787
  */
769
- const cookieGet = (name) => {
788
+ function cookieGet(name) {
770
789
  const { cookie } = document;
771
790
  if (!cookie)
772
791
  return '';
@@ -778,14 +797,14 @@
778
797
  return decodeURIComponent(val);
779
798
  }
780
799
  return '';
781
- };
800
+ }
782
801
  /**
783
802
  * 设置 cookie
784
803
  * @param {string} name
785
804
  * @param {string} value
786
805
  * @param {number | Date} [maxAge]
787
806
  */
788
- const cookieSet = (name, value, maxAge) => {
807
+ function cookieSet(name, value, maxAge) {
789
808
  const metas = [];
790
809
  const EXPIRES = 'expires';
791
810
  metas.push([name, encodeURIComponent(value)]);
@@ -804,13 +823,66 @@
804
823
  return `${key}=${val}`;
805
824
  })
806
825
  .join(';');
807
- };
826
+ }
808
827
  /**
809
828
  * 删除单个 cookie
810
829
  * @param name cookie 名称
811
830
  */
812
831
  const cookieDel = (name) => cookieSet(name, '', -1);
813
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
+ }
814
886
  /**
815
887
  * 格式化为日期对象(带自定义格式化模板)
816
888
  * @param {DateValue} value 可以是数值、字符串或 Date 对象
@@ -825,10 +897,69 @@
825
897
  * - mm:分
826
898
  * - ss:秒
827
899
  * - SSS:毫秒
828
- * - ww: 周
829
900
  * @returns {string}
830
901
  */
831
- const formatDate = (date = new Date(), format = 'YYYY-MM-DD HH:mm:ss') => {
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);
832
963
  let fmt = format;
833
964
  let ret;
834
965
  const opt = {
@@ -851,13 +982,13 @@
851
982
  }
852
983
  }
853
984
  return fmt;
854
- };
985
+ }
855
986
  /**
856
987
  * 计算向前或向后N天的具体日期
857
- * @param {string} strDate 参考日期
858
- * @param {number} n 正数:向后推算;负数:向前推算
859
- * @param {string} sep 日期格式的分隔符
860
- * @return {*} 目标日期
988
+ * @param {string} strDate - 参考日期
989
+ * @param {number} n - 正数:向后推算;负数:向前推算
990
+ * @param {string} sep - 日期格式的分隔符
991
+ * @returns {string} 计算后的目标日期
861
992
  */
862
993
  function calculateDate(strDate, n, sep = '-') {
863
994
  //strDate 为字符串日期 如:'2019-01-01' n为你要传入的参数,当前为0,前一天为-1,后一天为1
@@ -875,10 +1006,10 @@
875
1006
  }
876
1007
  /**
877
1008
  * 计算向前或向后N天的具体时间日期
878
- * @param {number} n 正数:向后推算;负数:向前推算
879
- * @param {string} dateSep 日期分隔符
880
- * @param {string} timeSep 时间分隔符
881
- * @return {*}
1009
+ * @param {number} n - 正数:向后推算;负数:向前推算
1010
+ * @param {string} dateSep - 日期分隔符
1011
+ * @param {string} timeSep - 时间分隔符
1012
+ * @returns {string} 转换后的目标日期时间
882
1013
  */
883
1014
  function calculateDateTime(n, dateSep = '-', timeSep = ':') {
884
1015
  const date = new Date();
@@ -975,7 +1106,7 @@
975
1106
  * @param {string} queryString
976
1107
  * @returns {Params}
977
1108
  */
978
- const qsParse = (queryString) => {
1109
+ function qsParse(queryString) {
979
1110
  const params = new URLSearchParams(queryString);
980
1111
  const result = {};
981
1112
  for (const [key, val] of params.entries()) {
@@ -989,7 +1120,7 @@
989
1120
  result[key] = params.getAll(key);
990
1121
  }
991
1122
  return result;
992
- };
1123
+ }
993
1124
  const defaultReplacer = (val) => {
994
1125
  if (isString(val))
995
1126
  return val;
@@ -1007,7 +1138,7 @@
1007
1138
  * @param {Replacer} replacer
1008
1139
  * @returns {string}
1009
1140
  */
1010
- const qsStringify = (query, replacer = defaultReplacer) => {
1141
+ function qsStringify(query, replacer = defaultReplacer) {
1011
1142
  const params = new URLSearchParams();
1012
1143
  objectEach(query, (val, key) => {
1013
1144
  if (isArray(val)) {
@@ -1026,7 +1157,7 @@
1026
1157
  }
1027
1158
  });
1028
1159
  return params.toString();
1029
- };
1160
+ }
1030
1161
 
1031
1162
  const anchorEl = document.createElement('a');
1032
1163
  /**
@@ -1102,15 +1233,15 @@
1102
1233
  * @param {string} url
1103
1234
  * @param {LooseParams} params
1104
1235
  */
1105
- const downloadURL = (url, params) => {
1236
+ function downloadURL(url, params) {
1106
1237
  window.open(params ? urlSetParams(url, params) : url);
1107
- };
1238
+ }
1108
1239
  /**
1109
1240
  * 通过 A 链接的方式下载
1110
1241
  * @param {string} href
1111
1242
  * @param {string} filename
1112
1243
  */
1113
- const downloadHref = (href, filename) => {
1244
+ function downloadHref(href, filename) {
1114
1245
  const eleLink = document.createElement('a');
1115
1246
  eleLink.download = filename;
1116
1247
  eleLink.style.display = 'none';
@@ -1118,17 +1249,17 @@
1118
1249
  document.body.appendChild(eleLink);
1119
1250
  eleLink.click();
1120
1251
  setTimeout(() => document.body.removeChild(eleLink));
1121
- };
1252
+ }
1122
1253
  /**
1123
1254
  * 将大文件对象通过 A 链接的方式下载
1124
1255
  * @param {Blob} blob
1125
1256
  * @param {string} filename
1126
1257
  */
1127
- const downloadBlob = (blob, filename) => {
1258
+ function downloadBlob(blob, filename) {
1128
1259
  const objURL = URL.createObjectURL(blob);
1129
1260
  downloadHref(objURL, filename);
1130
1261
  setTimeout(() => URL.revokeObjectURL(objURL));
1131
- };
1262
+ }
1132
1263
  /**
1133
1264
  * 将指定数据格式通过 A 链接的方式下载
1134
1265
  * @param {AnyObject | AnyObject[]} data
@@ -1136,7 +1267,7 @@
1136
1267
  * @param {string} filename
1137
1268
  * @param {string[]} [headers]
1138
1269
  */
1139
- const downloadData = (data, fileType, filename, headers) => {
1270
+ function downloadData(data, fileType, filename, headers) {
1140
1271
  filename = filename.replace(`.${fileType}`, '') + `.${fileType}`;
1141
1272
  if (fileType === 'json') {
1142
1273
  const blob = new Blob([JSON.stringify(data, null, 4)]);
@@ -1163,14 +1294,14 @@
1163
1294
  const href = 'data:' + MIMETypes[fileType] + ';charset=utf-8,\ufeff' + encodeURIComponent(headerStr + bodyStr);
1164
1295
  downloadHref(href, filename);
1165
1296
  }
1166
- };
1297
+ }
1167
1298
 
1168
1299
  /**
1169
1300
  * 等待一段时间
1170
1301
  * @param {number} timeout 等待时间,单位毫秒
1171
1302
  * @returns {Promise<void>}
1172
1303
  */
1173
- async function wait(timeout = 1) {
1304
+ function wait(timeout = 1) {
1174
1305
  return new Promise(resolve => setTimeout(resolve, timeout));
1175
1306
  }
1176
1307
  /**
@@ -1182,7 +1313,7 @@
1182
1313
  * @param {number} concurrency 并发数量,默认无限
1183
1314
  * @returns {Promise<R[]>}
1184
1315
  */
1185
- async function asyncMap(list, mapper, concurrency = Infinity) {
1316
+ function asyncMap(list, mapper, concurrency = Infinity) {
1186
1317
  return new Promise((resolve, reject) => {
1187
1318
  const iterator = list[Symbol.iterator]();
1188
1319
  const limit = Math.min(list.length, concurrency);
@@ -1220,10 +1351,11 @@
1220
1351
 
1221
1352
  /**
1222
1353
  * 选择本地文件
1223
- * @param {function} changeCb 选择文件回调
1224
- * @return {*}
1354
+ * @param {string} accept 上传的文件类型,用于过滤
1355
+ * @param {Function} changeCb 选择文件回调
1356
+ * @returns {HTMLInputElement}
1225
1357
  */
1226
- function chooseLocalFile({ accept }, changeCb) {
1358
+ function chooseLocalFile(accept, changeCb) {
1227
1359
  const inputObj = document.createElement('input');
1228
1360
  inputObj.setAttribute('id', String(Date.now()));
1229
1361
  inputObj.setAttribute('type', 'file');
@@ -1246,17 +1378,18 @@
1246
1378
  * @desc 网页加水印的工具类
1247
1379
  */
1248
1380
  /**
1249
- * canvas 实现 watermark
1381
+ * canvas 实现 水印, 具备防删除功能
1250
1382
  * @param {ICanvasWM} canvasWM
1383
+ * @example genCanvasWM({ content: 'QQMusicFE' })
1251
1384
  */
1252
- const genCanvasWM = (canvasWM) => {
1385
+ function genCanvasWM(canvasWM) {
1253
1386
  const { container = document.body, width = '300px', height = '200px', textAlign = 'center', textBaseline = 'middle', font = '20px PingFangSC-Medium,PingFang SC',
1254
1387
  // fontWeight = 500,
1255
1388
  fillStyle = 'rgba(189, 177, 167, .3)', content = '请勿外传', rotate = 30, zIndex = 2147483647 } = canvasWM;
1256
1389
  // 仅限主页面添加水印
1257
- if (!location.pathname.includes('/home')) {
1258
- return;
1259
- }
1390
+ // if (!location.pathname.includes('/home')) {
1391
+ // return;
1392
+ // }
1260
1393
  const args = canvasWM;
1261
1394
  const canvas = document.createElement('canvas');
1262
1395
  canvas.setAttribute('width', width);
@@ -1317,9 +1450,7 @@
1317
1450
  });
1318
1451
  mo.observe(container, { attributes: true, subtree: true, childList: true });
1319
1452
  }
1320
- };
1321
- // 调用
1322
- // __canvasWM({ content: 'QQMusicFE' })
1453
+ }
1323
1454
 
1324
1455
  /**
1325
1456
  * 防抖函数
@@ -1537,7 +1668,7 @@
1537
1668
  * @param {string} [hexPool] 进制池,默认 62 进制
1538
1669
  * @returns {string}
1539
1670
  */
1540
- const numberToHex = (decimal, hexPool = HEX_POOL) => {
1671
+ function numberToHex(decimal, hexPool = HEX_POOL) {
1541
1672
  if (hexPool.length < 2)
1542
1673
  throw new Error('进制池长度不能少于 2');
1543
1674
  if (!supportBigInt) {
@@ -1559,7 +1690,7 @@
1559
1690
  };
1560
1691
  execute();
1561
1692
  return ret.join('');
1562
- };
1693
+ }
1563
1694
  /**
1564
1695
  * 缩写
1565
1696
  * @param {number | string} num
@@ -1586,7 +1717,7 @@
1586
1717
  * 将数字格式化成千位分隔符显示的字符串
1587
1718
  * @param {number} val 数字
1588
1719
  * @param {'int' | 'float'} type 展示分段显示的类型 int:整型 | float:浮点型
1589
- * @return {string}
1720
+ * @returns {string}
1590
1721
  */
1591
1722
  function formatNumber(val, type = 'int') {
1592
1723
  return type === 'int' ? parseInt(String(val)).toLocaleString() : Number(val).toLocaleString('en-US');
@@ -1681,6 +1812,9 @@
1681
1812
  exports.cookieGet = cookieGet;
1682
1813
  exports.cookieSet = cookieSet;
1683
1814
  exports.copyText = copyText;
1815
+ exports.dateParse = dateParse;
1816
+ exports.dateToEnd = dateToEnd;
1817
+ exports.dateToStart = dateToStart;
1684
1818
  exports.debounce = debounce;
1685
1819
  exports.deepTraversal = deepTraversal;
1686
1820
  exports.downloadBlob = downloadBlob;
@@ -1713,6 +1847,7 @@
1713
1847
  exports.isString = isString;
1714
1848
  exports.isSymbol = isSymbol;
1715
1849
  exports.isUndefined = isUndefined;
1850
+ exports.isValidDate = isValidDate;
1716
1851
  exports.numberAbbr = numberAbbr;
1717
1852
  exports.numberToHex = numberToHex;
1718
1853
  exports.objectAssign = objectAssign;