// 工具函数库 /** * 格式化时间 */ const formatTime = date => { const year = date.getFullYear() const month = date.getMonth() + 1 const day = date.getDate() const hour = date.getHours() const minute = date.getMinutes() const second = date.getSeconds() return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}` } const formatNumber = n => { n = n.toString() return n[1] ? n : `0${n}` } /** * 显示成功提示 */ const showSuccess = (title = '操作成功') => { wx.showToast({ title: title, icon: 'success', duration: 2000 }) } /** * 显示失败提示 */ const showError = (title = '操作失败') => { wx.showToast({ title: title, icon: 'none', duration: 2000 }) } /** * 显示加载中 */ const showLoading = (title = '加载中...') => { wx.showLoading({ title: title, mask: true }) } /** * 隐藏加载 */ const hideLoading = () => { wx.hideLoading() } /** * 计算GPA * @param {Array} courses 课程列表 [{score: 90, credit: 4}, ...] */ const calculateGPA = (courses) => { if (!courses || courses.length === 0) return 0; let totalPoints = 0; let totalCredits = 0; courses.forEach(course => { const gradePoint = scoreToGradePoint(course.score); totalPoints += gradePoint * course.credit; totalCredits += course.credit; }); return totalCredits > 0 ? (totalPoints / totalCredits).toFixed(2) : 0; } /** * 分数转绩点 * 公式:60分及以上 = 成绩/10-5,60分以下 = 0 */ const scoreToGradePoint = (score) => { if (score >= 60) { return parseFloat((score / 10 - 5).toFixed(2)); } return 0; } /** * 计算倒计时 */ const getCountdown = (targetDate) => { const now = new Date().getTime(); const target = new Date(targetDate).getTime(); const diff = target - now; if (diff <= 0) { return { days: 0, hours: 0, minutes: 0, seconds: 0, isExpired: true }; } const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((diff % (1000 * 60)) / 1000); return { days, hours, minutes, seconds, isExpired: false }; } /** * 日期格式化 */ const formatDate = (date, format = 'YYYY-MM-DD') => { const d = new Date(date) 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) } /** * 相对时间 */ const formatRelativeTime = (timestamp) => { const now = Date.now() const diff = now - new Date(timestamp).getTime() const minute = 60 * 1000 const hour = 60 * minute const day = 24 * hour const week = 7 * day const month = 30 * day if (diff < minute) return '刚刚' if (diff < hour) return `${Math.floor(diff / minute)}分钟前` if (diff < day) return `${Math.floor(diff / hour)}小时前` if (diff < week) return `${Math.floor(diff / day)}天前` if (diff < month) return `${Math.floor(diff / week)}周前` return formatDate(timestamp, 'YYYY-MM-DD') } /** * 文件大小格式化 */ const 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 (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i] } /** * 验证手机号 */ const validatePhone = (phone) => { return /^1[3-9]\d{9}$/.test(phone) } /** * 验证邮箱 */ const validateEmail = (email) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) } /** * 验证学号(示例:8位数字) */ const validateStudentId = (id) => { return /^\d{8,10}$/.test(id) } /** * 深拷贝 */ const deepClone = (obj) => { if (obj === null || typeof obj !== 'object') return obj if (obj instanceof Date) return new Date(obj) if (obj instanceof Array) return obj.map(item => deepClone(item)) const clonedObj = {} for (const key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]) } } return clonedObj } /** * 数组去重 */ const unique = (arr, key) => { if (!key) return [...new Set(arr)] const seen = new Set() return arr.filter(item => { const val = item[key] if (seen.has(val)) return false seen.add(val) return true }) } /** * 数组分组 */ const groupBy = (arr, key) => { return arr.reduce((result, item) => { const group = typeof key === 'function' ? key(item) : item[key] if (!result[group]) result[group] = [] result[group].push(item) return result }, {}) } /** * 生成UUID */ const generateUUID = () => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0 const v = c === 'x' ? r : (r & 0x3 | 0x8) return v.toString(16) }) } /** * 节流 */ const throttle = (fn, delay = 300) => { let timer = null return function(...args) { if (timer) return timer = setTimeout(() => { fn.apply(this, args) timer = null }, delay) } } /** * 防抖 */ const debounce = (fn, delay = 300) => { let timer = null return function(...args) { clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, args) }, delay) } } /** * 分享配置 */ const getShareConfig = (title, imageUrl = '') => { return { title: title || '知芽小筑 - 学习辅助小程序', path: '/pages/index/index', imageUrl: imageUrl || '/images/share.png' } } module.exports = { formatTime, formatDate, formatRelativeTime, formatFileSize, formatNumber, showSuccess, showError, showLoading, hideLoading, calculateGPA, scoreToGradePoint, getCountdown, validatePhone, validateEmail, validateStudentId, deepClone, unique, groupBy, generateUUID, throttle, debounce, getShareConfig }