sculp-js 1.8.4 → 1.9.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 +1 -1
- 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 +49 -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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +48 -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 +1 -1
- 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 +1 -1
- package/lib/es/we-decode.js +1 -1
- package/lib/index.d.ts +32 -9
- package/lib/umd/index.js +561 -518
- package/package.json +1 -1
package/lib/umd/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* sculp-js v1.
|
|
2
|
+
* sculp-js v1.9.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);
|
|
@@ -571,426 +844,151 @@
|
|
|
571
844
|
}
|
|
572
845
|
|
|
573
846
|
/**
|
|
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}
|
|
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} isRemoveDom 计算后是否移除中间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, isRemoveDom = false) {
|
|
971
|
+
let strWidth = 0;
|
|
972
|
+
console.assert(isString(str), `${str} 不是有效的字符串`);
|
|
973
|
+
if (isString(str) && str.length > 0) {
|
|
974
|
+
let getEle = document.querySelector('#getStrWidth1494304949567');
|
|
975
|
+
if (!getEle) {
|
|
976
|
+
const _ele = document.createElement('span');
|
|
977
|
+
_ele.id = 'getStrWidth1494304949567';
|
|
978
|
+
_ele.style.fontSize = fontSize + 'px';
|
|
979
|
+
_ele.style.whiteSpace = 'nowrap';
|
|
980
|
+
_ele.style.visibility = 'hidden';
|
|
981
|
+
_ele.textContent = str;
|
|
982
|
+
document.body.appendChild(_ele);
|
|
983
|
+
getEle = _ele;
|
|
984
|
+
}
|
|
985
|
+
getEle.textContent = str;
|
|
986
|
+
strWidth = getEle.offsetWidth;
|
|
987
|
+
if (isRemoveDom) {
|
|
988
|
+
document.body.appendChild(getEle);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return strWidth;
|
|
994
992
|
}
|
|
995
993
|
|
|
996
994
|
/**
|
|
@@ -1107,21 +1105,30 @@
|
|
|
1107
1105
|
return params.toString();
|
|
1108
1106
|
}
|
|
1109
1107
|
|
|
1110
|
-
const anchorEl = document.createElement('a');
|
|
1111
1108
|
/**
|
|
1112
1109
|
* url 解析
|
|
1113
1110
|
* @param {string} url
|
|
1111
|
+
* @param {boolean} isModernApi 使用现代API:URL, 默认true (对无效url解析会抛错), 否则使用a标签来解析(兼容性更强)
|
|
1114
1112
|
* @returns {Url}
|
|
1115
1113
|
*/
|
|
1116
|
-
const urlParse = (url) => {
|
|
1117
|
-
|
|
1118
|
-
|
|
1114
|
+
const urlParse = (url, isModernApi = true) => {
|
|
1115
|
+
// @ts-ignore
|
|
1116
|
+
let urlObj = null;
|
|
1117
|
+
if (isFunction(URL) && isModernApi) {
|
|
1118
|
+
urlObj = new URL(url);
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
urlObj = document.createElement('a');
|
|
1122
|
+
urlObj.href = url;
|
|
1123
|
+
}
|
|
1124
|
+
const { protocol, username, password, host, port, hostname, hash, search, pathname: _pathname } = urlObj;
|
|
1119
1125
|
// fix: ie 浏览器下,解析出来的 pathname 是没有 / 根的
|
|
1120
1126
|
const pathname = pathJoin('/', _pathname);
|
|
1121
1127
|
const auth = username && password ? `${username}:${password}` : '';
|
|
1122
1128
|
const query = search.replace(/^\?/, '');
|
|
1123
1129
|
const searchParams = qsParse(query);
|
|
1124
1130
|
const path = `${pathname}${search}`;
|
|
1131
|
+
urlObj = null;
|
|
1125
1132
|
return {
|
|
1126
1133
|
protocol,
|
|
1127
1134
|
auth,
|
|
@@ -1882,35 +1889,69 @@
|
|
|
1882
1889
|
return ret.join('');
|
|
1883
1890
|
}
|
|
1884
1891
|
/**
|
|
1885
|
-
*
|
|
1892
|
+
* 将数字转换为携带单位的字符串
|
|
1886
1893
|
* @param {number | string} num
|
|
1887
1894
|
* @param {Array<string>} units
|
|
1888
|
-
* @param {
|
|
1889
|
-
* @param {number} exponent
|
|
1895
|
+
* @param {INumberAbbr} options default: { ratio: 1000, decimals: 0, separator: ' ' }
|
|
1890
1896
|
* @returns {string}
|
|
1891
1897
|
*/
|
|
1892
|
-
const numberAbbr = (num, units,
|
|
1898
|
+
const numberAbbr = (num, units, options = { ratio: 1000, decimals: 0, separator: ' ' }) => {
|
|
1899
|
+
const { ratio = 1000, decimals = 0, separator = ' ' } = options;
|
|
1893
1900
|
const { length } = units;
|
|
1894
1901
|
if (length === 0)
|
|
1895
|
-
throw new Error('
|
|
1902
|
+
throw new Error('At least one unit is required');
|
|
1896
1903
|
let num2 = Number(num);
|
|
1897
1904
|
let times = 0;
|
|
1898
1905
|
while (num2 >= ratio && times < length - 1) {
|
|
1899
1906
|
num2 = num2 / ratio;
|
|
1900
1907
|
times++;
|
|
1901
1908
|
}
|
|
1902
|
-
const value = num2.toFixed(
|
|
1909
|
+
const value = num2.toFixed(decimals);
|
|
1903
1910
|
const unit = units[times];
|
|
1904
|
-
return value
|
|
1911
|
+
return String(value) + separator + unit;
|
|
1905
1912
|
};
|
|
1913
|
+
/**
|
|
1914
|
+
* Converting file size in bytes to human-readable string
|
|
1915
|
+
* reference: https://zh.wikipedia.org/wiki/%E5%8D%83%E5%AD%97%E8%8A%82
|
|
1916
|
+
* @param {number | string} num bytes Number in Bytes
|
|
1917
|
+
* @param {IHumanFileSizeOptions} options default: { decimals = 0, si = false, separator = ' ' }
|
|
1918
|
+
* si: True to use metric (SI) units, aka powers of 1000, the units is
|
|
1919
|
+
* ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'].
|
|
1920
|
+
* False to use binary (IEC), aka powers of 1024, the units is
|
|
1921
|
+
* ['Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
|
1922
|
+
* @returns
|
|
1923
|
+
*/
|
|
1924
|
+
function humanFileSize(num, options) {
|
|
1925
|
+
const { decimals = 0, si = false, separator = ' ', maxUnit } = options;
|
|
1926
|
+
const units = si
|
|
1927
|
+
? ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
1928
|
+
: ['Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
|
1929
|
+
if (!isNullOrUnDef(maxUnit)) {
|
|
1930
|
+
const targetIndex = units.findIndex(el => el === maxUnit);
|
|
1931
|
+
if (targetIndex !== -1) {
|
|
1932
|
+
units.splice(targetIndex + 1);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
return numberAbbr(num, units, { ratio: si ? 1000 : 1024, decimals, separator });
|
|
1936
|
+
}
|
|
1906
1937
|
/**
|
|
1907
1938
|
* 将数字格式化成千位分隔符显示的字符串
|
|
1908
|
-
* @param {number}
|
|
1909
|
-
* @param {
|
|
1939
|
+
* @param {number|string} num 数字
|
|
1940
|
+
* @param {number} decimals 格式化成指定小数位精度的参数
|
|
1910
1941
|
* @returns {string}
|
|
1911
1942
|
*/
|
|
1912
|
-
function formatNumber(
|
|
1913
|
-
|
|
1943
|
+
function formatNumber(num, decimals) {
|
|
1944
|
+
if (isNullOrUnDef(decimals)) {
|
|
1945
|
+
return parseInt(String(num)).toLocaleString();
|
|
1946
|
+
}
|
|
1947
|
+
let prec = 0;
|
|
1948
|
+
if (!isNumber(decimals)) {
|
|
1949
|
+
throw new Error('Decimals must be a positive number not less than zero');
|
|
1950
|
+
}
|
|
1951
|
+
else if (decimals > 0) {
|
|
1952
|
+
prec = decimals;
|
|
1953
|
+
}
|
|
1954
|
+
return Number(Number(num).toFixed(prec)).toLocaleString('en-US');
|
|
1914
1955
|
}
|
|
1915
1956
|
|
|
1916
1957
|
const padStartWithZero = (str, maxLength = 2) => String(str).padStart(maxLength, '0');
|
|
@@ -2290,7 +2331,7 @@
|
|
|
2290
2331
|
...node,
|
|
2291
2332
|
[childField]: [] // 清空子级
|
|
2292
2333
|
};
|
|
2293
|
-
item
|
|
2334
|
+
objectHas(item, childField) && delete item[childField];
|
|
2294
2335
|
res.push(item);
|
|
2295
2336
|
if (node[childField]) {
|
|
2296
2337
|
const children = node[childField].map(item => ({
|
|
@@ -2992,6 +3033,7 @@
|
|
|
2992
3033
|
exports.flatTree = flatTree;
|
|
2993
3034
|
exports.forEachDeep = forEachDeep;
|
|
2994
3035
|
exports.formatDate = formatDate;
|
|
3036
|
+
exports.formatMoney = formatNumber;
|
|
2995
3037
|
exports.formatNumber = formatNumber;
|
|
2996
3038
|
exports.formatTree = formatTree;
|
|
2997
3039
|
exports.fuzzySearchTree = fuzzySearchTree;
|
|
@@ -3001,6 +3043,7 @@
|
|
|
3001
3043
|
exports.getStrWidthPx = getStrWidthPx;
|
|
3002
3044
|
exports.getStyle = getStyle;
|
|
3003
3045
|
exports.hasClass = hasClass;
|
|
3046
|
+
exports.humanFileSize = humanFileSize;
|
|
3004
3047
|
exports.isArray = isArray;
|
|
3005
3048
|
exports.isBigInt = isBigInt;
|
|
3006
3049
|
exports.isBoolean = isBoolean;
|