@i17hush/h5-utils 1.1.1 → 1.1.3

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