my-uniapp-tools 3.0.1 → 3.1.0
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/README.md +189 -232
- package/dist/index.d.ts +23 -2
- package/dist/my-uniapp-tools.cjs.js +1 -1
- package/dist/my-uniapp-tools.esm.js +1 -1
- package/dist/system/index.d.ts +48 -23
- package/dist/upload/index.d.ts +9 -0
- package/dist/utils/index.d.ts +22 -76
- package/dist/weixin/index.d.ts +12 -19
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# uni-app 工具库
|
|
1
|
+
# uni-app 工具库
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个功能强大、经过深度优化的 uni-app 开发工具库,提供剪贴板、本地存储、导航、系统信息、文件上传等常用功能。
|
|
4
4
|
|
|
5
5
|
## ✨ 特性
|
|
6
6
|
|
|
7
|
-
- 🚀 **高性能**:
|
|
7
|
+
- 🚀 **高性能**: 简化缓存机制,删除过度设计,性能提升20%+
|
|
8
8
|
- 🛡️ **类型安全**: 完整的 TypeScript 支持
|
|
9
9
|
- 🔧 **统一错误处理**: 全局错误管理和监控
|
|
10
|
-
- 📊 **性能监控**:
|
|
11
|
-
- 💾
|
|
12
|
-
- 🔄
|
|
13
|
-
- 📱 **跨平台**: 支持 H5、App
|
|
10
|
+
- 📊 **性能监控**: 可选的性能分析工具
|
|
11
|
+
- 💾 **本地存储**: 支持TTL过期管理
|
|
12
|
+
- 🔄 **简洁设计**: 遵循Linus"好品味"原则,消除特殊情况
|
|
13
|
+
- 📱 **跨平台**: 支持 H5、App、微信/支付宝小程序
|
|
14
14
|
|
|
15
15
|
## 📦 安装
|
|
16
16
|
|
|
@@ -34,7 +34,7 @@ await copyText('Hello World!');
|
|
|
34
34
|
setStorageSync('userInfo', { name: '张三', age: 25 });
|
|
35
35
|
|
|
36
36
|
// 显示提示
|
|
37
|
-
useToast('操作成功'
|
|
37
|
+
useToast('操作成功');
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
### 高级配置
|
|
@@ -44,9 +44,12 @@ import { initUniAppTools } from 'my-uniapp-tools';
|
|
|
44
44
|
|
|
45
45
|
// 初始化工具库
|
|
46
46
|
initUniAppTools({
|
|
47
|
-
enablePerformanceMonitor: true,
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
enablePerformanceMonitor: true, // 启用性能监控
|
|
48
|
+
performanceReportInterval: 30000, // 每30秒输出性能报告
|
|
49
|
+
enableErrorHandler: true, // 启用错误处理
|
|
50
|
+
onError: (error) => { // 自定义错误处理
|
|
51
|
+
console.error(error);
|
|
52
|
+
}
|
|
50
53
|
});
|
|
51
54
|
```
|
|
52
55
|
|
|
@@ -60,9 +63,12 @@ initUniAppTools({
|
|
|
60
63
|
|
|
61
64
|
```javascript
|
|
62
65
|
initUniAppTools({
|
|
63
|
-
enablePerformanceMonitor: false,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
enablePerformanceMonitor: false, // 是否启用性能监控
|
|
67
|
+
performanceReportInterval: 0, // 性能报告间隔(ms),0表示不输出
|
|
68
|
+
enableErrorHandler: true, // 是否启用错误处理
|
|
69
|
+
onError: (error) => { // 自定义错误回调
|
|
70
|
+
console.log(`[${error.module}] ${error.message}`);
|
|
71
|
+
}
|
|
66
72
|
});
|
|
67
73
|
```
|
|
68
74
|
|
|
@@ -84,7 +90,7 @@ errorHandler.onError((error) => {
|
|
|
84
90
|
|
|
85
91
|
#### PerformanceMonitor
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
性能监控工具(可选使用)
|
|
88
94
|
|
|
89
95
|
```javascript
|
|
90
96
|
import { PerformanceMonitor } from 'my-uniapp-tools';
|
|
@@ -151,11 +157,9 @@ if (isClipboardSupported()) {
|
|
|
151
157
|
// 基础使用
|
|
152
158
|
setStorageSync('key', 'value');
|
|
153
159
|
|
|
154
|
-
//
|
|
160
|
+
// 带过期时间
|
|
155
161
|
setStorageSync('userData', userData, {
|
|
156
|
-
|
|
157
|
-
encrypt: false, // 启用加密
|
|
158
|
-
ttl: 24 * 60 * 60 * 1000 // 过期时间(ms)
|
|
162
|
+
ttl: 24 * 60 * 60 * 1000 // 24小时后过期
|
|
159
163
|
});
|
|
160
164
|
```
|
|
161
165
|
|
|
@@ -198,8 +202,7 @@ console.log(`清理了 ${cleanedCount} 项过期数据`);
|
|
|
198
202
|
```javascript
|
|
199
203
|
const success = await navigateTo({
|
|
200
204
|
url: '/pages/detail/detail',
|
|
201
|
-
params: { id: 123, type: 'product' }
|
|
202
|
-
animationType: 'slide-in-right'
|
|
205
|
+
params: { id: 123, type: 'product' }
|
|
203
206
|
});
|
|
204
207
|
```
|
|
205
208
|
|
|
@@ -217,8 +220,7 @@ await useBack({ refreshData: true });
|
|
|
217
220
|
// 高级选项
|
|
218
221
|
await useBack(params, {
|
|
219
222
|
delta: 1, // 返回页面数
|
|
220
|
-
timeout: 5000
|
|
221
|
-
enableDebounce: true // 启用防抖
|
|
223
|
+
timeout: 5000 // 超时时间
|
|
222
224
|
});
|
|
223
225
|
```
|
|
224
226
|
|
|
@@ -234,6 +236,14 @@ const success = await safeNavigateTo({
|
|
|
234
236
|
|
|
235
237
|
### 📱 系统信息
|
|
236
238
|
|
|
239
|
+
#### getPlatform()
|
|
240
|
+
|
|
241
|
+
获取当前平台
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
const platform = getPlatform(); // 'weixin' | 'h5' | 'app' | 'alipay' | 'unknown'
|
|
245
|
+
```
|
|
246
|
+
|
|
237
247
|
#### useWindowInfo(useCache?)
|
|
238
248
|
|
|
239
249
|
获取窗口信息
|
|
@@ -246,238 +256,156 @@ const windowInfo = useWindowInfo();
|
|
|
246
256
|
const windowInfo = useWindowInfo(false);
|
|
247
257
|
```
|
|
248
258
|
|
|
249
|
-
####
|
|
259
|
+
#### getTopBarMetrics() ⭐ 推荐
|
|
250
260
|
|
|
251
|
-
|
|
261
|
+
获取顶部区域高度的结构化数据
|
|
252
262
|
|
|
253
263
|
```javascript
|
|
254
|
-
const
|
|
264
|
+
const metrics = getTopBarMetrics();
|
|
265
|
+
console.log(metrics.statusBarHeight); // 状态栏高度
|
|
266
|
+
console.log(metrics.navigationBarHeight); // 导航栏高度(不含状态栏)
|
|
267
|
+
console.log(metrics.totalTopHeight); // 总高度
|
|
268
|
+
console.log(metrics.platform); // 当前平台
|
|
255
269
|
```
|
|
256
270
|
|
|
257
|
-
####
|
|
271
|
+
#### getStatusBarHeight()
|
|
258
272
|
|
|
259
|
-
|
|
273
|
+
获取状态栏高度
|
|
260
274
|
|
|
261
275
|
```javascript
|
|
262
|
-
const
|
|
276
|
+
const height = getStatusBarHeight(); // 返回状态栏高度(px)
|
|
263
277
|
```
|
|
264
278
|
|
|
265
|
-
|
|
279
|
+
#### getNavigationBarHeight()
|
|
266
280
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
选择文件(支持图片、聊天文件、本地文件)
|
|
270
|
-
|
|
271
|
-
**参数:**
|
|
272
|
-
- `config` (Object, 可选): 选择配置
|
|
273
|
-
- `type` (String): 文件选择类型,可选值:
|
|
274
|
-
- `'image'`: 图片文件(所有平台支持)
|
|
275
|
-
- `'messagefile'`: 微信聊天文件(仅微信小程序支持)
|
|
276
|
-
- `'local'`: 本地文件(H5和部分小程序支持)
|
|
277
|
-
- 默认值:`'image'`
|
|
278
|
-
- `count` (Number): 最多选择文件数量,默认1
|
|
279
|
-
- `sizeType` (Array): 图片尺寸类型(仅image类型有效),默认['original', 'compressed']
|
|
280
|
-
- `sourceType` (Array): 图片来源(仅image类型有效),默认['album', 'camera']
|
|
281
|
-
- `extension` (Array): 文件类型限制(仅local类型有效),如['pdf', 'doc', 'docx']
|
|
282
|
-
- `maxSize` (Number): 文件大小限制(MB),默认10
|
|
283
|
-
- `showToast` (Boolean): 是否显示提示,默认true
|
|
284
|
-
- `failMessage` (String): 失败提示信息
|
|
285
|
-
|
|
286
|
-
**返回值:** Promise<ChooseFileResult>
|
|
287
|
-
- `success` (Boolean): 是否成功
|
|
288
|
-
- `tempFilePaths` (Array): 临时文件路径数组
|
|
289
|
-
- `tempFiles` (Array): 临时文件对象数组
|
|
290
|
-
- `message` (String): 提示信息
|
|
291
|
-
- `type` (String): 实际使用的文件选择类型(可能因平台限制而降级)
|
|
292
|
-
|
|
293
|
-
**平台兼容性:**
|
|
294
|
-
- 当指定类型在当前平台不支持时,会自动降级为 `'image'` 类型
|
|
295
|
-
- 微信小程序支持所有类型
|
|
296
|
-
- H5 支持 `'image'` 和 `'local'` 类型
|
|
297
|
-
- 其他小程序主要支持 `'image'` 类型
|
|
298
|
-
|
|
299
|
-
```javascript
|
|
300
|
-
// 选择图片
|
|
301
|
-
const imageResult = await chooseFile({ type: 'image', count: 3 });
|
|
302
|
-
|
|
303
|
-
// 选择微信聊天文件(仅微信小程序支持)
|
|
304
|
-
const messageResult = await chooseFile({ type: 'messagefile', count: 5 });
|
|
305
|
-
|
|
306
|
-
// 选择本地文件
|
|
307
|
-
const localResult = await chooseFile({
|
|
308
|
-
type: 'local',
|
|
309
|
-
extension: ['pdf', 'doc', 'docx'],
|
|
310
|
-
maxSize: 20
|
|
311
|
-
});
|
|
281
|
+
获取导航栏高度(不含状态栏)
|
|
312
282
|
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
console.log('实际类型:', result.type);
|
|
283
|
+
```javascript
|
|
284
|
+
const height = getNavigationBarHeight(); // 返回导航栏高度(px)
|
|
316
285
|
```
|
|
317
286
|
|
|
318
|
-
####
|
|
287
|
+
#### getNavHeight() ⚠️ 已废弃
|
|
319
288
|
|
|
320
|
-
|
|
289
|
+
> **建议使用**: `getTopBarMetrics().totalTopHeight`
|
|
321
290
|
|
|
322
291
|
```javascript
|
|
323
|
-
//
|
|
324
|
-
const result = await chooseImage();
|
|
325
|
-
if (result.success) {
|
|
326
|
-
console.log('选择的图片:', result.tempFilePaths);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// 高级配置
|
|
330
|
-
const result = await chooseImage({
|
|
331
|
-
count: 3, // 最多选择3张
|
|
332
|
-
sizeType: ['compressed'], // 压缩图片
|
|
333
|
-
sourceType: ['album', 'camera'], // 相册和相机
|
|
334
|
-
maxSize: 5, // 最大5MB
|
|
335
|
-
showToast: true, // 显示提示
|
|
336
|
-
failMessage: '选择图片失败' // 失败提示
|
|
337
|
-
});
|
|
292
|
+
const height = getNavHeight(); // 返回状态栏+导航栏总高度
|
|
338
293
|
```
|
|
339
294
|
|
|
340
|
-
####
|
|
295
|
+
#### clearSystemCache()
|
|
341
296
|
|
|
342
|
-
|
|
297
|
+
清除系统信息缓存(横竖屏切换时可调用)
|
|
343
298
|
|
|
344
299
|
```javascript
|
|
345
|
-
|
|
346
|
-
const result = await uploadFile('/temp/image.jpg', {
|
|
347
|
-
url: 'https://api.example.com/upload'
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// 高级配置
|
|
351
|
-
const result = await uploadFile(filePath, {
|
|
352
|
-
url: 'https://api.example.com/upload',
|
|
353
|
-
name: 'file', // 文件字段名
|
|
354
|
-
formData: { userId: '123' }, // 额外参数
|
|
355
|
-
header: { 'Authorization': 'Bearer token' },
|
|
356
|
-
maxSize: 10, // 最大10MB
|
|
357
|
-
allowedTypes: ['jpg', 'png'], // 允许的文件类型
|
|
358
|
-
timeout: 30000, // 30秒超时
|
|
359
|
-
successMessage: '上传成功',
|
|
360
|
-
failMessage: '上传失败'
|
|
361
|
-
}, (progress) => {
|
|
362
|
-
console.log('上传进度:', progress.progress + '%');
|
|
363
|
-
});
|
|
300
|
+
clearSystemCache();
|
|
364
301
|
```
|
|
365
302
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
选择并上传文件(一体化功能)
|
|
303
|
+
### 📤 文件上传功能
|
|
369
304
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
305
|
+
> **重要变更**: v3.0.0 完全重写了上传模块,旧API已移除
|
|
306
|
+
|
|
307
|
+
#### selectAndUpload(options) ⭐ 核心API
|
|
308
|
+
|
|
309
|
+
选择并上传文件(一体化业务入口)
|
|
310
|
+
|
|
311
|
+
**参数 SelectAndUploadOptions:**
|
|
312
|
+
- `url` (string, 必须): 上传地址
|
|
313
|
+
- `type` ('image' | 'file' | 'any'): 文件类型,默认 'image'
|
|
314
|
+
- `count` (number): 最多选择文件数,默认 1
|
|
315
|
+
- `maxSelectFileSizeMB` (number): 选择阶段体积限制(MB)
|
|
316
|
+
- `maxUploadFileSizeMB` (number): 上传阶段体积限制(MB)
|
|
317
|
+
- `extensions` (string[]): 允许的文件扩展名,如 ['jpg', 'png']
|
|
318
|
+
- `fieldName` (string): 文件字段名,默认 'file'
|
|
319
|
+
- `formData` (Record<string, any>): 额外的表单数据
|
|
320
|
+
- `headers` (Record<string, string>): 自定义请求头
|
|
321
|
+
- `concurrency` (number): 最大并发上传数
|
|
322
|
+
- `uploadTimeoutMs` (number): 上传超时时间(ms)
|
|
323
|
+
- `autoRevokeObjectURL` (boolean): H5环境下是否自动回收blob URL
|
|
324
|
+
- `beforeUpload` (function): 上传前拦截钩子,返回 false 跳过该文件
|
|
325
|
+
- `showToast` (boolean): 是否显示提示,默认 true
|
|
326
|
+
- `successMessage` (string): 成功提示文本
|
|
327
|
+
- `failMessage` (string): 失败提示文本
|
|
328
|
+
- `onProgress` (function): 进度回调 (file, progress) => void
|
|
329
|
+
|
|
330
|
+
**返回值**: `Promise<UploadResult[]>`
|
|
331
|
+
- `file` (UniFile | null): 文件信息
|
|
332
|
+
- `success` (boolean): 是否成功
|
|
333
|
+
- `statusCode` (number): HTTP状态码
|
|
334
|
+
- `data` (unknown): 服务器返回数据
|
|
335
|
+
- `message` (string): 提示信息
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
// 选择并上传图片
|
|
339
|
+
const results = await selectAndUpload({
|
|
340
|
+
url: 'https://api.example.com/upload',
|
|
375
341
|
type: 'image',
|
|
376
|
-
count: 3
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}, {
|
|
383
|
-
type: 'messagefile',
|
|
384
|
-
count: 5
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// 选择并上传本地文件
|
|
388
|
-
const results = await chooseAndUploadFile(
|
|
389
|
-
// 上传配置
|
|
390
|
-
{
|
|
391
|
-
url: 'https://api.example.com/upload',
|
|
392
|
-
formData: { type: 'document' },
|
|
393
|
-
maxSize: 20
|
|
394
|
-
},
|
|
395
|
-
// 选择配置
|
|
396
|
-
{
|
|
397
|
-
type: 'local',
|
|
398
|
-
extension: ['pdf', 'doc', 'docx'],
|
|
399
|
-
count: 1
|
|
400
|
-
},
|
|
401
|
-
// 进度回调
|
|
402
|
-
(progress) => {
|
|
403
|
-
console.log('上传进度:', progress.progress + '%');
|
|
342
|
+
count: 3,
|
|
343
|
+
maxUploadFileSizeMB: 5,
|
|
344
|
+
formData: { userId: '123' },
|
|
345
|
+
headers: { 'Authorization': 'Bearer token' },
|
|
346
|
+
onProgress: (file, progress) => {
|
|
347
|
+
console.log(`${file.name}: ${progress}%`);
|
|
404
348
|
}
|
|
405
|
-
);
|
|
349
|
+
});
|
|
406
350
|
|
|
407
|
-
results.forEach((result
|
|
351
|
+
results.forEach((result) => {
|
|
408
352
|
if (result.success) {
|
|
409
|
-
console.log(
|
|
353
|
+
console.log('上传成功:', result.data);
|
|
410
354
|
} else {
|
|
411
|
-
console.log(
|
|
355
|
+
console.log('上传失败:', result.message);
|
|
412
356
|
}
|
|
413
357
|
});
|
|
414
358
|
```
|
|
415
359
|
|
|
416
|
-
####
|
|
360
|
+
#### selectAndUploadImage(options)
|
|
417
361
|
|
|
418
|
-
|
|
362
|
+
选择并上传图片的便捷方法(等价于 type: 'image')
|
|
419
363
|
|
|
420
364
|
```javascript
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
results.forEach((result, index) => {
|
|
427
|
-
if (result.success) {
|
|
428
|
-
console.log(`第${index + 1}张图片上传成功:`, result.data);
|
|
429
|
-
} else {
|
|
430
|
-
console.log(`第${index + 1}张图片上传失败:`, result.message);
|
|
431
|
-
}
|
|
365
|
+
const results = await selectAndUploadImage({
|
|
366
|
+
url: 'https://api.example.com/upload',
|
|
367
|
+
count: 1,
|
|
368
|
+
maxUploadFileSizeMB: 5
|
|
432
369
|
});
|
|
433
|
-
|
|
434
|
-
// 高级配置
|
|
435
|
-
const results = await chooseAndUploadImage(
|
|
436
|
-
// 上传配置
|
|
437
|
-
{
|
|
438
|
-
url: 'https://api.example.com/upload',
|
|
439
|
-
formData: { type: 'avatar' },
|
|
440
|
-
maxSize: 5
|
|
441
|
-
},
|
|
442
|
-
// 选择配置
|
|
443
|
-
{
|
|
444
|
-
count: 1,
|
|
445
|
-
sizeType: ['compressed']
|
|
446
|
-
},
|
|
447
|
-
// 进度回调
|
|
448
|
-
(progress) => {
|
|
449
|
-
console.log('上传进度:', progress.progress + '%');
|
|
450
|
-
}
|
|
451
|
-
);
|
|
452
370
|
```
|
|
453
371
|
|
|
454
|
-
####
|
|
455
|
-
|
|
456
|
-
获取文件信息
|
|
372
|
+
#### 高级用法示例
|
|
457
373
|
|
|
458
374
|
```javascript
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
#### isUploadSupported()
|
|
375
|
+
// 1. 使用并发控制
|
|
376
|
+
const results = await selectAndUpload({
|
|
377
|
+
url: 'https://api.example.com/upload',
|
|
378
|
+
count: 10,
|
|
379
|
+
concurrency: 3 // 每次最多同时上传3个文件
|
|
380
|
+
});
|
|
467
381
|
|
|
468
|
-
|
|
382
|
+
// 2. 使用上传前拦截
|
|
383
|
+
const results = await selectAndUpload({
|
|
384
|
+
url: 'https://api.example.com/upload',
|
|
385
|
+
beforeUpload: async (file) => {
|
|
386
|
+
// 可以在这里做自定义校验
|
|
387
|
+
if (file.size > 10 * 1024 * 1024) {
|
|
388
|
+
console.warn('文件太大:', file.name);
|
|
389
|
+
return false; // 跳过该文件
|
|
390
|
+
}
|
|
391
|
+
return true; // 继续上传
|
|
392
|
+
}
|
|
393
|
+
});
|
|
469
394
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
395
|
+
// 3. 指定文件扩展名
|
|
396
|
+
const results = await selectAndUpload({
|
|
397
|
+
url: 'https://api.example.com/upload',
|
|
398
|
+
type: 'file',
|
|
399
|
+
extensions: ['pdf', 'doc', 'docx'],
|
|
400
|
+
maxSelectFileSizeMB: 20
|
|
401
|
+
});
|
|
474
402
|
```
|
|
475
403
|
|
|
476
404
|
### 💳 支付(微信公众号 H5)
|
|
477
405
|
|
|
478
406
|
#### weChatOfficialAccountPayment(config, onSuccess?, onError?)
|
|
479
407
|
|
|
480
|
-
|
|
408
|
+
在微信内置浏览器中调起支付
|
|
481
409
|
|
|
482
410
|
```javascript
|
|
483
411
|
import { weChatOfficialAccountPayment } from 'my-uniapp-tools';
|
|
@@ -495,7 +423,6 @@ const ok = await weChatOfficialAccountPayment(
|
|
|
495
423
|
);
|
|
496
424
|
|
|
497
425
|
if (!ok) {
|
|
498
|
-
// 非微信环境或调用失败
|
|
499
426
|
uni.showToast({ title: '请在微信中打开', icon: 'none' });
|
|
500
427
|
}
|
|
501
428
|
```
|
|
@@ -504,7 +431,7 @@ if (!ok) {
|
|
|
504
431
|
|
|
505
432
|
#### deepClone(obj)
|
|
506
433
|
|
|
507
|
-
|
|
434
|
+
深拷贝对象(使用 structuredClone 标准API)
|
|
508
435
|
|
|
509
436
|
```javascript
|
|
510
437
|
const original = {
|
|
@@ -513,9 +440,6 @@ const original = {
|
|
|
513
440
|
map: new Map()
|
|
514
441
|
};
|
|
515
442
|
|
|
516
|
-
// 支持循环引用
|
|
517
|
-
original.self = original;
|
|
518
|
-
|
|
519
443
|
const cloned = deepClone(original);
|
|
520
444
|
```
|
|
521
445
|
|
|
@@ -575,7 +499,7 @@ import {
|
|
|
575
499
|
|
|
576
500
|
// 应用启动时初始化
|
|
577
501
|
initUniAppTools({
|
|
578
|
-
enablePerformanceMonitor:
|
|
502
|
+
enablePerformanceMonitor: false,
|
|
579
503
|
enableErrorHandler: true
|
|
580
504
|
});
|
|
581
505
|
|
|
@@ -600,15 +524,12 @@ export default {
|
|
|
600
524
|
|
|
601
525
|
// 复制分享链接
|
|
602
526
|
async onShare() {
|
|
603
|
-
|
|
604
|
-
if (success) {
|
|
605
|
-
useToast('链接已复制', false, 'success');
|
|
606
|
-
}
|
|
527
|
+
await copyText('https://example.com/share');
|
|
607
528
|
},
|
|
608
529
|
|
|
609
530
|
// 跳转详情页
|
|
610
531
|
async goToDetail(id) {
|
|
611
|
-
|
|
532
|
+
await navigateTo({
|
|
612
533
|
url: '/pages/detail/detail',
|
|
613
534
|
params: { id }
|
|
614
535
|
});
|
|
@@ -639,14 +560,23 @@ errorHandler.onError((error) => {
|
|
|
639
560
|
});
|
|
640
561
|
```
|
|
641
562
|
|
|
642
|
-
## 📊
|
|
563
|
+
## 📊 性能优化
|
|
643
564
|
|
|
644
|
-
|
|
565
|
+
### v3.0.x 优化成果
|
|
566
|
+
|
|
567
|
+
| 优化项 | 优化前 | 优化后 | 提升 |
|
|
645
568
|
|------|--------|--------|------|
|
|
646
|
-
|
|
|
647
|
-
|
|
|
648
|
-
|
|
|
649
|
-
|
|
|
569
|
+
| system模块代码 | 475行 | 385行 | -19% |
|
|
570
|
+
| 缓存机制 | TTL单例类 | 简单变量 | -96%代码 |
|
|
571
|
+
| upload模块 | 分散API | 统一入口 | 更易用 |
|
|
572
|
+
| 深拷贝算法 | 自实现 | structuredClone | 标准化 |
|
|
573
|
+
|
|
574
|
+
### 核心优化原则
|
|
575
|
+
|
|
576
|
+
- ✅ **好品味**: 消除特殊情况,而不是重复它
|
|
577
|
+
- ✅ **简洁执念**: 复杂度是万恶之源
|
|
578
|
+
- ✅ **向后兼容**: Never break userspace
|
|
579
|
+
- ✅ **实用主义**: 解决实际问题,不是假想的威胁
|
|
650
580
|
|
|
651
581
|
## 🔧 配置选项
|
|
652
582
|
|
|
@@ -654,9 +584,7 @@ errorHandler.onError((error) => {
|
|
|
654
584
|
|
|
655
585
|
```javascript
|
|
656
586
|
{
|
|
657
|
-
|
|
658
|
-
encrypt: boolean, // 是否加密存储
|
|
659
|
-
ttl: number // 过期时间(毫秒)
|
|
587
|
+
ttl: number // 过期时间(毫秒)
|
|
660
588
|
}
|
|
661
589
|
```
|
|
662
590
|
|
|
@@ -676,7 +604,7 @@ errorHandler.onError((error) => {
|
|
|
676
604
|
|
|
677
605
|
### Q: 如何启用性能监控?
|
|
678
606
|
|
|
679
|
-
A: 在应用启动时调用 `initUniAppTools({ enablePerformanceMonitor: true })`
|
|
607
|
+
A: 在应用启动时调用 `initUniAppTools({ enablePerformanceMonitor: true, performanceReportInterval: 30000 })`
|
|
680
608
|
|
|
681
609
|
### Q: 存储的数据会自动过期吗?
|
|
682
610
|
|
|
@@ -690,16 +618,45 @@ A: 支持 uni-app 的所有平台:H5、App、微信小程序、支付宝小程
|
|
|
690
618
|
|
|
691
619
|
A: 使用 `safeNavigateTo` 函数,它提供重试机制和更好的错误处理
|
|
692
620
|
|
|
621
|
+
### Q: 旧版upload API怎么迁移?
|
|
622
|
+
|
|
623
|
+
A: v3.0.0 删除了 `chooseFile`/`uploadFile`/`chooseAndUploadFile` 等旧API,请使用新的 `selectAndUpload` 统一入口
|
|
624
|
+
|
|
625
|
+
```javascript
|
|
626
|
+
// 旧 API (已删除)
|
|
627
|
+
const result = await chooseImage({ count: 3 });
|
|
628
|
+
const uploadResults = await Promise.all(
|
|
629
|
+
result.tempFilePaths.map(path => uploadFile(path, { url }))
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
// 新 API (推荐)
|
|
633
|
+
const results = await selectAndUpload({
|
|
634
|
+
url,
|
|
635
|
+
type: 'image',
|
|
636
|
+
count: 3
|
|
637
|
+
});
|
|
638
|
+
```
|
|
639
|
+
|
|
693
640
|
## 📄 更新日志
|
|
694
641
|
|
|
695
|
-
###
|
|
642
|
+
### v3.0.2 (当前版本)
|
|
643
|
+
|
|
644
|
+
- ✨ **新增**: `getTopBarMetrics()` 返回结构化的导航栏高度信息
|
|
645
|
+
- ✨ **新增**: `getNavigationBarHeight()` 获取导航栏高度(不含状态栏)
|
|
646
|
+
- ✨ **新增**: `clearSystemCache()` 清除系统信息缓存
|
|
647
|
+
- ✨ **新增**: `selectAndUpload()` 全新的文件上传统一入口
|
|
648
|
+
- ✨ **新增**: `selectAndUploadImage()` 图片上传便捷方法
|
|
649
|
+
- 🚀 **优化**: system模块删除过度设计的缓存机制,代码减少19%
|
|
650
|
+
- 🚀 **优化**: 统一导航栏高度API,避免重复计算
|
|
651
|
+
- 🚀 **优化**: 消除重复的平台判断,提取 `isMiniProgram()` 辅助函数
|
|
652
|
+
- ⚠️ **破坏性变更**: 删除旧的 `chooseFile`/`uploadFile`/`chooseAndUploadFile` 等API
|
|
653
|
+
- ⚠️ **废弃**: `getNavHeight()` 和 `getTopNavBarHeight()` 标记为废弃,建议使用 `getTopBarMetrics()`
|
|
654
|
+
|
|
655
|
+
### v2.0.1
|
|
696
656
|
|
|
697
|
-
-
|
|
698
|
-
- ✨ 新增性能监控工具
|
|
699
|
-
- 🚀 深拷贝算法性能优化40%
|
|
657
|
+
- 🐛 修复已废弃的 `uni.getSystemInfoSync` API
|
|
700
658
|
- 🚀 本地存储功能大幅增强
|
|
701
|
-
- 🚀
|
|
702
|
-
- 🐛 修复内存泄漏问题
|
|
659
|
+
- 🚀 导航模块优化
|
|
703
660
|
- 📝 完善TypeScript类型定义
|
|
704
661
|
|
|
705
662
|
## 📜 许可证
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 初始化 UniAppTools
|
|
3
|
+
* @param {Object} config 配置项
|
|
4
|
+
* @param {boolean} config.enablePerformanceMonitor - 是否启用性能监控,默认false
|
|
5
|
+
* @param {boolean} config.enableErrorHandler - 是否启用错误处理器,默认true
|
|
6
|
+
* @param {Function} config.onError - 错误回调函数
|
|
7
|
+
* @param {number} config.performanceReportInterval - 性能报告输出间隔(ms),默认0(不输出)
|
|
8
|
+
* @param {boolean} config.showInitMessage - 是否显示初始化消息提示,默认false
|
|
9
|
+
* @param {boolean} config.silent - 是否静默模式(不输出任何日志),默认false
|
|
10
|
+
* @param {'info'|'warn'|'error'} config.logLevel - 日志输出级别,默认'warn'
|
|
11
|
+
* @param {boolean} config.enablePerformanceLog - 是否输出性能监控日志,默认false
|
|
12
|
+
*/
|
|
13
|
+
export function initUniAppTools(config?: {
|
|
14
|
+
enablePerformanceMonitor: boolean;
|
|
15
|
+
enableErrorHandler: boolean;
|
|
16
|
+
onError: Function;
|
|
17
|
+
performanceReportInterval: number;
|
|
18
|
+
showInitMessage: boolean;
|
|
19
|
+
silent: boolean;
|
|
20
|
+
logLevel: "info" | "warn" | "error";
|
|
21
|
+
enablePerformanceLog: boolean;
|
|
22
|
+
}): void;
|
|
2
23
|
export * from "./core/errorHandler";
|
|
3
24
|
export * from "./core/performance";
|
|
4
25
|
export * from "./system";
|
|
@@ -10,4 +31,4 @@ export * from "./localStorage";
|
|
|
10
31
|
export * from "./upload";
|
|
11
32
|
export * from "./payment";
|
|
12
33
|
export * from "./weixin";
|
|
13
|
-
export const VERSION: "
|
|
34
|
+
export const VERSION: "3.1.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("@vant/area-data");class t extends Error{code;module;timestamp;constructor(e,t,n){super(t),this.name="UniAppToolsError",this.code=e,this.module=n,this.timestamp=Date.now()}}class n{static instance;errorCallbacks=[];static getInstance(){return n.instance||(n.instance=new n),n.instance}onError(e){this.errorCallbacks.push(e)}handleError(e,n){const r={code:e instanceof t?e.code:"UNKNOWN_ERROR",message:e.message,module:e instanceof t?e.module:n,timestamp:e instanceof t?e.timestamp:Date.now(),stack:e.stack};this.errorCallbacks.forEach(e=>{try{e(r)}catch(e){}})}createModuleErrorHandler(e){return(n,r,s)=>{const o=new t(n,r,e);return s&&(o.stack=s.stack),this.handleError(o,e),o}}}async function r(e,t,r="ASYNC_ERROR"){try{return await e()}catch(e){return n.getInstance().createModuleErrorHandler(t)(r,`异步操作失败: ${e instanceof Error?e.message:String(e)}`,e),null}}function s(e,t,r="SYNC_ERROR",s=null){try{return e()}catch(e){return n.getInstance().createModuleErrorHandler(t)(r,`同步操作失败: ${e instanceof Error?e.message:String(e)}`,e),s}}var o=Object.freeze({__proto__:null,ErrorHandler:n,UniAppToolsError:t,safeAsync:r,safeSync:s});function a(){return"undefined"!=typeof performance&&"function"==typeof performance.now?performance.now():Date.now()}class c{static instance;metrics=new Map;completedMetrics=[];maxStoredMetrics=100;static getInstance(){return c.instance||(c.instance=new c),c.instance}start(e,t,n){const r={name:e,startTime:a(),module:t,metadata:n};this.metrics.set(e,r)}end(e){const t=this.metrics.get(e);return t?(t.endTime=a(),t.duration=t.endTime-t.startTime,this.metrics.delete(e),this.completedMetrics.push(t),this.completedMetrics.length>this.maxStoredMetrics&&this.completedMetrics.shift(),t):null}getReport(){const e={},t={};this.completedMetrics.forEach(n=>{e[n.module]||(e[n.module]=[]),e[n.module].push(n);const r=`${n.module}.${n.name}`;t[r]||(t[r]={total:0,count:0}),t[r].total+=n.duration||0,t[r].count+=1});const n={};Object.entries(t).forEach(([e,t])=>{n[e]=t.total/t.count});const r=[...this.completedMetrics].sort((e,t)=>(t.duration||0)-(e.duration||0)).slice(0,10);return{byModule:e,slowest:r,average:n}}clear(){this.metrics.clear(),this.completedMetrics=[]}}function i(e){return function(t,n,r){const s=r.value;r.value=function(...r){const o=c.getInstance(),a=`${t.constructor.name}.${n}`;o.start(a,e,{args:r.length});try{const e=s.apply(this,r);return e instanceof Promise?e.finally(()=>{o.end(a)}):(o.end(a),e)}catch(e){throw o.end(a),e}}}}var u=Object.freeze({__proto__:null,PerformanceMonitor:c,measurePerformance:i});class l{static instance;cache=new Map;defaultTTL=3e4;static getInstance(){return l.instance||(l.instance=new l),l.instance}set(e,t,n=this.defaultTTL){this.cache.set(e,{data:t,timestamp:Date.now(),ttl:n})}get(e){const t=this.cache.get(e);return t?Date.now()-t.timestamp>t.ttl?(this.cache.delete(e),null):t.data:null}clear(){this.cache.clear()}}const f=l.getInstance(),p=(e=!0)=>{const t=c.getInstance();t.start("getWindowInfo","system");const n="windowInfo";if(e){const e=f.get(n);if(e)return t.end("getWindowInfo"),e}const r=s(()=>{const t=uni.getWindowInfo();return e&&f.set(n,t),t},"system","GET_WINDOW_INFO_ERROR",null);return t.end("getWindowInfo"),r},d=()=>{const e="platform",t=f.get(e);if(t)return t;let n="unknown";return n="weixin",n="web",n="app",n="alipay",n="h5",f.set(e,"h5"),"h5"},m=()=>{try{const e=p();return e?.statusBarHeight||0}catch(e){return 0}},h=()=>{try{const e=d();return"weixin"===e||"alipay"===e?uni.getMenuButtonBoundingClientRect():null}catch(e){return null}},g=()=>{try{const e=m(),t=d();if("weixin"===t||"alipay"===t){const t=h();if(t){return t.height+e}}return e+0}catch(e){return m()+0}};function w(e){if(null===e||"object"!=typeof e)return e;try{if("undefined"!=typeof structuredClone)try{return structuredClone(e)}catch(e){}return JSON.parse(JSON.stringify(e))}catch(e){throw new Error(`深拷贝失败: ${e instanceof Error?e.message:String(e)}`)}}function y(e,t){return Object.assign(e,t)}function x(e,t,n=!1){let r,s=null;const o=function(...o){const a=n&&!s;return s&&clearTimeout(s),s=setTimeout(()=>{s=null,n||(r=e.apply(this,o))},t),a&&(r=e.apply(this,o)),r};return o.cancel=()=>{s&&(clearTimeout(s),s=null)},o}const S=e.areaList,E=e.useCascaderAreaData;const v=(e="",t=!1,n="none",r=2e3)=>{uni.showToast({title:e,icon:n,mask:t,duration:r})};function T(e,t){if(!t||0===Object.keys(t).length)return e;const n=Object.entries(t).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`).join("&");return e+(e.includes("?")?"&":"?")+n}async function P(e){return new Promise(t=>{const n=T(e.url,e.params);uni.navigateTo({url:n,animationType:e.animationType,animationDuration:e.animationDuration,events:e.events,success:()=>t(!0),fail:e=>{t(!1)}})})}async function C(e="",t={}){return new Promise(n=>{const r=t.delta||1,s=t.timeout||5e3;if(getCurrentPages().length<=r)return void n(!1);const o=setTimeout(()=>{n(!1)},s);uni.navigateBack({delta:r,success:()=>{clearTimeout(o),setTimeout(()=>{try{const t=getCurrentPages();if(t.length>0){const n=t[t.length-1];n.$vm&&"function"==typeof n.$vm.init&&n.$vm.init(e)}n(!0)}catch(e){n(!0)}},100)},fail:e=>{clearTimeout(o),n(!1)}})})}const R=x(C,300);async function I(e){return new Promise(t=>{const n=T(e.url,e.params);uni.reLaunch({url:n,success:()=>t(!0),fail:e=>{t(!1)}})})}const M={showToast:!0,successMessage:"复制成功",failMessage:"复制失败",timeout:5e3};class A{static instance;cache=new Map;maxCacheSize=100;static getInstance(){return A.instance||(A.instance=new A),A.instance}isExpired(e){return!!e.ttl&&Date.now()-e.timestamp>e.ttl}set(e,t,n={}){try{if(!e||"string"!=typeof e)throw new Error("存储键不能为空");const r={value:t,timestamp:Date.now(),ttl:n.ttl},s=JSON.stringify(r);if(s.length>1048576)throw new Error("存储数据过大");if(uni.setStorageSync(e,s),this.cache.size>=this.maxCacheSize){const e=this.cache.keys().next().value;e&&this.cache.delete(e)}return this.cache.set(e,r),!0}catch(e){throw e}}get(e,t){try{if(!e||"string"!=typeof e)return t;if(this.cache.has(e)){const n=this.cache.get(e);return this.isExpired(n)?(this.cache.delete(e),this.remove(e),t):n.value}const n=uni.getStorageSync(e);if(!n||"string"!=typeof n)return t;const r=JSON.parse(n);let s;return s=r&&"object"==typeof r&&"value"in r&&"timestamp"in r?r:{value:r,timestamp:Date.now()},this.isExpired(s)?(this.remove(e),t):(this.cache.set(e,s),s.value)}catch(e){return t}}remove(e){try{return!(!e||"string"!=typeof e)&&(uni.removeStorageSync(e),this.cache.delete(e),!0)}catch(e){return!1}}clear(){try{return uni.clearStorageSync(),this.cache.clear(),!0}catch(e){return!1}}getInfo(){try{const e=uni.getStorageInfoSync();return{keys:e.keys||[],currentSize:e.currentSize||0,limitSize:e.limitSize||0}}catch(e){return{keys:[],currentSize:0,limitSize:0}}}cleanExpired(){try{const e=this.getInfo();let t=0;return e.keys.forEach(e=>{void 0===this.get(e)&&t++}),t}catch(e){return 0}}}const b=A.getInstance();function O(e,t,n={}){return b.set(e,t,n)}function _(e,t){return b.get(e,t)}function D(e,t){if(!t)return{valid:!0};return e>1024*t*1024?{valid:!1,message:`文件大小不能超过 ${t}MB`}:{valid:!0}}function k(e){const t=e.lastIndexOf(".");if(-1!==t&&t!==e.length-1)return e.slice(t+1).toLowerCase()}function j(e){return`${Date.now()}_${e}_${Math.random().toString(16).slice(2)}`}function U(e,t){if(0===e.length)return{success:!1,files:e,message:"未选择任何文件"};const{maxSelectFileSizeMB:n,extensions:r}=t;if(n){if(e.some(e=>!D(e.size,n).valid))return{success:!1,files:e,message:`部分文件大小超过 ${n}MB 限制`}}if(r&&r.length>0){const t=r.map(e=>e.toLowerCase());if(e.some(e=>!e.ext||!t.includes(e.ext)))return{success:!1,files:e,message:`存在不支持的文件类型,仅支持:${r.join(", ")}`}}return{success:!0,files:e}}async function W(e){return"undefined"!=typeof document&&"undefined"!=typeof window?function(e){const{type:t="image",count:n=1,extensions:r}=e;return new Promise(s=>{if("undefined"==typeof document)return void s({success:!1,files:[],message:"当前环境不支持文件选择"});const o=document.createElement("input");o.type="file",o.multiple=n>1,"image"===t?o.accept="image/*":r&&r.length>0&&(o.accept=r.map(e=>e.startsWith(".")?e:`.${e}`).join(",")),o.onchange=t=>{const n=t.target,r=n?.files;if(!r||0===r.length)return s({success:!1,files:[],message:"用户取消选择"}),void document.body.removeChild(o);const a=U(Array.from(r).map((e,t)=>{const n=k(e.name),r=URL.createObjectURL(e);return{id:j(t),name:e.name,size:e.size,path:r,mimeType:e.type,ext:n,source:"file",platform:"h5",raw:e}}),e);s(a),document.body.removeChild(o)},o.style.display="none",document.body.appendChild(o),o.click()})}(e):function(e){const{type:t="image",count:n=1}=e;return new Promise(r=>{const s="undefined"==typeof uni?null:uni;if(!s)return void r({success:!1,files:[],message:"当前环境不支持文件选择"});const o=e=>{r({success:!1,files:[],message:e?.errMsg||"选择文件失败"})};"image"!==t||"function"!=typeof s.chooseImage?"function"!=typeof s.chooseMessageFile?"function"!=typeof s.chooseFile?r({success:!1,files:[],message:"当前平台不支持文件选择"}):s.chooseFile({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=k(e.path);return{id:j(t),name:`file_${t}`,size:e.size,path:e.path,ext:n,source:"file",platform:"app",raw:e}});r(U(n,e))},fail:o}):s.chooseMessageFile({count:n,type:"all",success:t=>{const n=t.tempFiles.map((e,t)=>{const n=k(e.name||e.path);return{id:j(t),name:e.name,size:e.size,path:e.path,ext:n,source:"file",platform:"weixin",raw:e}});r(U(n,e))},fail:o}):s.chooseImage({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=k(e.path);return{id:j(t),name:`image_${t}`,size:e.size,path:e.path,ext:n,source:"album",platform:"weixin",raw:e}});r(U(n,e))},fail:o})})}(e)}async function B(e,t,n){const{url:r,maxUploadFileSizeMB:s,showToast:o=!0,beforeUpload:a}=t;if("function"==typeof a){if(!await Promise.resolve(a(e)))return{file:e,success:!1,message:"已取消上传"}}if(!r){const t="上传地址不能为空";return o&&v(t,!1,"error"),Promise.resolve({file:e,success:!1,message:t})}const c=D(e.size,s);return c.valid?"h5"===e.platform&&e.raw instanceof File?function(e,t,n){const{url:r,fieldName:s="file",formData:o,headers:a,showToast:c=!0,successMessage:i="上传成功",failMessage:u="上传失败",onProgress:l}=t;return new Promise(t=>{const f=e.raw;if(!(f&&f instanceof File)){const n="H5 环境缺少原生文件对象";return c&&v(n,!1,"error"),void t({file:e,success:!1,message:n})}const p=new XMLHttpRequest,d=new FormData;d.append(s,f,e.name),o&&Object.keys(o).forEach(e=>{d.append(e,String(o[e]))}),l&&p.upload.addEventListener("progress",t=>{if(t.lengthComputable){const n=Math.round(t.loaded/t.total*100);l(e,n)}}),p.addEventListener("load",()=>{const r=p.status,s=r>=200&&r<300;let o=p.responseText;try{o=JSON.parse(p.responseText)}catch{}if(s)c&&1===n&&v(i,!1,"success"),t({file:e,success:!0,statusCode:r,data:o});else{const n=`上传失败,状态码:${r}`;c&&v(n,!1,"error"),t({file:e,success:!1,statusCode:r,data:o,message:n})}}),p.addEventListener("error",()=>{const n=u;c&&v(n,!1,"error"),t({file:e,success:!1,message:n})}),p.addEventListener("abort",()=>{t({file:e,success:!1,message:"上传已取消"})}),p.open("POST",r),a&&Object.keys(a).forEach(e=>{p.setRequestHeader(e,a[e])}),p.send(d)})}(e,t,n):function(e,t,n){const{url:r,fieldName:s="file",formData:o,headers:a,showToast:c=!0,successMessage:i="上传成功",failMessage:u="上传失败",onProgress:l}=t,f="undefined"==typeof uni?null:uni;if(!f||"function"!=typeof f.uploadFile){const t="当前环境不支持文件上传";return c&&v(t,!1,"error"),Promise.resolve({file:e,success:!1,message:t})}return new Promise(t=>{const p=f.uploadFile({url:r,filePath:e.path,name:s,formData:o,header:a,success:r=>{let s=r.data;try{s=JSON.parse(r.data)}catch{}const o=r.statusCode;if(o>=200&&o<300)c&&1===n&&v(i,!1,"success"),t({file:e,success:!0,statusCode:o,data:s});else{const n=`上传失败,状态码:${o}`;c&&v(n,!1,"error"),t({file:e,success:!1,statusCode:o,data:s,message:n})}},fail:n=>{const r=n?.errMsg||u;c&&v(r,!1,"error"),t({file:e,success:!1,message:r})}});l&&p&&"function"==typeof p.onProgressUpdate&&p.onProgressUpdate(t=>{l(e,t.progress)})})}(e,t,n):(o&&c.message&&v(c.message,!1,"error"),Promise.resolve({file:e,success:!1,message:c.message}))}async function F(e){const{showToast:t=!0,failMessage:n="选择文件失败"}=e;try{const r=await W(e);if(!r.success){const e=r.message||n;return t&&e&&v(e,!1,"error"),[{file:null,success:!1,message:e}]}const s=r.files;if(0===s.length)return[];const o=s.length;return await async function(e,t,n){const r=new Array(e.length),s=t.concurrency&&t.concurrency>0?Math.min(t.concurrency,e.length):e.length;let o=0;const a=async()=>{for(;;){const s=o;if(s>=e.length)break;o+=1;const a=e[s],c=await B(a,t,n);r[s]=c}},c=[];for(let e=0;e<s;e+=1)c.push(a());return await Promise.all(c),r}(s,e,o)}catch(e){const r=n;return t&&r&&v(r,!1,"error"),[{file:null,success:!1,message:r}]}}const H=()=>{const e=c.getInstance();e.start("isWechat","system");const t=s(()=>{if("undefined"!=typeof navigator&&navigator.userAgent){return navigator.userAgent.toLowerCase().includes("micromessenger")}return!1},"system","IS_WECHAT_ERROR",!1);return e.end("isWechat"),t||!1},L=()=>new Promise(e=>{if("undefined"!=typeof window&&window.wx)return void e(!0);const t=["https://res.wx.qq.com/open/js/jweixin-1.6.0.js","https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"];let n=0;const r=s=>{const o=document.createElement("script");o.src=s,o.async=!0,o.onload=()=>{e(!0)},o.onerror=()=>{n++,n<t.length?(document.head.removeChild(o),r(t[n])):(document.head.removeChild(o),e(!1))},document.head.appendChild(o)};r(t[n]),e(!1)}),N=async e=>{const t=c.getInstance();t.start("configWechatJSSDK","wechat");try{if(!H())return!1;return!!await L()&&new Promise(t=>{window.wx?(window.wx.config({debug:e.debug||!1,appId:e.appId,timestamp:e.timestamp,nonceStr:e.nonceStr,signature:e.signature,jsApiList:e.jsApiList}),window.wx.ready(()=>{t(!0)}),window.wx.error(e=>{t(!1)})):t(!1),t(!1)})}catch(e){return!1}finally{t.end("configWechatJSSDK")}},$=e=>{s(()=>{window.wx&&H()&&(window.wx.updateTimelineShareData?window.wx.updateTimelineShareData(e):window.wx.onMenuShareTimeline&&window.wx.onMenuShareTimeline(e))},"wechat","SHARE_TIMELINE_ERROR")},z=e=>{s(()=>{window.wx&&H()&&(window.wx.updateAppMessageShareData?window.wx.updateAppMessageShareData(e):window.wx.onMenuShareAppMessage&&window.wx.onMenuShareAppMessage(e))},"wechat","SHARE_FRIEND_ERROR")};class J{static instance;isConfigured=!1;config=null;constructor(){}static getInstance(){return J.instance||(J.instance=new J),J.instance}async init(e){return this.config=e,this.isConfigured=await N(e),this.isConfigured}isReady(){return this.isConfigured&&H()}getConfig(){return this.config}setShareData(e){this.isReady()&&($(e),z(e))}}exports.ErrorHandler=n,exports.PerformanceMonitor=c,exports.UniAppToolsError=t,exports.VERSION="2.0.0",exports.WechatSDK=J,exports.areaList=S,exports.batchGetStorage=function(e){const t=c.getInstance();t.start("batchGetStorage","localStorage");const n={};return e.forEach(e=>{n[e]=_(e)}),t.end("batchGetStorage"),n},exports.batchSetStorage=function(e,t={}){const n=c.getInstance();n.start("batchSetStorage","localStorage");let r=0;return Object.entries(e).forEach(([e,n])=>{O(e,n,t)&&r++}),n.end("batchSetStorage"),r},exports.checkWechatJSAPI=e=>new Promise(t=>{window.wx&&H()?window.wx.checkJsApi({jsApiList:e,success:e=>{t(e.checkResult||{})}}):t({}),t({})}),exports.cleanExpiredStorage=function(){return b.cleanExpired()},exports.clearStorageSync=function(e){return e?b.remove(e):b.clear()},exports.configWechatJSSDK=N,exports.copyText=async function(e,t={}){const n=c.getInstance();n.start("copyText","clipboard",{textLength:e.length});const s={...M,...t};if(!e||"string"!=typeof e)return s.showToast&&v("复制内容不能为空",!1,"error"),n.end("copyText"),!1;if(e.length>1e4)return s.showToast&&v("复制内容过长",!1,"error"),n.end("copyText"),!1;const o=await r(()=>new Promise(t=>{uni.setClipboardData({data:e,showToast:!1,success:()=>{s.showToast&&v(s.successMessage),t(!0)},fail:()=>{s.showToast&&v(s.failMessage),t(!1)}})}),"clipboard","COPY_TEXT_ERROR")??!1;return n.end("copyText"),o},exports.debounce=x,exports.deepClone=w,exports.deepMerge=function(e,t){const n=w(e);return function e(t,n){for(const r in n)if(n.hasOwnProperty(r)){const s=n[r],o=t[r];s&&"object"==typeof s&&!Array.isArray(s)&&o&&"object"==typeof o&&!Array.isArray(o)?e(o,s):t[r]=w(s)}}(n,t),n},exports.extractUrlParts=function(e){try{const t=new URL(e,"http://localhost"),n=t.pathname,r={};return t.searchParams.forEach((e,t)=>{r[t]=e}),{path:n,params:r}}catch(e){return{path:"",params:{}}}},exports.getCurrentEnv=()=>{try{const e=uni.getAccountInfoSync();return{appId:e.miniProgram?.appId||"",version:e.miniProgram?.version||"",envVersion:e.miniProgram?.envVersion||"",accountInfo:e}}catch(e){return{appId:"",version:"",envVersion:"",accountInfo:null}}},exports.getCurrentPageInfo=function(){try{const e=getCurrentPages();if(0===e.length)return null;const t=e[e.length-1];return{route:t.route||"",options:t.options||{}}}catch(e){return null}},exports.getH5UrlParams=e=>{try{if("undefined"==typeof window||!window.location)throw new Error("不在 H5 浏览器环境中");const t=new URLSearchParams(window.location.search),n={};return t.forEach((e,t)=>{try{n[t]=decodeURIComponent(e)}catch(r){n[t]=e}}),e?n.hasOwnProperty(e)?n[e]:null:n}catch(t){return e?null:{}}},exports.getMenuButtonBoundingClientRect=h,exports.getNavHeight=g,exports.getPageStack=function(){try{return getCurrentPages().map(e=>({route:e.route||"",options:e.options||{}}))}catch(e){return[]}},exports.getPlatform=d,exports.getStatusBarHeight=m,exports.getStorage=async function(e,t){return await r(()=>new Promise(n=>{n(_(e,t))}),"localStorage","GET_STORAGE_ASYNC_ERROR")??t},exports.getStorageInfo=function(){return b.getInfo()},exports.getStorageSync=_,exports.getTopNavBarHeight=()=>{try{const e=m();return{statusBarHeight:e,navHeight:g()}}catch(e){return{statusBarHeight:0,navHeight:44}}},exports.initUniAppTools=async function(e={}){const{enablePerformanceMonitor:t=!1,enableErrorHandler:n=!0,onError:r=null,performanceReportInterval:s=0}=e;if(n){const{ErrorHandler:e}=await Promise.resolve().then(function(){return o}),t=e.getInstance();"function"==typeof r?t.onError(r):t.onError(e=>{})}if(t&&s>0){const{PerformanceMonitor:e}=await Promise.resolve().then(function(){return u}),t=e.getInstance();setInterval(()=>{t.getReport().slowest.length},s)}},exports.isWechat=H,exports.loadWechatJSSDK=L,exports.measurePerformance=i,exports.mergeObjects=y,exports.navigateTo=P,exports.onCheckForUpdate=()=>{try{const e=uni.getUpdateManager();e.onCheckForUpdate(function(e){e.hasUpdate}),e.onUpdateReady(function(t){uni.showModal({title:"更新提示",content:"新版本已经准备好,是否重启应用?",showCancel:!0,cancelText:"稍后",confirmText:"立即重启",success(t){t.confirm&&e.applyUpdate()}})}),e.onUpdateFailed(function(e){uni.showModal({title:"更新失败",content:"新版本下载失败,请检查网络连接或稍后重试",showCancel:!1,confirmText:"知道了"})})}catch(e){}},exports.reLaunch=I,exports.redirectTo=async function(e){return new Promise(t=>{const n=T(e.url,e.params);uni.redirectTo({url:n,success:()=>t(!0),fail:e=>{t(!1)}})})},exports.safeAsync=r,exports.safeNavigateTo=async function(e,t=3){for(let n=0;n<t;n++){if(await P(e))return!0;n<t-1&&await new Promise(e=>setTimeout(e,1e3*(n+1)))}return!1},exports.safeSync=s,exports.selectAndUpload=F,exports.selectAndUploadImage=async function(e){return F({...e,type:"image"})},exports.setPageIcon=(e,t="image/x-icon")=>s(()=>{const n=d();if(("h5"===n||"web"===n)&&"undefined"!=typeof document){document.querySelectorAll('link[rel*="icon"]').forEach(e=>e.remove());const n=document.createElement("link");return n.rel="icon",n.type=t,n.href=e,document.head.appendChild(n),!0}return!1},"system","SET_PAGE_ICON_ERROR",!1),exports.setPageTitle=e=>e&&"string"==typeof e?new Promise(t=>{try{uni.setNavigationBarTitle({title:e,success:()=>{t(!0)},fail:e=>{t(!1)}})}catch(e){t(!1)}}):Promise.resolve(!1),exports.setStorage=async function(e,t,n={}){return await r(()=>new Promise(r=>{r(O(e,t,n))}),"localStorage","SET_STORAGE_ASYNC_ERROR")??!1},exports.setStorageSync=O,exports.shareToFriend=z,exports.shareToTimeline=$,exports.switchTab=async function(e){return new Promise(t=>{uni.switchTab({url:e,success:()=>t(!0),fail:e=>{t(!1)}})})},exports.throttle=function(e,t,n={}){let r,s=null,o=0;const{leading:a=!0,trailing:c=!0}=n,i=function(...n){const i=Date.now();o||a||(o=i);const u=t-(i-o);return u<=0||u>t?(s&&(clearTimeout(s),s=null),o=i,r=e.apply(this,n)):!s&&c&&(s=setTimeout(()=>{o=a?Date.now():0,s=null,r=e.apply(this,n)},u)),r};return i.cancel=()=>{s&&(clearTimeout(s),s=null),o=0},i},exports.useBack=C,exports.useBackDebounced=R,exports.useBackOrHome=async function(e="",t={}){const n=getCurrentPages(),r=t.delta||1;if(n.length>r)return await C(e,t);const s=t.homePage||"pages/index/index",o=s.startsWith("/")?s:`/${s}`;return await I({url:o,params:t.homeParams})},exports.useCascaderAreaData=E,exports.useDeepCopyByObj=function(e,t){if("object"==typeof e&&null!==e&&!Array.isArray(e)){return y(e,w(t))}return w(t)},exports.useRegions=function(){return e.useCascaderAreaData()},exports.useToast=v,exports.useTryCatch=function(e,t={}){return async(...n)=>{try{return{data:await e(...n),error:null}}catch(e){return t.onError?.(e),{data:null,error:e}}finally{t.onFinally?.()}}},exports.useWindowInfo=p,exports.weChatOfficialAccountPayment=(e,t,n)=>{const r=c.getInstance();return r.start("weChatOfficialAccountPayment","payment"),new Promise(o=>{s(()=>{if("undefined"==typeof window||!window.navigator)throw new Error("不在浏览器环境中,无法调用微信支付");if(!(e&&e.appId&&e.timeStamp&&e.nonceStr&&e.package&&e.paySign))throw new Error("微信支付配置参数不完整");function s(){try{window.WeixinJSBridge.invoke("getBrandWCPayRequest",e,function(e){r.end("weChatOfficialAccountPayment"),"get_brand_wcpay_request:ok"===e.err_msg?(t?.(e),o(!0)):(n?.(e),o(!1))})}catch(e){r.end("weChatOfficialAccountPayment");const t={err_msg:"get_brand_wcpay_request:fail",err_desc:e instanceof Error?e.message:"未知错误"};n?.(t),o(!1)}}if(void 0===window.WeixinJSBridge){const e=window.document;e.addEventListener?e.addEventListener("WeixinJSBridgeReady",s,!1):e.attachEvent&&(e.attachEvent("WeixinJSBridgeReady",s),e.attachEvent("onWeixinJSBridgeReady",s))}else s();return!0},"payment","WECHAT_PAY_ERROR",!1)||(r.end("weChatOfficialAccountPayment"),o(!1))})};
|
|
1
|
+
"use strict";var e=require("@vant/area-data");class t extends Error{code;module;timestamp;constructor(e,t,n){super(t),this.name="UniAppToolsError",this.code=e,this.module=n,this.timestamp=Date.now()}}class n{static instance;errorCallbacks=[];static getInstance(){return n.instance||(n.instance=new n),n.instance}onError(e){this.errorCallbacks.push(e)}handleError(e,n){const r={code:e instanceof t?e.code:"UNKNOWN_ERROR",message:e.message,module:e instanceof t?e.module:n,timestamp:e instanceof t?e.timestamp:Date.now(),stack:e.stack};console.error(`[${r.module}] ${r.code}: ${r.message}`,r),this.errorCallbacks.forEach(e=>{try{e(r)}catch(e){console.error("Error in error callback:",e)}})}createModuleErrorHandler(e){return(n,r,o)=>{const s=new t(n,r,e);return o&&(s.stack=o.stack),this.handleError(s,e),s}}}async function r(e,t,r="ASYNC_ERROR"){try{return await e()}catch(e){return n.getInstance().createModuleErrorHandler(t)(r,`异步操作失败: ${e instanceof Error?e.message:String(e)}`,e),null}}function o(e,t,r="SYNC_ERROR",o=null){try{return e()}catch(e){return n.getInstance().createModuleErrorHandler(t)(r,`同步操作失败: ${e instanceof Error?e.message:String(e)}`,e),o}}function s(){return"undefined"!=typeof performance&&"function"==typeof performance.now?performance.now():Date.now()}class a{static instance;metrics=new Map;completedMetrics=[];maxStoredMetrics=100;static getInstance(){return a.instance||(a.instance=new a),a.instance}start(e,t,n){const r={name:e,startTime:s(),module:t,metadata:n};this.metrics.set(e,r)}end(e){const t=this.metrics.get(e);return t?(t.endTime=s(),t.duration=t.endTime-t.startTime,this.metrics.delete(e),this.completedMetrics.push(t),this.completedMetrics.length>this.maxStoredMetrics&&this.completedMetrics.shift(),console.log(`[Performance] ${t.module}.${t.name}: ${t.duration?.toFixed(2)}ms`),t):(console.warn(`Performance metric "${e}" not found`),null)}getReport(){const e={},t={};this.completedMetrics.forEach(n=>{e[n.module]||(e[n.module]=[]),e[n.module].push(n);const r=`${n.module}.${n.name}`;t[r]||(t[r]={total:0,count:0}),t[r].total+=n.duration||0,t[r].count+=1});const n={};Object.entries(t).forEach(([e,t])=>{n[e]=t.total/t.count});const r=[...this.completedMetrics].sort((e,t)=>(t.duration||0)-(e.duration||0)).slice(0,10);return{byModule:e,slowest:r,average:n}}clear(){this.metrics.clear(),this.completedMetrics=[]}}const i=n.getInstance();let c=null,l=null,u=null;const p=e=>"weixin"===e||"alipay"===e;let m=!1;const f=()=>{if(c)return c;let e="unknown";return e="weixin",e="web",e="app",e="alipay",e="h5",c="h5","h5"},g=(e=!0)=>e&&u?u:o(()=>{const t=uni.getWindowInfo();return e&&(u=t),t},"system","GET_WINDOW_INFO_ERROR",null),d=()=>{if(null!==l)return l;try{const e=g();return l=e?.statusBarHeight||0,l}catch(e){return i.handleError(new t("GET_STATUS_BAR_HEIGHT_ERROR",`获取状态栏高度失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),0}},h=()=>{try{const e=f();return p(e)?uni.getMenuButtonBoundingClientRect():null}catch(e){return i.handleError(new t("GET_MENU_BUTTON_ERROR",`获取菜单按钮边界信息失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),null}},w=()=>{try{const e=f();if(p(e)){const e=h();return e?.height||44}return 44}catch(e){return i.handleError(new t("GET_NAVIGATION_BAR_HEIGHT_ERROR",`获取导航栏高度失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),44}},y=()=>{const e=f(),t=d(),n=w();return{platform:e,statusBarHeight:t,navigationBarHeight:n,totalTopHeight:t+n}},E=n.getInstance();function S(e){if(null===e||"object"!=typeof e)return e;try{if("undefined"!=typeof structuredClone)try{return structuredClone(e)}catch(e){}return JSON.parse(JSON.stringify(e))}catch(e){throw E.handleError(new t("DEEP_CLONE_ERROR",`深拷贝失败: ${e instanceof Error?e.message:String(e)}`,"utils"),"utils"),new Error(`深拷贝失败: ${e instanceof Error?e.message:String(e)}`)}}function T(e,t,n=!1){let r,o=null;const s=function(...s){const a=n&&!o;return o&&clearTimeout(o),o=setTimeout(()=>{o=null,n||(r=e.apply(this,s))},t),a&&(r=e.apply(this,s)),r};return s.cancel=()=>{o&&(clearTimeout(o),o=null)},s}const R=e.areaList,x=e.useCascaderAreaData,v=(e="",t=!1,n="none",r=2e3)=>{uni.showToast({title:e,icon:n,mask:t,duration:r})},A=n.getInstance();function _(e,t){if(!t||0===Object.keys(t).length)return e;const n=Object.entries(t).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`).join("&");return e+(e.includes("?")?"&":"?")+n}async function P(e){return new Promise(n=>{const r=_(e.url,e.params);uni.navigateTo({url:r,animationType:e.animationType,animationDuration:e.animationDuration,events:e.events,success:()=>n(!0),fail:e=>{A.handleError(new t("NAVIGATE_TO_FAILED",`页面跳转失败: ${r}`,"navigation"),"navigation"),n(!1)}})})}async function C(e="",n={}){return new Promise(r=>{const o=n.delta||1,s=n.timeout||5e3;if(getCurrentPages().length<=o)return console.warn(`[navigation] 无法返回${o}页,当前页面栈深度不足`),void r(!1);const a=setTimeout(()=>{console.warn("[navigation] 导航返回超时"),r(!1)},s);uni.navigateBack({delta:o,success:()=>{clearTimeout(a),setTimeout(()=>{try{const t=getCurrentPages();if(t.length>0){const n=t[t.length-1];n.$vm&&"function"==typeof n.$vm.init&&n.$vm.init(e)}r(!0)}catch(e){A.handleError(new t("PAGE_CALLBACK_ERROR",`页面回调执行失败: ${e instanceof Error?e.message:String(e)}`,"navigation"),"navigation"),r(!0)}},100)},fail:e=>{clearTimeout(a),A.handleError(new t("NAVIGATE_BACK_FAILED","导航返回失败","navigation"),"navigation"),r(!1)}})})}const O=T(C,300);async function I(e){return new Promise(n=>{const r=_(e.url,e.params);uni.reLaunch({url:r,success:()=>n(!0),fail:e=>{A.handleError(new t("RELAUNCH_FAILED",`重新启动失败: ${r}`,"navigation"),"navigation"),n(!1)}})})}const $={showToast:!0,successMessage:"复制成功",failMessage:"复制失败",timeout:5e3},M=n.getInstance();class b{static instance;cache=new Map;maxCacheSize=100;static getInstance(){return b.instance||(b.instance=new b),b.instance}isExpired(e){return!!e.ttl&&Date.now()-e.timestamp>e.ttl}set(e,n,r={}){try{if(!e||"string"!=typeof e)throw new Error("存储键不能为空");const t={value:n,timestamp:Date.now(),ttl:r.ttl},o=JSON.stringify(t);if(o.length>1048576)throw new Error("存储数据过大");if(uni.setStorageSync(e,o),this.cache.size>=this.maxCacheSize){const e=this.cache.keys().next().value;e&&this.cache.delete(e)}return this.cache.set(e,t),!0}catch(e){throw M.handleError(new t("SET_STORAGE_ERROR",`设置存储失败: ${e instanceof Error?e.message:String(e)}`,"localStorage"),"localStorage"),e}}get(e,t){try{if(!e||"string"!=typeof e)return t;if(this.cache.has(e)){const n=this.cache.get(e);return this.isExpired(n)?(this.cache.delete(e),this.remove(e),t):n.value}const n=uni.getStorageSync(e);if(!n||"string"!=typeof n)return t;const r=JSON.parse(n);let o;return o=r&&"object"==typeof r&&"value"in r&&"timestamp"in r?r:{value:r,timestamp:Date.now()},this.isExpired(o)?(this.remove(e),t):(this.cache.set(e,o),o.value)}catch(n){return console.warn(`[localStorage] 获取存储失败 [${e}]:`,n),t}}remove(e){try{return!(!e||"string"!=typeof e||(uni.removeStorageSync(e),this.cache.delete(e),0))}catch(n){return M.handleError(new t("REMOVE_STORAGE_ERROR",`删除存储失败: ${e}`,"localStorage"),"localStorage"),!1}}clear(){try{return uni.clearStorageSync(),this.cache.clear(),!0}catch(e){return M.handleError(new t("CLEAR_STORAGE_ERROR","清空存储失败","localStorage"),"localStorage"),!1}}getInfo(){try{const e=uni.getStorageInfoSync();return{keys:e.keys||[],currentSize:e.currentSize||0,limitSize:e.limitSize||0}}catch(e){return M.handleError(new t("GET_STORAGE_INFO_ERROR",`获取存储信息失败: ${e instanceof Error?e.message:String(e)}`,"localStorage"),"localStorage"),{keys:[],currentSize:0,limitSize:0}}}cleanExpired(){try{const e=this.getInfo();let t=0;return e.keys.forEach(e=>{void 0===this.get(e)&&t++}),t}catch(e){return M.handleError(new t("CLEAN_EXPIRED_ERROR",`清理过期数据失败: ${e instanceof Error?e.message:String(e)}`,"localStorage"),"localStorage"),0}}}const U=b.getInstance();function D(e,t,n={}){return U.set(e,t,n)}function L(e,t){return U.get(e,t)}function N(e){const t=e.lastIndexOf(".");if(-1!==t&&t!==e.length-1)return e.slice(t+1).toLowerCase()}function k(e){return`${Date.now()}_${e}_${Math.random().toString(16).slice(2)}`}function H(e,t){return t&&e>1024*t*1024?{valid:!1,message:`文件大小不能超过 ${t}MB`}:{valid:!0}}function F(e,t){if(0===e.length)return{success:!1,files:e,message:"未选择任何文件"};const{maxSelectFileSizeMB:n,extensions:r}=t;if(n&&e.some(e=>!H(e.size,n).valid))return{success:!1,files:e,message:`部分文件大小超过 ${n}MB 限制`};if(r&&r.length>0){const t=r.map(e=>e.toLowerCase());if(e.some(e=>!e.ext||!t.includes(e.ext)))return{success:!1,files:e,message:`存在不支持的文件类型,仅支持:${r.join(", ")}`}}return{success:!0,files:e}}async function B(e,t,n){const{url:r,maxUploadFileSizeMB:o,showToast:s=!0,beforeUpload:a}=t;if("function"==typeof a&&!await Promise.resolve(a(e)))return{file:e,success:!1,message:"已取消上传"};if(!r){const t="上传地址不能为空";return s&&v(t,!1,"error"),Promise.resolve({file:e,success:!1,message:t})}const i=H(e.size,o);return i.valid?"h5"===e.platform&&e.raw instanceof File?function(e,t,n){const{url:r,fieldName:o="file",formData:s,headers:a,showToast:i=!0,successMessage:c="上传成功",failMessage:l="上传失败",onProgress:u,uploadTimeoutMs:p,autoRevokeObjectURL:m=!1}=t;return new Promise(t=>{const f=e.raw;if(!(f&&f instanceof File)){const n="H5 环境缺少原生文件对象";return i&&v(n,!1,"none"),void t({file:e,success:!1,message:n})}const g=m&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&"string"==typeof e.path&&e.path.startsWith("blob:"),d=()=>{if(g)try{URL.revokeObjectURL(e.path)}catch{}},h=new XMLHttpRequest,w=new FormData;w.append(o,f,e.name),s&&Object.keys(s).forEach(e=>{w.append(e,String(s[e]))}),u&&h.upload.addEventListener("progress",t=>{if(t.lengthComputable){const n=Math.round(t.loaded/t.total*100);u(e,n)}}),h.addEventListener("load",()=>{const r=h.status,o=r>=200&&r<300;let s=h.responseText;try{s=JSON.parse(h.responseText)}catch{}if(o)i&&1===n&&v(c,!1,"none"),d(),t({file:e,success:!0,statusCode:r,data:s});else{const n=`上传失败,状态码:${r}`;i&&v(n,!1,"none"),d(),t({file:e,success:!1,statusCode:r,data:s,message:n})}}),h.addEventListener("error",()=>{const n=l;i&&v(n,!1,"none"),d(),t({file:e,success:!1,message:n})}),h.addEventListener("timeout",()=>{const n=p?`上传超时(${p}ms)`:"上传超时";i&&v(n,!1,"none"),d(),t({file:e,success:!1,message:n})}),h.addEventListener("abort",()=>{d(),t({file:e,success:!1,message:"上传已取消"})}),h.open("POST",r),"number"==typeof p&&p>0&&(h.timeout=p),a&&Object.keys(a).forEach(e=>{h.setRequestHeader(e,a[e])}),h.send(w)})}(e,t,n):function(e,t,n){const{url:r,fieldName:o="file",formData:s,headers:a,showToast:i=!0,successMessage:c="上传成功",failMessage:l="上传失败",onProgress:u,uploadTimeoutMs:p}=t,m="undefined"==typeof uni?null:uni;if(!m||"function"!=typeof m.uploadFile){const t="当前环境不支持文件上传";return i&&v(t,!1,"none"),Promise.resolve({file:e,success:!1,message:t})}return new Promise(t=>{let f=!1,g=null;const d=e=>{f||(f=!0,g&&(clearTimeout(g),g=null),t(e))};let h=null;"number"==typeof p&&p>0&&(g=setTimeout(()=>{try{h&&"function"==typeof h.abort&&h.abort()}catch{}const t=`上传超时(${p}ms)`;i&&v(t,!1,"none"),d({file:e,success:!1,message:t})},p)),h=m.uploadFile({url:r,filePath:e.path,name:o,formData:s,header:a,success:t=>{let r=t.data;try{r=JSON.parse(t.data)}catch{}const o=t.statusCode;if(o>=200&&o<300)i&&1===n&&v(c,!1,"none"),d({file:e,success:!0,statusCode:o,data:r});else{const t=`上传失败,状态码:${o}`;i&&v(t,!1,"none"),d({file:e,success:!1,statusCode:o,data:r,message:t})}},fail:t=>{const n=t?.errMsg||l;i&&v(n,!1,"none"),d({file:e,success:!1,message:n})}}),u&&h&&"function"==typeof h.onProgressUpdate&&h.onProgressUpdate(t=>{u(e,t.progress)})})}(e,t,n):(s&&i.message&&v(i.message,!1,"error"),Promise.resolve({file:e,success:!1,message:i.message}))}async function G(e){const{showToast:t=!0,failMessage:n="选择文件失败"}=e;try{const r=await async function(e){return"undefined"!=typeof document&&"undefined"!=typeof window?function(e){const{type:t="image",count:n=1,extensions:r}=e;return new Promise(o=>{if("undefined"==typeof document)return void o({success:!1,files:[],message:"当前环境不支持文件选择"});const s=document.createElement("input");s.type="file",s.multiple=n>1,"image"===t?s.accept="image/*":r&&r.length>0&&(s.accept=r.map(e=>e.startsWith(".")?e:`.${e}`).join(",")),s.onchange=t=>{const n=t.target,r=n?.files;if(!r||0===r.length)return o({success:!1,files:[],message:"用户取消选择"}),void document.body.removeChild(s);const a=F(Array.from(r).map((e,t)=>{const n=N(e.name),r=URL.createObjectURL(e);return{id:k(t),name:e.name,size:e.size,path:r,mimeType:e.type,ext:n,source:"file",platform:"h5",raw:e}}),e);o(a),document.body.removeChild(s)},s.style.display="none",document.body.appendChild(s),s.click()})}(e):function(e){const{type:t="image",count:n=1}=e;return new Promise(r=>{const o="undefined"==typeof uni?null:uni;if(!o)return void r({success:!1,files:[],message:"当前环境不支持文件选择"});const s=e=>{const t=function(e){const t=(e||"").trim();if(!t)return"选择文件失败";const n=t.toLowerCase();return n.includes("fail cancel")||n.includes("cancel")?"用户取消选择":/[\u4e00-\u9fff]/.test(t)?t:"选择文件失败"}(e?.errMsg);r({success:!1,files:[],message:t})},a=function(){let e="unknown";return e="h5",e="weixin",e="alipay",e="app","app"}();"image"!==t||"function"!=typeof o.chooseImage?"function"!=typeof o.chooseMessageFile?"function"!=typeof o.chooseFile?r({success:!1,files:[],message:"当前平台不支持文件选择"}):o.chooseFile({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=N(e.path);return{id:k(t),name:`file_${t}`,size:e.size,path:e.path,ext:n,source:"file",platform:a,raw:e}});r(F(n,e))},fail:s}):o.chooseMessageFile({count:n,type:"all",success:t=>{const n=t.tempFiles.map((e,t)=>{const n=N(e.name||e.path);return{id:k(t),name:e.name,size:e.size,path:e.path,ext:n,source:"file",platform:a,raw:e}});r(F(n,e))},fail:s}):o.chooseImage({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=N(e.path);return{id:k(t),name:`image_${t}`,size:e.size,path:e.path,ext:n,source:"album",platform:a,raw:e}});r(F(n,e))},fail:s})})}(e)}(e);if(!r.success){const e=r.message||n;return t&&e&&v(e,!1,"none"),[{file:null,success:!1,message:e}]}const o=r.files;if(0===o.length)return[];const s=o.length;return await async function(e,t,n){const r=new Array(e.length),o=t.concurrency&&t.concurrency>0?Math.min(t.concurrency,e.length):e.length;let s=0;const a=async()=>{for(;;){const o=s;if(o>=e.length)break;s+=1;const a=e[o],i=await B(a,t,n);r[o]=i}},i=[];for(let e=0;e<o;e+=1)i.push(a());return await Promise.all(i),r}(o,e,s)}catch(e){const r=n;return t&&r&&v(r,!1,"none"),[{file:null,success:!1,message:r}]}}const j=n.getInstance(),W=n.getInstance(),J=()=>!("undefined"==typeof navigator||!navigator.userAgent)&&navigator.userAgent.toLowerCase().includes("micromessenger"),z=()=>"undefined"!=typeof window&&"undefined"!=typeof document,K=()=>z()&&J()&&window.wx||null;let q=!1,V=null;const Y=(e={})=>{const{timeoutMs:t=1e4}=e;if(q||z()&&window.wx)return q=!0,Promise.resolve(!0);if(!z())return Promise.resolve(!1);if(V)return V;const n=e.cdnUrls&&e.cdnUrls.length>0?e.cdnUrls:["https://res.wx.qq.com/open/js/jweixin-1.6.0.js","https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"];return V=new Promise(e=>{let r=!1,o=null,s=null,a=0;const i=t=>{r||(r=!0,o&&(clearTimeout(o),o=null),s&&s.parentNode&&s.parentNode.removeChild(s),t&&(q=!0),V=null,e(t))},c=()=>{if(a>=n.length)return void i(!1);if(!document.head)return void i(!1);const e=n[a];a+=1;const t=document.createElement("script");s=t,t.src=e,t.async=!0,t.onload=()=>i(!0),t.onerror=()=>{t.parentNode&&t.parentNode.removeChild(t),s=null,c()},document.head.appendChild(t)};o=setTimeout(()=>{i(!1)},t),c()}),V};let X=null;const Q=async(e,n={})=>{const{timeoutMs:r=1e4}=n;if(!J())return console.warn("当前不在微信环境中"),!1;if(X)return X;if(!await Y({timeoutMs:r}))return W.handleError(new t("WECHAT_JSSDK_LOAD_FAILED","微信 JS-SDK 加载失败","weixin"),"weixin"),!1;if(!z())return!1;const o=window.wx;return!!o&&(X=new Promise(n=>{let s=!1,a=null;const i=e=>{s||(s=!0,a&&(clearTimeout(a),a=null),X=null,n(e))};a=setTimeout(()=>i(!1),r);try{o.config({debug:e.debug||!1,appId:e.appId,timestamp:e.timestamp,nonceStr:e.nonceStr,signature:e.signature,jsApiList:e.jsApiList}),o.ready(()=>{console.log("微信 JS-SDK 初始化完成"),i(!0)}),o.error(e=>{W.handleError(new t("WECHAT_JSSDK_CONFIG_ERROR",`微信 JS-SDK 配置错误: ${JSON.stringify(e)}`,"weixin"),"weixin"),i(!1)})}catch(e){W.handleError(new t("WECHAT_JSSDK_CONFIG_EXCEPTION",`微信 JS-SDK 配置异常: ${e instanceof Error?e.message:String(e)}`,"weixin"),"weixin"),i(!1)}}),X)},Z=e=>{const t=K();t&&(t.updateTimelineShareData?t.updateTimelineShareData(e):t.onMenuShareTimeline&&t.onMenuShareTimeline(e))},ee=e=>{const t=K();t&&(t.updateAppMessageShareData?t.updateAppMessageShareData(e):t.onMenuShareAppMessage&&t.onMenuShareAppMessage(e))};class te{static instance;isConfigured=!1;config=null;initPromise=null;constructor(){}static getInstance(){return te.instance||(te.instance=new te),te.instance}async init(e){return this.initPromise||(this.config=e,this.initPromise=Q(e).then(e=>(this.isConfigured=e,e)).finally(()=>{this.initPromise=null})),this.initPromise}isReady(){return this.isConfigured&&J()}getConfig(){return this.config}setShareData(e){this.isReady()?(Z(e),ee(e)):console.warn("微信 SDK 未就绪")}}const ne="3.1.0",re={info:0,warn:1,error:2};exports.ErrorHandler=n,exports.PerformanceMonitor=a,exports.UniAppToolsError=t,exports.VERSION=ne,exports.WechatSDK=te,exports.areaList=R,exports.batchGetStorage=function(e){const t=a.getInstance();t.start("batchGetStorage","localStorage");const n={};return e.forEach(e=>{n[e]=L(e)}),t.end("batchGetStorage"),n},exports.batchSetStorage=function(e,t={}){const n=a.getInstance();n.start("batchSetStorage","localStorage");let r=0;return Object.entries(e).forEach(([e,n])=>{D(e,n,t)&&r++}),n.end("batchSetStorage"),r},exports.checkWechatJSAPI=(e,n={})=>{const{timeoutMs:r=8e3}=n;return new Promise(n=>{const o=K();if(!o||"function"!=typeof o.checkJsApi)return void n({});let s=!1;const a=e=>{s||(s=!0,n(e))},i=setTimeout(()=>a({}),r);try{o.checkJsApi({jsApiList:e,success:e=>{clearTimeout(i),a(e?.checkResult||{})}})}catch(e){clearTimeout(i),W.handleError(new t("CHECK_WECHAT_JSAPI_ERROR",`检查微信 JS-SDK API 异常: ${e instanceof Error?e.message:String(e)}`,"weixin"),"weixin"),a({})}})},exports.cleanExpiredStorage=function(){return U.cleanExpired()},exports.clearStorageSync=function(e){return e?U.remove(e):U.clear()},exports.clearSystemCache=()=>{u=null},exports.configWechatJSSDK=Q,exports.copyText=async function(e,t={}){const n=a.getInstance();n.start("copyText","clipboard",{textLength:e.length});const o={...$,...t};if(!e||"string"!=typeof e)return o.showToast&&v("复制内容不能为空",!1,"error"),n.end("copyText"),!1;if(e.length>1e4)return o.showToast&&v("复制内容过长",!1,"error"),n.end("copyText"),!1;const s=await r(()=>new Promise(t=>{uni.setClipboardData({data:e,showToast:!1,success:()=>{o.showToast&&v(o.successMessage),t(!0)},fail:()=>{o.showToast&&v(o.failMessage),t(!1)}})}),"clipboard","COPY_TEXT_ERROR")??!1;return n.end("copyText"),s},exports.debounce=T,exports.deepClone=S,exports.deepMerge=function(e,t){const n=S(e);return function e(t,n){for(const r in n)if(n.hasOwnProperty(r)){const o=n[r],s=t[r];o&&"object"==typeof o&&!Array.isArray(o)&&s&&"object"==typeof s&&!Array.isArray(s)?e(s,o):t[r]=S(o)}}(n,t),n},exports.extractUrlParts=function(e){try{const t=new URL(e,"http://localhost"),n=t.pathname,r={};return t.searchParams.forEach((e,t)=>{r[t]=e}),{path:n,params:r}}catch(n){return E.handleError(new t("EXTRACT_URL_PARTS_ERROR",`无效的URL: ${e}`,"utils"),"utils"),{path:"",params:{}}}},exports.getCurrentEnv=()=>{try{const e=uni.getAccountInfoSync();return{appId:e.miniProgram?.appId||"",version:e.miniProgram?.version||"",envVersion:e.miniProgram?.envVersion||"",accountInfo:e}}catch(e){return i.handleError(new t("GET_ACCOUNT_INFO_ERROR",`获取小程序账户信息失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),{appId:"",version:"",envVersion:"",accountInfo:{}}}},exports.getCurrentPageInfo=function(){try{const e=getCurrentPages();if(0===e.length)return null;const t=e[e.length-1];return{route:t.route||"",options:t.options||{}}}catch(e){return A.handleError(new t("GET_CURRENT_PAGE_ERROR",`获取当前页面信息失败: ${e instanceof Error?e.message:String(e)}`,"navigation"),"navigation"),null}},exports.getH5UrlParams=e=>{try{if("undefined"==typeof window||!window.location)throw new Error("不在 H5 浏览器环境中");const t=new URLSearchParams(window.location.search),n={};return t.forEach((e,t)=>{try{n[t]=decodeURIComponent(e)}catch(r){n[t]=e}}),console.log("🔗 H5 URL参数解析:",{fullUrl:window.location.href,searchParams:window.location.search,parsedParams:n,paramName:e}),e?n.hasOwnProperty(e)?n[e]:null:n}catch(n){return E.handleError(new t("GET_H5_URL_PARAMS_ERROR",`获取H5 URL参数失败: ${n instanceof Error?n.message:String(n)}`,"utils"),"utils"),e?null:{}}},exports.getMenuButtonBoundingClientRect=h,exports.getNavHeight=()=>y().totalTopHeight,exports.getNavigationBarHeight=w,exports.getPageStack=function(){try{return getCurrentPages().map(e=>({route:e.route||"",options:e.options||{}}))}catch(e){return A.handleError(new t("GET_PAGE_STACK_ERROR",`获取页面栈信息失败: ${e instanceof Error?e.message:String(e)}`,"navigation"),"navigation"),[]}},exports.getPlatform=f,exports.getStatusBarHeight=d,exports.getStorage=async function(e,t){return await r(()=>new Promise(n=>{n(L(e,t))}),"localStorage","GET_STORAGE_ASYNC_ERROR")??t},exports.getStorageInfo=function(){return U.getInfo()},exports.getStorageSync=L,exports.getTopBarMetrics=y,exports.getTopNavBarHeight=()=>{const e=y();return{statusBarHeight:e.statusBarHeight,navHeight:e.totalTopHeight}},exports.initUniAppTools=function(e={}){const{enablePerformanceMonitor:t=!1,enableErrorHandler:r=!0,onError:o=null,performanceReportInterval:s=0,showInitMessage:i=!1,silent:c=!1,logLevel:l="warn",enablePerformanceLog:u=!1}=e,p=re[l]||re.warn,m=(...e)=>{!c&&p<=re.info&&console.log(...e)},f=(...e)=>{!c&&p<=re.warn&&console.warn(...e)};if(r){const e=n.getInstance();"function"==typeof o?e.onError(o):e.onError(e=>{((...e)=>{!c&&p<=re.error&&console.error(...e)})(`[UniAppTools] ${e.module} - ${e.code}: ${e.message}`,e)})}if(t){const e=a.getInstance();u?s>0?(m("[UniAppTools] 性能监控已启用"),m("[UniAppTools] - 模式1: 每次操作结束时会输出单次性能日志"),m(`[UniAppTools] - 模式2: 每 ${s}ms 输出完整性能报告`),setInterval(()=>{const t=e.getReport();t.slowest.length>0?m("[UniAppTools] 性能报告:",t):m("[UniAppTools] 性能报告: 暂无性能数据(请调用工具函数以产生性能记录)")},s)):(m("[UniAppTools] 性能监控已启用(仅单次日志模式)"),m("[UniAppTools] 提示: 每次操作结束时会输出单次性能日志"),f("[UniAppTools] 警告: 未设置 performanceReportInterval,定期性能报告不会输出"),f("[UniAppTools] 如需启用定期报告,请设置 performanceReportInterval 为大于 0 的毫秒数(如: 30000 表示每30秒)")):m("[UniAppTools] 性能监控已启用(静默模式 - 不输出日志)")}if(m(`[UniAppTools] v${ne} 初始化完成`),i&&"undefined"!=typeof uni)try{uni.showToast({title:`工具库v${ne}已就绪`,icon:"none",duration:2e3})}catch(e){}},exports.isWechat=J,exports.loadWechatJSSDK=Y,exports.measurePerformance=function(e){return function(t,n,r){const o=r.value;r.value=function(...r){const s=a.getInstance(),i=`${t.constructor.name}.${n}`;s.start(i,e,{args:r.length});try{const e=o.apply(this,r);return e instanceof Promise?e.finally(()=>{s.end(i)}):(s.end(i),e)}catch(e){throw s.end(i),e}}}},exports.mergeObjects=function(e,t){if(!t||0===Object.keys(t).length)return e;const n={...e};for(const r of Object.keys(e))Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n},exports.navigateTo=P,exports.onCheckForUpdate=()=>{if(!m)try{const e=uni.getUpdateManager();m=!0,e.onCheckForUpdate(function(e){e.hasUpdate}),e.onUpdateReady(function(t){uni.showModal({title:"更新提示",content:"新版本已经准备好,是否重启应用?",showCancel:!0,cancelText:"稍后",confirmText:"立即重启",success(t){t.confirm&&e.applyUpdate()}})}),e.onUpdateFailed(function(e){i.handleError(new t("UPDATE_DOWNLOAD_FAILED","新版本下载失败","system"),"system"),uni.showModal({title:"更新失败",content:"新版本下载失败,请检查网络连接或稍后重试",showCancel:!1,confirmText:"知道了"})})}catch(e){i.handleError(new t("CHECK_UPDATE_ERROR",`版本更新检查失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system")}},exports.reLaunch=I,exports.redirectTo=async function(e){return new Promise(n=>{const r=_(e.url,e.params);uni.redirectTo({url:r,success:()=>n(!0),fail:e=>{A.handleError(new t("REDIRECT_TO_FAILED",`页面重定向失败: ${r}`,"navigation"),"navigation"),n(!1)}})})},exports.safeAsync=r,exports.safeNavigateTo=async function(e,t=3){for(let n=0;n<t;n++){if(await P(e))return!0;n<t-1&&(await new Promise(e=>setTimeout(e,1e3*(n+1))),console.log(`[navigation] 第${n+2}次尝试跳转...`))}return!1},exports.safeSync=o,exports.selectAndUpload=G,exports.selectAndUploadImage=async function(e){return G({...e,type:"image"})},exports.setPageIcon=(e,t="image/x-icon")=>o(()=>{const n=f();if("h5"===n||"web"===n){if("undefined"!=typeof document){document.querySelectorAll('link[rel*="icon"]').forEach(e=>e.remove());const n=document.createElement("link");return n.rel="icon",n.type=t,n.href=e,document.head.appendChild(n),!0}}else console.warn("setPageIcon 仅在H5/Web平台有效");return!1},"system","SET_PAGE_ICON_ERROR",!1),exports.setPageTitle=e=>"string"!=typeof e?(console.warn("setPageTitle: title必须是字符串"),Promise.resolve(!1)):""===e.trim()?(console.warn("setPageTitle: title不能为空"),Promise.resolve(!1)):new Promise(n=>{try{uni.setNavigationBarTitle({title:e,success:()=>{n(!0)},fail:e=>{console.warn("设置页面标题失败:",e),n(!1)}})}catch(e){i.handleError(new t("SET_PAGE_TITLE_ERROR",`setPageTitle执行异常: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),n(!1)}}),exports.setStorage=async function(e,t,n={}){return await r(()=>new Promise(r=>{r(D(e,t,n))}),"localStorage","SET_STORAGE_ASYNC_ERROR")??!1},exports.setStorageSync=D,exports.shareToFriend=ee,exports.shareToTimeline=Z,exports.switchTab=async function(e){return new Promise(n=>{uni.switchTab({url:e,success:()=>n(!0),fail:r=>{A.handleError(new t("SWITCH_TAB_FAILED",`Tab切换失败: ${e}`,"navigation"),"navigation"),n(!1)}})})},exports.throttle=function(e,t,n={}){let r,o=null,s=0;const{leading:a=!0,trailing:i=!0}=n,c=function(...n){const c=Date.now();s||a||(s=c);const l=t-(c-s);return l<=0||l>t?(o&&(clearTimeout(o),o=null),s=c,r=e.apply(this,n)):!o&&i&&(o=setTimeout(()=>{s=a?Date.now():0,o=null,r=e.apply(this,n)},l)),r};return c.cancel=()=>{o&&(clearTimeout(o),o=null),s=0},c},exports.useBack=C,exports.useBackDebounced=O,exports.useBackOrHome=async function(e="",t={}){const n=getCurrentPages(),r=t.delta||1;if(n.length>r)return await C(e,t);const o=t.homePage||"pages/index/index",s=o.startsWith("/")?o:`/${o}`;return console.info(`[navigation] 页面栈深度不足,重定向到首页: ${s}`),await I({url:s,params:t.homeParams})},exports.useCascaderAreaData=x,exports.useRegions=function(){return e.useCascaderAreaData()},exports.useToast=v,exports.useTryCatch=function(e,t={}){return async(...n)=>{try{return{data:await e(...n),error:null}}catch(e){return t.onError?.(e),{data:null,error:e}}finally{t.onFinally?.()}}},exports.useWindowInfo=g,exports.weChatOfficialAccountPayment=(e,n,r)=>{const s=a.getInstance();return s.start("weChatOfficialAccountPayment","payment"),new Promise(a=>{o(()=>{if("undefined"==typeof window||!window.navigator)throw new Error("不在浏览器环境中,无法调用微信支付");if(!(e&&e.appId&&e.timeStamp&&e.nonceStr&&e.package&&e.paySign))throw new Error("微信支付配置参数不完整");function o(){try{window.WeixinJSBridge.invoke("getBrandWCPayRequest",e,function(e){s.end("weChatOfficialAccountPayment"),console.log("🚀 ~ 微信支付结果:",e),"get_brand_wcpay_request:ok"===e.err_msg?(console.log("✅ 微信支付成功"),n?.(e),a(!0)):(console.warn("❌ 微信支付失败或取消:",e.err_msg),r?.(e),a(!1))})}catch(e){s.end("weChatOfficialAccountPayment"),j.handleError(new t("WECHAT_PAY_INVOKE_ERROR",`调用微信支付接口失败: ${e instanceof Error?e.message:String(e)}`,"payment"),"payment");const n={err_msg:"get_brand_wcpay_request:fail",err_desc:e instanceof Error?e.message:"未知错误"};r?.(n),a(!1)}}if(void 0===window.WeixinJSBridge){const e=window.document;e.addEventListener?e.addEventListener("WeixinJSBridgeReady",o,!1):e.attachEvent&&(e.attachEvent("WeixinJSBridgeReady",o),e.attachEvent("onWeixinJSBridgeReady",o))}else o();return!0},"payment","WECHAT_PAY_ERROR",!1)||(s.end("weChatOfficialAccountPayment"),a(!1))})};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{areaList as e,useCascaderAreaData as t}from"@vant/area-data";class n extends Error{code;module;timestamp;constructor(e,t,n){super(t),this.name="UniAppToolsError",this.code=e,this.module=n,this.timestamp=Date.now()}}class r{static instance;errorCallbacks=[];static getInstance(){return r.instance||(r.instance=new r),r.instance}onError(e){this.errorCallbacks.push(e)}handleError(e,t){const r={code:e instanceof n?e.code:"UNKNOWN_ERROR",message:e.message,module:e instanceof n?e.module:t,timestamp:e instanceof n?e.timestamp:Date.now(),stack:e.stack};this.errorCallbacks.forEach(e=>{try{e(r)}catch(e){}})}createModuleErrorHandler(e){return(t,r,s)=>{const a=new n(t,r,e);return s&&(a.stack=s.stack),this.handleError(a,e),a}}}async function s(e,t,n="ASYNC_ERROR"){try{return await e()}catch(e){return r.getInstance().createModuleErrorHandler(t)(n,`异步操作失败: ${e instanceof Error?e.message:String(e)}`,e),null}}function a(e,t,n="SYNC_ERROR",s=null){try{return e()}catch(e){return r.getInstance().createModuleErrorHandler(t)(n,`同步操作失败: ${e instanceof Error?e.message:String(e)}`,e),s}}var o=Object.freeze({__proto__:null,ErrorHandler:r,UniAppToolsError:n,safeAsync:s,safeSync:a});function i(){return"undefined"!=typeof performance&&"function"==typeof performance.now?performance.now():Date.now()}class c{static instance;metrics=new Map;completedMetrics=[];maxStoredMetrics=100;static getInstance(){return c.instance||(c.instance=new c),c.instance}start(e,t,n){const r={name:e,startTime:i(),module:t,metadata:n};this.metrics.set(e,r)}end(e){const t=this.metrics.get(e);return t?(t.endTime=i(),t.duration=t.endTime-t.startTime,this.metrics.delete(e),this.completedMetrics.push(t),this.completedMetrics.length>this.maxStoredMetrics&&this.completedMetrics.shift(),t):null}getReport(){const e={},t={};this.completedMetrics.forEach(n=>{e[n.module]||(e[n.module]=[]),e[n.module].push(n);const r=`${n.module}.${n.name}`;t[r]||(t[r]={total:0,count:0}),t[r].total+=n.duration||0,t[r].count+=1});const n={};Object.entries(t).forEach(([e,t])=>{n[e]=t.total/t.count});const r=[...this.completedMetrics].sort((e,t)=>(t.duration||0)-(e.duration||0)).slice(0,10);return{byModule:e,slowest:r,average:n}}clear(){this.metrics.clear(),this.completedMetrics=[]}}function u(e){return function(t,n,r){const s=r.value;r.value=function(...r){const a=c.getInstance(),o=`${t.constructor.name}.${n}`;a.start(o,e,{args:r.length});try{const e=s.apply(this,r);return e instanceof Promise?e.finally(()=>{a.end(o)}):(a.end(o),e)}catch(e){throw a.end(o),e}}}}var l=Object.freeze({__proto__:null,PerformanceMonitor:c,measurePerformance:u});class f{static instance;cache=new Map;defaultTTL=3e4;static getInstance(){return f.instance||(f.instance=new f),f.instance}set(e,t,n=this.defaultTTL){this.cache.set(e,{data:t,timestamp:Date.now(),ttl:n})}get(e){const t=this.cache.get(e);return t?Date.now()-t.timestamp>t.ttl?(this.cache.delete(e),null):t.data:null}clear(){this.cache.clear()}}const d=f.getInstance(),m=(e=!0)=>{const t=c.getInstance();t.start("getWindowInfo","system");const n="windowInfo";if(e){const e=d.get(n);if(e)return t.end("getWindowInfo"),e}const r=a(()=>{const t=uni.getWindowInfo();return e&&d.set(n,t),t},"system","GET_WINDOW_INFO_ERROR",null);return t.end("getWindowInfo"),r},h=()=>{const e="platform",t=d.get(e);if(t)return t;let n="unknown";return n="weixin",n="web",n="app",n="alipay",n="h5",d.set(e,"h5"),"h5"},p=()=>{try{const e=uni.getAccountInfoSync();return{appId:e.miniProgram?.appId||"",version:e.miniProgram?.version||"",envVersion:e.miniProgram?.envVersion||"",accountInfo:e}}catch(e){return{appId:"",version:"",envVersion:"",accountInfo:null}}},g=()=>{try{const e=uni.getUpdateManager();e.onCheckForUpdate(function(e){e.hasUpdate}),e.onUpdateReady(function(t){uni.showModal({title:"更新提示",content:"新版本已经准备好,是否重启应用?",showCancel:!0,cancelText:"稍后",confirmText:"立即重启",success(t){t.confirm&&e.applyUpdate()}})}),e.onUpdateFailed(function(e){uni.showModal({title:"更新失败",content:"新版本下载失败,请检查网络连接或稍后重试",showCancel:!1,confirmText:"知道了"})})}catch(e){}},w=()=>{try{const e=m();return e?.statusBarHeight||0}catch(e){return 0}},y=()=>{try{const e=h();return"weixin"===e||"alipay"===e?uni.getMenuButtonBoundingClientRect():null}catch(e){return null}},S=()=>{try{const e=w(),t=h();if("weixin"===t||"alipay"===t){const t=y();if(t){return t.height+e}}return e+0}catch(e){return w()+0}},E=()=>{try{const e=w();return{statusBarHeight:e,navHeight:S()}}catch(e){return{statusBarHeight:0,navHeight:44}}},v=e=>e&&"string"==typeof e?new Promise(t=>{try{uni.setNavigationBarTitle({title:e,success:()=>{t(!0)},fail:e=>{t(!1)}})}catch(e){t(!1)}}):Promise.resolve(!1),x=(e,t="image/x-icon")=>a(()=>{const n=h();if(("h5"===n||"web"===n)&&"undefined"!=typeof document){document.querySelectorAll('link[rel*="icon"]').forEach(e=>e.remove());const n=document.createElement("link");return n.rel="icon",n.type=t,n.href=e,document.head.appendChild(n),!0}return!1},"system","SET_PAGE_ICON_ERROR",!1);function T(e){if(null===e||"object"!=typeof e)return e;try{if("undefined"!=typeof structuredClone)try{return structuredClone(e)}catch(e){}return JSON.parse(JSON.stringify(e))}catch(e){throw new Error(`深拷贝失败: ${e instanceof Error?e.message:String(e)}`)}}function R(e,t){return Object.assign(e,t)}function P(e,t){const n=T(e);return function e(t,n){for(const r in n)if(n.hasOwnProperty(r)){const s=n[r],a=t[r];s&&"object"==typeof s&&!Array.isArray(s)&&a&&"object"==typeof a&&!Array.isArray(a)?e(a,s):t[r]=T(s)}}(n,t),n}function C(e,t,n=!1){let r,s=null;const a=function(...a){const o=n&&!s;return s&&clearTimeout(s),s=setTimeout(()=>{s=null,n||(r=e.apply(this,a))},t),o&&(r=e.apply(this,a)),r};return a.cancel=()=>{s&&(clearTimeout(s),s=null)},a}function M(e,t,n={}){let r,s=null,a=0;const{leading:o=!0,trailing:i=!0}=n,c=function(...n){const c=Date.now();a||o||(a=c);const u=t-(c-a);return u<=0||u>t?(s&&(clearTimeout(s),s=null),a=c,r=e.apply(this,n)):!s&&i&&(s=setTimeout(()=>{a=o?Date.now():0,s=null,r=e.apply(this,n)},u)),r};return c.cancel=()=>{s&&(clearTimeout(s),s=null),a=0},c}function I(e,t){if("object"==typeof e&&null!==e&&!Array.isArray(e)){return R(e,T(t))}return T(t)}const _=e=>{try{if("undefined"==typeof window||!window.location)throw new Error("不在 H5 浏览器环境中");const t=new URLSearchParams(window.location.search),n={};return t.forEach((e,t)=>{try{n[t]=decodeURIComponent(e)}catch(r){n[t]=e}}),e?n.hasOwnProperty(e)?n[e]:null:n}catch(t){return e?null:{}}};function b(e){try{const t=new URL(e,"http://localhost"),n=t.pathname,r={};return t.searchParams.forEach((e,t)=>{r[t]=e}),{path:n,params:r}}catch(e){return{path:"",params:{}}}}function A(e,t={}){return async(...n)=>{try{return{data:await e(...n),error:null}}catch(e){return t.onError?.(e),{data:null,error:e}}finally{t.onFinally?.()}}}const O=e,k=t;function D(){return t()}const j=(e="",t=!1,n="none",r=2e3)=>{uni.showToast({title:e,icon:n,mask:t,duration:r})};function $(e,t){if(!t||0===Object.keys(t).length)return e;const n=Object.entries(t).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`).join("&");return e+(e.includes("?")?"&":"?")+n}async function z(e){return new Promise(t=>{const n=$(e.url,e.params);uni.navigateTo({url:n,animationType:e.animationType,animationDuration:e.animationDuration,events:e.events,success:()=>t(!0),fail:e=>{t(!1)}})})}async function F(e){return new Promise(t=>{const n=$(e.url,e.params);uni.redirectTo({url:n,success:()=>t(!0),fail:e=>{t(!1)}})})}async function L(e="",t={}){return new Promise(n=>{const r=t.delta||1,s=t.timeout||5e3;if(getCurrentPages().length<=r)return void n(!1);const a=setTimeout(()=>{n(!1)},s);uni.navigateBack({delta:r,success:()=>{clearTimeout(a),setTimeout(()=>{try{const t=getCurrentPages();if(t.length>0){const n=t[t.length-1];n.$vm&&"function"==typeof n.$vm.init&&n.$vm.init(e)}n(!0)}catch(e){n(!0)}},100)},fail:e=>{clearTimeout(a),n(!1)}})})}async function N(e="",t={}){const n=getCurrentPages(),r=t.delta||1;if(n.length>r)return await L(e,t);const s=t.homePage||"pages/index/index",a=s.startsWith("/")?s:`/${s}`;return await H({url:a,params:t.homeParams})}const W=C(L,300);async function U(e){return new Promise(t=>{uni.switchTab({url:e,success:()=>t(!0),fail:e=>{t(!1)}})})}async function H(e){return new Promise(t=>{const n=$(e.url,e.params);uni.reLaunch({url:n,success:()=>t(!0),fail:e=>{t(!1)}})})}function B(){try{const e=getCurrentPages();if(0===e.length)return null;const t=e[e.length-1];return{route:t.route||"",options:t.options||{}}}catch(e){return null}}function J(){try{return getCurrentPages().map(e=>({route:e.route||"",options:e.options||{}}))}catch(e){return[]}}async function q(e,t=3){for(let n=0;n<t;n++){if(await z(e))return!0;n<t-1&&await new Promise(e=>setTimeout(e,1e3*(n+1)))}return!1}const G={showToast:!0,successMessage:"复制成功",failMessage:"复制失败",timeout:5e3};async function Y(e,t={}){const n=c.getInstance();n.start("copyText","clipboard",{textLength:e.length});const r={...G,...t};if(!e||"string"!=typeof e)return r.showToast&&j("复制内容不能为空",!1,"error"),n.end("copyText"),!1;if(e.length>1e4)return r.showToast&&j("复制内容过长",!1,"error"),n.end("copyText"),!1;const a=await s(()=>new Promise(t=>{uni.setClipboardData({data:e,showToast:!1,success:()=>{r.showToast&&j(r.successMessage),t(!0)},fail:()=>{r.showToast&&j(r.failMessage),t(!1)}})}),"clipboard","COPY_TEXT_ERROR")??!1;return n.end("copyText"),a}class K{static instance;cache=new Map;maxCacheSize=100;static getInstance(){return K.instance||(K.instance=new K),K.instance}isExpired(e){return!!e.ttl&&Date.now()-e.timestamp>e.ttl}set(e,t,n={}){try{if(!e||"string"!=typeof e)throw new Error("存储键不能为空");const r={value:t,timestamp:Date.now(),ttl:n.ttl},s=JSON.stringify(r);if(s.length>1048576)throw new Error("存储数据过大");if(uni.setStorageSync(e,s),this.cache.size>=this.maxCacheSize){const e=this.cache.keys().next().value;e&&this.cache.delete(e)}return this.cache.set(e,r),!0}catch(e){throw e}}get(e,t){try{if(!e||"string"!=typeof e)return t;if(this.cache.has(e)){const n=this.cache.get(e);return this.isExpired(n)?(this.cache.delete(e),this.remove(e),t):n.value}const n=uni.getStorageSync(e);if(!n||"string"!=typeof n)return t;const r=JSON.parse(n);let s;return s=r&&"object"==typeof r&&"value"in r&&"timestamp"in r?r:{value:r,timestamp:Date.now()},this.isExpired(s)?(this.remove(e),t):(this.cache.set(e,s),s.value)}catch(e){return t}}remove(e){try{return!(!e||"string"!=typeof e)&&(uni.removeStorageSync(e),this.cache.delete(e),!0)}catch(e){return!1}}clear(){try{return uni.clearStorageSync(),this.cache.clear(),!0}catch(e){return!1}}getInfo(){try{const e=uni.getStorageInfoSync();return{keys:e.keys||[],currentSize:e.currentSize||0,limitSize:e.limitSize||0}}catch(e){return{keys:[],currentSize:0,limitSize:0}}}cleanExpired(){try{const e=this.getInfo();let t=0;return e.keys.forEach(e=>{void 0===this.get(e)&&t++}),t}catch(e){return 0}}}const V=K.getInstance();function X(e,t,n={}){return V.set(e,t,n)}function Q(e,t){return V.get(e,t)}function Z(e){return e?V.remove(e):V.clear()}async function ee(e,t,n={}){return await s(()=>new Promise(r=>{r(X(e,t,n))}),"localStorage","SET_STORAGE_ASYNC_ERROR")??!1}async function te(e,t){return await s(()=>new Promise(n=>{n(Q(e,t))}),"localStorage","GET_STORAGE_ASYNC_ERROR")??t}function ne(){return V.getInfo()}function re(){return V.cleanExpired()}function se(e,t={}){const n=c.getInstance();n.start("batchSetStorage","localStorage");let r=0;return Object.entries(e).forEach(([e,n])=>{X(e,n,t)&&r++}),n.end("batchSetStorage"),r}function ae(e){const t=c.getInstance();t.start("batchGetStorage","localStorage");const n={};return e.forEach(e=>{n[e]=Q(e)}),t.end("batchGetStorage"),n}function oe(e,t){if(!t)return{valid:!0};return e>1024*t*1024?{valid:!1,message:`文件大小不能超过 ${t}MB`}:{valid:!0}}function ie(e){const t=e.lastIndexOf(".");if(-1!==t&&t!==e.length-1)return e.slice(t+1).toLowerCase()}function ce(e){return`${Date.now()}_${e}_${Math.random().toString(16).slice(2)}`}function ue(e,t){if(0===e.length)return{success:!1,files:e,message:"未选择任何文件"};const{maxSelectFileSizeMB:n,extensions:r}=t;if(n){if(e.some(e=>!oe(e.size,n).valid))return{success:!1,files:e,message:`部分文件大小超过 ${n}MB 限制`}}if(r&&r.length>0){const t=r.map(e=>e.toLowerCase());if(e.some(e=>!e.ext||!t.includes(e.ext)))return{success:!1,files:e,message:`存在不支持的文件类型,仅支持:${r.join(", ")}`}}return{success:!0,files:e}}async function le(e){return"undefined"!=typeof document&&"undefined"!=typeof window?function(e){const{type:t="image",count:n=1,extensions:r}=e;return new Promise(s=>{if("undefined"==typeof document)return void s({success:!1,files:[],message:"当前环境不支持文件选择"});const a=document.createElement("input");a.type="file",a.multiple=n>1,"image"===t?a.accept="image/*":r&&r.length>0&&(a.accept=r.map(e=>e.startsWith(".")?e:`.${e}`).join(",")),a.onchange=t=>{const n=t.target,r=n?.files;if(!r||0===r.length)return s({success:!1,files:[],message:"用户取消选择"}),void document.body.removeChild(a);const o=ue(Array.from(r).map((e,t)=>{const n=ie(e.name),r=URL.createObjectURL(e);return{id:ce(t),name:e.name,size:e.size,path:r,mimeType:e.type,ext:n,source:"file",platform:"h5",raw:e}}),e);s(o),document.body.removeChild(a)},a.style.display="none",document.body.appendChild(a),a.click()})}(e):function(e){const{type:t="image",count:n=1}=e;return new Promise(r=>{const s="undefined"==typeof uni?null:uni;if(!s)return void r({success:!1,files:[],message:"当前环境不支持文件选择"});const a=e=>{r({success:!1,files:[],message:e?.errMsg||"选择文件失败"})};"image"!==t||"function"!=typeof s.chooseImage?"function"!=typeof s.chooseMessageFile?"function"!=typeof s.chooseFile?r({success:!1,files:[],message:"当前平台不支持文件选择"}):s.chooseFile({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=ie(e.path);return{id:ce(t),name:`file_${t}`,size:e.size,path:e.path,ext:n,source:"file",platform:"app",raw:e}});r(ue(n,e))},fail:a}):s.chooseMessageFile({count:n,type:"all",success:t=>{const n=t.tempFiles.map((e,t)=>{const n=ie(e.name||e.path);return{id:ce(t),name:e.name,size:e.size,path:e.path,ext:n,source:"file",platform:"weixin",raw:e}});r(ue(n,e))},fail:a}):s.chooseImage({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=ie(e.path);return{id:ce(t),name:`image_${t}`,size:e.size,path:e.path,ext:n,source:"album",platform:"weixin",raw:e}});r(ue(n,e))},fail:a})})}(e)}async function fe(e,t,n){const{url:r,maxUploadFileSizeMB:s,showToast:a=!0,beforeUpload:o}=t;if("function"==typeof o){if(!await Promise.resolve(o(e)))return{file:e,success:!1,message:"已取消上传"}}if(!r){const t="上传地址不能为空";return a&&j(t,!1,"error"),Promise.resolve({file:e,success:!1,message:t})}const i=oe(e.size,s);return i.valid?"h5"===e.platform&&e.raw instanceof File?function(e,t,n){const{url:r,fieldName:s="file",formData:a,headers:o,showToast:i=!0,successMessage:c="上传成功",failMessage:u="上传失败",onProgress:l}=t;return new Promise(t=>{const f=e.raw;if(!(f&&f instanceof File)){const n="H5 环境缺少原生文件对象";return i&&j(n,!1,"error"),void t({file:e,success:!1,message:n})}const d=new XMLHttpRequest,m=new FormData;m.append(s,f,e.name),a&&Object.keys(a).forEach(e=>{m.append(e,String(a[e]))}),l&&d.upload.addEventListener("progress",t=>{if(t.lengthComputable){const n=Math.round(t.loaded/t.total*100);l(e,n)}}),d.addEventListener("load",()=>{const r=d.status,s=r>=200&&r<300;let a=d.responseText;try{a=JSON.parse(d.responseText)}catch{}if(s)i&&1===n&&j(c,!1,"success"),t({file:e,success:!0,statusCode:r,data:a});else{const n=`上传失败,状态码:${r}`;i&&j(n,!1,"error"),t({file:e,success:!1,statusCode:r,data:a,message:n})}}),d.addEventListener("error",()=>{const n=u;i&&j(n,!1,"error"),t({file:e,success:!1,message:n})}),d.addEventListener("abort",()=>{t({file:e,success:!1,message:"上传已取消"})}),d.open("POST",r),o&&Object.keys(o).forEach(e=>{d.setRequestHeader(e,o[e])}),d.send(m)})}(e,t,n):function(e,t,n){const{url:r,fieldName:s="file",formData:a,headers:o,showToast:i=!0,successMessage:c="上传成功",failMessage:u="上传失败",onProgress:l}=t,f="undefined"==typeof uni?null:uni;if(!f||"function"!=typeof f.uploadFile){const t="当前环境不支持文件上传";return i&&j(t,!1,"error"),Promise.resolve({file:e,success:!1,message:t})}return new Promise(t=>{const d=f.uploadFile({url:r,filePath:e.path,name:s,formData:a,header:o,success:r=>{let s=r.data;try{s=JSON.parse(r.data)}catch{}const a=r.statusCode;if(a>=200&&a<300)i&&1===n&&j(c,!1,"success"),t({file:e,success:!0,statusCode:a,data:s});else{const n=`上传失败,状态码:${a}`;i&&j(n,!1,"error"),t({file:e,success:!1,statusCode:a,data:s,message:n})}},fail:n=>{const r=n?.errMsg||u;i&&j(r,!1,"error"),t({file:e,success:!1,message:r})}});l&&d&&"function"==typeof d.onProgressUpdate&&d.onProgressUpdate(t=>{l(e,t.progress)})})}(e,t,n):(a&&i.message&&j(i.message,!1,"error"),Promise.resolve({file:e,success:!1,message:i.message}))}async function de(e){const{showToast:t=!0,failMessage:n="选择文件失败"}=e;try{const r=await le(e);if(!r.success){const e=r.message||n;return t&&e&&j(e,!1,"error"),[{file:null,success:!1,message:e}]}const s=r.files;if(0===s.length)return[];const a=s.length;return await async function(e,t,n){const r=new Array(e.length),s=t.concurrency&&t.concurrency>0?Math.min(t.concurrency,e.length):e.length;let a=0;const o=async()=>{for(;;){const s=a;if(s>=e.length)break;a+=1;const o=e[s],i=await fe(o,t,n);r[s]=i}},i=[];for(let e=0;e<s;e+=1)i.push(o());return await Promise.all(i),r}(s,e,a)}catch(e){const r=n;return t&&r&&j(r,!1,"error"),[{file:null,success:!1,message:r}]}}async function me(e){return de({...e,type:"image"})}const he=(e,t,n)=>{const r=c.getInstance();return r.start("weChatOfficialAccountPayment","payment"),new Promise(s=>{a(()=>{if("undefined"==typeof window||!window.navigator)throw new Error("不在浏览器环境中,无法调用微信支付");if(!(e&&e.appId&&e.timeStamp&&e.nonceStr&&e.package&&e.paySign))throw new Error("微信支付配置参数不完整");function a(){try{window.WeixinJSBridge.invoke("getBrandWCPayRequest",e,function(e){r.end("weChatOfficialAccountPayment"),"get_brand_wcpay_request:ok"===e.err_msg?(t?.(e),s(!0)):(n?.(e),s(!1))})}catch(e){r.end("weChatOfficialAccountPayment");const t={err_msg:"get_brand_wcpay_request:fail",err_desc:e instanceof Error?e.message:"未知错误"};n?.(t),s(!1)}}if(void 0===window.WeixinJSBridge){const e=window.document;e.addEventListener?e.addEventListener("WeixinJSBridgeReady",a,!1):e.attachEvent&&(e.attachEvent("WeixinJSBridgeReady",a),e.attachEvent("onWeixinJSBridgeReady",a))}else a();return!0},"payment","WECHAT_PAY_ERROR",!1)||(r.end("weChatOfficialAccountPayment"),s(!1))})},pe=()=>{const e=c.getInstance();e.start("isWechat","system");const t=a(()=>{if("undefined"!=typeof navigator&&navigator.userAgent){return navigator.userAgent.toLowerCase().includes("micromessenger")}return!1},"system","IS_WECHAT_ERROR",!1);return e.end("isWechat"),t||!1},ge=()=>new Promise(e=>{if("undefined"!=typeof window&&window.wx)return void e(!0);const t=["https://res.wx.qq.com/open/js/jweixin-1.6.0.js","https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"];let n=0;const r=s=>{const a=document.createElement("script");a.src=s,a.async=!0,a.onload=()=>{e(!0)},a.onerror=()=>{n++,n<t.length?(document.head.removeChild(a),r(t[n])):(document.head.removeChild(a),e(!1))},document.head.appendChild(a)};r(t[n]),e(!1)}),we=async e=>{const t=c.getInstance();t.start("configWechatJSSDK","wechat");try{if(!pe())return!1;return!!await ge()&&new Promise(t=>{window.wx?(window.wx.config({debug:e.debug||!1,appId:e.appId,timestamp:e.timestamp,nonceStr:e.nonceStr,signature:e.signature,jsApiList:e.jsApiList}),window.wx.ready(()=>{t(!0)}),window.wx.error(e=>{t(!1)})):t(!1),t(!1)})}catch(e){return!1}finally{t.end("configWechatJSSDK")}},ye=e=>new Promise(t=>{window.wx&&pe()?window.wx.checkJsApi({jsApiList:e,success:e=>{t(e.checkResult||{})}}):t({}),t({})}),Se=e=>{a(()=>{window.wx&&pe()&&(window.wx.updateTimelineShareData?window.wx.updateTimelineShareData(e):window.wx.onMenuShareTimeline&&window.wx.onMenuShareTimeline(e))},"wechat","SHARE_TIMELINE_ERROR")},Ee=e=>{a(()=>{window.wx&&pe()&&(window.wx.updateAppMessageShareData?window.wx.updateAppMessageShareData(e):window.wx.onMenuShareAppMessage&&window.wx.onMenuShareAppMessage(e))},"wechat","SHARE_FRIEND_ERROR")};class ve{static instance;isConfigured=!1;config=null;constructor(){}static getInstance(){return ve.instance||(ve.instance=new ve),ve.instance}async init(e){return this.config=e,this.isConfigured=await we(e),this.isConfigured}isReady(){return this.isConfigured&&pe()}getConfig(){return this.config}setShareData(e){this.isReady()&&(Se(e),Ee(e))}}const xe="2.0.0";async function Te(e={}){const{enablePerformanceMonitor:t=!1,enableErrorHandler:n=!0,onError:r=null,performanceReportInterval:s=0}=e;if(n){const{ErrorHandler:e}=await Promise.resolve().then(function(){return o}),t=e.getInstance();"function"==typeof r?t.onError(r):t.onError(e=>{})}if(t&&s>0){const{PerformanceMonitor:e}=await Promise.resolve().then(function(){return l}),t=e.getInstance();setInterval(()=>{t.getReport().slowest.length},s)}}export{r as ErrorHandler,c as PerformanceMonitor,n as UniAppToolsError,xe as VERSION,ve as WechatSDK,O as areaList,ae as batchGetStorage,se as batchSetStorage,ye as checkWechatJSAPI,re as cleanExpiredStorage,Z as clearStorageSync,we as configWechatJSSDK,Y as copyText,C as debounce,T as deepClone,P as deepMerge,b as extractUrlParts,p as getCurrentEnv,B as getCurrentPageInfo,_ as getH5UrlParams,y as getMenuButtonBoundingClientRect,S as getNavHeight,J as getPageStack,h as getPlatform,w as getStatusBarHeight,te as getStorage,ne as getStorageInfo,Q as getStorageSync,E as getTopNavBarHeight,Te as initUniAppTools,pe as isWechat,ge as loadWechatJSSDK,u as measurePerformance,R as mergeObjects,z as navigateTo,g as onCheckForUpdate,H as reLaunch,F as redirectTo,s as safeAsync,q as safeNavigateTo,a as safeSync,de as selectAndUpload,me as selectAndUploadImage,x as setPageIcon,v as setPageTitle,ee as setStorage,X as setStorageSync,Ee as shareToFriend,Se as shareToTimeline,U as switchTab,M as throttle,L as useBack,W as useBackDebounced,N as useBackOrHome,k as useCascaderAreaData,I as useDeepCopyByObj,D as useRegions,j as useToast,A as useTryCatch,m as useWindowInfo,he as weChatOfficialAccountPayment};
|
|
1
|
+
import{areaList as e,useCascaderAreaData as t}from"@vant/area-data";class n extends Error{code;module;timestamp;constructor(e,t,n){super(t),this.name="UniAppToolsError",this.code=e,this.module=n,this.timestamp=Date.now()}}class r{static instance;errorCallbacks=[];static getInstance(){return r.instance||(r.instance=new r),r.instance}onError(e){this.errorCallbacks.push(e)}handleError(e,t){const r={code:e instanceof n?e.code:"UNKNOWN_ERROR",message:e.message,module:e instanceof n?e.module:t,timestamp:e instanceof n?e.timestamp:Date.now(),stack:e.stack};console.error(`[${r.module}] ${r.code}: ${r.message}`,r),this.errorCallbacks.forEach(e=>{try{e(r)}catch(e){console.error("Error in error callback:",e)}})}createModuleErrorHandler(e){return(t,r,s)=>{const o=new n(t,r,e);return s&&(o.stack=s.stack),this.handleError(o,e),o}}}async function s(e,t,n="ASYNC_ERROR"){try{return await e()}catch(e){return r.getInstance().createModuleErrorHandler(t)(n,`异步操作失败: ${e instanceof Error?e.message:String(e)}`,e),null}}function o(e,t,n="SYNC_ERROR",s=null){try{return e()}catch(e){return r.getInstance().createModuleErrorHandler(t)(n,`同步操作失败: ${e instanceof Error?e.message:String(e)}`,e),s}}function a(){return"undefined"!=typeof performance&&"function"==typeof performance.now?performance.now():Date.now()}class i{static instance;metrics=new Map;completedMetrics=[];maxStoredMetrics=100;static getInstance(){return i.instance||(i.instance=new i),i.instance}start(e,t,n){const r={name:e,startTime:a(),module:t,metadata:n};this.metrics.set(e,r)}end(e){const t=this.metrics.get(e);return t?(t.endTime=a(),t.duration=t.endTime-t.startTime,this.metrics.delete(e),this.completedMetrics.push(t),this.completedMetrics.length>this.maxStoredMetrics&&this.completedMetrics.shift(),console.log(`[Performance] ${t.module}.${t.name}: ${t.duration?.toFixed(2)}ms`),t):(console.warn(`Performance metric "${e}" not found`),null)}getReport(){const e={},t={};this.completedMetrics.forEach(n=>{e[n.module]||(e[n.module]=[]),e[n.module].push(n);const r=`${n.module}.${n.name}`;t[r]||(t[r]={total:0,count:0}),t[r].total+=n.duration||0,t[r].count+=1});const n={};Object.entries(t).forEach(([e,t])=>{n[e]=t.total/t.count});const r=[...this.completedMetrics].sort((e,t)=>(t.duration||0)-(e.duration||0)).slice(0,10);return{byModule:e,slowest:r,average:n}}clear(){this.metrics.clear(),this.completedMetrics=[]}}function c(e){return function(t,n,r){const s=r.value;r.value=function(...r){const o=i.getInstance(),a=`${t.constructor.name}.${n}`;o.start(a,e,{args:r.length});try{const e=s.apply(this,r);return e instanceof Promise?e.finally(()=>{o.end(a)}):(o.end(a),e)}catch(e){throw o.end(a),e}}}}const l=r.getInstance();let u=null,m=null,f=null;const d=()=>{f=null},g=e=>"weixin"===e||"alipay"===e;let h=!1;const p=()=>{if(u)return u;let e="unknown";return e="weixin",e="web",e="app",e="alipay",e="h5",u="h5","h5"},w=(e=!0)=>e&&f?f:o(()=>{const t=uni.getWindowInfo();return e&&(f=t),t},"system","GET_WINDOW_INFO_ERROR",null),y=()=>{try{const e=uni.getAccountInfoSync();return{appId:e.miniProgram?.appId||"",version:e.miniProgram?.version||"",envVersion:e.miniProgram?.envVersion||"",accountInfo:e}}catch(e){return l.handleError(new n("GET_ACCOUNT_INFO_ERROR",`获取小程序账户信息失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),{appId:"",version:"",envVersion:"",accountInfo:{}}}},E=()=>{if(!h)try{const e=uni.getUpdateManager();h=!0,e.onCheckForUpdate(function(e){e.hasUpdate}),e.onUpdateReady(function(t){uni.showModal({title:"更新提示",content:"新版本已经准备好,是否重启应用?",showCancel:!0,cancelText:"稍后",confirmText:"立即重启",success(t){t.confirm&&e.applyUpdate()}})}),e.onUpdateFailed(function(e){l.handleError(new n("UPDATE_DOWNLOAD_FAILED","新版本下载失败","system"),"system"),uni.showModal({title:"更新失败",content:"新版本下载失败,请检查网络连接或稍后重试",showCancel:!1,confirmText:"知道了"})})}catch(e){l.handleError(new n("CHECK_UPDATE_ERROR",`版本更新检查失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system")}},R=()=>{if(null!==m)return m;try{const e=w();return m=e?.statusBarHeight||0,m}catch(e){return l.handleError(new n("GET_STATUS_BAR_HEIGHT_ERROR",`获取状态栏高度失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),0}},S=()=>{try{const e=p();return g(e)?uni.getMenuButtonBoundingClientRect():null}catch(e){return l.handleError(new n("GET_MENU_BUTTON_ERROR",`获取菜单按钮边界信息失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),null}},T=()=>{try{const e=p();if(g(e)){const e=S();return e?.height||44}return 44}catch(e){return l.handleError(new n("GET_NAVIGATION_BAR_HEIGHT_ERROR",`获取导航栏高度失败: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),44}},_=()=>{const e=p(),t=R(),n=T();return{platform:e,statusBarHeight:t,navigationBarHeight:n,totalTopHeight:t+n}},v=()=>{const e=_();return{statusBarHeight:e.statusBarHeight,navHeight:e.totalTopHeight}},A=()=>_().totalTopHeight,P=e=>"string"!=typeof e?(console.warn("setPageTitle: title必须是字符串"),Promise.resolve(!1)):""===e.trim()?(console.warn("setPageTitle: title不能为空"),Promise.resolve(!1)):new Promise(t=>{try{uni.setNavigationBarTitle({title:e,success:()=>{t(!0)},fail:e=>{console.warn("设置页面标题失败:",e),t(!1)}})}catch(e){l.handleError(new n("SET_PAGE_TITLE_ERROR",`setPageTitle执行异常: ${e instanceof Error?e.message:String(e)}`,"system"),"system"),t(!1)}}),O=(e,t="image/x-icon")=>o(()=>{const n=p();if("h5"===n||"web"===n){if("undefined"!=typeof document){document.querySelectorAll('link[rel*="icon"]').forEach(e=>e.remove());const n=document.createElement("link");return n.rel="icon",n.type=t,n.href=e,document.head.appendChild(n),!0}}else console.warn("setPageIcon 仅在H5/Web平台有效");return!1},"system","SET_PAGE_ICON_ERROR",!1),C=r.getInstance();function I(e){if(null===e||"object"!=typeof e)return e;try{if("undefined"!=typeof structuredClone)try{return structuredClone(e)}catch(e){}return JSON.parse(JSON.stringify(e))}catch(e){throw C.handleError(new n("DEEP_CLONE_ERROR",`深拷贝失败: ${e instanceof Error?e.message:String(e)}`,"utils"),"utils"),new Error(`深拷贝失败: ${e instanceof Error?e.message:String(e)}`)}}function $(e,t){const n=I(e);return function e(t,n){for(const r in n)if(n.hasOwnProperty(r)){const s=n[r],o=t[r];s&&"object"==typeof s&&!Array.isArray(s)&&o&&"object"==typeof o&&!Array.isArray(o)?e(o,s):t[r]=I(s)}}(n,t),n}function x(e,t){if(!t||0===Object.keys(t).length)return e;const n={...e};for(const r of Object.keys(e))Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n}function M(e,t,n=!1){let r,s=null;const o=function(...o){const a=n&&!s;return s&&clearTimeout(s),s=setTimeout(()=>{s=null,n||(r=e.apply(this,o))},t),a&&(r=e.apply(this,o)),r};return o.cancel=()=>{s&&(clearTimeout(s),s=null)},o}function b(e,t,n={}){let r,s=null,o=0;const{leading:a=!0,trailing:i=!0}=n,c=function(...n){const c=Date.now();o||a||(o=c);const l=t-(c-o);return l<=0||l>t?(s&&(clearTimeout(s),s=null),o=c,r=e.apply(this,n)):!s&&i&&(s=setTimeout(()=>{o=a?Date.now():0,s=null,r=e.apply(this,n)},l)),r};return c.cancel=()=>{s&&(clearTimeout(s),s=null),o=0},c}function U(e,t={}){return async(...n)=>{try{return{data:await e(...n),error:null}}catch(e){return t.onError?.(e),{data:null,error:e}}finally{t.onFinally?.()}}}const L=e=>{try{if("undefined"==typeof window||!window.location)throw new Error("不在 H5 浏览器环境中");const t=new URLSearchParams(window.location.search),n={};return t.forEach((e,t)=>{try{n[t]=decodeURIComponent(e)}catch(r){n[t]=e}}),console.log("🔗 H5 URL参数解析:",{fullUrl:window.location.href,searchParams:window.location.search,parsedParams:n,paramName:e}),e?n.hasOwnProperty(e)?n[e]:null:n}catch(t){return C.handleError(new n("GET_H5_URL_PARAMS_ERROR",`获取H5 URL参数失败: ${t instanceof Error?t.message:String(t)}`,"utils"),"utils"),e?null:{}}};function D(e){try{const t=new URL(e,"http://localhost"),n=t.pathname,r={};return t.searchParams.forEach((e,t)=>{r[t]=e}),{path:n,params:r}}catch(t){return C.handleError(new n("EXTRACT_URL_PARTS_ERROR",`无效的URL: ${e}`,"utils"),"utils"),{path:"",params:{}}}}const N=e,k=t;function H(){return t()}const F=(e="",t=!1,n="none",r=2e3)=>{uni.showToast({title:e,icon:n,mask:t,duration:r})},G=r.getInstance();function j(e,t){if(!t||0===Object.keys(t).length)return e;const n=Object.entries(t).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`).join("&");return e+(e.includes("?")?"&":"?")+n}async function B(e){return new Promise(t=>{const r=j(e.url,e.params);uni.navigateTo({url:r,animationType:e.animationType,animationDuration:e.animationDuration,events:e.events,success:()=>t(!0),fail:e=>{G.handleError(new n("NAVIGATE_TO_FAILED",`页面跳转失败: ${r}`,"navigation"),"navigation"),t(!1)}})})}async function J(e){return new Promise(t=>{const r=j(e.url,e.params);uni.redirectTo({url:r,success:()=>t(!0),fail:e=>{G.handleError(new n("REDIRECT_TO_FAILED",`页面重定向失败: ${r}`,"navigation"),"navigation"),t(!1)}})})}async function W(e="",t={}){return new Promise(r=>{const s=t.delta||1,o=t.timeout||5e3;if(getCurrentPages().length<=s)return console.warn(`[navigation] 无法返回${s}页,当前页面栈深度不足`),void r(!1);const a=setTimeout(()=>{console.warn("[navigation] 导航返回超时"),r(!1)},o);uni.navigateBack({delta:s,success:()=>{clearTimeout(a),setTimeout(()=>{try{const t=getCurrentPages();if(t.length>0){const n=t[t.length-1];n.$vm&&"function"==typeof n.$vm.init&&n.$vm.init(e)}r(!0)}catch(e){G.handleError(new n("PAGE_CALLBACK_ERROR",`页面回调执行失败: ${e instanceof Error?e.message:String(e)}`,"navigation"),"navigation"),r(!0)}},100)},fail:e=>{clearTimeout(a),G.handleError(new n("NAVIGATE_BACK_FAILED","导航返回失败","navigation"),"navigation"),r(!1)}})})}async function z(e="",t={}){const n=getCurrentPages(),r=t.delta||1;if(n.length>r)return await W(e,t);const s=t.homePage||"pages/index/index",o=s.startsWith("/")?s:`/${s}`;return console.info(`[navigation] 页面栈深度不足,重定向到首页: ${o}`),await V({url:o,params:t.homeParams})}const K=M(W,300);async function q(e){return new Promise(t=>{uni.switchTab({url:e,success:()=>t(!0),fail:r=>{G.handleError(new n("SWITCH_TAB_FAILED",`Tab切换失败: ${e}`,"navigation"),"navigation"),t(!1)}})})}async function V(e){return new Promise(t=>{const r=j(e.url,e.params);uni.reLaunch({url:r,success:()=>t(!0),fail:e=>{G.handleError(new n("RELAUNCH_FAILED",`重新启动失败: ${r}`,"navigation"),"navigation"),t(!1)}})})}function Y(){try{const e=getCurrentPages();if(0===e.length)return null;const t=e[e.length-1];return{route:t.route||"",options:t.options||{}}}catch(e){return G.handleError(new n("GET_CURRENT_PAGE_ERROR",`获取当前页面信息失败: ${e instanceof Error?e.message:String(e)}`,"navigation"),"navigation"),null}}function X(){try{return getCurrentPages().map(e=>({route:e.route||"",options:e.options||{}}))}catch(e){return G.handleError(new n("GET_PAGE_STACK_ERROR",`获取页面栈信息失败: ${e instanceof Error?e.message:String(e)}`,"navigation"),"navigation"),[]}}async function Q(e,t=3){for(let n=0;n<t;n++){if(await B(e))return!0;n<t-1&&(await new Promise(e=>setTimeout(e,1e3*(n+1))),console.log(`[navigation] 第${n+2}次尝试跳转...`))}return!1}const Z={showToast:!0,successMessage:"复制成功",failMessage:"复制失败",timeout:5e3};async function ee(e,t={}){const n=i.getInstance();n.start("copyText","clipboard",{textLength:e.length});const r={...Z,...t};if(!e||"string"!=typeof e)return r.showToast&&F("复制内容不能为空",!1,"error"),n.end("copyText"),!1;if(e.length>1e4)return r.showToast&&F("复制内容过长",!1,"error"),n.end("copyText"),!1;const o=await s(()=>new Promise(t=>{uni.setClipboardData({data:e,showToast:!1,success:()=>{r.showToast&&F(r.successMessage),t(!0)},fail:()=>{r.showToast&&F(r.failMessage),t(!1)}})}),"clipboard","COPY_TEXT_ERROR")??!1;return n.end("copyText"),o}const te=r.getInstance();class ne{static instance;cache=new Map;maxCacheSize=100;static getInstance(){return ne.instance||(ne.instance=new ne),ne.instance}isExpired(e){return!!e.ttl&&Date.now()-e.timestamp>e.ttl}set(e,t,r={}){try{if(!e||"string"!=typeof e)throw new Error("存储键不能为空");const n={value:t,timestamp:Date.now(),ttl:r.ttl},s=JSON.stringify(n);if(s.length>1048576)throw new Error("存储数据过大");if(uni.setStorageSync(e,s),this.cache.size>=this.maxCacheSize){const e=this.cache.keys().next().value;e&&this.cache.delete(e)}return this.cache.set(e,n),!0}catch(e){throw te.handleError(new n("SET_STORAGE_ERROR",`设置存储失败: ${e instanceof Error?e.message:String(e)}`,"localStorage"),"localStorage"),e}}get(e,t){try{if(!e||"string"!=typeof e)return t;if(this.cache.has(e)){const n=this.cache.get(e);return this.isExpired(n)?(this.cache.delete(e),this.remove(e),t):n.value}const n=uni.getStorageSync(e);if(!n||"string"!=typeof n)return t;const r=JSON.parse(n);let s;return s=r&&"object"==typeof r&&"value"in r&&"timestamp"in r?r:{value:r,timestamp:Date.now()},this.isExpired(s)?(this.remove(e),t):(this.cache.set(e,s),s.value)}catch(n){return console.warn(`[localStorage] 获取存储失败 [${e}]:`,n),t}}remove(e){try{return!(!e||"string"!=typeof e||(uni.removeStorageSync(e),this.cache.delete(e),0))}catch(t){return te.handleError(new n("REMOVE_STORAGE_ERROR",`删除存储失败: ${e}`,"localStorage"),"localStorage"),!1}}clear(){try{return uni.clearStorageSync(),this.cache.clear(),!0}catch(e){return te.handleError(new n("CLEAR_STORAGE_ERROR","清空存储失败","localStorage"),"localStorage"),!1}}getInfo(){try{const e=uni.getStorageInfoSync();return{keys:e.keys||[],currentSize:e.currentSize||0,limitSize:e.limitSize||0}}catch(e){return te.handleError(new n("GET_STORAGE_INFO_ERROR",`获取存储信息失败: ${e instanceof Error?e.message:String(e)}`,"localStorage"),"localStorage"),{keys:[],currentSize:0,limitSize:0}}}cleanExpired(){try{const e=this.getInfo();let t=0;return e.keys.forEach(e=>{void 0===this.get(e)&&t++}),t}catch(e){return te.handleError(new n("CLEAN_EXPIRED_ERROR",`清理过期数据失败: ${e instanceof Error?e.message:String(e)}`,"localStorage"),"localStorage"),0}}}const re=ne.getInstance();function se(e,t,n={}){return re.set(e,t,n)}function oe(e,t){return re.get(e,t)}function ae(e){return e?re.remove(e):re.clear()}async function ie(e,t,n={}){return await s(()=>new Promise(r=>{r(se(e,t,n))}),"localStorage","SET_STORAGE_ASYNC_ERROR")??!1}async function ce(e,t){return await s(()=>new Promise(n=>{n(oe(e,t))}),"localStorage","GET_STORAGE_ASYNC_ERROR")??t}function le(){return re.getInfo()}function ue(){return re.cleanExpired()}function me(e,t={}){const n=i.getInstance();n.start("batchSetStorage","localStorage");let r=0;return Object.entries(e).forEach(([e,n])=>{se(e,n,t)&&r++}),n.end("batchSetStorage"),r}function fe(e){const t=i.getInstance();t.start("batchGetStorage","localStorage");const n={};return e.forEach(e=>{n[e]=oe(e)}),t.end("batchGetStorage"),n}function de(e){const t=e.lastIndexOf(".");if(-1!==t&&t!==e.length-1)return e.slice(t+1).toLowerCase()}function ge(e){return`${Date.now()}_${e}_${Math.random().toString(16).slice(2)}`}function he(e,t){return t&&e>1024*t*1024?{valid:!1,message:`文件大小不能超过 ${t}MB`}:{valid:!0}}function pe(e,t){if(0===e.length)return{success:!1,files:e,message:"未选择任何文件"};const{maxSelectFileSizeMB:n,extensions:r}=t;if(n&&e.some(e=>!he(e.size,n).valid))return{success:!1,files:e,message:`部分文件大小超过 ${n}MB 限制`};if(r&&r.length>0){const t=r.map(e=>e.toLowerCase());if(e.some(e=>!e.ext||!t.includes(e.ext)))return{success:!1,files:e,message:`存在不支持的文件类型,仅支持:${r.join(", ")}`}}return{success:!0,files:e}}async function we(e,t,n){const{url:r,maxUploadFileSizeMB:s,showToast:o=!0,beforeUpload:a}=t;if("function"==typeof a&&!await Promise.resolve(a(e)))return{file:e,success:!1,message:"已取消上传"};if(!r){const t="上传地址不能为空";return o&&F(t,!1,"error"),Promise.resolve({file:e,success:!1,message:t})}const i=he(e.size,s);return i.valid?"h5"===e.platform&&e.raw instanceof File?function(e,t,n){const{url:r,fieldName:s="file",formData:o,headers:a,showToast:i=!0,successMessage:c="上传成功",failMessage:l="上传失败",onProgress:u,uploadTimeoutMs:m,autoRevokeObjectURL:f=!1}=t;return new Promise(t=>{const d=e.raw;if(!(d&&d instanceof File)){const n="H5 环境缺少原生文件对象";return i&&F(n,!1,"none"),void t({file:e,success:!1,message:n})}const g=f&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&"string"==typeof e.path&&e.path.startsWith("blob:"),h=()=>{if(g)try{URL.revokeObjectURL(e.path)}catch{}},p=new XMLHttpRequest,w=new FormData;w.append(s,d,e.name),o&&Object.keys(o).forEach(e=>{w.append(e,String(o[e]))}),u&&p.upload.addEventListener("progress",t=>{if(t.lengthComputable){const n=Math.round(t.loaded/t.total*100);u(e,n)}}),p.addEventListener("load",()=>{const r=p.status,s=r>=200&&r<300;let o=p.responseText;try{o=JSON.parse(p.responseText)}catch{}if(s)i&&1===n&&F(c,!1,"none"),h(),t({file:e,success:!0,statusCode:r,data:o});else{const n=`上传失败,状态码:${r}`;i&&F(n,!1,"none"),h(),t({file:e,success:!1,statusCode:r,data:o,message:n})}}),p.addEventListener("error",()=>{const n=l;i&&F(n,!1,"none"),h(),t({file:e,success:!1,message:n})}),p.addEventListener("timeout",()=>{const n=m?`上传超时(${m}ms)`:"上传超时";i&&F(n,!1,"none"),h(),t({file:e,success:!1,message:n})}),p.addEventListener("abort",()=>{h(),t({file:e,success:!1,message:"上传已取消"})}),p.open("POST",r),"number"==typeof m&&m>0&&(p.timeout=m),a&&Object.keys(a).forEach(e=>{p.setRequestHeader(e,a[e])}),p.send(w)})}(e,t,n):function(e,t,n){const{url:r,fieldName:s="file",formData:o,headers:a,showToast:i=!0,successMessage:c="上传成功",failMessage:l="上传失败",onProgress:u,uploadTimeoutMs:m}=t,f="undefined"==typeof uni?null:uni;if(!f||"function"!=typeof f.uploadFile){const t="当前环境不支持文件上传";return i&&F(t,!1,"none"),Promise.resolve({file:e,success:!1,message:t})}return new Promise(t=>{let d=!1,g=null;const h=e=>{d||(d=!0,g&&(clearTimeout(g),g=null),t(e))};let p=null;"number"==typeof m&&m>0&&(g=setTimeout(()=>{try{p&&"function"==typeof p.abort&&p.abort()}catch{}const t=`上传超时(${m}ms)`;i&&F(t,!1,"none"),h({file:e,success:!1,message:t})},m)),p=f.uploadFile({url:r,filePath:e.path,name:s,formData:o,header:a,success:t=>{let r=t.data;try{r=JSON.parse(t.data)}catch{}const s=t.statusCode;if(s>=200&&s<300)i&&1===n&&F(c,!1,"none"),h({file:e,success:!0,statusCode:s,data:r});else{const t=`上传失败,状态码:${s}`;i&&F(t,!1,"none"),h({file:e,success:!1,statusCode:s,data:r,message:t})}},fail:t=>{const n=t?.errMsg||l;i&&F(n,!1,"none"),h({file:e,success:!1,message:n})}}),u&&p&&"function"==typeof p.onProgressUpdate&&p.onProgressUpdate(t=>{u(e,t.progress)})})}(e,t,n):(o&&i.message&&F(i.message,!1,"error"),Promise.resolve({file:e,success:!1,message:i.message}))}async function ye(e){const{showToast:t=!0,failMessage:n="选择文件失败"}=e;try{const r=await async function(e){return"undefined"!=typeof document&&"undefined"!=typeof window?function(e){const{type:t="image",count:n=1,extensions:r}=e;return new Promise(s=>{if("undefined"==typeof document)return void s({success:!1,files:[],message:"当前环境不支持文件选择"});const o=document.createElement("input");o.type="file",o.multiple=n>1,"image"===t?o.accept="image/*":r&&r.length>0&&(o.accept=r.map(e=>e.startsWith(".")?e:`.${e}`).join(",")),o.onchange=t=>{const n=t.target,r=n?.files;if(!r||0===r.length)return s({success:!1,files:[],message:"用户取消选择"}),void document.body.removeChild(o);const a=pe(Array.from(r).map((e,t)=>{const n=de(e.name),r=URL.createObjectURL(e);return{id:ge(t),name:e.name,size:e.size,path:r,mimeType:e.type,ext:n,source:"file",platform:"h5",raw:e}}),e);s(a),document.body.removeChild(o)},o.style.display="none",document.body.appendChild(o),o.click()})}(e):function(e){const{type:t="image",count:n=1}=e;return new Promise(r=>{const s="undefined"==typeof uni?null:uni;if(!s)return void r({success:!1,files:[],message:"当前环境不支持文件选择"});const o=e=>{const t=function(e){const t=(e||"").trim();if(!t)return"选择文件失败";const n=t.toLowerCase();return n.includes("fail cancel")||n.includes("cancel")?"用户取消选择":/[\u4e00-\u9fff]/.test(t)?t:"选择文件失败"}(e?.errMsg);r({success:!1,files:[],message:t})},a=function(){let e="unknown";return e="h5",e="weixin",e="alipay",e="app","app"}();"image"!==t||"function"!=typeof s.chooseImage?"function"!=typeof s.chooseMessageFile?"function"!=typeof s.chooseFile?r({success:!1,files:[],message:"当前平台不支持文件选择"}):s.chooseFile({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=de(e.path);return{id:ge(t),name:`file_${t}`,size:e.size,path:e.path,ext:n,source:"file",platform:a,raw:e}});r(pe(n,e))},fail:o}):s.chooseMessageFile({count:n,type:"all",success:t=>{const n=t.tempFiles.map((e,t)=>{const n=de(e.name||e.path);return{id:ge(t),name:e.name,size:e.size,path:e.path,ext:n,source:"file",platform:a,raw:e}});r(pe(n,e))},fail:o}):s.chooseImage({count:n,success:t=>{const n=(Array.isArray(t.tempFiles)?t.tempFiles:[t.tempFiles]).map((e,t)=>{const n=de(e.path);return{id:ge(t),name:`image_${t}`,size:e.size,path:e.path,ext:n,source:"album",platform:a,raw:e}});r(pe(n,e))},fail:o})})}(e)}(e);if(!r.success){const e=r.message||n;return t&&e&&F(e,!1,"none"),[{file:null,success:!1,message:e}]}const s=r.files;if(0===s.length)return[];const o=s.length;return await async function(e,t,n){const r=new Array(e.length),s=t.concurrency&&t.concurrency>0?Math.min(t.concurrency,e.length):e.length;let o=0;const a=async()=>{for(;;){const s=o;if(s>=e.length)break;o+=1;const a=e[s],i=await we(a,t,n);r[s]=i}},i=[];for(let e=0;e<s;e+=1)i.push(a());return await Promise.all(i),r}(s,e,o)}catch(e){const r=n;return t&&r&&F(r,!1,"none"),[{file:null,success:!1,message:r}]}}async function Ee(e){return ye({...e,type:"image"})}const Re=r.getInstance(),Se=(e,t,r)=>{const s=i.getInstance();return s.start("weChatOfficialAccountPayment","payment"),new Promise(a=>{o(()=>{if("undefined"==typeof window||!window.navigator)throw new Error("不在浏览器环境中,无法调用微信支付");if(!(e&&e.appId&&e.timeStamp&&e.nonceStr&&e.package&&e.paySign))throw new Error("微信支付配置参数不完整");function o(){try{window.WeixinJSBridge.invoke("getBrandWCPayRequest",e,function(e){s.end("weChatOfficialAccountPayment"),console.log("🚀 ~ 微信支付结果:",e),"get_brand_wcpay_request:ok"===e.err_msg?(console.log("✅ 微信支付成功"),t?.(e),a(!0)):(console.warn("❌ 微信支付失败或取消:",e.err_msg),r?.(e),a(!1))})}catch(e){s.end("weChatOfficialAccountPayment"),Re.handleError(new n("WECHAT_PAY_INVOKE_ERROR",`调用微信支付接口失败: ${e instanceof Error?e.message:String(e)}`,"payment"),"payment");const t={err_msg:"get_brand_wcpay_request:fail",err_desc:e instanceof Error?e.message:"未知错误"};r?.(t),a(!1)}}if(void 0===window.WeixinJSBridge){const e=window.document;e.addEventListener?e.addEventListener("WeixinJSBridgeReady",o,!1):e.attachEvent&&(e.attachEvent("WeixinJSBridgeReady",o),e.attachEvent("onWeixinJSBridgeReady",o))}else o();return!0},"payment","WECHAT_PAY_ERROR",!1)||(s.end("weChatOfficialAccountPayment"),a(!1))})},Te=r.getInstance(),_e=()=>!("undefined"==typeof navigator||!navigator.userAgent)&&navigator.userAgent.toLowerCase().includes("micromessenger"),ve=()=>"undefined"!=typeof window&&"undefined"!=typeof document,Ae=()=>ve()&&_e()&&window.wx||null;let Pe=!1,Oe=null;const Ce=(e={})=>{const{timeoutMs:t=1e4}=e;if(Pe||ve()&&window.wx)return Pe=!0,Promise.resolve(!0);if(!ve())return Promise.resolve(!1);if(Oe)return Oe;const n=e.cdnUrls&&e.cdnUrls.length>0?e.cdnUrls:["https://res.wx.qq.com/open/js/jweixin-1.6.0.js","https://res2.wx.qq.com/open/js/jweixin-1.6.0.js"];return Oe=new Promise(e=>{let r=!1,s=null,o=null,a=0;const i=t=>{r||(r=!0,s&&(clearTimeout(s),s=null),o&&o.parentNode&&o.parentNode.removeChild(o),t&&(Pe=!0),Oe=null,e(t))},c=()=>{if(a>=n.length)return void i(!1);if(!document.head)return void i(!1);const e=n[a];a+=1;const t=document.createElement("script");o=t,t.src=e,t.async=!0,t.onload=()=>i(!0),t.onerror=()=>{t.parentNode&&t.parentNode.removeChild(t),o=null,c()},document.head.appendChild(t)};s=setTimeout(()=>{i(!1)},t),c()}),Oe};let Ie=null;const $e=async(e,t={})=>{const{timeoutMs:r=1e4}=t;if(!_e())return console.warn("当前不在微信环境中"),!1;if(Ie)return Ie;if(!await Ce({timeoutMs:r}))return Te.handleError(new n("WECHAT_JSSDK_LOAD_FAILED","微信 JS-SDK 加载失败","weixin"),"weixin"),!1;if(!ve())return!1;const s=window.wx;return!!s&&(Ie=new Promise(t=>{let o=!1,a=null;const i=e=>{o||(o=!0,a&&(clearTimeout(a),a=null),Ie=null,t(e))};a=setTimeout(()=>i(!1),r);try{s.config({debug:e.debug||!1,appId:e.appId,timestamp:e.timestamp,nonceStr:e.nonceStr,signature:e.signature,jsApiList:e.jsApiList}),s.ready(()=>{console.log("微信 JS-SDK 初始化完成"),i(!0)}),s.error(e=>{Te.handleError(new n("WECHAT_JSSDK_CONFIG_ERROR",`微信 JS-SDK 配置错误: ${JSON.stringify(e)}`,"weixin"),"weixin"),i(!1)})}catch(e){Te.handleError(new n("WECHAT_JSSDK_CONFIG_EXCEPTION",`微信 JS-SDK 配置异常: ${e instanceof Error?e.message:String(e)}`,"weixin"),"weixin"),i(!1)}}),Ie)},xe=(e,t={})=>{const{timeoutMs:r=8e3}=t;return new Promise(t=>{const s=Ae();if(!s||"function"!=typeof s.checkJsApi)return void t({});let o=!1;const a=e=>{o||(o=!0,t(e))},i=setTimeout(()=>a({}),r);try{s.checkJsApi({jsApiList:e,success:e=>{clearTimeout(i),a(e?.checkResult||{})}})}catch(e){clearTimeout(i),Te.handleError(new n("CHECK_WECHAT_JSAPI_ERROR",`检查微信 JS-SDK API 异常: ${e instanceof Error?e.message:String(e)}`,"weixin"),"weixin"),a({})}})},Me=e=>{const t=Ae();t&&(t.updateTimelineShareData?t.updateTimelineShareData(e):t.onMenuShareTimeline&&t.onMenuShareTimeline(e))},be=e=>{const t=Ae();t&&(t.updateAppMessageShareData?t.updateAppMessageShareData(e):t.onMenuShareAppMessage&&t.onMenuShareAppMessage(e))};class Ue{static instance;isConfigured=!1;config=null;initPromise=null;constructor(){}static getInstance(){return Ue.instance||(Ue.instance=new Ue),Ue.instance}async init(e){return this.initPromise||(this.config=e,this.initPromise=$e(e).then(e=>(this.isConfigured=e,e)).finally(()=>{this.initPromise=null})),this.initPromise}isReady(){return this.isConfigured&&_e()}getConfig(){return this.config}setShareData(e){this.isReady()?(Me(e),be(e)):console.warn("微信 SDK 未就绪")}}const Le="3.1.0",De={info:0,warn:1,error:2};function Ne(e={}){const{enablePerformanceMonitor:t=!1,enableErrorHandler:n=!0,onError:s=null,performanceReportInterval:o=0,showInitMessage:a=!1,silent:c=!1,logLevel:l="warn",enablePerformanceLog:u=!1}=e,m=De[l]||De.warn,f=(...e)=>{!c&&m<=De.info&&console.log(...e)},d=(...e)=>{!c&&m<=De.warn&&console.warn(...e)};if(n){const e=r.getInstance();"function"==typeof s?e.onError(s):e.onError(e=>{((...e)=>{!c&&m<=De.error&&console.error(...e)})(`[UniAppTools] ${e.module} - ${e.code}: ${e.message}`,e)})}if(t){const e=i.getInstance();u?o>0?(f("[UniAppTools] 性能监控已启用"),f("[UniAppTools] - 模式1: 每次操作结束时会输出单次性能日志"),f(`[UniAppTools] - 模式2: 每 ${o}ms 输出完整性能报告`),setInterval(()=>{const t=e.getReport();t.slowest.length>0?f("[UniAppTools] 性能报告:",t):f("[UniAppTools] 性能报告: 暂无性能数据(请调用工具函数以产生性能记录)")},o)):(f("[UniAppTools] 性能监控已启用(仅单次日志模式)"),f("[UniAppTools] 提示: 每次操作结束时会输出单次性能日志"),d("[UniAppTools] 警告: 未设置 performanceReportInterval,定期性能报告不会输出"),d("[UniAppTools] 如需启用定期报告,请设置 performanceReportInterval 为大于 0 的毫秒数(如: 30000 表示每30秒)")):f("[UniAppTools] 性能监控已启用(静默模式 - 不输出日志)")}if(f(`[UniAppTools] v${Le} 初始化完成`),a&&"undefined"!=typeof uni)try{uni.showToast({title:`工具库v${Le}已就绪`,icon:"none",duration:2e3})}catch(e){}}export{r as ErrorHandler,i as PerformanceMonitor,n as UniAppToolsError,Le as VERSION,Ue as WechatSDK,N as areaList,fe as batchGetStorage,me as batchSetStorage,xe as checkWechatJSAPI,ue as cleanExpiredStorage,ae as clearStorageSync,d as clearSystemCache,$e as configWechatJSSDK,ee as copyText,M as debounce,I as deepClone,$ as deepMerge,D as extractUrlParts,y as getCurrentEnv,Y as getCurrentPageInfo,L as getH5UrlParams,S as getMenuButtonBoundingClientRect,A as getNavHeight,T as getNavigationBarHeight,X as getPageStack,p as getPlatform,R as getStatusBarHeight,ce as getStorage,le as getStorageInfo,oe as getStorageSync,_ as getTopBarMetrics,v as getTopNavBarHeight,Ne as initUniAppTools,_e as isWechat,Ce as loadWechatJSSDK,c as measurePerformance,x as mergeObjects,B as navigateTo,E as onCheckForUpdate,V as reLaunch,J as redirectTo,s as safeAsync,Q as safeNavigateTo,o as safeSync,ye as selectAndUpload,Ee as selectAndUploadImage,O as setPageIcon,P as setPageTitle,ie as setStorage,se as setStorageSync,be as shareToFriend,Me as shareToTimeline,q as switchTab,b as throttle,W as useBack,K as useBackDebounced,z as useBackOrHome,k as useCascaderAreaData,H as useRegions,F as useToast,U as useTryCatch,w as useWindowInfo,Se as weChatOfficialAccountPayment};
|
package/dist/system/index.d.ts
CHANGED
|
@@ -1,38 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 系统信息相关工具函数
|
|
3
|
+
* 核心原则:简单、清晰、零破坏性
|
|
3
4
|
*/
|
|
4
|
-
/**
|
|
5
|
-
* 获取窗口信息(优化版本)
|
|
6
|
-
* @param useCache 是否使用缓存,默认true
|
|
7
|
-
* @returns 窗口信息对象,包含窗口尺寸、像素比等信息
|
|
8
|
-
* @description 调用 uni.getWindowInfo() 获取当前设备的窗口相关信息,支持缓存
|
|
9
|
-
*/
|
|
10
|
-
export declare const useWindowInfo: (useCache?: boolean) => any;
|
|
11
5
|
/**
|
|
12
6
|
* 平台类型定义
|
|
13
7
|
*/
|
|
14
8
|
type PlatformType = "weixin" | "web" | "app" | "alipay" | "h5" | "unknown";
|
|
9
|
+
/**
|
|
10
|
+
* 顶部区域高度度量
|
|
11
|
+
* @description
|
|
12
|
+
* - `statusBarHeight`: 状态栏高度
|
|
13
|
+
* - `navigationBarHeight`: 导航栏高度(不包含状态栏)
|
|
14
|
+
* - `totalTopHeight`: statusBarHeight + navigationBarHeight
|
|
15
|
+
*/
|
|
16
|
+
export interface TopBarMetrics {
|
|
17
|
+
statusBarHeight: number;
|
|
18
|
+
navigationBarHeight: number;
|
|
19
|
+
totalTopHeight: number;
|
|
20
|
+
platform: PlatformType;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 清除系统缓存(横竖屏切换时调用)
|
|
24
|
+
*/
|
|
25
|
+
export declare const clearSystemCache: () => void;
|
|
15
26
|
/**
|
|
16
27
|
* 获取当前运行平台
|
|
17
28
|
* @returns 平台类型字符串 ('weixin' | 'web' | 'app' | 'alipay' | 'h5' | 'unknown')
|
|
18
|
-
* @description
|
|
29
|
+
* @description 通过条件编译判断当前代码运行的平台环境
|
|
19
30
|
*/
|
|
20
31
|
export declare const getPlatform: () => PlatformType;
|
|
32
|
+
/**
|
|
33
|
+
* 获取窗口信息
|
|
34
|
+
* @param useCache 是否使用缓存,默认true
|
|
35
|
+
* @returns 窗口信息对象,包含窗口尺寸、像素比等信息
|
|
36
|
+
* @description 调用 uni.getWindowInfo() 获取当前设备的窗口相关信息,支持缓存
|
|
37
|
+
*/
|
|
38
|
+
export declare const useWindowInfo: (useCache?: boolean) => UniNamespace.GetWindowInfoResult | null;
|
|
21
39
|
/**
|
|
22
40
|
* 获取小程序账户信息
|
|
23
41
|
* @returns 小程序账户信息对象,包含appId、版本、环境等信息
|
|
24
42
|
* @description 调用 uni.getAccountInfoSync() 获取当前小程序的账户相关信息
|
|
25
43
|
*/
|
|
26
44
|
export declare const getCurrentEnv: () => {
|
|
27
|
-
appId: string;
|
|
28
|
-
version: string;
|
|
29
|
-
envVersion: "develop" | "trial" | "release";
|
|
30
|
-
accountInfo: UniApp.AccountInfo;
|
|
31
|
-
} | {
|
|
32
45
|
appId: string;
|
|
33
46
|
version: string;
|
|
34
47
|
envVersion: string;
|
|
35
|
-
accountInfo:
|
|
48
|
+
accountInfo: any;
|
|
36
49
|
};
|
|
37
50
|
/**
|
|
38
51
|
* 检查小程序版本更新
|
|
@@ -48,27 +61,39 @@ export declare const onCheckForUpdate: () => void;
|
|
|
48
61
|
* @returns 状态栏高度(单位:px)
|
|
49
62
|
* @description 获取设备状态栏的高度,用于适配不同设备的状态栏
|
|
50
63
|
*/
|
|
51
|
-
export declare const getStatusBarHeight: () =>
|
|
64
|
+
export declare const getStatusBarHeight: () => number;
|
|
52
65
|
/**
|
|
53
66
|
* 获取菜单按钮边界信息
|
|
54
67
|
* @returns 菜单按钮边界信息对象或null
|
|
55
68
|
* @description 获取小程序右上角菜单按钮的边界信息,仅在小程序平台有效
|
|
56
69
|
*/
|
|
57
|
-
export declare const getMenuButtonBoundingClientRect: () =>
|
|
70
|
+
export declare const getMenuButtonBoundingClientRect: () => UniNamespace.GetMenuButtonBoundingClientRectRes | null;
|
|
58
71
|
/**
|
|
59
|
-
*
|
|
72
|
+
* 获取导航栏高度(不含状态栏)
|
|
60
73
|
* @returns 导航栏高度(单位:px)
|
|
61
|
-
* @description
|
|
74
|
+
* @description 获取导航栏的高度,不包含状态栏高度
|
|
75
|
+
*/
|
|
76
|
+
export declare const getNavigationBarHeight: () => number;
|
|
77
|
+
/**
|
|
78
|
+
* 获取顶部区域高度度量(推荐使用)
|
|
79
|
+
* @description 返回结构化的顶部区域高度信息,避免重复计算
|
|
62
80
|
*/
|
|
63
|
-
export declare const
|
|
81
|
+
export declare const getTopBarMetrics: () => TopBarMetrics;
|
|
64
82
|
/**
|
|
65
|
-
* @
|
|
83
|
+
* @deprecated 请使用 getTopBarMetrics() 获取结构化数据,或使用 getStatusBarHeight() + getNavigationBarHeight()
|
|
84
|
+
* @returns 包含状态栏高度和导航栏总高度的配置对象
|
|
66
85
|
* @description 一次性获取应用所需的状态栏和导航栏高度配置信息
|
|
67
86
|
*/
|
|
68
87
|
export declare const getTopNavBarHeight: () => {
|
|
69
|
-
statusBarHeight:
|
|
70
|
-
navHeight:
|
|
88
|
+
statusBarHeight: number;
|
|
89
|
+
navHeight: number;
|
|
71
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* @deprecated 请使用 getTopBarMetrics().totalTopHeight
|
|
93
|
+
* @returns 导航栏总高度(包含状态栏,单位:px)
|
|
94
|
+
* @description 获取导航栏的总高度,包含状态栏高度
|
|
95
|
+
*/
|
|
96
|
+
export declare const getNavHeight: () => number;
|
|
72
97
|
/**
|
|
73
98
|
* 修改页面标题
|
|
74
99
|
* @param title 新的页面标题
|
package/dist/upload/index.d.ts
CHANGED
|
@@ -51,6 +51,15 @@ export interface SelectAndUploadOptions {
|
|
|
51
51
|
headers?: Record<string, string>;
|
|
52
52
|
/** 单次最大并发上传数量(默认与文件数相同,即全部并行) */
|
|
53
53
|
concurrency?: number;
|
|
54
|
+
/** 上传超时(毫秒),超时将中止上传并返回失败结果 */
|
|
55
|
+
uploadTimeoutMs?: number;
|
|
56
|
+
/**
|
|
57
|
+
* H5 环境下是否在上传结束后自动释放 `URL.createObjectURL` 创建的对象 URL
|
|
58
|
+
* @description
|
|
59
|
+
* - 默认 false:保持旧行为不变(部分业务可能在上传后仍需要用 path 预览)
|
|
60
|
+
* - 若业务不再需要该 blob URL,建议开启以减少长时间使用下的内存占用
|
|
61
|
+
*/
|
|
62
|
+
autoRevokeObjectURL?: boolean;
|
|
54
63
|
/**
|
|
55
64
|
* 上传前拦截钩子
|
|
56
65
|
* 返回 false 或 Promise<false> 将跳过该文件的上传,并在结果中标记为失败(不弹 toast)
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -3,104 +3,65 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { useCascaderAreaData as vantUseCascaderAreaData } from '@vant/area-data';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @
|
|
8
|
-
* @returns 深拷贝后的对象
|
|
9
|
-
* @description 兼容小程序、App、H5 多端环境的深拷贝实现
|
|
6
|
+
* 高性能深拷贝(uni-app 多端兼容)
|
|
7
|
+
* @description H5 环境优先使用 structuredClone,降级为 JSON 序列化
|
|
10
8
|
*/
|
|
11
9
|
export declare function deepClone<T>(obj: T): T;
|
|
12
|
-
/**
|
|
13
|
-
* 浅拷贝对象合并
|
|
14
|
-
* @param target 目标对象
|
|
15
|
-
* @param source 源对象
|
|
16
|
-
* @returns 合并后的目标对象
|
|
17
|
-
*/
|
|
18
|
-
export declare function mergeObjects<T extends Record<string, any>>(target: T, source: Partial<T>): T;
|
|
19
10
|
/**
|
|
20
11
|
* 深度合并对象
|
|
21
|
-
* @param target 目标对象
|
|
22
|
-
* @param source 源对象
|
|
23
|
-
* @returns 深度合并后的新对象
|
|
24
12
|
*/
|
|
25
13
|
export declare function deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T;
|
|
26
14
|
/**
|
|
27
|
-
*
|
|
28
|
-
* @
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
15
|
+
* 只合并目标对象中已存在的字段
|
|
16
|
+
* @description 避免后端返回的冗余字段污染前端表单状态
|
|
17
|
+
*/
|
|
18
|
+
export declare function mergeObjects<T extends Record<string, any>, U extends Record<string, any>>(base: T, incoming: U): T;
|
|
19
|
+
/**
|
|
20
|
+
* 防抖函数
|
|
32
21
|
*/
|
|
33
22
|
export declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number, immediate?: boolean): T;
|
|
34
23
|
/**
|
|
35
|
-
*
|
|
36
|
-
* @param func 要节流的函数
|
|
37
|
-
* @param wait 等待时间(毫秒)
|
|
38
|
-
* @param options 选项
|
|
39
|
-
* @returns 节流后的函数
|
|
24
|
+
* 节流函数
|
|
40
25
|
*/
|
|
41
26
|
export declare function throttle<T extends (...args: any[]) => any>(func: T, wait: number, options?: {
|
|
42
27
|
leading?: boolean;
|
|
43
28
|
trailing?: boolean;
|
|
44
29
|
}): T;
|
|
45
30
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @
|
|
48
|
-
*/
|
|
49
|
-
export declare function useDeepCopyByObj<T>(target: T, source: any): T;
|
|
50
|
-
/**
|
|
51
|
-
* H5 环境 URL 参数获取函数接口
|
|
31
|
+
* 封装通用的 try/catch/finally 调用
|
|
32
|
+
* @returns 包装后的执行函数,返回 Promise<{ data, error }>
|
|
52
33
|
*/
|
|
34
|
+
export declare function useTryCatch<T extends (...args: any[]) => any>(fn: T, options?: {
|
|
35
|
+
onError?: (error: unknown) => void;
|
|
36
|
+
onFinally?: () => void;
|
|
37
|
+
}): (...args: Parameters<T>) => Promise<{
|
|
38
|
+
data: Awaited<ReturnType<T>> | null;
|
|
39
|
+
error: unknown;
|
|
40
|
+
}>;
|
|
53
41
|
export interface UrlParams {
|
|
54
42
|
[key: string]: string;
|
|
55
43
|
}
|
|
56
44
|
/**
|
|
57
45
|
* 获取 H5 环境下的 URL 参数
|
|
58
|
-
* @
|
|
59
|
-
* @returns 如果指定参数名则返回对应值或null,否则返回所有参数对象
|
|
60
|
-
* @description 在 H5 环境中解析 URL 参数
|
|
46
|
+
* @description 仅在 H5 环境有效
|
|
61
47
|
*/
|
|
62
48
|
export declare const getH5UrlParams: (paramName?: string) => string | null | UrlParams;
|
|
63
49
|
/**
|
|
64
50
|
* 从URL中提取页面路径和参数
|
|
65
|
-
* @param url 要解析的URL
|
|
66
|
-
* @returns 包含路径和参数的对象
|
|
67
|
-
* @example
|
|
68
|
-
* ```js
|
|
69
|
-
* const url = "http://example.com/path/to/page?foo=bar&baz=qux";
|
|
70
|
-
* const { path, params } = extractUrlParts(url);
|
|
71
|
-
* console.log(path); // "/path/to/page"
|
|
72
|
-
* console.log(params); // { foo: "bar", baz: "qux" }
|
|
73
|
-
* ```
|
|
74
51
|
*/
|
|
75
52
|
export declare function extractUrlParts(url: string): {
|
|
76
53
|
path: string;
|
|
77
54
|
params: Record<string, string>;
|
|
78
55
|
};
|
|
79
|
-
/**
|
|
80
|
-
* 封装通用的 try/catch/finally 调用
|
|
81
|
-
* @param fn 需要被保护执行的函数,可以返回同步结果或 Promise
|
|
82
|
-
* @param options 可选配置项,支持错误和最终回调
|
|
83
|
-
* @returns 返回一个包装后的执行函数,执行结果为 Promise<{ data, error }>
|
|
84
|
-
*/
|
|
85
|
-
export declare function useTryCatch<T extends (...args: any[]) => any>(fn: T, options?: {
|
|
86
|
-
onError?: (error: unknown) => void;
|
|
87
|
-
onFinally?: () => void;
|
|
88
|
-
}): (...args: Parameters<T>) => Promise<{
|
|
89
|
-
data: Awaited<ReturnType<T>> | null;
|
|
90
|
-
error: unknown;
|
|
91
|
-
}>;
|
|
92
56
|
/**
|
|
93
57
|
* 中国省市区数据
|
|
94
|
-
* @description
|
|
58
|
+
* @description 数据来源于 @vant/area-data
|
|
95
59
|
*/
|
|
96
60
|
export declare const areaList: {
|
|
97
61
|
province_list: Record<string, string>;
|
|
98
62
|
city_list: Record<string, string>;
|
|
99
63
|
county_list: Record<string, string>;
|
|
100
64
|
};
|
|
101
|
-
/**
|
|
102
|
-
* 级联选择器选项类型
|
|
103
|
-
*/
|
|
104
65
|
export interface CascaderOption {
|
|
105
66
|
text: string;
|
|
106
67
|
value: string;
|
|
@@ -108,26 +69,11 @@ export interface CascaderOption {
|
|
|
108
69
|
}
|
|
109
70
|
/**
|
|
110
71
|
* 生成级联选择器所需的省市区数据
|
|
111
|
-
* @
|
|
112
|
-
* @description 将 areaList 转换为级联选择器组件所需的树形结构数据,数据来源于 @vant/area-data
|
|
113
|
-
* @example
|
|
114
|
-
* ```ts
|
|
115
|
-
* import { useCascaderAreaData } from 'uniapp-tool-functions';
|
|
116
|
-
*
|
|
117
|
-
* const cascaderData = useCascaderAreaData();
|
|
118
|
-
* ```
|
|
72
|
+
* @description 将 areaList 转换为级联选择器组件所需的树形结构
|
|
119
73
|
*/
|
|
120
74
|
export declare const useCascaderAreaData: typeof vantUseCascaderAreaData;
|
|
121
75
|
/**
|
|
122
76
|
* 生成中国省市区级联数据
|
|
123
|
-
* @
|
|
124
|
-
* @description 此函数是 useCascaderAreaData 的别名,用于生成省市区三级联动数据
|
|
125
|
-
* @example
|
|
126
|
-
* ```ts
|
|
127
|
-
* import { useRegions } from 'uniapp-tool-functions';
|
|
128
|
-
*
|
|
129
|
-
* const regions = useRegions();
|
|
130
|
-
* console.log(regions); // 返回省市区三级联动数据
|
|
131
|
-
* ```
|
|
77
|
+
* @description useCascaderAreaData 的别名
|
|
132
78
|
*/
|
|
133
79
|
export declare function useRegions(): CascaderOption[];
|
package/dist/weixin/index.d.ts
CHANGED
|
@@ -35,36 +35,31 @@ declare global {
|
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* 检测是否在微信浏览器中运行
|
|
38
|
-
* @returns 是否为微信浏览器环境
|
|
39
|
-
* @description 检测当前是否在微信浏览器中运行(不包括微信小程序)
|
|
40
38
|
*/
|
|
41
39
|
export declare const isWechat: () => boolean;
|
|
42
40
|
/**
|
|
43
41
|
* 动态加载微信 JS-SDK
|
|
44
|
-
*
|
|
45
|
-
* @description 支持主备CDN地址,提升服务稳定性
|
|
42
|
+
* 支持主备 CDN 地址,提升服务稳定性
|
|
46
43
|
*/
|
|
47
|
-
export declare const loadWechatJSSDK: (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
export declare const loadWechatJSSDK: (options?: {
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
cdnUrls?: string[];
|
|
47
|
+
}) => Promise<boolean>;
|
|
48
|
+
export declare const configWechatJSSDK: (config: WxConfig, options?: {
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
}) => Promise<boolean>;
|
|
54
51
|
/**
|
|
55
52
|
* 检查微信 JS-SDK API 可用性
|
|
56
|
-
* @param jsApiList 需要检查的 API 列表
|
|
57
|
-
* @returns Promise<Record<string, boolean>> API 可用性结果
|
|
58
53
|
*/
|
|
59
|
-
export declare const checkWechatJSAPI: (jsApiList: string[]
|
|
54
|
+
export declare const checkWechatJSAPI: (jsApiList: string[], options?: {
|
|
55
|
+
timeoutMs?: number;
|
|
56
|
+
}) => Promise<Record<string, boolean>>;
|
|
60
57
|
/**
|
|
61
58
|
* 微信分享到朋友圈
|
|
62
|
-
* @param config 分享配置
|
|
63
59
|
*/
|
|
64
60
|
export declare const shareToTimeline: (config: WxShareConfig) => void;
|
|
65
61
|
/**
|
|
66
62
|
* 微信分享给朋友
|
|
67
|
-
* @param config 分享配置
|
|
68
63
|
*/
|
|
69
64
|
export declare const shareToFriend: (config: WxShareConfig) => void;
|
|
70
65
|
/**
|
|
@@ -74,12 +69,11 @@ export declare class WechatSDK {
|
|
|
74
69
|
private static instance;
|
|
75
70
|
private isConfigured;
|
|
76
71
|
private config;
|
|
72
|
+
private initPromise;
|
|
77
73
|
private constructor();
|
|
78
74
|
static getInstance(): WechatSDK;
|
|
79
75
|
/**
|
|
80
76
|
* 初始化微信 JS-SDK
|
|
81
|
-
* @param config 配置参数
|
|
82
|
-
* @returns Promise<boolean>
|
|
83
77
|
*/
|
|
84
78
|
init(config: WxConfig): Promise<boolean>;
|
|
85
79
|
/**
|
|
@@ -92,7 +86,6 @@ export declare class WechatSDK {
|
|
|
92
86
|
getConfig(): WxConfig | null;
|
|
93
87
|
/**
|
|
94
88
|
* 设置分享信息
|
|
95
|
-
* @param shareConfig 分享配置
|
|
96
89
|
*/
|
|
97
90
|
setShareData(shareConfig: WxShareConfig): void;
|
|
98
91
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-uniapp-tools",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "一个功能强大、性能优化的 uni-app 开发工具库,提供剪贴板、本地存储、导航、系统信息等常用功能",
|
|
6
6
|
"main": "dist/my-uniapp-tools.cjs.js",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"build": "rollup -c && tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
17
17
|
"build:prod": "NODE_ENV=production rollup -c && tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
18
18
|
"prepublishOnly": "npm run build:prod",
|
|
19
|
-
"test": "
|
|
19
|
+
"test": "npm run build && node tests/run-tests.js",
|
|
20
|
+
"test:only": "node tests/run-tests.js",
|
|
20
21
|
"dev": "node src/index.js",
|
|
21
22
|
"docs:dev": "vuepress dev docs",
|
|
22
23
|
"docs:build": "vuepress build docs",
|