Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

262 lines
6.6 KiB
JavaScript

/**
* 通用工具函数
*/
/**
* 格式化时间
* @param {Date|string|number} timestamp 时间戳或日期字符串
* @param {string} format 格式,默认:yyyy-MM-dd HH:mm:ss
* @returns {string} 格式化后的时间字符串
*/
export const formatTime = (timestamp, format = 'yyyy-MM-dd HH:mm:ss') => {
if (!timestamp) return ''
const date = new Date(timestamp)
if (isNaN(date.getTime())) return ''
const map = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'H+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
'S': date.getMilliseconds()
}
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
}
for (const k in map) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1,
RegExp.$1.length === 1 ? map[k] : ('00' + map[k]).substr(('' + map[k]).length))
}
}
return format
}
/**
* 格式化相对时间(如:3分钟前)
* @param {Date|string|number} timestamp 时间戳或日期字符串
* @returns {string} 相对时间字符串
*/
export const formatRelativeTime = (timestamp) => {
if (!timestamp) return ''
const date = new Date(timestamp)
if (isNaN(date.getTime())) return ''
const now = new Date()
const diff = now.getTime() - date.getTime()
const minute = 60 * 1000
const hour = 60 * minute
const day = 24 * hour
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 < month) {
return `${Math.floor(diff / day)}天前`
} else if (diff < year) {
return `${Math.floor(diff / month)}个月前`
} else {
return `${Math.floor(diff / year)}年前`
}
}
/**
* 防抖函数
* @param {Function} func 要执行的函数
* @param {number} wait 等待时间(毫秒)
* @param {boolean} immediate 是否立即执行
* @returns {Function} 防抖后的函数
*/
export const debounce = (func, wait = 300, immediate = false) => {
let timeout
return function executedFunction(...args) {
const context = this
const later = () => {
timeout = null
if (!immediate) func.apply(context, args)
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(context, args)
}
}
/**
* 节流函数
* @param {Function} func 要执行的函数
* @param {number} limit 限制时间(毫秒)
* @returns {Function} 节流后的函数
*/
export const throttle = (func, limit = 300) => {
let inThrottle
let lastFunc
let lastRan
return function executedFunction(...args) {
const context = this
if (!inThrottle) {
func.apply(context, args)
lastRan = Date.now()
inThrottle = true
} else {
clearTimeout(lastFunc)
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(context, args)
lastRan = Date.now()
}
}, limit - (Date.now() - lastRan))
}
}
}
/**
* 深度克隆对象
* @param {any} obj 要克隆的对象
* @returns {any} 克隆后的对象
*/
export const deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj.getTime())
if (obj instanceof RegExp) return new RegExp(obj)
if (Array.isArray(obj)) {
const arrCopy = []
for (let i = 0; i < obj.length; i++) {
arrCopy[i] = deepClone(obj[i])
}
return arrCopy
}
if (typeof obj === 'object') {
const objCopy = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key])
}
}
return objCopy
}
return obj
}
/**
* 生成随机ID
* @param {number} length ID长度
* @returns {string} 随机ID
*/
export const generateId = (length = 8) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
}
return result
}
/**
* 检查是否为空(null、undefined、空字符串、空数组、空对象)
* @param {any} value 要检查的值
* @returns {boolean} 是否为空
*/
export const isEmpty = (value) => {
if (value === null || value === undefined) return true
if (typeof value === 'string' && value.trim() === '') return true
if (Array.isArray(value) && value.length === 0) return true
if (typeof value === 'object' && Object.keys(value).length === 0) return true
return false
}
/**
* 安全获取嵌套对象属性
* @param {Object} obj 对象
* @param {string} path 属性路径,如:'a.b.c'
* @param {any} defaultValue 默认值
* @returns {any} 属性值或默认值
*/
export const getSafe = (obj, path, defaultValue = null) => {
if (!obj || typeof obj !== 'object') return defaultValue
const keys = path.split('.')
let result = obj
for (const key of keys) {
if (result === null || result === undefined || typeof result !== 'object') {
return defaultValue
}
result = result[key]
}
return result === undefined ? defaultValue : result
}
/**
* 格式化文件大小
* @param {number} bytes 字节数
* @returns {string} 格式化后的文件大小
*/
export 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} text 要复制的文本
* @returns {Promise} 复制结果
*/
export const copyToClipboard = (text) => {
return new Promise((resolve, reject) => {
if (uni.setClipboardData) {
uni.setClipboardData({
data: text,
success: () => resolve(true),
fail: (err) => reject(err)
})
} else {
// 兼容H5环境
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.position = 'fixed'
textarea.style.opacity = '0'
document.body.appendChild(textarea)
textarea.select()
try {
const successful = document.execCommand('copy')
document.body.removeChild(textarea)
successful ? resolve(true) : reject(new Error('复制失败'))
} catch (err) {
document.body.removeChild(textarea)
reject(err)
}
}
})
}