This commit is contained in:
ChuXun
2025-10-19 20:28:31 +08:00
parent c81f8a8b03
commit eaab9a762a
100 changed files with 23416 additions and 0 deletions

302
utils/util.js Normal file
View File

@@ -0,0 +1,302 @@
// 工具函数库
/**
* 格式化时间
*/
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-560分以下 = 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
}