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