feat: code refactor
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* 通用工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 格式化时间戳为可读格式
|
||||
* @param timestamp 时间戳字符串
|
||||
* @param format 格式类型
|
||||
* @returns 格式化后的时间字符串
|
||||
*/
|
||||
export function formatTimestamp(timestamp: string, format: 'datetime' | 'date' | 'time' = 'datetime'): string {
|
||||
const date = new Date(timestamp)
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hours = date.getHours().toString().padStart(2, '0')
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0')
|
||||
|
||||
switch (format) {
|
||||
case 'date':
|
||||
return `${year}-${month}-${day}`
|
||||
case 'time':
|
||||
return `${hours}:${minutes}:${seconds}`
|
||||
case 'datetime':
|
||||
default:
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param bytes 字节数
|
||||
* @param decimals 小数位数
|
||||
* @returns 格式化后的文件大小字符串
|
||||
*/
|
||||
export function formatFileSize(bytes: number, decimals: number = 2): string {
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
|
||||
const k = 1024
|
||||
const dm = decimals < 0 ? 0 : decimals
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化持续时间
|
||||
* @param seconds 秒数
|
||||
* @returns 格式化后的持续时间字符串
|
||||
*/
|
||||
export function formatDuration(seconds: number): string {
|
||||
if (seconds === 0) return '永久'
|
||||
|
||||
const units = [
|
||||
{ name: '天', value: 86400 },
|
||||
{ name: '小时', value: 3600 },
|
||||
{ name: '分钟', value: 60 },
|
||||
{ name: '秒', value: 1 }
|
||||
]
|
||||
|
||||
for (const unit of units) {
|
||||
if (seconds >= unit.value) {
|
||||
const value = Math.floor(seconds / unit.value)
|
||||
return `${value}${unit.name}`
|
||||
}
|
||||
}
|
||||
|
||||
return `${seconds}秒`
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文本到剪贴板
|
||||
* @param text 要复制的文本
|
||||
* @returns Promise<boolean> 是否复制成功
|
||||
*/
|
||||
export async function copyToClipboard(text: string): Promise<boolean> {
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
return true
|
||||
} else {
|
||||
// 降级方案
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = text
|
||||
textArea.style.position = 'fixed'
|
||||
textArea.style.left = '-999999px'
|
||||
textArea.style.top = '-999999px'
|
||||
document.body.appendChild(textArea)
|
||||
textArea.focus()
|
||||
textArea.select()
|
||||
const result = document.execCommand('copy')
|
||||
textArea.remove()
|
||||
return result
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 防抖函数
|
||||
* @param func 要防抖的函数
|
||||
* @param wait 等待时间(毫秒)
|
||||
* @returns 防抖后的函数
|
||||
*/
|
||||
export function debounce<T extends (...args: unknown[]) => unknown>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timeout: number | null = null
|
||||
|
||||
return (...args: Parameters<T>) => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
timeout = setTimeout(() => func(...args), wait)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节流函数
|
||||
* @param func 要节流的函数
|
||||
* @param limit 限制时间(毫秒)
|
||||
* @returns 节流后的函数
|
||||
*/
|
||||
export function throttle<T extends (...args: unknown[]) => unknown>(
|
||||
func: T,
|
||||
limit: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let inThrottle: boolean = false
|
||||
|
||||
return (...args: Parameters<T>) => {
|
||||
if (!inThrottle) {
|
||||
func(...args)
|
||||
inThrottle = true
|
||||
setTimeout(() => inThrottle = false, limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱格式
|
||||
* @param email 邮箱地址
|
||||
* @returns 是否为有效邮箱
|
||||
*/
|
||||
export function isValidEmail(email: string): boolean {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
return emailRegex.test(email)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证URL格式
|
||||
* @param url URL地址
|
||||
* @returns 是否为有效URL
|
||||
*/
|
||||
export function isValidUrl(url: string): boolean {
|
||||
try {
|
||||
new URL(url)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @param length 字符串长度
|
||||
* @param chars 可选字符集
|
||||
* @returns 随机字符串
|
||||
*/
|
||||
export function generateRandomString(
|
||||
length: number = 8,
|
||||
chars: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
): string {
|
||||
let result = ''
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 深度克隆对象
|
||||
* @param obj 要克隆的对象
|
||||
* @returns 克隆后的对象
|
||||
*/
|
||||
export function deepClone<T>(obj: T): T {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as T
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item)) as T
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const clonedObj = {} as T
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
clonedObj[key] = deepClone(obj[key])
|
||||
}
|
||||
}
|
||||
return clonedObj
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
* @param filename 文件名
|
||||
* @returns 文件扩展名(不包含点)
|
||||
*/
|
||||
export function getFileExtension(filename: string): string {
|
||||
return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为移动设备
|
||||
* @returns 是否为移动设备
|
||||
*/
|
||||
export function isMobile(): boolean {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数字,添加千分位分隔符
|
||||
* @param num 数字
|
||||
* @returns 格式化后的数字字符串
|
||||
*/
|
||||
export function formatNumber(num: number): string {
|
||||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
Reference in New Issue
Block a user