/** * 通用工具函数 */ /** * 防抖函数 * @param {Function} func 要防抖的函数 * @param {number} wait 等待时间 * @param {boolean} immediate 是否立即执行 * @returns {Function} */ export function debounce(func, wait, immediate) { let timeout return function executedFunction(...args) { const later = () => { timeout = null if (!immediate) func(...args) } const callNow = immediate && !timeout clearTimeout(timeout) timeout = setTimeout(later, wait) if (callNow) func(...args) } } /** * 节流函数 * @param {Function} func 要节流的函数 * @param {number} limit 时间间隔 * @returns {Function} */ export function throttle(func, limit) { let inThrottle return function(...args) { if (!inThrottle) { func.apply(this, args) inThrottle = true setTimeout(() => inThrottle = false, limit) } } } /** * 深拷贝 * @param {*} obj 要拷贝的对象 * @returns {*} */ export function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj if (obj instanceof Date) return new Date(obj.getTime()) if (obj instanceof Array) return obj.map(item => deepClone(item)) if (typeof obj === 'object') { const clonedObj = {} for (const key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]) } } return clonedObj } } /** * 格式化日期 * @param {Date|string|number} date 日期 * @param {string} format 格式化字符串 * @returns {string} */ export function 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) } /** * 生成唯一ID * @returns {string} */ export function generateId() { return Date.now().toString(36) + Math.random().toString(36).substr(2) } /** * 验证邮箱格式 * @param {string} email 邮箱地址 * @returns {boolean} */ export function validateEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ return re.test(email) } /** * 验证手机号格式 * @param {string} phone 手机号 * @returns {boolean} */ export function validatePhone(phone) { const re = /^1[3-9]\d{9}$/ return re.test(phone) } /** * 获取文件扩展名 * @param {string} filename 文件名 * @returns {string} */ export function getFileExtension(filename) { return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2) } /** * 格式化文件大小 * @param {number} bytes 字节数 * @returns {string} */ export function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes' const k = 1024 const sizes = ['Bytes', '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 {Array} tree 树形数据 * @param {string} childrenKey 子节点键名 * @returns {Array} */ export function flattenTree(tree, childrenKey = 'children') { const result = [] function traverse(nodes, parent = null) { nodes.forEach(node => { const item = { ...node, parent } delete item[childrenKey] result.push(item) if (node[childrenKey] && node[childrenKey].length > 0) { traverse(node[childrenKey], node) } }) } traverse(tree) return result } /** * 数组转树形结构 * @param {Array} list 扁平数组 * @param {string} idKey ID键名 * @param {string} parentIdKey 父ID键名 * @param {string} childrenKey 子节点键名 * @returns {Array} */ export function arrayToTree(list, idKey = 'id', parentIdKey = 'parentId', childrenKey = 'children') { const map = {} const roots = [] // 创建映射 list.forEach(item => { map[item[idKey]] = { ...item, [childrenKey]: [] } }) // 构建树形结构 list.forEach(item => { const node = map[item[idKey]] if (item[parentIdKey] && map[item[parentIdKey]]) { map[item[parentIdKey]][childrenKey].push(node) } else { roots.push(node) } }) return roots }