@i17hush/h5-utils 1.1.1 → 1.1.2
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/dist/index.cjs.js +281 -176
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.esm.js +281 -176
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -219,15 +219,36 @@ function createTaroAdapter(taro) {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
let currentAdapter = null;
|
|
222
|
+
/**
|
|
223
|
+
* 运行时检测是否在 Taro 环境中
|
|
224
|
+
* 不依赖 process.env.TARO_ENV(构建时替换,预编译库无法获取)
|
|
225
|
+
* 改为检测 Taro 运行时特征
|
|
226
|
+
*/
|
|
227
|
+
function isTaroEnv() {
|
|
228
|
+
// 1. 检测小程序全局对象(微信 wx、支付宝 my、字节 tt、百度 swan、QQ qq)
|
|
229
|
+
if (typeof wx !== 'undefined' ||
|
|
230
|
+
typeof my !== 'undefined' ||
|
|
231
|
+
typeof tt !== 'undefined' ||
|
|
232
|
+
typeof swan !== 'undefined' ||
|
|
233
|
+
typeof qq !== 'undefined') {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
// 2. 检测 Taro 运行时注入的全局变量
|
|
237
|
+
const g = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : {};
|
|
238
|
+
if (g.__TARO || g.__taro__) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
// 3. process.env.TARO_ENV 作为补充(Taro H5 模式下可能有效)
|
|
242
|
+
if (typeof process !== 'undefined' && process.env && process.env.TARO_ENV) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
222
247
|
/**
|
|
223
248
|
* 自动检测运行环境并创建对应适配器
|
|
224
249
|
*/
|
|
225
250
|
function detectAdapter() {
|
|
226
|
-
|
|
227
|
-
const env = typeof process !== 'undefined' && process.env
|
|
228
|
-
? process.env.TARO_ENV
|
|
229
|
-
: '';
|
|
230
|
-
if (env) {
|
|
251
|
+
if (isTaroEnv()) {
|
|
231
252
|
try {
|
|
232
253
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
233
254
|
const Taro = require('@tarojs/taro');
|
|
@@ -255,6 +276,35 @@ function setAdapter(adapter) {
|
|
|
255
276
|
currentAdapter = adapter;
|
|
256
277
|
}
|
|
257
278
|
|
|
279
|
+
/**
|
|
280
|
+
* 内部错误处理工具
|
|
281
|
+
*/
|
|
282
|
+
const PREFIX = '[h5-utils]';
|
|
283
|
+
/**
|
|
284
|
+
* 安全执行函数,出错时打印错误并返回默认值
|
|
285
|
+
*/
|
|
286
|
+
function safeCall(fn, fallback, method) {
|
|
287
|
+
try {
|
|
288
|
+
return fn();
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
console.error(`${PREFIX} ${method} error:`, (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
292
|
+
return fallback;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 安全执行异步函数
|
|
297
|
+
*/
|
|
298
|
+
async function safeCallAsync(fn, fallback, method) {
|
|
299
|
+
try {
|
|
300
|
+
return await fn();
|
|
301
|
+
}
|
|
302
|
+
catch (e) {
|
|
303
|
+
console.error(`${PREFIX} ${method} error:`, (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
304
|
+
return fallback;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
258
308
|
/**
|
|
259
309
|
* URL 解析与操作工具
|
|
260
310
|
*/
|
|
@@ -262,57 +312,63 @@ function setAdapter(adapter) {
|
|
|
262
312
|
* 解析 URL 为结构化对象
|
|
263
313
|
*/
|
|
264
314
|
function parseUrl(url) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
a.search
|
|
270
|
-
.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
315
|
+
return safeCall(() => {
|
|
316
|
+
const a = document.createElement('a');
|
|
317
|
+
a.href = url;
|
|
318
|
+
const query = {};
|
|
319
|
+
if (a.search) {
|
|
320
|
+
a.search
|
|
321
|
+
.slice(1)
|
|
322
|
+
.split('&')
|
|
323
|
+
.forEach((pair) => {
|
|
324
|
+
const [key, val] = pair.split('=');
|
|
325
|
+
if (key)
|
|
326
|
+
query[decodeURIComponent(key)] = decodeURIComponent(val || '');
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
protocol: a.protocol,
|
|
331
|
+
host: a.host,
|
|
332
|
+
port: a.port,
|
|
333
|
+
pathname: a.pathname,
|
|
334
|
+
search: a.search,
|
|
335
|
+
hash: a.hash,
|
|
336
|
+
query,
|
|
337
|
+
};
|
|
338
|
+
}, { protocol: '', host: '', port: '', pathname: '', search: '', hash: '', query: {} }, 'parseUrl');
|
|
287
339
|
}
|
|
288
340
|
/**
|
|
289
341
|
* 拼接带查询参数的 URL
|
|
290
342
|
*/
|
|
291
343
|
function buildUrl(base, params) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
344
|
+
return safeCall(() => {
|
|
345
|
+
const search = Object.entries(params)
|
|
346
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
347
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
348
|
+
.join('&');
|
|
349
|
+
if (!search)
|
|
350
|
+
return base;
|
|
351
|
+
const separator = base.includes('?') ? '&' : '?';
|
|
352
|
+
return `${base}${separator}${search}`;
|
|
353
|
+
}, base, 'buildUrl');
|
|
300
354
|
}
|
|
301
355
|
/**
|
|
302
356
|
* 获取当前页面指定查询参数
|
|
303
357
|
*/
|
|
304
358
|
function getQueryParam(name) {
|
|
305
|
-
return new URLSearchParams(window.location.search).get(name);
|
|
359
|
+
return safeCall(() => new URLSearchParams(window.location.search).get(name), null, 'getQueryParam');
|
|
306
360
|
}
|
|
307
361
|
/**
|
|
308
362
|
* 获取当前页面所有查询参数
|
|
309
363
|
*/
|
|
310
364
|
function getAllQueryParams() {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
365
|
+
return safeCall(() => {
|
|
366
|
+
const params = {};
|
|
367
|
+
new URLSearchParams(window.location.search).forEach((value, key) => {
|
|
368
|
+
params[key] = value;
|
|
369
|
+
});
|
|
370
|
+
return params;
|
|
371
|
+
}, {}, 'getAllQueryParams');
|
|
316
372
|
}
|
|
317
373
|
|
|
318
374
|
/**
|
|
@@ -324,42 +380,46 @@ function getAllQueryParams() {
|
|
|
324
380
|
* 获取存储值,支持 JSON 自动反序列化
|
|
325
381
|
*/
|
|
326
382
|
function get$1(key) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
try {
|
|
331
|
-
const item = JSON.parse(raw);
|
|
332
|
-
if (item.expireAt !== null && Date.now() > item.expireAt) {
|
|
333
|
-
getAdapter().storage.remove(key);
|
|
383
|
+
return safeCall(() => {
|
|
384
|
+
const raw = getAdapter().storage.get(key);
|
|
385
|
+
if (raw === null)
|
|
334
386
|
return null;
|
|
387
|
+
try {
|
|
388
|
+
const item = JSON.parse(raw);
|
|
389
|
+
if (item.expireAt !== null && Date.now() > item.expireAt) {
|
|
390
|
+
getAdapter().storage.remove(key);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
return item.value;
|
|
335
394
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
395
|
+
catch (_a) {
|
|
396
|
+
return raw;
|
|
397
|
+
}
|
|
398
|
+
}, null, 'getStorage');
|
|
341
399
|
}
|
|
342
400
|
/**
|
|
343
401
|
* 设置存储值,支持过期时间
|
|
344
402
|
*/
|
|
345
403
|
function set$1(key, value, options) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
404
|
+
safeCall(() => {
|
|
405
|
+
const item = {
|
|
406
|
+
value,
|
|
407
|
+
expireAt: (options === null || options === void 0 ? void 0 : options.expires) ? Date.now() + options.expires : null,
|
|
408
|
+
};
|
|
409
|
+
getAdapter().storage.set(key, JSON.stringify(item), options);
|
|
410
|
+
}, undefined, 'setStorage');
|
|
351
411
|
}
|
|
352
412
|
/**
|
|
353
413
|
* 删除存储值
|
|
354
414
|
*/
|
|
355
415
|
function remove$1(key) {
|
|
356
|
-
getAdapter().storage.remove(key);
|
|
416
|
+
safeCall(() => getAdapter().storage.remove(key), undefined, 'removeStorage');
|
|
357
417
|
}
|
|
358
418
|
/**
|
|
359
419
|
* 清空所有存储
|
|
360
420
|
*/
|
|
361
421
|
function clear() {
|
|
362
|
-
getAdapter().storage.clear();
|
|
422
|
+
safeCall(() => getAdapter().storage.clear(), undefined, 'clearStorage');
|
|
363
423
|
}
|
|
364
424
|
|
|
365
425
|
/**
|
|
@@ -369,27 +429,31 @@ function clear() {
|
|
|
369
429
|
* 获取 Cookie 值
|
|
370
430
|
*/
|
|
371
431
|
function get(name) {
|
|
372
|
-
|
|
373
|
-
|
|
432
|
+
return safeCall(() => {
|
|
433
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${encodeURIComponent(name)}=([^;]*)`));
|
|
434
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
435
|
+
}, null, 'getCookie');
|
|
374
436
|
}
|
|
375
437
|
/**
|
|
376
438
|
* 设置 Cookie
|
|
377
439
|
*/
|
|
378
440
|
function set(name, value, options = {}) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
441
|
+
safeCall(() => {
|
|
442
|
+
const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
|
|
443
|
+
if (options.days) {
|
|
444
|
+
const expires = new Date(Date.now() + options.days * 864e5).toUTCString();
|
|
445
|
+
parts.push(`expires=${expires}`);
|
|
446
|
+
}
|
|
447
|
+
if (options.path)
|
|
448
|
+
parts.push(`path=${options.path}`);
|
|
449
|
+
if (options.domain)
|
|
450
|
+
parts.push(`domain=${options.domain}`);
|
|
451
|
+
if (options.secure)
|
|
452
|
+
parts.push('secure');
|
|
453
|
+
if (options.sameSite)
|
|
454
|
+
parts.push(`samesite=${options.sameSite}`);
|
|
455
|
+
document.cookie = parts.join('; ');
|
|
456
|
+
}, undefined, 'setCookie');
|
|
393
457
|
}
|
|
394
458
|
/**
|
|
395
459
|
* 删除 Cookie
|
|
@@ -405,60 +469,73 @@ function remove(name, options = {}) {
|
|
|
405
469
|
* 是否为 iOS
|
|
406
470
|
*/
|
|
407
471
|
function isIOS() {
|
|
408
|
-
return getAdapter().device.getInfo().ios;
|
|
472
|
+
return safeCall(() => getAdapter().device.getInfo().ios, false, 'isIOS');
|
|
409
473
|
}
|
|
410
474
|
/**
|
|
411
475
|
* 是否为 Android
|
|
412
476
|
*/
|
|
413
477
|
function isAndroid() {
|
|
414
|
-
return getAdapter().device.getInfo().android;
|
|
478
|
+
return safeCall(() => getAdapter().device.getInfo().android, false, 'isAndroid');
|
|
415
479
|
}
|
|
416
480
|
/**
|
|
417
481
|
* 是否为微信浏览器
|
|
418
482
|
*/
|
|
419
483
|
function isWeChat() {
|
|
420
|
-
return getAdapter().device.getInfo().weChat;
|
|
484
|
+
return safeCall(() => getAdapter().device.getInfo().weChat, false, 'isWeChat');
|
|
421
485
|
}
|
|
422
486
|
/**
|
|
423
487
|
* 是否为移动端
|
|
424
488
|
*/
|
|
425
489
|
function isMobile() {
|
|
426
|
-
return getAdapter().device.getInfo().mobile;
|
|
490
|
+
return safeCall(() => getAdapter().device.getInfo().mobile, false, 'isMobile');
|
|
427
491
|
}
|
|
428
492
|
/**
|
|
429
493
|
* 获取浏览器类型(H5 专用)
|
|
430
494
|
*/
|
|
431
495
|
function getBrowserType() {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
496
|
+
return safeCall(() => {
|
|
497
|
+
const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';
|
|
498
|
+
if (/MicroMessenger/i.test(ua))
|
|
499
|
+
return 'weixin';
|
|
500
|
+
if (/\bQQ\//i.test(ua))
|
|
501
|
+
return 'qq';
|
|
502
|
+
if (/UCBrowser/i.test(ua))
|
|
503
|
+
return 'uc';
|
|
504
|
+
if (/Edg/i.test(ua))
|
|
505
|
+
return 'edge';
|
|
506
|
+
if (/Firefox/i.test(ua))
|
|
507
|
+
return 'firefox';
|
|
508
|
+
if (/MSIE|Trident/i.test(ua))
|
|
509
|
+
return 'ie';
|
|
510
|
+
if (/Chrome/i.test(ua))
|
|
511
|
+
return 'chrome';
|
|
512
|
+
if (/Safari/i.test(ua))
|
|
513
|
+
return 'safari';
|
|
514
|
+
return 'unknown';
|
|
515
|
+
}, 'unknown', 'getBrowserType');
|
|
450
516
|
}
|
|
451
517
|
/**
|
|
452
518
|
* 获取操作系统信息
|
|
453
519
|
*/
|
|
454
520
|
function getOS() {
|
|
455
|
-
return getAdapter().device.getInfo().os;
|
|
521
|
+
return safeCall(() => getAdapter().device.getInfo().os, 'Unknown', 'getOS');
|
|
456
522
|
}
|
|
457
523
|
/**
|
|
458
524
|
* 获取完整设备信息
|
|
459
525
|
*/
|
|
460
526
|
function getDeviceInfo() {
|
|
461
|
-
return getAdapter().device.getInfo()
|
|
527
|
+
return safeCall(() => getAdapter().device.getInfo(), {
|
|
528
|
+
ios: false,
|
|
529
|
+
android: false,
|
|
530
|
+
mobile: false,
|
|
531
|
+
weChat: false,
|
|
532
|
+
os: 'Unknown',
|
|
533
|
+
brand: '',
|
|
534
|
+
model: '',
|
|
535
|
+
screenWidth: 0,
|
|
536
|
+
screenHeight: 0,
|
|
537
|
+
pixelRatio: 1,
|
|
538
|
+
}, 'getDeviceInfo');
|
|
462
539
|
}
|
|
463
540
|
|
|
464
541
|
/**
|
|
@@ -468,53 +545,56 @@ function getDeviceInfo() {
|
|
|
468
545
|
* 查询单个元素
|
|
469
546
|
*/
|
|
470
547
|
function $(selector) {
|
|
471
|
-
return getAdapter().dom.select(selector);
|
|
548
|
+
return safeCall(() => getAdapter().dom.select(selector), null, '$');
|
|
472
549
|
}
|
|
473
550
|
/**
|
|
474
551
|
* 查询多个元素
|
|
475
552
|
*/
|
|
476
553
|
function $$(selector) {
|
|
477
|
-
return getAdapter().dom.selectAll(selector);
|
|
554
|
+
return safeCall(() => getAdapter().dom.selectAll(selector), [], '$$');
|
|
478
555
|
}
|
|
479
556
|
/**
|
|
480
557
|
* 添加 class
|
|
481
558
|
*/
|
|
482
559
|
function addClass(el, cls) {
|
|
483
|
-
el.classList.add(cls);
|
|
560
|
+
safeCall(() => el.classList.add(cls), undefined, 'addClass');
|
|
484
561
|
}
|
|
485
562
|
/**
|
|
486
563
|
* 移除 class
|
|
487
564
|
*/
|
|
488
565
|
function removeClass(el, cls) {
|
|
489
|
-
el.classList.remove(cls);
|
|
566
|
+
safeCall(() => el.classList.remove(cls), undefined, 'removeClass');
|
|
490
567
|
}
|
|
491
568
|
/**
|
|
492
569
|
* 切换 class
|
|
493
570
|
*/
|
|
494
571
|
function toggleClass(el, cls) {
|
|
495
|
-
el.classList.toggle(cls);
|
|
572
|
+
safeCall(() => el.classList.toggle(cls), undefined, 'toggleClass');
|
|
496
573
|
}
|
|
497
574
|
/**
|
|
498
575
|
* 判断是否包含 class
|
|
499
576
|
*/
|
|
500
577
|
function hasClass(el, cls) {
|
|
501
|
-
return el.classList.contains(cls);
|
|
578
|
+
return safeCall(() => el.classList.contains(cls), false, 'hasClass');
|
|
502
579
|
}
|
|
503
580
|
/**
|
|
504
581
|
* 获取元素样式
|
|
505
582
|
*/
|
|
506
583
|
function getStyle(el, prop) {
|
|
507
|
-
return (el.style.getPropertyValue(prop) ||
|
|
584
|
+
return safeCall(() => el.style.getPropertyValue(prop) ||
|
|
585
|
+
getComputedStyle(el).getPropertyValue(prop), '', 'getStyle');
|
|
508
586
|
}
|
|
509
587
|
/**
|
|
510
588
|
* 判断元素是否在可视区域内
|
|
511
589
|
*/
|
|
512
590
|
function isInViewport(el) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
rect.
|
|
516
|
-
|
|
517
|
-
|
|
591
|
+
return safeCall(() => {
|
|
592
|
+
const rect = el.getBoundingClientRect();
|
|
593
|
+
return (rect.top >= 0 &&
|
|
594
|
+
rect.left >= 0 &&
|
|
595
|
+
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
596
|
+
rect.right <= (window.innerWidth || document.documentElement.clientWidth));
|
|
597
|
+
}, false, 'isInViewport');
|
|
518
598
|
}
|
|
519
599
|
|
|
520
600
|
/**
|
|
@@ -524,19 +604,19 @@ function isInViewport(el) {
|
|
|
524
604
|
* 绑定事件
|
|
525
605
|
*/
|
|
526
606
|
function on(el, event, handler, options) {
|
|
527
|
-
el.addEventListener(event, handler, options);
|
|
607
|
+
safeCall(() => el.addEventListener(event, handler, options), undefined, 'on');
|
|
528
608
|
}
|
|
529
609
|
/**
|
|
530
610
|
* 解绑事件
|
|
531
611
|
*/
|
|
532
612
|
function off(el, event, handler, options) {
|
|
533
|
-
el.removeEventListener(event, handler, options);
|
|
613
|
+
safeCall(() => el.removeEventListener(event, handler, options), undefined, 'off');
|
|
534
614
|
}
|
|
535
615
|
/**
|
|
536
616
|
* 单次监听事件
|
|
537
617
|
*/
|
|
538
618
|
function once(el, event, handler) {
|
|
539
|
-
el.addEventListener(event, handler, { once: true });
|
|
619
|
+
safeCall(() => el.addEventListener(event, handler, { once: true }), undefined, 'once');
|
|
540
620
|
}
|
|
541
621
|
/**
|
|
542
622
|
* 事件委托
|
|
@@ -546,14 +626,16 @@ function once(el, event, handler) {
|
|
|
546
626
|
* @param handler 回调函数
|
|
547
627
|
*/
|
|
548
628
|
function delegate(parent, selector, event, handler) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
629
|
+
safeCall(() => {
|
|
630
|
+
const wrappedHandler = (e) => {
|
|
631
|
+
var _a;
|
|
632
|
+
const target = (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest(selector);
|
|
633
|
+
if (target) {
|
|
634
|
+
handler(target, e);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
parent.addEventListener(event, wrappedHandler);
|
|
638
|
+
}, undefined, 'delegate');
|
|
557
639
|
}
|
|
558
640
|
|
|
559
641
|
/**
|
|
@@ -563,13 +645,13 @@ function delegate(parent, selector, event, handler) {
|
|
|
563
645
|
* 复制文本到剪贴板
|
|
564
646
|
*/
|
|
565
647
|
function copyText(text) {
|
|
566
|
-
return getAdapter().clipboard.write(text);
|
|
648
|
+
return safeCallAsync(() => getAdapter().clipboard.write(text), undefined, 'copyText');
|
|
567
649
|
}
|
|
568
650
|
/**
|
|
569
651
|
* 读取剪贴板文本
|
|
570
652
|
*/
|
|
571
653
|
function readText() {
|
|
572
|
-
return getAdapter().clipboard.read();
|
|
654
|
+
return safeCallAsync(() => getAdapter().clipboard.read(), '', 'readText');
|
|
573
655
|
}
|
|
574
656
|
|
|
575
657
|
/**
|
|
@@ -580,34 +662,40 @@ let scrollLockCount = 0;
|
|
|
580
662
|
* 滚动到页面顶部
|
|
581
663
|
*/
|
|
582
664
|
function scrollToTop(smooth = true) {
|
|
583
|
-
getAdapter().scroll.scrollTo({ top: 0, animated: smooth });
|
|
665
|
+
safeCall(() => getAdapter().scroll.scrollTo({ top: 0, animated: smooth }), undefined, 'scrollToTop');
|
|
584
666
|
}
|
|
585
667
|
/**
|
|
586
668
|
* 滚动到指定元素
|
|
587
669
|
*/
|
|
588
670
|
function scrollToElement(el, options = {}) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
671
|
+
safeCall(() => {
|
|
672
|
+
const { offset = 0, smooth = true } = options;
|
|
673
|
+
const rect = el.getBoundingClientRect();
|
|
674
|
+
const top = window.scrollY + rect.top + offset;
|
|
675
|
+
getAdapter().scroll.scrollTo({ top, animated: smooth });
|
|
676
|
+
}, undefined, 'scrollToElement');
|
|
593
677
|
}
|
|
594
678
|
/**
|
|
595
679
|
* 锁定页面滚动
|
|
596
680
|
*/
|
|
597
681
|
function lockScroll() {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
682
|
+
safeCall(() => {
|
|
683
|
+
scrollLockCount++;
|
|
684
|
+
if (scrollLockCount === 1) {
|
|
685
|
+
getAdapter().scroll.lockScroll();
|
|
686
|
+
}
|
|
687
|
+
}, undefined, 'lockScroll');
|
|
602
688
|
}
|
|
603
689
|
/**
|
|
604
690
|
* 解锁页面滚动
|
|
605
691
|
*/
|
|
606
692
|
function unlockScroll() {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
693
|
+
safeCall(() => {
|
|
694
|
+
scrollLockCount = Math.max(0, scrollLockCount - 1);
|
|
695
|
+
if (scrollLockCount === 0) {
|
|
696
|
+
getAdapter().scroll.unlockScroll();
|
|
697
|
+
}
|
|
698
|
+
}, undefined, 'unlockScroll');
|
|
611
699
|
}
|
|
612
700
|
|
|
613
701
|
/**
|
|
@@ -618,49 +706,53 @@ function unlockScroll() {
|
|
|
618
706
|
* @example formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
|
|
619
707
|
*/
|
|
620
708
|
function formatDate(date, fmt = 'YYYY-MM-DD HH:mm:ss') {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
709
|
+
return safeCall(() => {
|
|
710
|
+
const d = new Date(date);
|
|
711
|
+
const map = {
|
|
712
|
+
YYYY: String(d.getFullYear()),
|
|
713
|
+
MM: String(d.getMonth() + 1).padStart(2, '0'),
|
|
714
|
+
DD: String(d.getDate()).padStart(2, '0'),
|
|
715
|
+
HH: String(d.getHours()).padStart(2, '0'),
|
|
716
|
+
mm: String(d.getMinutes()).padStart(2, '0'),
|
|
717
|
+
ss: String(d.getSeconds()).padStart(2, '0'),
|
|
718
|
+
};
|
|
719
|
+
let result = fmt;
|
|
720
|
+
for (const [key, val] of Object.entries(map)) {
|
|
721
|
+
result = result.replace(key, val);
|
|
722
|
+
}
|
|
723
|
+
return result;
|
|
724
|
+
}, '', 'formatDate');
|
|
635
725
|
}
|
|
636
726
|
/**
|
|
637
727
|
* 数字格式化(千分位等)
|
|
638
728
|
*/
|
|
639
729
|
function formatNumber(num, options = {}) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
730
|
+
return safeCall(() => {
|
|
731
|
+
const { precision, separator = ',' } = options;
|
|
732
|
+
let str = precision !== undefined ? num.toFixed(precision) : String(num);
|
|
733
|
+
const [intPart, decPart] = str.split('.');
|
|
734
|
+
const formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
735
|
+
return decPart !== undefined ? `${formatted}.${decPart}` : formatted;
|
|
736
|
+
}, String(num), 'formatNumber');
|
|
645
737
|
}
|
|
646
738
|
/**
|
|
647
739
|
* 手机号格式化 (xxx xxxx xxxx)
|
|
648
740
|
*/
|
|
649
741
|
function formatPhone(phone) {
|
|
650
|
-
return phone.replace(/(\d{3})(\d{0,4})(\d{0,4})/, (_, a, b, c) => {
|
|
742
|
+
return safeCall(() => phone.replace(/(\d{3})(\d{0,4})(\d{0,4})/, (_, a, b, c) => {
|
|
651
743
|
let result = a;
|
|
652
744
|
if (b)
|
|
653
745
|
result += ` ${b}`;
|
|
654
746
|
if (c)
|
|
655
747
|
result += ` ${c}`;
|
|
656
748
|
return result;
|
|
657
|
-
});
|
|
749
|
+
}), phone, 'formatPhone');
|
|
658
750
|
}
|
|
659
751
|
/**
|
|
660
752
|
* 金额格式化
|
|
661
753
|
*/
|
|
662
754
|
function formatMoney(amount, precision = 2) {
|
|
663
|
-
return formatNumber(amount, { precision });
|
|
755
|
+
return safeCall(() => formatNumber(amount, { precision }), String(amount), 'formatMoney');
|
|
664
756
|
}
|
|
665
757
|
/**
|
|
666
758
|
* 节流
|
|
@@ -671,7 +763,12 @@ function throttle(fn, delay) {
|
|
|
671
763
|
const now = Date.now();
|
|
672
764
|
if (now - last >= delay) {
|
|
673
765
|
last = now;
|
|
674
|
-
|
|
766
|
+
try {
|
|
767
|
+
fn.apply(this, args);
|
|
768
|
+
}
|
|
769
|
+
catch (e) {
|
|
770
|
+
console.error('[h5-utils] throttle error:', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
771
|
+
}
|
|
675
772
|
}
|
|
676
773
|
};
|
|
677
774
|
}
|
|
@@ -683,7 +780,14 @@ function debounce(fn, delay) {
|
|
|
683
780
|
return function (...args) {
|
|
684
781
|
if (timer)
|
|
685
782
|
clearTimeout(timer);
|
|
686
|
-
timer = setTimeout(() =>
|
|
783
|
+
timer = setTimeout(() => {
|
|
784
|
+
try {
|
|
785
|
+
fn.apply(this, args);
|
|
786
|
+
}
|
|
787
|
+
catch (e) {
|
|
788
|
+
console.error('[h5-utils] debounce error:', (e === null || e === void 0 ? void 0 : e.message) || e);
|
|
789
|
+
}
|
|
790
|
+
}, delay);
|
|
687
791
|
};
|
|
688
792
|
}
|
|
689
793
|
|
|
@@ -701,40 +805,41 @@ const patterns = {
|
|
|
701
805
|
* 手机号格式校验
|
|
702
806
|
*/
|
|
703
807
|
function isMobilePhone(str) {
|
|
704
|
-
return patterns.mobile.test(str);
|
|
808
|
+
return safeCall(() => patterns.mobile.test(str), false, 'isMobilePhone');
|
|
705
809
|
}
|
|
706
810
|
/**
|
|
707
811
|
* 邮箱校验
|
|
708
812
|
*/
|
|
709
813
|
function isEmail(str) {
|
|
710
|
-
return patterns.email.test(str);
|
|
814
|
+
return safeCall(() => patterns.email.test(str), false, 'isEmail');
|
|
711
815
|
}
|
|
712
816
|
/**
|
|
713
817
|
* 身份证号校验(18位)
|
|
714
818
|
*/
|
|
715
819
|
function isIdCard(str) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
820
|
+
return safeCall(() => {
|
|
821
|
+
if (!patterns.idCard.test(str))
|
|
822
|
+
return false;
|
|
823
|
+
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
|
824
|
+
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
|
825
|
+
let sum = 0;
|
|
826
|
+
for (let i = 0; i < 17; i++) {
|
|
827
|
+
sum += parseInt(str[i], 10) * weights[i];
|
|
828
|
+
}
|
|
829
|
+
return checkCodes[sum % 11] === str[17].toUpperCase();
|
|
830
|
+
}, false, 'isIdCard');
|
|
726
831
|
}
|
|
727
832
|
/**
|
|
728
833
|
* URL 校验
|
|
729
834
|
*/
|
|
730
835
|
function isUrl(str) {
|
|
731
|
-
return patterns.url.test(str);
|
|
836
|
+
return safeCall(() => patterns.url.test(str), false, 'isUrl');
|
|
732
837
|
}
|
|
733
838
|
/**
|
|
734
839
|
* 中文校验
|
|
735
840
|
*/
|
|
736
841
|
function isChinese(str) {
|
|
737
|
-
return patterns.chinese.test(str);
|
|
842
|
+
return safeCall(() => patterns.chinese.test(str), false, 'isChinese');
|
|
738
843
|
}
|
|
739
844
|
|
|
740
845
|
export { $, $$, addClass, buildUrl, clear as clearStorage, copyText, debounce, delegate, formatDate, formatMoney, formatNumber, formatPhone, getAdapter, getAllQueryParams, getBrowserType, get as getCookie, getDeviceInfo, getOS, getQueryParam, get$1 as getStorage, getStyle, hasClass, isAndroid, isChinese, isEmail, isIOS, isIdCard, isInViewport, isMobile, isMobilePhone, isUrl, isWeChat, lockScroll, off, on, once, parseUrl, readText, removeClass, remove as removeCookie, remove$1 as removeStorage, scrollToElement, scrollToTop, setAdapter, set as setCookie, set$1 as setStorage, throttle, toggleClass, unlockScroll };
|