@i17hush/h5-utils 1.1.0 → 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 +384 -173
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.esm.js +384 -173
- package/dist/index.esm.js.map +1 -1
- package/package.json +8 -2
package/dist/index.esm.js
CHANGED
|
@@ -96,7 +96,9 @@ const browserAdapter = {
|
|
|
96
96
|
};
|
|
97
97
|
},
|
|
98
98
|
async getNetworkType() {
|
|
99
|
-
const conn = navigator.connection ||
|
|
99
|
+
const conn = navigator.connection ||
|
|
100
|
+
navigator.mozConnection ||
|
|
101
|
+
navigator.webkitConnection;
|
|
100
102
|
return (conn === null || conn === void 0 ? void 0 : conn.effectiveType) || 'unknown';
|
|
101
103
|
},
|
|
102
104
|
},
|
|
@@ -116,18 +118,140 @@ function getBrowserOS(ua) {
|
|
|
116
118
|
return 'Unknown';
|
|
117
119
|
}
|
|
118
120
|
|
|
121
|
+
/**
|
|
122
|
+
* 创建 Taro 适配器
|
|
123
|
+
* @param taro Taro 实例,由用户传入以避免硬依赖
|
|
124
|
+
*/
|
|
125
|
+
function createTaroAdapter(taro) {
|
|
126
|
+
return {
|
|
127
|
+
storage: {
|
|
128
|
+
get(key) {
|
|
129
|
+
const value = taro.getStorageSync(key);
|
|
130
|
+
return value !== '' && value !== undefined && value !== null ? String(value) : null;
|
|
131
|
+
},
|
|
132
|
+
set(key, value) {
|
|
133
|
+
taro.setStorageSync(key, value);
|
|
134
|
+
},
|
|
135
|
+
remove(key) {
|
|
136
|
+
taro.removeStorageSync(key);
|
|
137
|
+
},
|
|
138
|
+
clear() {
|
|
139
|
+
taro.clearStorageSync();
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
clipboard: {
|
|
143
|
+
async write(text) {
|
|
144
|
+
await taro.setClipboardData({ data: text });
|
|
145
|
+
},
|
|
146
|
+
async read() {
|
|
147
|
+
const res = await taro.getClipboardData();
|
|
148
|
+
return res.data;
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
event: {
|
|
152
|
+
on(event, handler) {
|
|
153
|
+
taro.eventCenter.on(event, handler);
|
|
154
|
+
},
|
|
155
|
+
off(event, handler) {
|
|
156
|
+
taro.eventCenter.off(event, handler);
|
|
157
|
+
},
|
|
158
|
+
emit(event, ...args) {
|
|
159
|
+
taro.eventCenter.trigger(event, ...args);
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
dom: {
|
|
163
|
+
select(selector) {
|
|
164
|
+
const query = taro.createSelectorQuery();
|
|
165
|
+
const result = query.select(selector).node();
|
|
166
|
+
query.exec();
|
|
167
|
+
return result || null;
|
|
168
|
+
},
|
|
169
|
+
selectAll(selector) {
|
|
170
|
+
const query = taro.createSelectorQuery();
|
|
171
|
+
const result = query.selectAll(selector).nodes();
|
|
172
|
+
query.exec();
|
|
173
|
+
return Array.isArray(result) ? result : [];
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
scroll: {
|
|
177
|
+
scrollTo(options) {
|
|
178
|
+
var _a;
|
|
179
|
+
taro.pageScrollTo({
|
|
180
|
+
scrollTop: (_a = options.top) !== null && _a !== void 0 ? _a : 0,
|
|
181
|
+
duration: options.animated !== false ? 300 : 0,
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
getScrollPosition() {
|
|
185
|
+
// Taro 需要通过 onPageScroll 事件获取,此处返回默认值
|
|
186
|
+
// 实际项目中建议通过 Taro.usePageScroll hook 获取
|
|
187
|
+
return { scrollTop: 0, scrollLeft: 0 };
|
|
188
|
+
},
|
|
189
|
+
lockScroll() {
|
|
190
|
+
},
|
|
191
|
+
unlockScroll() {
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
device: {
|
|
195
|
+
getInfo() {
|
|
196
|
+
const info = taro.getSystemInfoSync();
|
|
197
|
+
const system = info.system || '';
|
|
198
|
+
const isIOS = /ios/i.test(system) || /iPhone|iPad|iPod/i.test(info.model || '');
|
|
199
|
+
const isAndroid = /android/i.test(system);
|
|
200
|
+
return {
|
|
201
|
+
ios: isIOS,
|
|
202
|
+
android: isAndroid,
|
|
203
|
+
mobile: isIOS || isAndroid,
|
|
204
|
+
weChat: true, // 在 Taro 环境中,通常是小程序
|
|
205
|
+
os: system.split(' ')[0] || info.platform || 'Unknown',
|
|
206
|
+
brand: info.brand || '',
|
|
207
|
+
model: info.model || '',
|
|
208
|
+
screenWidth: info.screenWidth,
|
|
209
|
+
screenHeight: info.screenHeight,
|
|
210
|
+
pixelRatio: info.pixelRatio,
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
async getNetworkType() {
|
|
214
|
+
const res = await taro.getNetworkType();
|
|
215
|
+
return res.networkType;
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
119
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
|
+
}
|
|
120
247
|
/**
|
|
121
248
|
* 自动检测运行环境并创建对应适配器
|
|
122
249
|
*/
|
|
123
250
|
function detectAdapter() {
|
|
124
|
-
|
|
125
|
-
const env = typeof process !== 'undefined' && process.env ? process.env.TARO_ENV : '';
|
|
126
|
-
if (env) {
|
|
251
|
+
if (isTaroEnv()) {
|
|
127
252
|
try {
|
|
128
253
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
129
254
|
const Taro = require('@tarojs/taro');
|
|
130
|
-
const { createTaroAdapter } = require('./taro');
|
|
131
255
|
return createTaroAdapter(Taro);
|
|
132
256
|
}
|
|
133
257
|
catch (_a) {
|
|
@@ -152,6 +276,35 @@ function setAdapter(adapter) {
|
|
|
152
276
|
currentAdapter = adapter;
|
|
153
277
|
}
|
|
154
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
|
+
|
|
155
308
|
/**
|
|
156
309
|
* URL 解析与操作工具
|
|
157
310
|
*/
|
|
@@ -159,54 +312,63 @@ function setAdapter(adapter) {
|
|
|
159
312
|
* 解析 URL 为结构化对象
|
|
160
313
|
*/
|
|
161
314
|
function parseUrl(url) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
a.search
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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');
|
|
181
339
|
}
|
|
182
340
|
/**
|
|
183
341
|
* 拼接带查询参数的 URL
|
|
184
342
|
*/
|
|
185
343
|
function buildUrl(base, params) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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');
|
|
194
354
|
}
|
|
195
355
|
/**
|
|
196
356
|
* 获取当前页面指定查询参数
|
|
197
357
|
*/
|
|
198
358
|
function getQueryParam(name) {
|
|
199
|
-
return new URLSearchParams(window.location.search).get(name);
|
|
359
|
+
return safeCall(() => new URLSearchParams(window.location.search).get(name), null, 'getQueryParam');
|
|
200
360
|
}
|
|
201
361
|
/**
|
|
202
362
|
* 获取当前页面所有查询参数
|
|
203
363
|
*/
|
|
204
364
|
function getAllQueryParams() {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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');
|
|
210
372
|
}
|
|
211
373
|
|
|
212
374
|
/**
|
|
@@ -218,42 +380,46 @@ function getAllQueryParams() {
|
|
|
218
380
|
* 获取存储值,支持 JSON 自动反序列化
|
|
219
381
|
*/
|
|
220
382
|
function get$1(key) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
const item = JSON.parse(raw);
|
|
226
|
-
if (item.expireAt !== null && Date.now() > item.expireAt) {
|
|
227
|
-
getAdapter().storage.remove(key);
|
|
383
|
+
return safeCall(() => {
|
|
384
|
+
const raw = getAdapter().storage.get(key);
|
|
385
|
+
if (raw === null)
|
|
228
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;
|
|
229
394
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
395
|
+
catch (_a) {
|
|
396
|
+
return raw;
|
|
397
|
+
}
|
|
398
|
+
}, null, 'getStorage');
|
|
235
399
|
}
|
|
236
400
|
/**
|
|
237
401
|
* 设置存储值,支持过期时间
|
|
238
402
|
*/
|
|
239
403
|
function set$1(key, value, options) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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');
|
|
245
411
|
}
|
|
246
412
|
/**
|
|
247
413
|
* 删除存储值
|
|
248
414
|
*/
|
|
249
415
|
function remove$1(key) {
|
|
250
|
-
getAdapter().storage.remove(key);
|
|
416
|
+
safeCall(() => getAdapter().storage.remove(key), undefined, 'removeStorage');
|
|
251
417
|
}
|
|
252
418
|
/**
|
|
253
419
|
* 清空所有存储
|
|
254
420
|
*/
|
|
255
421
|
function clear() {
|
|
256
|
-
getAdapter().storage.clear();
|
|
422
|
+
safeCall(() => getAdapter().storage.clear(), undefined, 'clearStorage');
|
|
257
423
|
}
|
|
258
424
|
|
|
259
425
|
/**
|
|
@@ -263,27 +429,31 @@ function clear() {
|
|
|
263
429
|
* 获取 Cookie 值
|
|
264
430
|
*/
|
|
265
431
|
function get(name) {
|
|
266
|
-
|
|
267
|
-
|
|
432
|
+
return safeCall(() => {
|
|
433
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${encodeURIComponent(name)}=([^;]*)`));
|
|
434
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
435
|
+
}, null, 'getCookie');
|
|
268
436
|
}
|
|
269
437
|
/**
|
|
270
438
|
* 设置 Cookie
|
|
271
439
|
*/
|
|
272
440
|
function set(name, value, options = {}) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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');
|
|
287
457
|
}
|
|
288
458
|
/**
|
|
289
459
|
* 删除 Cookie
|
|
@@ -299,60 +469,73 @@ function remove(name, options = {}) {
|
|
|
299
469
|
* 是否为 iOS
|
|
300
470
|
*/
|
|
301
471
|
function isIOS() {
|
|
302
|
-
return getAdapter().device.getInfo().ios;
|
|
472
|
+
return safeCall(() => getAdapter().device.getInfo().ios, false, 'isIOS');
|
|
303
473
|
}
|
|
304
474
|
/**
|
|
305
475
|
* 是否为 Android
|
|
306
476
|
*/
|
|
307
477
|
function isAndroid() {
|
|
308
|
-
return getAdapter().device.getInfo().android;
|
|
478
|
+
return safeCall(() => getAdapter().device.getInfo().android, false, 'isAndroid');
|
|
309
479
|
}
|
|
310
480
|
/**
|
|
311
481
|
* 是否为微信浏览器
|
|
312
482
|
*/
|
|
313
483
|
function isWeChat() {
|
|
314
|
-
return getAdapter().device.getInfo().weChat;
|
|
484
|
+
return safeCall(() => getAdapter().device.getInfo().weChat, false, 'isWeChat');
|
|
315
485
|
}
|
|
316
486
|
/**
|
|
317
487
|
* 是否为移动端
|
|
318
488
|
*/
|
|
319
489
|
function isMobile() {
|
|
320
|
-
return getAdapter().device.getInfo().mobile;
|
|
490
|
+
return safeCall(() => getAdapter().device.getInfo().mobile, false, 'isMobile');
|
|
321
491
|
}
|
|
322
492
|
/**
|
|
323
493
|
* 获取浏览器类型(H5 专用)
|
|
324
494
|
*/
|
|
325
495
|
function getBrowserType() {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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');
|
|
344
516
|
}
|
|
345
517
|
/**
|
|
346
518
|
* 获取操作系统信息
|
|
347
519
|
*/
|
|
348
520
|
function getOS() {
|
|
349
|
-
return getAdapter().device.getInfo().os;
|
|
521
|
+
return safeCall(() => getAdapter().device.getInfo().os, 'Unknown', 'getOS');
|
|
350
522
|
}
|
|
351
523
|
/**
|
|
352
524
|
* 获取完整设备信息
|
|
353
525
|
*/
|
|
354
526
|
function getDeviceInfo() {
|
|
355
|
-
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');
|
|
356
539
|
}
|
|
357
540
|
|
|
358
541
|
/**
|
|
@@ -362,53 +545,56 @@ function getDeviceInfo() {
|
|
|
362
545
|
* 查询单个元素
|
|
363
546
|
*/
|
|
364
547
|
function $(selector) {
|
|
365
|
-
return getAdapter().dom.select(selector);
|
|
548
|
+
return safeCall(() => getAdapter().dom.select(selector), null, '$');
|
|
366
549
|
}
|
|
367
550
|
/**
|
|
368
551
|
* 查询多个元素
|
|
369
552
|
*/
|
|
370
553
|
function $$(selector) {
|
|
371
|
-
return getAdapter().dom.selectAll(selector);
|
|
554
|
+
return safeCall(() => getAdapter().dom.selectAll(selector), [], '$$');
|
|
372
555
|
}
|
|
373
556
|
/**
|
|
374
557
|
* 添加 class
|
|
375
558
|
*/
|
|
376
559
|
function addClass(el, cls) {
|
|
377
|
-
el.classList.add(cls);
|
|
560
|
+
safeCall(() => el.classList.add(cls), undefined, 'addClass');
|
|
378
561
|
}
|
|
379
562
|
/**
|
|
380
563
|
* 移除 class
|
|
381
564
|
*/
|
|
382
565
|
function removeClass(el, cls) {
|
|
383
|
-
el.classList.remove(cls);
|
|
566
|
+
safeCall(() => el.classList.remove(cls), undefined, 'removeClass');
|
|
384
567
|
}
|
|
385
568
|
/**
|
|
386
569
|
* 切换 class
|
|
387
570
|
*/
|
|
388
571
|
function toggleClass(el, cls) {
|
|
389
|
-
el.classList.toggle(cls);
|
|
572
|
+
safeCall(() => el.classList.toggle(cls), undefined, 'toggleClass');
|
|
390
573
|
}
|
|
391
574
|
/**
|
|
392
575
|
* 判断是否包含 class
|
|
393
576
|
*/
|
|
394
577
|
function hasClass(el, cls) {
|
|
395
|
-
return el.classList.contains(cls);
|
|
578
|
+
return safeCall(() => el.classList.contains(cls), false, 'hasClass');
|
|
396
579
|
}
|
|
397
580
|
/**
|
|
398
581
|
* 获取元素样式
|
|
399
582
|
*/
|
|
400
583
|
function getStyle(el, prop) {
|
|
401
|
-
return el.style.getPropertyValue(prop) ||
|
|
584
|
+
return safeCall(() => el.style.getPropertyValue(prop) ||
|
|
585
|
+
getComputedStyle(el).getPropertyValue(prop), '', 'getStyle');
|
|
402
586
|
}
|
|
403
587
|
/**
|
|
404
588
|
* 判断元素是否在可视区域内
|
|
405
589
|
*/
|
|
406
590
|
function isInViewport(el) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
rect.
|
|
410
|
-
|
|
411
|
-
|
|
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');
|
|
412
598
|
}
|
|
413
599
|
|
|
414
600
|
/**
|
|
@@ -418,19 +604,19 @@ function isInViewport(el) {
|
|
|
418
604
|
* 绑定事件
|
|
419
605
|
*/
|
|
420
606
|
function on(el, event, handler, options) {
|
|
421
|
-
el.addEventListener(event, handler, options);
|
|
607
|
+
safeCall(() => el.addEventListener(event, handler, options), undefined, 'on');
|
|
422
608
|
}
|
|
423
609
|
/**
|
|
424
610
|
* 解绑事件
|
|
425
611
|
*/
|
|
426
612
|
function off(el, event, handler, options) {
|
|
427
|
-
el.removeEventListener(event, handler, options);
|
|
613
|
+
safeCall(() => el.removeEventListener(event, handler, options), undefined, 'off');
|
|
428
614
|
}
|
|
429
615
|
/**
|
|
430
616
|
* 单次监听事件
|
|
431
617
|
*/
|
|
432
618
|
function once(el, event, handler) {
|
|
433
|
-
el.addEventListener(event, handler, { once: true });
|
|
619
|
+
safeCall(() => el.addEventListener(event, handler, { once: true }), undefined, 'once');
|
|
434
620
|
}
|
|
435
621
|
/**
|
|
436
622
|
* 事件委托
|
|
@@ -440,14 +626,16 @@ function once(el, event, handler) {
|
|
|
440
626
|
* @param handler 回调函数
|
|
441
627
|
*/
|
|
442
628
|
function delegate(parent, selector, event, handler) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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');
|
|
451
639
|
}
|
|
452
640
|
|
|
453
641
|
/**
|
|
@@ -457,13 +645,13 @@ function delegate(parent, selector, event, handler) {
|
|
|
457
645
|
* 复制文本到剪贴板
|
|
458
646
|
*/
|
|
459
647
|
function copyText(text) {
|
|
460
|
-
return getAdapter().clipboard.write(text);
|
|
648
|
+
return safeCallAsync(() => getAdapter().clipboard.write(text), undefined, 'copyText');
|
|
461
649
|
}
|
|
462
650
|
/**
|
|
463
651
|
* 读取剪贴板文本
|
|
464
652
|
*/
|
|
465
653
|
function readText() {
|
|
466
|
-
return getAdapter().clipboard.read();
|
|
654
|
+
return safeCallAsync(() => getAdapter().clipboard.read(), '', 'readText');
|
|
467
655
|
}
|
|
468
656
|
|
|
469
657
|
/**
|
|
@@ -474,34 +662,40 @@ let scrollLockCount = 0;
|
|
|
474
662
|
* 滚动到页面顶部
|
|
475
663
|
*/
|
|
476
664
|
function scrollToTop(smooth = true) {
|
|
477
|
-
getAdapter().scroll.scrollTo({ top: 0, animated: smooth });
|
|
665
|
+
safeCall(() => getAdapter().scroll.scrollTo({ top: 0, animated: smooth }), undefined, 'scrollToTop');
|
|
478
666
|
}
|
|
479
667
|
/**
|
|
480
668
|
* 滚动到指定元素
|
|
481
669
|
*/
|
|
482
670
|
function scrollToElement(el, options = {}) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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');
|
|
487
677
|
}
|
|
488
678
|
/**
|
|
489
679
|
* 锁定页面滚动
|
|
490
680
|
*/
|
|
491
681
|
function lockScroll() {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
682
|
+
safeCall(() => {
|
|
683
|
+
scrollLockCount++;
|
|
684
|
+
if (scrollLockCount === 1) {
|
|
685
|
+
getAdapter().scroll.lockScroll();
|
|
686
|
+
}
|
|
687
|
+
}, undefined, 'lockScroll');
|
|
496
688
|
}
|
|
497
689
|
/**
|
|
498
690
|
* 解锁页面滚动
|
|
499
691
|
*/
|
|
500
692
|
function unlockScroll() {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
693
|
+
safeCall(() => {
|
|
694
|
+
scrollLockCount = Math.max(0, scrollLockCount - 1);
|
|
695
|
+
if (scrollLockCount === 0) {
|
|
696
|
+
getAdapter().scroll.unlockScroll();
|
|
697
|
+
}
|
|
698
|
+
}, undefined, 'unlockScroll');
|
|
505
699
|
}
|
|
506
700
|
|
|
507
701
|
/**
|
|
@@ -512,49 +706,53 @@ function unlockScroll() {
|
|
|
512
706
|
* @example formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')
|
|
513
707
|
*/
|
|
514
708
|
function formatDate(date, fmt = 'YYYY-MM-DD HH:mm:ss') {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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');
|
|
529
725
|
}
|
|
530
726
|
/**
|
|
531
727
|
* 数字格式化(千分位等)
|
|
532
728
|
*/
|
|
533
729
|
function formatNumber(num, options = {}) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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');
|
|
539
737
|
}
|
|
540
738
|
/**
|
|
541
739
|
* 手机号格式化 (xxx xxxx xxxx)
|
|
542
740
|
*/
|
|
543
741
|
function formatPhone(phone) {
|
|
544
|
-
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) => {
|
|
545
743
|
let result = a;
|
|
546
744
|
if (b)
|
|
547
745
|
result += ` ${b}`;
|
|
548
746
|
if (c)
|
|
549
747
|
result += ` ${c}`;
|
|
550
748
|
return result;
|
|
551
|
-
});
|
|
749
|
+
}), phone, 'formatPhone');
|
|
552
750
|
}
|
|
553
751
|
/**
|
|
554
752
|
* 金额格式化
|
|
555
753
|
*/
|
|
556
754
|
function formatMoney(amount, precision = 2) {
|
|
557
|
-
return formatNumber(amount, { precision });
|
|
755
|
+
return safeCall(() => formatNumber(amount, { precision }), String(amount), 'formatMoney');
|
|
558
756
|
}
|
|
559
757
|
/**
|
|
560
758
|
* 节流
|
|
@@ -565,7 +763,12 @@ function throttle(fn, delay) {
|
|
|
565
763
|
const now = Date.now();
|
|
566
764
|
if (now - last >= delay) {
|
|
567
765
|
last = now;
|
|
568
|
-
|
|
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
|
+
}
|
|
569
772
|
}
|
|
570
773
|
};
|
|
571
774
|
}
|
|
@@ -577,7 +780,14 @@ function debounce(fn, delay) {
|
|
|
577
780
|
return function (...args) {
|
|
578
781
|
if (timer)
|
|
579
782
|
clearTimeout(timer);
|
|
580
|
-
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);
|
|
581
791
|
};
|
|
582
792
|
}
|
|
583
793
|
|
|
@@ -595,40 +805,41 @@ const patterns = {
|
|
|
595
805
|
* 手机号格式校验
|
|
596
806
|
*/
|
|
597
807
|
function isMobilePhone(str) {
|
|
598
|
-
return patterns.mobile.test(str);
|
|
808
|
+
return safeCall(() => patterns.mobile.test(str), false, 'isMobilePhone');
|
|
599
809
|
}
|
|
600
810
|
/**
|
|
601
811
|
* 邮箱校验
|
|
602
812
|
*/
|
|
603
813
|
function isEmail(str) {
|
|
604
|
-
return patterns.email.test(str);
|
|
814
|
+
return safeCall(() => patterns.email.test(str), false, 'isEmail');
|
|
605
815
|
}
|
|
606
816
|
/**
|
|
607
817
|
* 身份证号校验(18位)
|
|
608
818
|
*/
|
|
609
819
|
function isIdCard(str) {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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');
|
|
620
831
|
}
|
|
621
832
|
/**
|
|
622
833
|
* URL 校验
|
|
623
834
|
*/
|
|
624
835
|
function isUrl(str) {
|
|
625
|
-
return patterns.url.test(str);
|
|
836
|
+
return safeCall(() => patterns.url.test(str), false, 'isUrl');
|
|
626
837
|
}
|
|
627
838
|
/**
|
|
628
839
|
* 中文校验
|
|
629
840
|
*/
|
|
630
841
|
function isChinese(str) {
|
|
631
|
-
return patterns.chinese.test(str);
|
|
842
|
+
return safeCall(() => patterns.chinese.test(str), false, 'isChinese');
|
|
632
843
|
}
|
|
633
844
|
|
|
634
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 };
|