/** * 通用工具函数 */ /** * 格式化时间 * @param {Date|String|Number} date 日期 * @param {String} format 格式化字符串 * @returns {String} 格式化后的时间字符串 */ function formatTime(date, format = 'YYYY-MM-DD HH:mm:ss') { if (!date) return ''; const d = new Date(date); if (isNaN(d.getTime())) return ''; const year = d.getFullYear(); const month = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const hour = String(d.getHours()).padStart(2, '0'); const minute = String(d.getMinutes()).padStart(2, '0'); const second = String(d.getSeconds()).padStart(2, '0'); return format .replace('YYYY', year) .replace('MM', month) .replace('DD', day) .replace('HH', hour) .replace('mm', minute) .replace('ss', second); } /** * 格式化相对时间 * @param {Date|String|Number} date 日期 * @returns {String} 相对时间字符串 */ function formatRelativeTime(date) { if (!date) return ''; const d = new Date(date); if (isNaN(d.getTime())) return ''; const now = new Date(); const diff = now.getTime() - d.getTime(); const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (seconds < 60) { return '刚刚'; } else if (minutes < 60) { return `${minutes}分钟前`; } else if (hours < 24) { return `${hours}小时前`; } else if (days < 7) { return `${days}天前`; } else { return formatTime(date, 'MM-DD'); } } /** * 防抖函数 * @param {Function} func 要防抖的函数 * @param {Number} delay 延迟时间 * @returns {Function} 防抖后的函数 */ function debounce(func, delay = 300) { let timer = null; return function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, delay); }; } /** * 节流函数 * @param {Function} func 要节流的函数 * @param {Number} delay 延迟时间 * @returns {Function} 节流后的函数 */ function throttle(func, delay = 300) { let timer = null; return function (...args) { if (!timer) { timer = setTimeout(() => { func.apply(this, args); timer = null; }, delay); } }; } /** * 深拷贝 * @param {Any} obj 要拷贝的对象 * @returns {Any} 拷贝后的对象 */ function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof Array) { return obj.map(item => deepClone(item)); } if (typeof obj === 'object') { const cloned = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key]); } } return cloned; } return obj; } /** * 生成唯一ID * @param {String} prefix 前缀 * @returns {String} 唯一ID */ function generateId(prefix = '') { const timestamp = Date.now().toString(36); const random = Math.random().toString(36).substr(2, 5); return `${prefix}${timestamp}${random}`; } /** * 验证手机号 * @param {String} phone 手机号 * @returns {Boolean} 是否有效 */ function validatePhone(phone) { const phoneRegex = /^1[3-9]\d{9}$/; return phoneRegex.test(phone); } /** * 验证邮箱 * @param {String} email 邮箱 * @returns {Boolean} 是否有效 */ function validateEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } /** * 验证身份证号 * @param {String} idCard 身份证号 * @returns {Boolean} 是否有效 */ function validateIdCard(idCard) { const idCardRegex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; return idCardRegex.test(idCard); } /** * 格式化金额 * @param {Number} amount 金额 * @param {Number} decimals 小数位数 * @returns {String} 格式化后的金额 */ function formatAmount(amount, decimals = 2) { if (isNaN(amount)) return '0.00'; return Number(amount).toFixed(decimals); } /** * 格式化文件大小 * @param {Number} bytes 字节数 * @returns {String} 格式化后的文件大小 */ function formatFileSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } /** * 获取URL参数 * @param {String} url URL地址 * @returns {Object} 参数对象 */ function getUrlParams(url) { const params = {}; const urlObj = new URL(url); for (const [key, value] of urlObj.searchParams) { params[key] = value; } return params; } /** * 构建URL参数 * @param {Object} params 参数对象 * @returns {String} 参数字符串 */ function buildUrlParams(params) { return Object.keys(params) .filter(key => params[key] !== undefined && params[key] !== null) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&'); } /** * 存储数据到本地 * @param {String} key 键名 * @param {Any} data 数据 * @param {Number} expire 过期时间(毫秒) */ function setStorage(key, data, expire = 0) { const item = { data, timestamp: Date.now(), expire }; try { wx.setStorageSync(key, JSON.stringify(item)); } catch (error) { console.error('Storage set error:', error); } } /** * 从本地获取数据 * @param {String} key 键名 * @returns {Any} 数据 */ function getStorage(key) { try { const item = wx.getStorageSync(key); if (!item) return null; const parsed = JSON.parse(item); const { data, timestamp, expire } = parsed; // 检查是否过期 if (expire > 0 && Date.now() - timestamp > expire) { wx.removeStorageSync(key); return null; } return data; } catch (error) { console.error('Storage get error:', error); return null; } } /** * 删除本地存储数据 * @param {String} key 键名 */ function removeStorage(key) { try { wx.removeStorageSync(key); } catch (error) { console.error('Storage remove error:', error); } } /** * 清空本地存储 */ function clearStorage() { try { wx.clearStorageSync(); } catch (error) { console.error('Storage clear error:', error); } } /** * 显示成功提示 * @param {String} title 提示内容 * @param {Number} duration 显示时长 */ function showSuccess(title, duration = 2000) { wx.showToast({ title, icon: 'success', duration }); } /** * 显示错误提示 * @param {String} title 提示内容 * @param {Number} duration 显示时长 */ function showError(title, duration = 2000) { wx.showToast({ title, icon: 'error', duration }); } /** * 显示普通提示 * @param {String} title 提示内容 * @param {Number} duration 显示时长 */ function showToast(title, duration = 2000) { wx.showToast({ title, icon: 'none', duration }); } /** * 显示加载提示 * @param {String} title 提示内容 */ function showLoading(title = '加载中...') { wx.showLoading({ title, mask: true }); } /** * 隐藏加载提示 */ function hideLoading() { wx.hideLoading(); } /** * 显示确认对话框 * @param {String} content 内容 * @param {String} title 标题 * @returns {Promise} 用户选择结果 */ function showConfirm(content, title = '提示') { return new Promise((resolve) => { wx.showModal({ title, content, success: (res) => { resolve(res.confirm); }, fail: () => { resolve(false); } }); }); } /** * 获取系统信息 * @returns {Object} 系统信息 */ function getSystemInfo() { try { return wx.getSystemInfoSync(); } catch (error) { console.error('Get system info error:', error); return {}; } } /** * 检查网络状态 * @returns {Promise} 网络状态 */ function getNetworkType() { return new Promise((resolve, reject) => { wx.getNetworkType({ success: resolve, fail: reject }); }); } /** * 复制到剪贴板 * @param {String} data 要复制的内容 * @returns {Promise} 复制结果 */ function copyToClipboard(data) { return new Promise((resolve) => { wx.setClipboardData({ data, success: () => { showSuccess('复制成功'); resolve(true); }, fail: () => { showError('复制失败'); resolve(false); } }); }); } /** * 预览图片 * @param {String} current 当前图片URL * @param {Array} urls 图片URL列表 */ function previewImage(current, urls = []) { wx.previewImage({ current, urls: urls.length > 0 ? urls : [current] }); } /** * 拨打电话 * @param {String} phoneNumber 电话号码 */ function makePhoneCall(phoneNumber) { wx.makePhoneCall({ phoneNumber, fail: () => { showError('拨打电话失败'); } }); } /** * 获取会员等级文本 * @param {String} level 会员等级 * @returns {String} 等级文本 */ function getMemberLevelText(level) { const levelMap = { 'free': '普通用户', 'vip': 'VIP会员', 'svip': 'SVIP会员' }; return levelMap[level] || '未知等级'; } /** * 获取会员等级颜色 * @param {String} level 会员等级 * @returns {String} 颜色值 */ function getMemberLevelColor(level) { const colorMap = { 'free': '#6C757D', 'vip': '#FFD700', 'svip': '#FF6B35' }; return colorMap[level] || '#6C757D'; } /** * 计算会员到期时间 * @param {String} expireTime 到期时间 * @returns {Object} 时间信息 */ function calculateMemberExpire(expireTime) { if (!expireTime) { return { isExpired: true, daysLeft: 0, text: '已过期' }; } const expire = new Date(expireTime); const now = new Date(); const diff = expire.getTime() - now.getTime(); const daysLeft = Math.ceil(diff / (1000 * 60 * 60 * 24)); if (daysLeft <= 0) { return { isExpired: true, daysLeft: 0, text: '已过期' }; } else if (daysLeft <= 7) { return { isExpired: false, daysLeft, text: `${daysLeft}天后到期` }; } else { return { isExpired: false, daysLeft, text: formatTime(expireTime, 'YYYY-MM-DD') + ' 到期' }; } } module.exports = { formatTime, formatRelativeTime, debounce, throttle, deepClone, generateId, validatePhone, validateEmail, validateIdCard, formatAmount, formatFileSize, getUrlParams, buildUrlParams, setStorage, getStorage, removeStorage, clearStorage, showSuccess, showError, showToast, showLoading, hideLoading, showConfirm, getSystemInfo, getNetworkType, copyToClipboard, previewImage, makePhoneCall, getMemberLevelText, getMemberLevelColor, calculateMemberExpire };