Files
ZhiQiXiaoYuan/utils/util.js
ChuXun eaab9a762a 1
2025-10-19 20:28:31 +08:00

303 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 工具函数库
/**
* 格式化时间
*/
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
}