UNPKG

my-uniapp-tools

Version:

一个简洁稳定的 uni-app 开发工具库,提供剪贴板、本地存储、导航、系统信息等常用功能

629 lines (462 loc) 16.8 kB
# uni-app 工具库 一个简洁稳定的 uni-app 开发工具库,提供剪贴板、本地存储、导航、系统信息、文件上传等常用功能。 ## ✨ 特性 - 🚀 **简洁稳定**: 简化缓存机制,删除过度设计,减少维护成本 - 🛡️ **类型安全**: 完整的 TypeScript 支持 - 🔧 **统一错误处理**: 全局错误管理和监控 - 💾 **本地存储**: 支持TTL过期管理 - 🔄 **简洁设计**: 遵循Linus"好品味"原则,消除特殊情况 - 📱 **跨平台**: 支持 H5、App、微信/支付宝小程序 ## 📦 安装 ```bash npm install my-uniapp-tools # 或 yarn add my-uniapp-tools ``` ## 🚀 快速开始 ### 基础使用 ```javascript import { copyText } from 'my-uniapp-tools/clipboard'; import { setStorageSync } from 'my-uniapp-tools/localStorage'; import { useToast } from 'my-uniapp-tools/ui'; // 复制文本 await copyText('Hello World!'); // 本地存储 setStorageSync('userInfo', { name: '张三', age: 25 }); // 显示提示 useToast('操作成功'); ``` ### 按需引入 新项目推荐使用模块级子路径入口,减少无关模块被打包器纳入依赖图。根入口仍保留,兼容已有项目。 ```javascript import { deepClone } from 'my-uniapp-tools/utils'; import { selectAndUpload } from 'my-uniapp-tools/upload'; import { areaList } from 'my-uniapp-tools/regions'; ``` 省市区数据单独放在 `my-uniapp-tools/regions`,避免只使用 `utils` 时携带 `@vant/area-data`。 ### 错误监听(可选) ```javascript import { ErrorHandler } from 'my-uniapp-tools/core'; ErrorHandler.getInstance().onError((error) => { console.error(`[${error.module}] ${error.code}: ${error.message}`, error); }); ``` ## 📚 API 文档 ### 🎯 核心功能 #### ErrorHandler 全局错误处理器 ```javascript import { ErrorHandler } from 'my-uniapp-tools/core'; const errorHandler = ErrorHandler.getInstance(); // 注册错误监听 errorHandler.onError((error) => { console.log(`[${error.module}] ${error.message}`); // 上报错误到服务器 }); ``` ### 📋 剪贴板功能 #### copyText(text, config?) 跨平台文本复制 ```javascript // 基础使用 await copyText('要复制的文本'); // 高级配置 await copyText('要复制的文本', { showToast: true, // 是否显示提示 successMessage: '复制成功', // 成功提示文本 failMessage: '复制失败', // 失败提示文本 timeout: 5000 // 超时时间(ms) }); ``` ### 💾 本地存储功能 #### setStorageSync(key, value, options?) 设置本地存储(同步) ```javascript // 基础使用 setStorageSync('key', 'value'); // 带过期时间 setStorageSync('userData', userData, { ttl: 24 * 60 * 60 * 1000 // 24小时后过期 }); ``` #### getStorageSync(key, defaultValue?) 获取本地存储(同步) ```javascript const userData = getStorageSync('userData', {}); ``` #### 批量操作 ```javascript // 批量设置 const count = batchSetStorage({ 'key1': 'value1', 'key2': 'value2' }); // 批量获取 const data = batchGetStorage(['key1', 'key2']); ``` #### cleanExpiredStorage() 清理过期数据 ```javascript const cleanedCount = cleanExpiredStorage(); console.log(`清理了 ${cleanedCount} 项过期数据`); ``` ### 🧭 导航功能 本模块以页面返回与页面信息查询为主,常用 API 包括: - `configureNavigation(config)`:配置导航模块(例如设置默认首页) - `useBuildUrl(url, params)`:构建带参数的页面 URL - `useBack(params?, options?)`:返回上一页并支持传参和超时保护 - `useBackOrHome(params?, options?)`:返回上一页或在页面栈不足时跳转到首页 - `useBackDebounced`:`useBack` 的防抖版本(300ms) - `useCurrentPageInfo()` / `getCurrentPageInfo()`:获取当前页面信息(推荐使用 `useCurrentPageInfo`) - `usePageStack()` / `getPageStack()`:获取页面栈信息(推荐使用 `usePageStack`) 如果需要页面跳转(如 `navigateTo` / `redirectTo` / `switchTab` / `reLaunch`),建议直接使用 `uni` 提供的原生 API,或在应用层实现自己的“安全导航”封装(例如 `useSafeNavigateTo`)。下面给出常用示例: ```javascript import { configureNavigation, useBuildUrl, useBack, useBackOrHome } from 'my-uniapp-tools/navigation'; // 配置默认首页 configureNavigation({ defaultHomePage: '/pages/home/home' }); // 构建带参数的 URL const url = useBuildUrl('/pages/detail/detail', { id: 123 }); // 返回上一页并传参 await useBack({ refreshData: true }); // 页面栈不足时返回或跳转首页 await useBackOrHome('', { homePage: '/pages/home/home' }); ``` ### 📱 系统信息 #### getPlatform() 获取当前平台 ```javascript const platform = getPlatform(); // 'weixin' | 'h5' | 'app' | 'alipay' | 'unknown' ``` #### useWindowInfo(useCache?) 获取窗口信息 ```javascript // 使用缓存(默认) const windowInfo = useWindowInfo(); // 强制刷新 const windowInfo = useWindowInfo(false); ``` #### getTopBarMetrics() ⭐ 推荐 获取顶部区域高度的结构化数据 ```javascript const metrics = getTopBarMetrics(); console.log(metrics.statusBarHeight); // 状态栏高度 console.log(metrics.navigationBarHeight); // 导航栏高度(不含状态栏) console.log(metrics.totalTopHeight); // 总高度 console.log(metrics.platform); // 当前平台 ``` #### getStatusBarHeight() 获取状态栏高度 ```javascript const height = getStatusBarHeight(); // 返回状态栏高度(px) ``` #### getNavigationBarHeight() 获取导航栏高度(不含状态栏) ```javascript const height = getNavigationBarHeight(); // 返回导航栏高度(px) ``` #### getNavHeight() ⚠️ 已废弃 > **建议使用**: `getTopBarMetrics().totalTopHeight` ```javascript const height = getNavHeight(); // 返回状态栏+导航栏总高度 ``` #### clearSystemCache() 清除系统信息缓存(横竖屏切换时可调用) ```javascript clearSystemCache(); ``` ### 📤 文件上传功能 > **重要变更**: v5.0.0 扁平化 UploadOptions,移除历史兼容层与多层嵌套配置 #### selectAndUpload(options) ⭐ 核心API 选择并上传文件(一体化业务入口) **参数 UploadOptions(v5):** - `url` (string, 必须): 上传地址 - `files` (UniFile[]): 直接上传已有文件(跳过选择阶段) - `type` ('image' | 'file' | 'any'): 文件类型(选择阶段),默认 'image' - `count` (number): 最多选择文件数,默认 1 - `maxSizeMB` (number): 文件体积限制(MB),`0` 表示不允许选择/上传任何文件 - `extensions` (string[]): 允许的文件扩展名白名单(严格模式),如 ['jpg', 'png'] - `fieldName` (string): 文件字段名,默认 'file' - `formData` (Record<string, any>): 额外的表单数据 - `headers` (Record<string, string>): 自定义请求头 - `timeoutMs` (number): 上传超时时间(ms) - `autoRevokeObjectURL` (boolean): H5环境下是否自动回收 blob URL - `concurrency` (number): 最大并发上传数(不传默认全并发) - `signal` (AbortSignal): 取消信号(AbortController.signal) - `beforeUpload` (function): 上传前拦截钩子,返回 false 跳过该文件 - `onProgress` (function): 进度回调 (file, progress) => void - `showToast` (boolean): 是否显示提示,默认 true - `successMessage` (string): 成功提示文本(单文件成功时) - `failMessage` (string): 失败提示文本(保留字段) 说明:普通文件选择会优先使用平台支持的 `chooseFile` 能力,`extensions` 会在选择器能力允许时前置过滤,并在上传前统一二次校验;所有失败场景都返回结构化 `UploadResult[]`。 **返回值**: `Promise<UploadResult[]>` - `file` (UniFile | null): 文件信息 - `success` (boolean): 是否成功 - `statusCode` (number): HTTP状态码 - `data` (unknown): 服务器返回数据 - `message` (string): 提示信息 ```javascript // 选择并上传图片 const results = await selectAndUpload({ url: 'https://api.example.com/upload', type: 'image', count: 3, maxSizeMB: 5, autoRevokeObjectURL: true, formData: { userId: '123' }, headers: { 'Authorization': 'Bearer token' }, onProgress: (file, progress) => { console.log(`${file.name}: ${progress}%`); } }); results.forEach((result) => { if (result.success) { console.log('上传成功:', result.data); } else { console.log('上传失败:', result.message); } }); ``` #### selectAndUploadImage(options) 选择并上传图片的便捷方法(等价于 type: 'image') ```javascript const results = await selectAndUploadImage({ url: 'https://api.example.com/upload', count: 1, maxSizeMB: 5 }); ``` #### 高级用法示例 ```javascript // 1. 使用并发控制 const concurrencyResults = await selectAndUpload({ url: 'https://api.example.com/upload', count: 10, concurrency: 3 // 每次最多同时上传3个文件 }); // 2. 使用上传前拦截 const checkedResults = await selectAndUpload({ url: 'https://api.example.com/upload', beforeUpload: async (file) => { // 可以在这里做自定义校验 if (file.size > 10 * 1024 * 1024) { console.warn('文件太大:', file.name); return false; // 跳过该文件 } return true; // 继续上传 } }); // 3. 指定文件扩展名 const documentResults = await selectAndUpload({ url: 'https://api.example.com/upload', type: 'file', extensions: ['pdf', 'doc', 'docx'], maxSizeMB: 20 }); // 4. 取消上传 const controller = new AbortController(); const uploadTask = selectAndUpload({ url: 'https://api.example.com/upload', signal: controller.signal }); // 用户点击取消时调用 controller.abort(); const canceledResults = await uploadTask; const failed = canceledResults.find((item) => !item.success); if (failed) { console.warn(failed.message); } ``` ### 💳 支付(微信公众号 H5) #### wechatH5Pay(config, options?) 在微信内置浏览器中调起支付,返回结构化的支付结果 **参数**: - `config` (WeChatPayConfig): 微信支付配置对象 - `options` (WeChatPayOptions): 可选配置 - `reportError` (boolean): 是否上报错误到 ErrorHandler,默认 true **返回值**: `Promise<PaymentResult>` - `success` (boolean): 是否支付成功 - `status` ('success' | 'error' | 'cancel'): 支付状态 - `code` (string): 状态码 - `message` (string): 状态描述 - `raw` (WeChatPayResult | null): 微信原始回调数据 ```javascript import { wechatH5Pay } from 'my-uniapp-tools/payment'; // 从服务端获取签名后的支付参数 const payConfig = await fetch('/api/pay/wechat/unified-order', { method: 'POST', body: JSON.stringify({ orderId: '123456' }) }).then(r => r.json()); // 调用支付(结构化返回值) const result = await wechatH5Pay(payConfig); // 根据支付结果处理 if (result.success) { uni.showToast({ title: '支付成功', icon: 'success' }); // 处理支付成功逻辑 } else if (result.status === 'cancel') { uni.showToast({ title: '已取消支付', icon: 'none' }); } else { uni.showToast({ title: result.message || '支付失败', icon: 'none' }); } // 测试时禁用错误上报 const result = await wechatH5Pay(payConfig, { reportError: false }); ``` ### 🛠️ 工具函数 #### deepClone(obj) 深拷贝对象(使用 structuredClone 标准API) ```javascript const original = { data: [1, 2, 3], date: new Date(), map: new Map() }; const cloned = deepClone(original); ``` #### deepMerge(target, source) 深度合并对象 ```javascript const target = { a: 1, b: { c: 2 } }; const source = { b: { d: 3 }, e: 4 }; const merged = deepMerge(target, source); // 结果: { a: 1, b: { c: 2, d: 3 }, e: 4 } ``` #### debounce(func, wait, immediate?) 防抖函数 ```javascript const debouncedFn = debounce(() => { console.log('执行'); }, 1000); // 取消防抖 debouncedFn.cancel(); ``` #### throttle(func, wait, options?) 节流函数 ```javascript const throttledFn = throttle(() => { console.log('执行'); }, 1000, { leading: true, // 首次立即执行 trailing: true // 结束后执行 }); // 取消节流 throttledFn.cancel(); ``` ## 🎨 使用示例 ### 完整示例 ```javascript import { copyText } from 'my-uniapp-tools/clipboard'; import { getStorageSync, setStorageSync } from 'my-uniapp-tools/localStorage'; import { useBuildUrl } from 'my-uniapp-tools/navigation'; import { useToast } from 'my-uniapp-tools/ui'; import { debounce } from 'my-uniapp-tools/utils'; // 页面中使用 export default { data() { return { userInfo: {} }; }, onLoad() { // 获取用户信息 this.userInfo = getStorageSync('userInfo', {}); }, methods: { // 防抖搜索 onSearch: debounce(function(keyword) { // 执行搜索 }, 500), // 复制分享链接 async onShare() { await copyText('https://example.com/share'); }, // 跳转详情页(示例:使用 useBuildUrl + uni.navigateTo) async goToDetail(id) { const url = useBuildUrl('/pages/detail/detail', { id }); uni.navigateTo({ url }); } } }; ``` ### 错误处理示例 ```javascript import { ErrorHandler } from 'my-uniapp-tools/core'; // 全局错误监听 const errorHandler = ErrorHandler.getInstance(); errorHandler.onError((error) => { // 上报错误 uni.request({ url: 'https://api.example.com/error-report', method: 'POST', data: { module: error.module, code: error.code, message: error.message, timestamp: error.timestamp } }); }); ``` ## 📊 结构优化 ### v3.0.x 优化方向 | 优化项 | 调整前 | 调整后 | 结果 | |------|--------|--------|------| | system模块 | 分散获取系统信息 | 统一入口和缓存 | 结构更清晰 | | 缓存机制 | 复杂类封装 | 简单模块变量 | 行为更直接 | | upload模块 | 分散API | 统一入口 | 调用更稳定 | | 深拷贝算法 | 自实现 | 优先 structuredClone | 使用平台标准能力 | ### 核心优化原则 - ✅ **好品味**: 消除特殊情况,而不是重复它 - ✅ **简洁执念**: 复杂度是万恶之源 - ✅ **向后兼容**: Never break userspace - ✅ **实用主义**: 解决实际问题,不是假想的威胁 ## 🔧 配置选项 ### 存储选项 ```javascript { ttl: number // 过期时间(毫秒) } ``` ### 导航配置 ```javascript configureNavigation({ defaultHomePage: '/pages/index/index' }); ``` ## 🐛 常见问题 ### Q: 存储的数据会自动过期吗? A: 是的,设置了TTL的数据会自动过期,可以调用 `cleanExpiredStorage()` 手动清理 ### Q: 支持哪些平台? A: 支持 uni-app 的所有平台:H5、App、微信小程序、支付宝小程序等 ### Q: 如何处理导航失败? A: 本仓库未直接提供 `safeNavigateTo` 包装函数;建议调用 `uni.navigateTo`/`uni.redirectTo` 等原生 API,或在应用层实现带重试/去重/错误处理的自定义封装(例如 `useSafeNavigateTo`),以满足项目特定需求。 ## 📄 更新日志 ### v5.0.2 (当前版本) - ✨ **上传能力**: `selectAndUpload()` 使用扁平化 `UploadOptions`,支持直接传入 `files`、体积限制、扩展名限制、进度回调与取消信号。 - 🐛 **边界修复**: 修复扩展名解析、`maxSizeMB=0`、Node 环境安全失败等边界行为。 - 🔧 **发布质量**: 构建前清理 `dist`,发布包包含完整声明文件,并增加真实消费验证。 ### v3.0.2 - ✨ **新增**: `getTopBarMetrics()` 返回结构化的导航栏高度信息 - ✨ **新增**: `getNavigationBarHeight()` 获取导航栏高度(不含状态栏) - ✨ **新增**: `clearSystemCache()` 清除系统信息缓存 - ✨ **新增**: `selectAndUpload()` 全新的文件上传统一入口 - ✨ **新增**: `selectAndUploadImage()` 图片上传便捷方法 - 🚀 **优化**: system模块删除过度设计的缓存机制 - 🚀 **优化**: 统一导航栏高度API,避免重复计算 - 🚀 **优化**: 消除重复的平台判断,提取 `isMiniProgram()` 辅助函数 - ⚠️ **破坏性变更**: 删除旧的 `chooseFile`/`uploadFile`/`chooseAndUploadFile` 等API - ⚠️ **废弃**: `getNavHeight()` 和 `getTopNavBarHeight()` 标记为废弃,建议使用 `getTopBarMetrics()` ### v2.0.1 - 🐛 修复已废弃的 `uni.getSystemInfoSync` API - 🚀 本地存储功能大幅增强 - 🚀 导航模块优化 - 📝 完善TypeScript类型定义 ## 📜 许可证 MIT License ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! --- **注意**: 本工具库专为 uni-app 开发优化,在其他环境中可能无法正常工作。