mirror of
https://gitee.com/qingqing-coconut-tea/RcKtFezyRAZX
synced 2026-07-21 10:32:32 +08:00
297 lines
6.7 KiB
JavaScript
297 lines
6.7 KiB
JavaScript
// utils/util.js
|
||
// 工具函数封装
|
||
|
||
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}`
|
||
}
|
||
|
||
/**
|
||
* 格式化日期
|
||
* @param {Date|string|number} date - 日期
|
||
* @param {string} format - 格式化模板
|
||
* @returns {string}
|
||
*/
|
||
const formatDate = (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 hours = String(d.getHours()).padStart(2, '0')
|
||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||
|
||
return format
|
||
.replace('YYYY', year)
|
||
.replace('MM', month)
|
||
.replace('DD', day)
|
||
.replace('HH', hours)
|
||
.replace('mm', minutes)
|
||
.replace('ss', seconds)
|
||
}
|
||
|
||
/**
|
||
* 相对时间格式化
|
||
* @param {Date|string|number} date - 日期
|
||
* @returns {string}
|
||
*/
|
||
const formatRelativeTime = (date) => {
|
||
if (!date) return ''
|
||
|
||
const now = Date.now()
|
||
const d = new Date(date).getTime()
|
||
const diff = now - d
|
||
|
||
const minute = 60 * 1000
|
||
const hour = 60 * minute
|
||
const day = 24 * hour
|
||
const week = 7 * day
|
||
const month = 30 * day
|
||
const year = 365 * day
|
||
|
||
if (diff < minute) {
|
||
return '刚刚'
|
||
} else if (diff < hour) {
|
||
return Math.floor(diff / minute) + '分钟前'
|
||
} else if (diff < day) {
|
||
return Math.floor(diff / hour) + '小时前'
|
||
} else if (diff < week) {
|
||
return Math.floor(diff / day) + '天前'
|
||
} else if (diff < month) {
|
||
return Math.floor(diff / week) + '周前'
|
||
} else if (diff < year) {
|
||
return Math.floor(diff / month) + '月前'
|
||
} else {
|
||
return Math.floor(diff / year) + '年前'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 手机号脱敏
|
||
* @param {string} phone - 手机号
|
||
* @returns {string}
|
||
*/
|
||
const maskPhone = (phone) => {
|
||
if (!phone || phone.length !== 11) return phone
|
||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||
}
|
||
|
||
/**
|
||
* 邮箱脱敏
|
||
* @param {string} email - 邮箱
|
||
* @returns {string}
|
||
*/
|
||
const maskEmail = (email) => {
|
||
if (!email || !email.includes('@')) return email
|
||
const [username, domain] = email.split('@')
|
||
if (username.length <= 3) {
|
||
return `***@${domain}`
|
||
}
|
||
return username.substring(0, 3) + '***@' + domain
|
||
}
|
||
|
||
/**
|
||
* 生成随机ID
|
||
* @param {number} length - 长度
|
||
* @returns {string}
|
||
*/
|
||
const generateId = (length = 16) => {
|
||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||
let result = ''
|
||
for (let i = 0; i < length; i++) {
|
||
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
||
}
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* 防抖函数
|
||
* @param {Function} func - 执行函数
|
||
* @param {number} wait - 等待时间(ms)
|
||
* @returns {Function}
|
||
*/
|
||
const debounce = (func, wait = 300) => {
|
||
let timeout
|
||
return function (...args) {
|
||
clearTimeout(timeout)
|
||
timeout = setTimeout(() => {
|
||
func.apply(this, args)
|
||
}, wait)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 节流函数
|
||
* @param {Function} func - 执行函数
|
||
* @param {number} wait - 间隔时间(ms)
|
||
* @returns {Function}
|
||
*/
|
||
const throttle = (func, wait = 300) => {
|
||
let timeout
|
||
return function (...args) {
|
||
if (!timeout) {
|
||
timeout = setTimeout(() => {
|
||
timeout = null
|
||
func.apply(this, args)
|
||
}, wait)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 深拷贝
|
||
* @param {any} obj - 对象
|
||
* @returns {any}
|
||
*/
|
||
const deepClone = (obj) => {
|
||
if (obj === null || typeof obj !== 'object') return obj
|
||
|
||
if (Array.isArray(obj)) {
|
||
return obj.map(item => deepClone(item))
|
||
}
|
||
|
||
const clone = {}
|
||
for (const key in obj) {
|
||
if (obj.hasOwnProperty(key)) {
|
||
clone[key] = deepClone(obj[key])
|
||
}
|
||
}
|
||
return clone
|
||
}
|
||
|
||
/**
|
||
* URL参数解析
|
||
* @param {string} url - URL
|
||
* @returns {object}
|
||
*/
|
||
const parseQuery = (url) => {
|
||
const query = {}
|
||
const index = url.indexOf('?')
|
||
if (index !== -1) {
|
||
const params = url.substring(index + 1).split('&')
|
||
params.forEach(param => {
|
||
const [key, value] = param.split('=')
|
||
query[decodeURIComponent(key)] = decodeURIComponent(value || '')
|
||
})
|
||
}
|
||
return query
|
||
}
|
||
|
||
/**
|
||
* 对象转URL参数
|
||
* @param {object} obj - 对象
|
||
* @returns {string}
|
||
*/
|
||
const stringifyQuery = (obj) => {
|
||
const params = []
|
||
for (const key in obj) {
|
||
if (obj.hasOwnProperty(key) && obj[key] !== null && obj[key] !== undefined) {
|
||
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
|
||
}
|
||
}
|
||
return params.join('&')
|
||
}
|
||
|
||
/**
|
||
* 验证手机号
|
||
* @param {string} phone - 手机号
|
||
* @returns {boolean}
|
||
*/
|
||
const isValidPhone = (phone) => {
|
||
return /^1[3-9]\d{9}$/.test(phone)
|
||
}
|
||
|
||
/**
|
||
* 验证邮箱
|
||
* @param {string} email - 邮箱
|
||
* @returns {boolean}
|
||
*/
|
||
const isValidEmail = (email) => {
|
||
return /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/.test(email)
|
||
}
|
||
|
||
/**
|
||
* 验证URL
|
||
* @param {string} url - URL
|
||
* @returns {boolean}
|
||
*/
|
||
const isValidUrl = (url) => {
|
||
return /^https?:\/\/.+/.test(url)
|
||
}
|
||
|
||
/**
|
||
* 数字格式化(千分位)
|
||
* @param {number} num - 数字
|
||
* @returns {string}
|
||
*/
|
||
const formatNumber = (num) => {
|
||
if (num === null || num === undefined) return ''
|
||
return String(num).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||
}
|
||
|
||
/**
|
||
* 文件大小格式化
|
||
* @param {number} bytes - 字节
|
||
* @returns {string}
|
||
*/
|
||
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 parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||
}
|
||
|
||
/**
|
||
* 字符串截断
|
||
* @param {string} str - 字符串
|
||
* @param {number} length - 长度
|
||
* @param {string} suffix - 后缀
|
||
* @returns {string}
|
||
*/
|
||
const truncate = (str, length = 50, suffix = '...') => {
|
||
if (!str || str.length <= length) return str
|
||
return str.substring(0, length) + suffix
|
||
}
|
||
|
||
module.exports = {
|
||
formatTime,
|
||
formatDate,
|
||
formatRelativeTime,
|
||
maskPhone,
|
||
maskEmail,
|
||
generateId,
|
||
debounce,
|
||
throttle,
|
||
deepClone,
|
||
parseQuery,
|
||
stringifyQuery,
|
||
isValidPhone,
|
||
isValidEmail,
|
||
isValidUrl,
|
||
formatNumber,
|
||
formatFileSize,
|
||
truncate
|
||
}
|
||
|
||
/**
|
||
* @联系作者:16768118056
|
||
* @描述:(此项目非免费分享)源码百分百可用,搞定毕设不发愁,支持远程调试安装、二次开发、定制、讲解、文档类。
|
||
* @访问:https://www.notmaker.com/detail/8326a897119d4542896c863a69d7bbe7/gtt20260415,查看完整运行演示。
|
||
*/
|