303 lines
6.2 KiB
JavaScript
303 lines
6.2 KiB
JavaScript
// 工具函数库
|
||
|
||
/**
|
||
* 格式化时间
|
||
*/
|
||
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
|
||
}
|