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