@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 +410 -182
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.esm.js +410 -182
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
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
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
239
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
a.search
|
|
272
|
-
.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
375
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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) ||
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
rect.
|
|
518
|
-
|
|
519
|
-
|
|
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
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
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(() =>
|
|
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
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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.$ = $;
|