sculp-js 1.8.4 → 1.10.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/cjs/array.js +1 -1
- package/lib/cjs/async.js +1 -1
- package/lib/cjs/base64.js +1 -1
- package/lib/cjs/clipboard.js +7 -11
- package/lib/cjs/cloneDeep.js +1 -1
- package/lib/cjs/cookie.js +1 -1
- package/lib/cjs/date.js +1 -1
- package/lib/cjs/dom.js +11 -7
- package/lib/cjs/download.js +1 -1
- package/lib/cjs/easing.js +1 -1
- package/lib/cjs/file.js +1 -1
- package/lib/cjs/func.js +1 -1
- package/lib/cjs/index.js +3 -1
- package/lib/cjs/isEqual.js +1 -1
- package/lib/cjs/math.js +1 -1
- package/lib/cjs/number.js +55 -12
- package/lib/cjs/object.js +1 -1
- package/lib/cjs/path.js +1 -1
- package/lib/cjs/qs.js +1 -1
- package/lib/cjs/random.js +1 -1
- package/lib/cjs/string.js +1 -1
- package/lib/cjs/tooltip.js +20 -16
- package/lib/cjs/tree.js +2 -2
- package/lib/cjs/type.js +1 -1
- package/lib/cjs/unique.js +1 -1
- package/lib/cjs/url.js +15 -5
- package/lib/cjs/validator.js +1 -1
- package/lib/cjs/variable.js +1 -1
- package/lib/cjs/watermark.js +20 -19
- package/lib/cjs/we-decode.js +1 -1
- package/lib/es/array.js +1 -1
- package/lib/es/async.js +1 -1
- package/lib/es/base64.js +1 -1
- package/lib/es/clipboard.js +7 -11
- package/lib/es/cloneDeep.js +1 -1
- package/lib/es/cookie.js +1 -1
- package/lib/es/date.js +1 -1
- package/lib/es/dom.js +11 -7
- package/lib/es/download.js +1 -1
- package/lib/es/easing.js +1 -1
- package/lib/es/file.js +1 -1
- package/lib/es/func.js +1 -1
- package/lib/es/index.js +2 -2
- package/lib/es/isEqual.js +1 -1
- package/lib/es/math.js +1 -1
- package/lib/es/number.js +54 -13
- package/lib/es/object.js +1 -1
- package/lib/es/path.js +1 -1
- package/lib/es/qs.js +1 -1
- package/lib/es/random.js +1 -1
- package/lib/es/string.js +1 -1
- package/lib/es/tooltip.js +20 -16
- package/lib/es/tree.js +2 -2
- package/lib/es/type.js +1 -1
- package/lib/es/unique.js +1 -1
- package/lib/es/url.js +15 -5
- package/lib/es/validator.js +1 -1
- package/lib/es/variable.js +1 -1
- package/lib/es/watermark.js +20 -19
- package/lib/es/we-decode.js +1 -1
- package/lib/index.d.ts +55 -29
- package/lib/umd/index.js +607 -552
- package/package.json +2 -1
package/lib/umd/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* sculp-js v1.
|
|
2
|
+
* sculp-js v1.10.0
|
|
3
3
|
* (c) 2023-present chandq
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -97,6 +97,29 @@
|
|
|
97
97
|
return array;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* 复制文本
|
|
102
|
+
* @param {string} text
|
|
103
|
+
*/
|
|
104
|
+
function copyText(text) {
|
|
105
|
+
const textEl = document.createElement('textarea');
|
|
106
|
+
textEl.style.position = 'absolute';
|
|
107
|
+
textEl.style.top = '-9999px';
|
|
108
|
+
textEl.style.left = '-9999px';
|
|
109
|
+
textEl.value = text;
|
|
110
|
+
document.body.appendChild(textEl);
|
|
111
|
+
textEl.focus({ preventScroll: true });
|
|
112
|
+
textEl.select();
|
|
113
|
+
try {
|
|
114
|
+
document.execCommand('copy');
|
|
115
|
+
textEl.blur();
|
|
116
|
+
document.body.removeChild(textEl);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
// ignore
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
100
123
|
// 常用类型定义
|
|
101
124
|
/**
|
|
102
125
|
* 判断对象内是否有该静态属性
|
|
@@ -213,123 +236,373 @@
|
|
|
213
236
|
return !Object.keys(value).length;
|
|
214
237
|
}
|
|
215
238
|
|
|
216
|
-
// @ref https://cubic-bezier.com/
|
|
217
|
-
const easingDefines = {
|
|
218
|
-
linear: [0, 0, 1, 1],
|
|
219
|
-
ease: [0.25, 0.1, 0.25, 1],
|
|
220
|
-
'ease-in': [0.42, 0, 1, 1],
|
|
221
|
-
'ease-out': [0, 0, 0.58, 1],
|
|
222
|
-
'ease-in-out': [0.42, 0, 0.58, 1]
|
|
223
|
-
};
|
|
224
239
|
/**
|
|
225
|
-
*
|
|
226
|
-
* @param {
|
|
227
|
-
* @returns {
|
|
240
|
+
* 获取cookie
|
|
241
|
+
* @param {string} name
|
|
242
|
+
* @returns {string}
|
|
228
243
|
*/
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
|
|
244
|
+
function cookieGet(name) {
|
|
245
|
+
const { cookie } = document;
|
|
246
|
+
if (!cookie)
|
|
247
|
+
return '';
|
|
248
|
+
const result = cookie.split(';');
|
|
249
|
+
for (let i = 0; i < result.length; i++) {
|
|
250
|
+
const item = result[i];
|
|
251
|
+
const [key, val = ''] = item.split('=');
|
|
252
|
+
if (key === name)
|
|
253
|
+
return decodeURIComponent(val);
|
|
233
254
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
255
|
+
return '';
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 设置 cookie
|
|
259
|
+
* @param {string} name
|
|
260
|
+
* @param {string} value
|
|
261
|
+
* @param {number | Date} [maxAge]
|
|
262
|
+
*/
|
|
263
|
+
function cookieSet(name, value, maxAge) {
|
|
264
|
+
const metas = [];
|
|
265
|
+
const EXPIRES = 'expires';
|
|
266
|
+
metas.push([name, encodeURIComponent(value)]);
|
|
267
|
+
if (isNumber(maxAge)) {
|
|
268
|
+
const d = new Date();
|
|
269
|
+
d.setTime(d.getTime() + maxAge);
|
|
270
|
+
metas.push([EXPIRES, d.toUTCString()]);
|
|
240
271
|
}
|
|
241
|
-
|
|
272
|
+
else if (isDate(maxAge)) {
|
|
273
|
+
metas.push([EXPIRES, maxAge.toUTCString()]);
|
|
274
|
+
}
|
|
275
|
+
metas.push(['path', '/']);
|
|
276
|
+
document.cookie = metas
|
|
277
|
+
.map(item => {
|
|
278
|
+
const [key, val] = item;
|
|
279
|
+
return `${key}=${val}`;
|
|
280
|
+
})
|
|
281
|
+
.join(';');
|
|
242
282
|
}
|
|
243
|
-
|
|
244
283
|
/**
|
|
245
|
-
*
|
|
246
|
-
* @param
|
|
247
|
-
* @returns {boolean}
|
|
284
|
+
* 删除单个 cookie
|
|
285
|
+
* @param name cookie 名称
|
|
248
286
|
*/
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (!
|
|
255
|
-
return
|
|
256
|
-
|
|
257
|
-
return
|
|
287
|
+
const cookieDel = (name) => cookieSet(name, '', -1);
|
|
288
|
+
|
|
289
|
+
const isValidDate = (any) => isDate(any) && !isNaN(any.getTime());
|
|
290
|
+
/* istanbul ignore next */
|
|
291
|
+
const guessDateSeparator = (value) => {
|
|
292
|
+
if (!isString(value))
|
|
293
|
+
return;
|
|
294
|
+
const value2 = value.replace(/-/g, '/');
|
|
295
|
+
return new Date(value2);
|
|
296
|
+
};
|
|
297
|
+
/* istanbul ignore next */
|
|
298
|
+
const guessDateTimezone = (value) => {
|
|
299
|
+
if (!isString(value))
|
|
300
|
+
return;
|
|
301
|
+
const re = /([+-])(\d\d)(\d\d)$/;
|
|
302
|
+
const matches = re.exec(value);
|
|
303
|
+
if (!matches)
|
|
304
|
+
return;
|
|
305
|
+
const value2 = value.replace(re, 'Z');
|
|
306
|
+
const d = new Date(value2);
|
|
307
|
+
if (!isValidDate(d))
|
|
308
|
+
return;
|
|
309
|
+
const [, flag, hours, minutes] = matches;
|
|
310
|
+
const hours2 = parseInt(hours, 10);
|
|
311
|
+
const minutes2 = parseInt(minutes, 10);
|
|
312
|
+
const offset = (a, b) => (flag === '+' ? a - b : a + b);
|
|
313
|
+
d.setHours(offset(d.getHours(), hours2));
|
|
314
|
+
d.setMinutes(offset(d.getMinutes(), minutes2));
|
|
315
|
+
return d;
|
|
258
316
|
};
|
|
259
317
|
/**
|
|
260
|
-
*
|
|
261
|
-
* @param {
|
|
262
|
-
* @
|
|
318
|
+
* 解析为Date对象
|
|
319
|
+
* @param {DateValue} value - 可以是数值、字符串或 Date 对象
|
|
320
|
+
* @returns {Date} - 转换后的目标Date
|
|
263
321
|
*/
|
|
264
|
-
function
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
322
|
+
function dateParse(value) {
|
|
323
|
+
const d1 = new Date(value);
|
|
324
|
+
if (isValidDate(d1))
|
|
325
|
+
return d1;
|
|
326
|
+
// safari 浏览器的日期解析有问题
|
|
327
|
+
// new Date('2020-06-26 18:06:15') 返回值是一个非法日期对象
|
|
328
|
+
/* istanbul ignore next */
|
|
329
|
+
const d2 = guessDateSeparator(value);
|
|
330
|
+
/* istanbul ignore next */
|
|
331
|
+
if (isValidDate(d2))
|
|
332
|
+
return d2;
|
|
333
|
+
// safari 浏览器的日期解析有问题
|
|
334
|
+
// new Date('2020-06-26T18:06:15.000+0800') 返回值是一个非法日期对象
|
|
335
|
+
/* istanbul ignore next */
|
|
336
|
+
const d3 = guessDateTimezone(value);
|
|
337
|
+
/* istanbul ignore next */
|
|
338
|
+
if (isValidDate(d3))
|
|
339
|
+
return d3;
|
|
340
|
+
throw new SyntaxError(`${value.toString()} 不是一个合法的日期描述`);
|
|
271
341
|
}
|
|
272
342
|
/**
|
|
273
|
-
*
|
|
274
|
-
* @param {
|
|
275
|
-
* @param {
|
|
343
|
+
* 格式化为日期对象(带自定义格式化模板)
|
|
344
|
+
* @param {DateValue} value 可以是数值、字符串或 Date 对象
|
|
345
|
+
* @param {string} [format] 模板,默认是 YYYY-MM-DD HH:mm:ss,模板字符:
|
|
346
|
+
* - YYYY:年
|
|
347
|
+
* - yyyy: 年
|
|
348
|
+
* - MM:月
|
|
349
|
+
* - DD:日
|
|
350
|
+
* - dd: 日
|
|
351
|
+
* - HH:时(24 小时制)
|
|
352
|
+
* - hh:时(12 小时制)
|
|
353
|
+
* - mm:分
|
|
354
|
+
* - ss:秒
|
|
355
|
+
* - SSS:毫秒
|
|
356
|
+
* @returns {string}
|
|
276
357
|
*/
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
358
|
+
// export const dateStringify = (value: DateValue, format = 'YYYY-MM-DD HH:mm:ss'): string => {
|
|
359
|
+
// const date = dateParse(value);
|
|
360
|
+
// let fmt = format;
|
|
361
|
+
// let ret;
|
|
362
|
+
// const opt: DateObj = {
|
|
363
|
+
// 'Y+': `${date.getFullYear()}`, // 年
|
|
364
|
+
// 'y+': `${date.getFullYear()}`, // 年
|
|
365
|
+
// 'M+': `${date.getMonth() + 1}`, // 月
|
|
366
|
+
// 'D+': `${date.getDate()}`, // 日
|
|
367
|
+
// 'd+': `${date.getDate()}`, // 日
|
|
368
|
+
// 'H+': `${date.getHours()}`, // 时
|
|
369
|
+
// 'm+': `${date.getMinutes()}`, // 分
|
|
370
|
+
// 's+': `${date.getSeconds()}`, // 秒
|
|
371
|
+
// 'S+': `${date.getMilliseconds()}` // 豪秒
|
|
372
|
+
// };
|
|
373
|
+
// for (const k in opt) {
|
|
374
|
+
// ret = new RegExp(`(${k})`).exec(fmt);
|
|
375
|
+
// if (ret) {
|
|
376
|
+
// fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
|
|
377
|
+
// }
|
|
378
|
+
// }
|
|
379
|
+
// return fmt;
|
|
380
|
+
// };
|
|
285
381
|
/**
|
|
286
|
-
*
|
|
287
|
-
* @param {
|
|
288
|
-
* @
|
|
289
|
-
* @returns {Record<Extract<keyof O, string>, T>}
|
|
382
|
+
* 将日期转换为一天的开始时间,即0点0分0秒0毫秒
|
|
383
|
+
* @param {DateValue} value
|
|
384
|
+
* @returns {Date}
|
|
290
385
|
*/
|
|
291
|
-
function
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
if (!objectHas(obj, key))
|
|
295
|
-
continue;
|
|
296
|
-
obj2[key] = iterator(obj[key], key);
|
|
297
|
-
}
|
|
298
|
-
return obj2;
|
|
386
|
+
function dateToStart(value) {
|
|
387
|
+
const d = dateParse(value);
|
|
388
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
|
|
299
389
|
}
|
|
300
390
|
/**
|
|
301
|
-
*
|
|
302
|
-
* @param {
|
|
303
|
-
* @
|
|
304
|
-
* @returns {Pick<O, ArrayElements<K>>}
|
|
391
|
+
* 将日期转换为一天的结束时间,即23点59分59秒999毫秒
|
|
392
|
+
* @param {DateValue} value
|
|
393
|
+
* @returns {Date}
|
|
305
394
|
*/
|
|
306
|
-
function
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
// @ts-ignore
|
|
311
|
-
obj2[k] = v;
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
return obj2;
|
|
395
|
+
function dateToEnd(value) {
|
|
396
|
+
const d = dateToStart(value);
|
|
397
|
+
d.setDate(d.getDate() + 1);
|
|
398
|
+
return dateParse(d.getTime() - 1);
|
|
315
399
|
}
|
|
316
400
|
/**
|
|
317
|
-
*
|
|
318
|
-
* @param {
|
|
319
|
-
* @param {
|
|
320
|
-
*
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
401
|
+
* 格式化为日期对象(带自定义格式化模板)
|
|
402
|
+
* @param {Date} value - 可以是数值、字符串或 Date 对象
|
|
403
|
+
* @param {string} [format] - 模板,默认是 YYYY-MM-DD HH:mm:ss,模板字符:
|
|
404
|
+
* - YYYY:年
|
|
405
|
+
* - yyyy: 年
|
|
406
|
+
* - MM:月
|
|
407
|
+
* - DD:日
|
|
408
|
+
* - dd: 日
|
|
409
|
+
* - HH:时(24 小时制)
|
|
410
|
+
* - mm:分
|
|
411
|
+
* - ss:秒
|
|
412
|
+
* - SSS:毫秒
|
|
413
|
+
* - ww: 周
|
|
414
|
+
* @returns {string} 格式化后的日期字符串
|
|
415
|
+
*/
|
|
416
|
+
function formatDate(value, format = 'YYYY-MM-DD HH:mm:ss') {
|
|
417
|
+
const date = dateParse(value);
|
|
418
|
+
let fmt = format;
|
|
419
|
+
let ret;
|
|
420
|
+
const opt = {
|
|
421
|
+
'Y+': `${date.getFullYear()}`,
|
|
422
|
+
'y+': `${date.getFullYear()}`,
|
|
423
|
+
'M+': `${date.getMonth() + 1}`,
|
|
424
|
+
'D+': `${date.getDate()}`,
|
|
425
|
+
'd+': `${date.getDate()}`,
|
|
426
|
+
'H+': `${date.getHours()}`,
|
|
427
|
+
'm+': `${date.getMinutes()}`,
|
|
428
|
+
's+': `${date.getSeconds()}`,
|
|
429
|
+
'S+': `${date.getMilliseconds()}`,
|
|
430
|
+
'w+': ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][date.getDay()] // 周
|
|
431
|
+
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
|
432
|
+
};
|
|
433
|
+
for (const k in opt) {
|
|
434
|
+
ret = new RegExp('(' + k + ')').exec(fmt);
|
|
435
|
+
if (ret) {
|
|
436
|
+
fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return fmt;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* 计算向前或向后N天的具体日期
|
|
443
|
+
* @param {DateValue} originDate - 参考日期
|
|
444
|
+
* @param {number} n - 正数:向后推算;负数:向前推算
|
|
445
|
+
* @param {string} sep - 日期格式的分隔符
|
|
446
|
+
* @returns {string} 计算后的目标日期
|
|
447
|
+
*/
|
|
448
|
+
function calculateDate(originDate, n, sep = '-') {
|
|
449
|
+
//originDate 为字符串日期 如:'2019-01-01' n为你要传入的参数,当前为0,前一天为-1,后一天为1
|
|
450
|
+
const date = new Date(originDate); //这边给定一个特定时间
|
|
451
|
+
const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
452
|
+
const millisecondGap = newDate.getTime() + 1000 * 60 * 60 * 24 * parseInt(String(n)); //计算前几天用减,计算后几天用加,最后一个就是多少天的数量
|
|
453
|
+
const targetDate = new Date(millisecondGap);
|
|
454
|
+
const finalNewDate = targetDate.getFullYear() +
|
|
455
|
+
sep +
|
|
456
|
+
String(targetDate.getMonth() + 1).padStart(2, '0') +
|
|
457
|
+
'-' +
|
|
458
|
+
String(targetDate.getDate()).padStart(2, '0');
|
|
459
|
+
return finalNewDate;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* 计算向前或向后N天的具体日期时间
|
|
463
|
+
* @param {DateValue} originDateTime - 参考日期时间
|
|
464
|
+
* @param {number} n - 正数:向后推算;负数:向前推算
|
|
465
|
+
* @param {string} dateSep - 日期分隔符
|
|
466
|
+
* @param {string} timeSep - 时间分隔符
|
|
467
|
+
* @returns {string} 转换后的目标日期时间
|
|
468
|
+
*/
|
|
469
|
+
function calculateDateTime(originDateTime, n, dateSep = '-', timeSep = ':') {
|
|
470
|
+
const date = new Date(originDateTime);
|
|
471
|
+
const separator1 = dateSep;
|
|
472
|
+
const separator2 = timeSep;
|
|
473
|
+
const dateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
|
|
474
|
+
const millisecondGap = dateTime.getTime() + 1000 * 60 * 60 * 24 * parseInt(String(n)); //计算前几天用减,计算后几天用加,最后一个就是多少天的数量
|
|
475
|
+
const targetDateTime = new Date(millisecondGap);
|
|
476
|
+
return (targetDateTime.getFullYear() +
|
|
477
|
+
separator1 +
|
|
478
|
+
String(targetDateTime.getMonth() + 1).padStart(2, '0') +
|
|
479
|
+
separator1 +
|
|
480
|
+
String(targetDateTime.getDate()).padStart(2, '0') +
|
|
481
|
+
' ' +
|
|
482
|
+
String(targetDateTime.getHours()).padStart(2, '0') +
|
|
483
|
+
separator2 +
|
|
484
|
+
String(targetDateTime.getMinutes()).padStart(2, '0') +
|
|
485
|
+
separator2 +
|
|
486
|
+
String(targetDateTime.getSeconds()).padStart(2, '0'));
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// @ref https://cubic-bezier.com/
|
|
490
|
+
const easingDefines = {
|
|
491
|
+
linear: [0, 0, 1, 1],
|
|
492
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
493
|
+
'ease-in': [0.42, 0, 1, 1],
|
|
494
|
+
'ease-out': [0, 0, 0.58, 1],
|
|
495
|
+
'ease-in-out': [0.42, 0, 0.58, 1]
|
|
496
|
+
};
|
|
497
|
+
/**
|
|
498
|
+
* 缓冲函数化,用于 js 计算缓冲进度
|
|
499
|
+
* @param {EasingNameOrDefine} [name=linear]
|
|
500
|
+
* @returns {EasingFunction}
|
|
501
|
+
*/
|
|
502
|
+
function easingFunctional(name) {
|
|
503
|
+
let fn;
|
|
504
|
+
if (isArray(name)) {
|
|
505
|
+
fn = bezier(...name);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
const define = easingDefines[name];
|
|
509
|
+
if (!define) {
|
|
510
|
+
throw new Error(`${name} 缓冲函数未定义`);
|
|
511
|
+
}
|
|
512
|
+
fn = bezier(...define);
|
|
513
|
+
}
|
|
514
|
+
return (input) => fn(Math.max(0, Math.min(input, 1)));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* 判断对象是否为纯对象
|
|
519
|
+
* @param {object} obj
|
|
520
|
+
* @returns {boolean}
|
|
521
|
+
*/
|
|
522
|
+
const isPlainObject = (obj) => {
|
|
523
|
+
if (!isObject(obj))
|
|
524
|
+
return false;
|
|
525
|
+
const proto = Object.getPrototypeOf(obj);
|
|
526
|
+
// 对象无原型
|
|
527
|
+
if (!proto)
|
|
528
|
+
return true;
|
|
529
|
+
// 是否对象直接实例
|
|
530
|
+
return proto === Object.prototype;
|
|
531
|
+
};
|
|
532
|
+
/**
|
|
533
|
+
* 遍历对象,返回 false 中断遍历
|
|
534
|
+
* @param {O} obj
|
|
535
|
+
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
536
|
+
*/
|
|
537
|
+
function objectEach(obj, iterator) {
|
|
538
|
+
for (const key in obj) {
|
|
539
|
+
if (!objectHas(obj, key))
|
|
540
|
+
continue;
|
|
541
|
+
if (iterator(obj[key], key) === false)
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* 异步遍历对象,返回 false 中断遍历
|
|
547
|
+
* @param {O} obj
|
|
548
|
+
* @param {(val: O[keyof O], key: keyof O) => (boolean | void)} iterator
|
|
549
|
+
*/
|
|
550
|
+
async function objectEachAsync(obj, iterator) {
|
|
551
|
+
for (const key in obj) {
|
|
552
|
+
if (!objectHas(obj, key))
|
|
553
|
+
continue;
|
|
554
|
+
if ((await iterator(obj[key], key)) === false)
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* 对象映射
|
|
560
|
+
* @param {O} obj
|
|
561
|
+
* @param {(val: O[keyof O], key: Extract<keyof O, string>) => any} iterator
|
|
562
|
+
* @returns {Record<Extract<keyof O, string>, T>}
|
|
563
|
+
*/
|
|
564
|
+
function objectMap(obj, iterator) {
|
|
565
|
+
const obj2 = {};
|
|
566
|
+
for (const key in obj) {
|
|
567
|
+
if (!objectHas(obj, key))
|
|
568
|
+
continue;
|
|
569
|
+
obj2[key] = iterator(obj[key], key);
|
|
570
|
+
}
|
|
571
|
+
return obj2;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* 对象提取
|
|
575
|
+
* @param {O} obj
|
|
576
|
+
* @param {K} keys
|
|
577
|
+
* @returns {Pick<O, ArrayElements<K>>}
|
|
578
|
+
*/
|
|
579
|
+
function objectPick(obj, keys) {
|
|
580
|
+
const obj2 = {};
|
|
581
|
+
objectEach(obj, (v, k) => {
|
|
582
|
+
if (keys.includes(k)) {
|
|
583
|
+
// @ts-ignore
|
|
584
|
+
obj2[k] = v;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
return obj2;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* 对象去除
|
|
591
|
+
* @param {O} obj
|
|
592
|
+
* @param {K} keys
|
|
593
|
+
* @returns {Pick<O, ArrayElements<K>>}
|
|
594
|
+
*/
|
|
595
|
+
function objectOmit(obj, keys) {
|
|
596
|
+
const obj2 = {};
|
|
597
|
+
objectEach(obj, (v, k) => {
|
|
598
|
+
if (!keys.includes(k)) {
|
|
599
|
+
// @ts-ignore
|
|
600
|
+
obj2[k] = v;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
return obj2;
|
|
604
|
+
}
|
|
605
|
+
const merge = (map, source, target) => {
|
|
333
606
|
if (isUndefined(target))
|
|
334
607
|
return source;
|
|
335
608
|
const sourceType = typeIs(source);
|
|
@@ -570,427 +843,156 @@
|
|
|
570
843
|
return queryObj;
|
|
571
844
|
}
|
|
572
845
|
|
|
573
|
-
/**
|
|
574
|
-
* 判断元素是否包含某个样式名
|
|
575
|
-
* @param {HTMLElement} el
|
|
576
|
-
* @param {string} className
|
|
577
|
-
* @returns {boolean}
|
|
578
|
-
*/
|
|
579
|
-
function hasClass(el, className) {
|
|
580
|
-
if (className.indexOf(' ') !== -1)
|
|
581
|
-
throw new Error('className should not contain space.');
|
|
582
|
-
return el.classList.contains(className);
|
|
583
|
-
}
|
|
584
|
-
const eachClassName = (classNames, func) => {
|
|
585
|
-
const classNameList = classNames.split(/\s+/g);
|
|
586
|
-
classNameList.forEach(func);
|
|
587
|
-
};
|
|
588
|
-
/**
|
|
589
|
-
* 给元素增加样式名
|
|
590
|
-
* @param {HTMLElement} el
|
|
591
|
-
* @param {string} classNames
|
|
592
|
-
*/
|
|
593
|
-
function addClass(el, classNames) {
|
|
594
|
-
eachClassName(classNames, className => el.classList.add(className));
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* 给元素移除样式名
|
|
598
|
-
* @param {HTMLElement} el
|
|
599
|
-
* @param {string} classNames
|
|
600
|
-
*/
|
|
601
|
-
function removeClass(el, classNames) {
|
|
602
|
-
eachClassName(classNames, className => el.classList.remove(className));
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* 设置元素样式
|
|
606
|
-
* @param {HTMLElement} el
|
|
607
|
-
* @param {string | Style} key
|
|
608
|
-
* @param {string} val
|
|
609
|
-
*/
|
|
610
|
-
const setStyle = (el, key, val) => {
|
|
611
|
-
if (isObject(key)) {
|
|
612
|
-
objectEach(key, (val1, key1) => {
|
|
613
|
-
setStyle(el, key1, val1);
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
else {
|
|
617
|
-
el.style.setProperty(stringKebabCase(key), val);
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
/**
|
|
621
|
-
* 获取元素样式
|
|
622
|
-
* @param {HTMLElement} el 元素
|
|
623
|
-
* @param {string} key
|
|
624
|
-
* @returns {string}
|
|
625
|
-
*/
|
|
626
|
-
function getStyle(el, key) {
|
|
627
|
-
return getComputedStyle(el).getPropertyValue(key);
|
|
628
|
-
}
|
|
629
|
-
function smoothScroll(options) {
|
|
630
|
-
return new Promise(resolve => {
|
|
631
|
-
const defaults = {
|
|
632
|
-
el: document,
|
|
633
|
-
to: 0,
|
|
634
|
-
duration: 567,
|
|
635
|
-
easing: 'ease'
|
|
636
|
-
};
|
|
637
|
-
const { el, to, duration, easing } = objectAssign(defaults, options);
|
|
638
|
-
const htmlEl = document.documentElement;
|
|
639
|
-
const bodyEl = document.body;
|
|
640
|
-
const globalMode = el === window || el === document || el === htmlEl || el === bodyEl;
|
|
641
|
-
const els = globalMode ? [htmlEl, bodyEl] : [el];
|
|
642
|
-
const query = () => {
|
|
643
|
-
let value = 0;
|
|
644
|
-
arrayEach(els, el => {
|
|
645
|
-
if ('scrollTop' in el) {
|
|
646
|
-
value = el.scrollTop;
|
|
647
|
-
return false;
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
return value;
|
|
651
|
-
};
|
|
652
|
-
const update = (val) => {
|
|
653
|
-
els.forEach(el => {
|
|
654
|
-
if ('scrollTop' in el) {
|
|
655
|
-
el.scrollTop = val;
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
};
|
|
659
|
-
let startTime;
|
|
660
|
-
const startValue = query();
|
|
661
|
-
const length = to - startValue;
|
|
662
|
-
const easingFn = easingFunctional(easing);
|
|
663
|
-
const render = () => {
|
|
664
|
-
const now = performance.now();
|
|
665
|
-
const passingTime = startTime ? now - startTime : 0;
|
|
666
|
-
const t = passingTime / duration;
|
|
667
|
-
const p = easingFn(t);
|
|
668
|
-
if (!startTime)
|
|
669
|
-
startTime = now;
|
|
670
|
-
update(startValue + length * p);
|
|
671
|
-
if (t >= 1)
|
|
672
|
-
resolve();
|
|
673
|
-
else
|
|
674
|
-
requestAnimationFrame(render);
|
|
675
|
-
};
|
|
676
|
-
render();
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* 获取元素样式属性的计算值
|
|
681
|
-
* @param {HTMLElement} el
|
|
682
|
-
* @param {string} property
|
|
683
|
-
* @param {boolean} reNumber
|
|
684
|
-
* @returns {string|number}
|
|
685
|
-
*/
|
|
686
|
-
function getComputedCssVal(el, property, reNumber = true) {
|
|
687
|
-
const originVal = getComputedStyle(el).getPropertyValue(property) ?? '';
|
|
688
|
-
return reNumber ? Number(originVal.replace(/([0-9]*)(.*)/g, '$1')) : originVal;
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* 字符串的像素宽度
|
|
692
|
-
* @param {string} str 目标字符串
|
|
693
|
-
* @param {number} fontSize 字符串字体大小
|
|
694
|
-
* @param {boolean} isRemoveDom 计算后是否移除中间dom元素
|
|
695
|
-
* @returns {*}
|
|
696
|
-
*/
|
|
697
|
-
function getStrWidthPx(str, fontSize = 14, isRemoveDom = false) {
|
|
698
|
-
let strWidth = 0;
|
|
699
|
-
console.assert(isString(str), `${str} 不是有效的字符串`);
|
|
700
|
-
if (isString(str) && str.length > 0) {
|
|
701
|
-
let getEle = document.querySelector('#getStrWidth1494304949567');
|
|
702
|
-
if (!getEle) {
|
|
703
|
-
const _ele = document.createElement('span');
|
|
704
|
-
_ele.id = 'getStrWidth1494304949567';
|
|
705
|
-
_ele.style.fontSize = fontSize + 'px';
|
|
706
|
-
_ele.style.whiteSpace = 'nowrap';
|
|
707
|
-
_ele.style.visibility = 'hidden';
|
|
708
|
-
_ele.textContent = str;
|
|
709
|
-
document.body.appendChild(_ele);
|
|
710
|
-
getEle = _ele;
|
|
711
|
-
}
|
|
712
|
-
getEle.textContent = str;
|
|
713
|
-
strWidth = getEle.offsetWidth;
|
|
714
|
-
if (isRemoveDom) {
|
|
715
|
-
document.body.appendChild(getEle);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
return strWidth;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
const textEl = document.createElement('textarea');
|
|
722
|
-
setStyle(textEl, {
|
|
723
|
-
position: 'absolute',
|
|
724
|
-
top: '-9999px',
|
|
725
|
-
left: '-9999px',
|
|
726
|
-
opacity: 0
|
|
727
|
-
});
|
|
728
|
-
document.body.appendChild(textEl);
|
|
729
|
-
/**
|
|
730
|
-
* 复制文本
|
|
731
|
-
* @param {string} text
|
|
732
|
-
*/
|
|
733
|
-
function copyText(text) {
|
|
734
|
-
textEl.value = text;
|
|
735
|
-
textEl.focus({ preventScroll: true });
|
|
736
|
-
textEl.select();
|
|
737
|
-
try {
|
|
738
|
-
document.execCommand('copy');
|
|
739
|
-
textEl.blur();
|
|
740
|
-
}
|
|
741
|
-
catch (err) {
|
|
742
|
-
// ignore
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
* 获取cookie
|
|
748
|
-
* @param {string} name
|
|
749
|
-
* @returns {string}
|
|
750
|
-
*/
|
|
751
|
-
function cookieGet(name) {
|
|
752
|
-
const { cookie } = document;
|
|
753
|
-
if (!cookie)
|
|
754
|
-
return '';
|
|
755
|
-
const result = cookie.split(';');
|
|
756
|
-
for (let i = 0; i < result.length; i++) {
|
|
757
|
-
const item = result[i];
|
|
758
|
-
const [key, val = ''] = item.split('=');
|
|
759
|
-
if (key === name)
|
|
760
|
-
return decodeURIComponent(val);
|
|
761
|
-
}
|
|
762
|
-
return '';
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
|
-
* 设置 cookie
|
|
766
|
-
* @param {string} name
|
|
767
|
-
* @param {string} value
|
|
768
|
-
* @param {number | Date} [maxAge]
|
|
769
|
-
*/
|
|
770
|
-
function cookieSet(name, value, maxAge) {
|
|
771
|
-
const metas = [];
|
|
772
|
-
const EXPIRES = 'expires';
|
|
773
|
-
metas.push([name, encodeURIComponent(value)]);
|
|
774
|
-
if (isNumber(maxAge)) {
|
|
775
|
-
const d = new Date();
|
|
776
|
-
d.setTime(d.getTime() + maxAge);
|
|
777
|
-
metas.push([EXPIRES, d.toUTCString()]);
|
|
778
|
-
}
|
|
779
|
-
else if (isDate(maxAge)) {
|
|
780
|
-
metas.push([EXPIRES, maxAge.toUTCString()]);
|
|
781
|
-
}
|
|
782
|
-
metas.push(['path', '/']);
|
|
783
|
-
document.cookie = metas
|
|
784
|
-
.map(item => {
|
|
785
|
-
const [key, val] = item;
|
|
786
|
-
return `${key}=${val}`;
|
|
787
|
-
})
|
|
788
|
-
.join(';');
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* 删除单个 cookie
|
|
792
|
-
* @param name cookie 名称
|
|
793
|
-
*/
|
|
794
|
-
const cookieDel = (name) => cookieSet(name, '', -1);
|
|
795
|
-
|
|
796
|
-
const isValidDate = (any) => isDate(any) && !isNaN(any.getTime());
|
|
797
|
-
/* istanbul ignore next */
|
|
798
|
-
const guessDateSeparator = (value) => {
|
|
799
|
-
if (!isString(value))
|
|
800
|
-
return;
|
|
801
|
-
const value2 = value.replace(/-/g, '/');
|
|
802
|
-
return new Date(value2);
|
|
803
|
-
};
|
|
804
|
-
/* istanbul ignore next */
|
|
805
|
-
const guessDateTimezone = (value) => {
|
|
806
|
-
if (!isString(value))
|
|
807
|
-
return;
|
|
808
|
-
const re = /([+-])(\d\d)(\d\d)$/;
|
|
809
|
-
const matches = re.exec(value);
|
|
810
|
-
if (!matches)
|
|
811
|
-
return;
|
|
812
|
-
const value2 = value.replace(re, 'Z');
|
|
813
|
-
const d = new Date(value2);
|
|
814
|
-
if (!isValidDate(d))
|
|
815
|
-
return;
|
|
816
|
-
const [, flag, hours, minutes] = matches;
|
|
817
|
-
const hours2 = parseInt(hours, 10);
|
|
818
|
-
const minutes2 = parseInt(minutes, 10);
|
|
819
|
-
const offset = (a, b) => (flag === '+' ? a - b : a + b);
|
|
820
|
-
d.setHours(offset(d.getHours(), hours2));
|
|
821
|
-
d.setMinutes(offset(d.getMinutes(), minutes2));
|
|
822
|
-
return d;
|
|
823
|
-
};
|
|
824
|
-
/**
|
|
825
|
-
* 解析为Date对象
|
|
826
|
-
* @param {DateValue} value - 可以是数值、字符串或 Date 对象
|
|
827
|
-
* @returns {Date} - 转换后的目标Date
|
|
828
|
-
*/
|
|
829
|
-
function dateParse(value) {
|
|
830
|
-
const d1 = new Date(value);
|
|
831
|
-
if (isValidDate(d1))
|
|
832
|
-
return d1;
|
|
833
|
-
// safari 浏览器的日期解析有问题
|
|
834
|
-
// new Date('2020-06-26 18:06:15') 返回值是一个非法日期对象
|
|
835
|
-
/* istanbul ignore next */
|
|
836
|
-
const d2 = guessDateSeparator(value);
|
|
837
|
-
/* istanbul ignore next */
|
|
838
|
-
if (isValidDate(d2))
|
|
839
|
-
return d2;
|
|
840
|
-
// safari 浏览器的日期解析有问题
|
|
841
|
-
// new Date('2020-06-26T18:06:15.000+0800') 返回值是一个非法日期对象
|
|
842
|
-
/* istanbul ignore next */
|
|
843
|
-
const d3 = guessDateTimezone(value);
|
|
844
|
-
/* istanbul ignore next */
|
|
845
|
-
if (isValidDate(d3))
|
|
846
|
-
return d3;
|
|
847
|
-
throw new SyntaxError(`${value.toString()} 不是一个合法的日期描述`);
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* 格式化为日期对象(带自定义格式化模板)
|
|
851
|
-
* @param {DateValue} value 可以是数值、字符串或 Date 对象
|
|
852
|
-
* @param {string} [format] 模板,默认是 YYYY-MM-DD HH:mm:ss,模板字符:
|
|
853
|
-
* - YYYY:年
|
|
854
|
-
* - yyyy: 年
|
|
855
|
-
* - MM:月
|
|
856
|
-
* - DD:日
|
|
857
|
-
* - dd: 日
|
|
858
|
-
* - HH:时(24 小时制)
|
|
859
|
-
* - hh:时(12 小时制)
|
|
860
|
-
* - mm:分
|
|
861
|
-
* - ss:秒
|
|
862
|
-
* - SSS:毫秒
|
|
863
|
-
* @returns {string}
|
|
846
|
+
/**
|
|
847
|
+
* 判断元素是否包含某个样式名
|
|
848
|
+
* @param {HTMLElement} el
|
|
849
|
+
* @param {string} className
|
|
850
|
+
* @returns {boolean}
|
|
864
851
|
*/
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
// 'd+': `${date.getDate()}`, // 日
|
|
875
|
-
// 'H+': `${date.getHours()}`, // 时
|
|
876
|
-
// 'm+': `${date.getMinutes()}`, // 分
|
|
877
|
-
// 's+': `${date.getSeconds()}`, // 秒
|
|
878
|
-
// 'S+': `${date.getMilliseconds()}` // 豪秒
|
|
879
|
-
// };
|
|
880
|
-
// for (const k in opt) {
|
|
881
|
-
// ret = new RegExp(`(${k})`).exec(fmt);
|
|
882
|
-
// if (ret) {
|
|
883
|
-
// fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
|
|
884
|
-
// }
|
|
885
|
-
// }
|
|
886
|
-
// return fmt;
|
|
887
|
-
// };
|
|
852
|
+
function hasClass(el, className) {
|
|
853
|
+
if (className.indexOf(' ') !== -1)
|
|
854
|
+
throw new Error('className should not contain space.');
|
|
855
|
+
return el.classList.contains(className);
|
|
856
|
+
}
|
|
857
|
+
const eachClassName = (classNames, func) => {
|
|
858
|
+
const classNameList = classNames.split(/\s+/g);
|
|
859
|
+
classNameList.forEach(func);
|
|
860
|
+
};
|
|
888
861
|
/**
|
|
889
|
-
*
|
|
890
|
-
* @param {
|
|
891
|
-
* @
|
|
862
|
+
* 给元素增加样式名
|
|
863
|
+
* @param {HTMLElement} el
|
|
864
|
+
* @param {string} classNames
|
|
892
865
|
*/
|
|
893
|
-
function
|
|
894
|
-
|
|
895
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
|
|
866
|
+
function addClass(el, classNames) {
|
|
867
|
+
eachClassName(classNames, className => el.classList.add(className));
|
|
896
868
|
}
|
|
897
869
|
/**
|
|
898
|
-
*
|
|
899
|
-
* @param {
|
|
900
|
-
* @
|
|
870
|
+
* 给元素移除样式名
|
|
871
|
+
* @param {HTMLElement} el
|
|
872
|
+
* @param {string} classNames
|
|
901
873
|
*/
|
|
902
|
-
function
|
|
903
|
-
|
|
904
|
-
d.setDate(d.getDate() + 1);
|
|
905
|
-
return dateParse(d.getTime() - 1);
|
|
874
|
+
function removeClass(el, classNames) {
|
|
875
|
+
eachClassName(classNames, className => el.classList.remove(className));
|
|
906
876
|
}
|
|
907
877
|
/**
|
|
908
|
-
*
|
|
909
|
-
* @param {
|
|
910
|
-
* @param {string
|
|
911
|
-
*
|
|
912
|
-
* - yyyy: 年
|
|
913
|
-
* - MM:月
|
|
914
|
-
* - DD:日
|
|
915
|
-
* - dd: 日
|
|
916
|
-
* - HH:时(24 小时制)
|
|
917
|
-
* - mm:分
|
|
918
|
-
* - ss:秒
|
|
919
|
-
* - SSS:毫秒
|
|
920
|
-
* - ww: 周
|
|
921
|
-
* @returns {string} 格式化后的日期字符串
|
|
878
|
+
* 设置元素样式
|
|
879
|
+
* @param {HTMLElement} el
|
|
880
|
+
* @param {string | Style} key
|
|
881
|
+
* @param {string} val
|
|
922
882
|
*/
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
'Y+': `${date.getFullYear()}`,
|
|
929
|
-
'y+': `${date.getFullYear()}`,
|
|
930
|
-
'M+': `${date.getMonth() + 1}`,
|
|
931
|
-
'D+': `${date.getDate()}`,
|
|
932
|
-
'd+': `${date.getDate()}`,
|
|
933
|
-
'H+': `${date.getHours()}`,
|
|
934
|
-
'm+': `${date.getMinutes()}`,
|
|
935
|
-
's+': `${date.getSeconds()}`,
|
|
936
|
-
'S+': `${date.getMilliseconds()}`,
|
|
937
|
-
'w+': ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][date.getDay()] // 周
|
|
938
|
-
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
|
939
|
-
};
|
|
940
|
-
for (const k in opt) {
|
|
941
|
-
ret = new RegExp('(' + k + ')').exec(fmt);
|
|
942
|
-
if (ret) {
|
|
943
|
-
fmt = fmt.replace(ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
|
|
944
|
-
}
|
|
883
|
+
const setStyle = (el, key, val) => {
|
|
884
|
+
if (isObject(key)) {
|
|
885
|
+
objectEach(key, (val1, key1) => {
|
|
886
|
+
setStyle(el, key1, val1);
|
|
887
|
+
});
|
|
945
888
|
}
|
|
946
|
-
|
|
889
|
+
else {
|
|
890
|
+
el.style.setProperty(stringKebabCase(key), val);
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
/**
|
|
894
|
+
* 获取元素样式
|
|
895
|
+
* @param {HTMLElement} el 元素
|
|
896
|
+
* @param {string} key
|
|
897
|
+
* @returns {string}
|
|
898
|
+
*/
|
|
899
|
+
function getStyle(el, key) {
|
|
900
|
+
return getComputedStyle(el).getPropertyValue(key);
|
|
901
|
+
}
|
|
902
|
+
function smoothScroll(options) {
|
|
903
|
+
return new Promise(resolve => {
|
|
904
|
+
const defaults = {
|
|
905
|
+
el: document,
|
|
906
|
+
to: 0,
|
|
907
|
+
duration: 567,
|
|
908
|
+
easing: 'ease'
|
|
909
|
+
};
|
|
910
|
+
const { el, to, duration, easing } = objectAssign(defaults, options);
|
|
911
|
+
const htmlEl = document.documentElement;
|
|
912
|
+
const bodyEl = document.body;
|
|
913
|
+
const globalMode = el === window || el === document || el === htmlEl || el === bodyEl;
|
|
914
|
+
const els = globalMode ? [htmlEl, bodyEl] : [el];
|
|
915
|
+
const query = () => {
|
|
916
|
+
let value = 0;
|
|
917
|
+
arrayEach(els, el => {
|
|
918
|
+
if ('scrollTop' in el) {
|
|
919
|
+
value = el.scrollTop;
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
return value;
|
|
924
|
+
};
|
|
925
|
+
const update = (val) => {
|
|
926
|
+
els.forEach(el => {
|
|
927
|
+
if ('scrollTop' in el) {
|
|
928
|
+
el.scrollTop = val;
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
};
|
|
932
|
+
let startTime;
|
|
933
|
+
const startValue = query();
|
|
934
|
+
const length = to - startValue;
|
|
935
|
+
const easingFn = easingFunctional(easing);
|
|
936
|
+
const render = () => {
|
|
937
|
+
const now = performance.now();
|
|
938
|
+
const passingTime = startTime ? now - startTime : 0;
|
|
939
|
+
const t = passingTime / duration;
|
|
940
|
+
const p = easingFn(t);
|
|
941
|
+
if (!startTime)
|
|
942
|
+
startTime = now;
|
|
943
|
+
update(startValue + length * p);
|
|
944
|
+
if (t >= 1)
|
|
945
|
+
resolve();
|
|
946
|
+
else
|
|
947
|
+
requestAnimationFrame(render);
|
|
948
|
+
};
|
|
949
|
+
render();
|
|
950
|
+
});
|
|
947
951
|
}
|
|
948
952
|
/**
|
|
949
|
-
*
|
|
950
|
-
* @param {
|
|
951
|
-
* @param {
|
|
952
|
-
* @param {
|
|
953
|
-
* @returns {string}
|
|
953
|
+
* 获取元素样式属性的计算值
|
|
954
|
+
* @param {HTMLElement} el
|
|
955
|
+
* @param {string} property
|
|
956
|
+
* @param {boolean} reNumber
|
|
957
|
+
* @returns {string|number}
|
|
954
958
|
*/
|
|
955
|
-
function
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const newDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
959
|
-
const millisecondGap = newDate.getTime() + 1000 * 60 * 60 * 24 * parseInt(String(n)); //计算前几天用减,计算后几天用加,最后一个就是多少天的数量
|
|
960
|
-
const targetDate = new Date(millisecondGap);
|
|
961
|
-
const finalNewDate = targetDate.getFullYear() +
|
|
962
|
-
sep +
|
|
963
|
-
String(targetDate.getMonth() + 1).padStart(2, '0') +
|
|
964
|
-
'-' +
|
|
965
|
-
String(targetDate.getDate()).padStart(2, '0');
|
|
966
|
-
return finalNewDate;
|
|
959
|
+
function getComputedCssVal(el, property, reNumber = true) {
|
|
960
|
+
const originVal = getComputedStyle(el).getPropertyValue(property) ?? '';
|
|
961
|
+
return reNumber ? Number(originVal.replace(/([0-9]*)(.*)/g, '$1')) : originVal;
|
|
967
962
|
}
|
|
968
963
|
/**
|
|
969
|
-
*
|
|
970
|
-
* @param {
|
|
971
|
-
* @param {number}
|
|
972
|
-
* @param {
|
|
973
|
-
* @
|
|
974
|
-
* @returns {string} 转换后的目标日期时间
|
|
964
|
+
* 字符串的像素宽度
|
|
965
|
+
* @param {string} str 目标字符串
|
|
966
|
+
* @param {number} fontSize 字符串字体大小
|
|
967
|
+
* @param {boolean} isRemove 计算后是否移除创建的dom元素
|
|
968
|
+
* @returns {*}
|
|
975
969
|
*/
|
|
976
|
-
function
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
970
|
+
function getStrWidthPx(str, fontSize = 14, isRemove = true) {
|
|
971
|
+
let strWidth = 0;
|
|
972
|
+
console.assert(isString(str), `${str} 不是有效的字符串`);
|
|
973
|
+
if (isString(str) && str.length > 0) {
|
|
974
|
+
const id = 'getStrWidth1494304949567';
|
|
975
|
+
let getEle = document.querySelector(`#${id}`);
|
|
976
|
+
if (!getEle) {
|
|
977
|
+
const _ele = document.createElement('span');
|
|
978
|
+
_ele.id = id;
|
|
979
|
+
_ele.style.fontSize = fontSize + 'px';
|
|
980
|
+
_ele.style.whiteSpace = 'nowrap';
|
|
981
|
+
_ele.style.visibility = 'hidden';
|
|
982
|
+
_ele.style.position = 'absolute';
|
|
983
|
+
_ele.style.top = '-9999px';
|
|
984
|
+
_ele.style.left = '-9999px';
|
|
985
|
+
_ele.textContent = str;
|
|
986
|
+
document.body.appendChild(_ele);
|
|
987
|
+
getEle = _ele;
|
|
988
|
+
}
|
|
989
|
+
getEle.textContent = str;
|
|
990
|
+
strWidth = getEle.offsetWidth;
|
|
991
|
+
if (isRemove) {
|
|
992
|
+
getEle.remove();
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return strWidth;
|
|
994
996
|
}
|
|
995
997
|
|
|
996
998
|
/**
|
|
@@ -1107,21 +1109,30 @@
|
|
|
1107
1109
|
return params.toString();
|
|
1108
1110
|
}
|
|
1109
1111
|
|
|
1110
|
-
const anchorEl = document.createElement('a');
|
|
1111
1112
|
/**
|
|
1112
1113
|
* url 解析
|
|
1113
1114
|
* @param {string} url
|
|
1115
|
+
* @param {boolean} isModernApi 使用现代API:URL, 默认true (对无效url解析会抛错), 否则使用a标签来解析(兼容性更强)
|
|
1114
1116
|
* @returns {Url}
|
|
1115
1117
|
*/
|
|
1116
|
-
const urlParse = (url) => {
|
|
1117
|
-
|
|
1118
|
-
|
|
1118
|
+
const urlParse = (url, isModernApi = true) => {
|
|
1119
|
+
// @ts-ignore
|
|
1120
|
+
let urlObj = null;
|
|
1121
|
+
if (isFunction(URL) && isModernApi) {
|
|
1122
|
+
urlObj = new URL(url);
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
urlObj = document.createElement('a');
|
|
1126
|
+
urlObj.href = url;
|
|
1127
|
+
}
|
|
1128
|
+
const { protocol, username, password, host, port, hostname, hash, search, pathname: _pathname } = urlObj;
|
|
1119
1129
|
// fix: ie 浏览器下,解析出来的 pathname 是没有 / 根的
|
|
1120
1130
|
const pathname = pathJoin('/', _pathname);
|
|
1121
1131
|
const auth = username && password ? `${username}:${password}` : '';
|
|
1122
1132
|
const query = search.replace(/^\?/, '');
|
|
1123
1133
|
const searchParams = qsParse(query);
|
|
1124
1134
|
const path = `${pathname}${search}`;
|
|
1135
|
+
urlObj = null;
|
|
1125
1136
|
return {
|
|
1126
1137
|
protocol,
|
|
1127
1138
|
auth,
|
|
@@ -1599,15 +1610,14 @@
|
|
|
1599
1610
|
* @param {ICanvasWM} canvasWM
|
|
1600
1611
|
* @example genCanvasWM({ content: 'QQMusicFE' })
|
|
1601
1612
|
*/
|
|
1602
|
-
function genCanvasWM(canvasWM) {
|
|
1603
|
-
const {
|
|
1613
|
+
function genCanvasWM(content = '请勿外传', canvasWM) {
|
|
1614
|
+
const { rootContainer = document.body, width = '300px', height = '150px', textAlign = 'center', textBaseline = 'middle', font = '20px PingFangSC-Medium,PingFang SC',
|
|
1604
1615
|
// fontWeight = 500,
|
|
1605
|
-
fillStyle = 'rgba(189, 177, 167, .3)',
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
const args = canvasWM;
|
|
1616
|
+
fillStyle = 'rgba(189, 177, 167, .3)', rotate = -20, zIndex = 2147483647, watermarkId = '__wm' } = isNullOrUnDef(canvasWM) ? {} : canvasWM;
|
|
1617
|
+
const container = isString(rootContainer) ? document.querySelector(rootContainer) : rootContainer;
|
|
1618
|
+
if (!container) {
|
|
1619
|
+
throw new Error(`${rootContainer} is not valid Html Element or element selector`);
|
|
1620
|
+
}
|
|
1611
1621
|
const canvas = document.createElement('canvas');
|
|
1612
1622
|
canvas.setAttribute('width', width);
|
|
1613
1623
|
canvas.setAttribute('height', height);
|
|
@@ -1620,36 +1630,36 @@
|
|
|
1620
1630
|
ctx.rotate((Math.PI / 180) * rotate);
|
|
1621
1631
|
ctx.fillText(content, parseFloat(width) / 4, parseFloat(height) / 2);
|
|
1622
1632
|
const base64Url = canvas.toDataURL();
|
|
1623
|
-
const __wm = document.querySelector(
|
|
1633
|
+
const __wm = document.querySelector(`#${watermarkId}`);
|
|
1624
1634
|
const watermarkDiv = __wm || document.createElement('div');
|
|
1625
1635
|
const styleStr = `opacity: 1 !important; display: block !important; visibility: visible !important; position:absolute; left:0; top:0; width:100%; height:100%; z-index:${zIndex}; pointer-events:none; background-repeat:repeat; background-image:url('${base64Url}')`;
|
|
1626
1636
|
watermarkDiv.setAttribute('style', styleStr);
|
|
1627
|
-
watermarkDiv.
|
|
1637
|
+
watermarkDiv.setAttribute('id', watermarkId);
|
|
1628
1638
|
watermarkDiv.classList.add('nav-height');
|
|
1629
1639
|
if (!__wm) {
|
|
1630
1640
|
container.style.position = 'relative';
|
|
1631
1641
|
container.appendChild(watermarkDiv);
|
|
1632
1642
|
}
|
|
1633
1643
|
const getMutableStyle = (ele) => {
|
|
1634
|
-
const
|
|
1644
|
+
const computedStyle = getComputedStyle(ele);
|
|
1635
1645
|
return {
|
|
1636
|
-
opacity:
|
|
1637
|
-
zIndex:
|
|
1638
|
-
display:
|
|
1639
|
-
visibility:
|
|
1646
|
+
opacity: computedStyle.getPropertyValue('opacity'),
|
|
1647
|
+
zIndex: computedStyle.getPropertyValue('z-index'),
|
|
1648
|
+
display: computedStyle.getPropertyValue('display'),
|
|
1649
|
+
visibility: computedStyle.getPropertyValue('visibility')
|
|
1640
1650
|
};
|
|
1641
1651
|
};
|
|
1642
1652
|
//@ts-ignore
|
|
1643
1653
|
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
|
1644
1654
|
if (MutationObserver) {
|
|
1645
1655
|
let mo = new MutationObserver(function () {
|
|
1646
|
-
const __wm = document.querySelector(
|
|
1656
|
+
const __wm = document.querySelector(`#${watermarkId}`); // 只在__wm元素变动才重新调用 __canvasWM
|
|
1647
1657
|
if (!__wm) {
|
|
1648
1658
|
// 避免一直触发
|
|
1649
1659
|
// console.log('regenerate watermark by delete::')
|
|
1650
1660
|
mo.disconnect();
|
|
1651
1661
|
mo = null;
|
|
1652
|
-
genCanvasWM(
|
|
1662
|
+
genCanvasWM(content, canvasWM);
|
|
1653
1663
|
}
|
|
1654
1664
|
else {
|
|
1655
1665
|
const { opacity, zIndex, display, visibility } = getMutableStyle(__wm);
|
|
@@ -1661,7 +1671,7 @@
|
|
|
1661
1671
|
mo.disconnect();
|
|
1662
1672
|
mo = null;
|
|
1663
1673
|
container.removeChild(__wm);
|
|
1664
|
-
genCanvasWM(
|
|
1674
|
+
genCanvasWM(content, canvasWM);
|
|
1665
1675
|
}
|
|
1666
1676
|
}
|
|
1667
1677
|
});
|
|
@@ -1882,35 +1892,75 @@
|
|
|
1882
1892
|
return ret.join('');
|
|
1883
1893
|
}
|
|
1884
1894
|
/**
|
|
1885
|
-
*
|
|
1895
|
+
* 将数字转换为携带单位的字符串
|
|
1886
1896
|
* @param {number | string} num
|
|
1887
1897
|
* @param {Array<string>} units
|
|
1888
|
-
* @param {
|
|
1889
|
-
* @param {number} exponent
|
|
1898
|
+
* @param {INumberAbbr} options default: { ratio: 1000, decimals: 0, separator: ' ' }
|
|
1890
1899
|
* @returns {string}
|
|
1891
1900
|
*/
|
|
1892
|
-
const numberAbbr = (num, units,
|
|
1901
|
+
const numberAbbr = (num, units, options = { ratio: 1000, decimals: 0, separator: ' ' }) => {
|
|
1902
|
+
const { ratio = 1000, decimals = 0, separator = ' ' } = options;
|
|
1893
1903
|
const { length } = units;
|
|
1894
1904
|
if (length === 0)
|
|
1895
|
-
throw new Error('
|
|
1905
|
+
throw new Error('At least one unit is required');
|
|
1896
1906
|
let num2 = Number(num);
|
|
1897
1907
|
let times = 0;
|
|
1898
1908
|
while (num2 >= ratio && times < length - 1) {
|
|
1899
1909
|
num2 = num2 / ratio;
|
|
1900
1910
|
times++;
|
|
1901
1911
|
}
|
|
1902
|
-
const value = num2.toFixed(
|
|
1912
|
+
const value = num2.toFixed(decimals);
|
|
1903
1913
|
const unit = units[times];
|
|
1904
|
-
return value
|
|
1914
|
+
return String(value) + separator + unit;
|
|
1905
1915
|
};
|
|
1916
|
+
/**
|
|
1917
|
+
* Converting file size in bytes to human-readable string
|
|
1918
|
+
* reference: https://zh.wikipedia.org/wiki/%E5%8D%83%E5%AD%97%E8%8A%82
|
|
1919
|
+
* @param {number | string} num bytes Number in Bytes
|
|
1920
|
+
* @param {IHumanFileSizeOptions} options default: { decimals = 0, si = false, separator = ' ' }
|
|
1921
|
+
* si: True to use metric (SI) units, aka powers of 1000, the units is
|
|
1922
|
+
* ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'].
|
|
1923
|
+
* False to use binary (IEC), aka powers of 1024, the units is
|
|
1924
|
+
* ['Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
|
1925
|
+
* @returns
|
|
1926
|
+
*/
|
|
1927
|
+
function humanFileSize(num, options = { decimals: 0, si: false, separator: ' ' }) {
|
|
1928
|
+
const { decimals = 0, si = false, separator = ' ', baseUnit, maxUnit } = options;
|
|
1929
|
+
let units = si
|
|
1930
|
+
? ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
1931
|
+
: ['Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
|
1932
|
+
if (!isNullOrUnDef(baseUnit)) {
|
|
1933
|
+
const targetIndex = units.findIndex(el => el === baseUnit);
|
|
1934
|
+
if (targetIndex !== -1) {
|
|
1935
|
+
units = units.slice(targetIndex);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
if (!isNullOrUnDef(maxUnit)) {
|
|
1939
|
+
const targetIndex = units.findIndex(el => el === maxUnit);
|
|
1940
|
+
if (targetIndex !== -1) {
|
|
1941
|
+
units.splice(targetIndex + 1);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
return numberAbbr(num, units, { ratio: si ? 1000 : 1024, decimals, separator });
|
|
1945
|
+
}
|
|
1906
1946
|
/**
|
|
1907
1947
|
* 将数字格式化成千位分隔符显示的字符串
|
|
1908
|
-
* @param {number}
|
|
1909
|
-
* @param {
|
|
1948
|
+
* @param {number|string} num 数字
|
|
1949
|
+
* @param {number} decimals 格式化成指定小数位精度的参数
|
|
1910
1950
|
* @returns {string}
|
|
1911
1951
|
*/
|
|
1912
|
-
function formatNumber(
|
|
1913
|
-
|
|
1952
|
+
function formatNumber(num, decimals) {
|
|
1953
|
+
if (isNullOrUnDef(decimals)) {
|
|
1954
|
+
return parseInt(String(num)).toLocaleString();
|
|
1955
|
+
}
|
|
1956
|
+
let prec = 0;
|
|
1957
|
+
if (!isNumber(decimals)) {
|
|
1958
|
+
throw new Error('Decimals must be a positive number not less than zero');
|
|
1959
|
+
}
|
|
1960
|
+
else if (decimals > 0) {
|
|
1961
|
+
prec = decimals;
|
|
1962
|
+
}
|
|
1963
|
+
return Number(Number(num).toFixed(prec)).toLocaleString('en-US');
|
|
1914
1964
|
}
|
|
1915
1965
|
|
|
1916
1966
|
const padStartWithZero = (str, maxLength = 2) => String(str).padStart(maxLength, '0');
|
|
@@ -1986,45 +2036,48 @@
|
|
|
1986
2036
|
* @Desc 自定义的tooltip方法, 支持拖动悬浮提示
|
|
1987
2037
|
* Created by chendeqiao on 2017/5/8.
|
|
1988
2038
|
* @example
|
|
1989
|
-
* <span onmouseleave="handleMouseLeave('#root')" onmousemove="handleMouseEnter({
|
|
1990
|
-
* onmouseenter="handleMouseEnter({'#root', title: 'title content', event: event})">title content </span>
|
|
2039
|
+
* <span onmouseleave="handleMouseLeave('#root')" onmousemove="handleMouseEnter({rootContainer: '#root', title: 'title content', event: event})"
|
|
2040
|
+
* onmouseenter="handleMouseEnter({rootContainer:'#root', title: 'title content', event: event})">title content </span>
|
|
1991
2041
|
*/
|
|
1992
2042
|
/**
|
|
1993
2043
|
* 自定义title提示功能的mouseenter事件句柄
|
|
1994
2044
|
* @param {ITooltipParams} param1
|
|
1995
2045
|
* @returns {*}
|
|
1996
2046
|
*/
|
|
1997
|
-
function handleMouseEnter({
|
|
2047
|
+
function handleMouseEnter({ rootContainer = '#root', title, event, bgColor = '#000', color = '#fff' }) {
|
|
1998
2048
|
try {
|
|
1999
|
-
const $rootEl = document.querySelector(
|
|
2000
|
-
|
|
2049
|
+
const $rootEl = isString(rootContainer) ? document.querySelector(rootContainer) : rootContainer;
|
|
2050
|
+
if (!$rootEl) {
|
|
2051
|
+
throw new Error(`${rootContainer} is not valid Html Element or element selector`);
|
|
2052
|
+
}
|
|
2001
2053
|
let $customTitle = null;
|
|
2054
|
+
const styleId = 'style-tooltip-inner1494304949567';
|
|
2002
2055
|
// 动态创建class样式,并加入到head中
|
|
2003
|
-
if (!document.querySelector(
|
|
2056
|
+
if (!document.querySelector(`#${styleId}`)) {
|
|
2004
2057
|
const tooltipWrapperClass = document.createElement('style');
|
|
2005
2058
|
tooltipWrapperClass.type = 'text/css';
|
|
2059
|
+
tooltipWrapperClass.id = styleId;
|
|
2006
2060
|
tooltipWrapperClass.innerHTML = `
|
|
2007
2061
|
.tooltip-inner1494304949567 {
|
|
2008
2062
|
max-width: 250px;
|
|
2009
2063
|
padding: 3px 8px;
|
|
2010
|
-
color:
|
|
2064
|
+
color: ${color};
|
|
2011
2065
|
text-decoration: none;
|
|
2012
2066
|
border-radius: 4px;
|
|
2013
2067
|
text-align: left;
|
|
2068
|
+
background-color: ${bgColor};
|
|
2014
2069
|
}
|
|
2015
2070
|
`;
|
|
2016
2071
|
document.querySelector('head').appendChild(tooltipWrapperClass);
|
|
2017
2072
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2073
|
+
$customTitle = document.querySelector('#customTitle1494304949567');
|
|
2074
|
+
if ($customTitle) {
|
|
2020
2075
|
mouseenter($customTitle, title, event);
|
|
2021
2076
|
}
|
|
2022
2077
|
else {
|
|
2023
2078
|
const $contentContainer = document.createElement('div');
|
|
2024
|
-
$contentContainer.className = 'customTitle';
|
|
2025
2079
|
$contentContainer.id = 'customTitle1494304949567';
|
|
2026
|
-
$contentContainer.
|
|
2027
|
-
$contentContainer.style.cssText = 'z-index: 99999999; visibility: hidden;';
|
|
2080
|
+
$contentContainer.style.cssText = 'z-index: 99999999; visibility: hidden; position: absolute;';
|
|
2028
2081
|
$contentContainer.innerHTML =
|
|
2029
2082
|
'<div class="tooltip-inner1494304949567" style="word-wrap: break-word; max-width: 44px;">皮肤</div>';
|
|
2030
2083
|
$rootEl.appendChild($contentContainer);
|
|
@@ -2077,11 +2130,11 @@
|
|
|
2077
2130
|
}
|
|
2078
2131
|
/**
|
|
2079
2132
|
* 移除提示文案dom的事件句柄
|
|
2080
|
-
* @param {string}
|
|
2133
|
+
* @param {string} rootContainer
|
|
2081
2134
|
* @returns {*}
|
|
2082
2135
|
*/
|
|
2083
|
-
function handleMouseLeave(
|
|
2084
|
-
const rootEl = document.querySelector(
|
|
2136
|
+
function handleMouseLeave(rootContainer = '#root') {
|
|
2137
|
+
const rootEl = isString(rootContainer) ? document.querySelector(rootContainer) : rootContainer, titleEl = document.querySelector('#customTitle1494304949567');
|
|
2085
2138
|
if (rootEl && titleEl) {
|
|
2086
2139
|
rootEl.removeChild(titleEl);
|
|
2087
2140
|
}
|
|
@@ -2290,7 +2343,7 @@
|
|
|
2290
2343
|
...node,
|
|
2291
2344
|
[childField]: [] // 清空子级
|
|
2292
2345
|
};
|
|
2293
|
-
item
|
|
2346
|
+
objectHas(item, childField) && delete item[childField];
|
|
2294
2347
|
res.push(item);
|
|
2295
2348
|
if (node[childField]) {
|
|
2296
2349
|
const children = node[childField].map(item => ({
|
|
@@ -2992,6 +3045,7 @@
|
|
|
2992
3045
|
exports.flatTree = flatTree;
|
|
2993
3046
|
exports.forEachDeep = forEachDeep;
|
|
2994
3047
|
exports.formatDate = formatDate;
|
|
3048
|
+
exports.formatMoney = formatNumber;
|
|
2995
3049
|
exports.formatNumber = formatNumber;
|
|
2996
3050
|
exports.formatTree = formatTree;
|
|
2997
3051
|
exports.fuzzySearchTree = fuzzySearchTree;
|
|
@@ -3001,6 +3055,7 @@
|
|
|
3001
3055
|
exports.getStrWidthPx = getStrWidthPx;
|
|
3002
3056
|
exports.getStyle = getStyle;
|
|
3003
3057
|
exports.hasClass = hasClass;
|
|
3058
|
+
exports.humanFileSize = humanFileSize;
|
|
3004
3059
|
exports.isArray = isArray;
|
|
3005
3060
|
exports.isBigInt = isBigInt;
|
|
3006
3061
|
exports.isBoolean = isBoolean;
|