// 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,查看完整运行演示。 */