feat: code refactor

This commit is contained in:
Lan
2025-09-04 14:50:53 +08:00
parent 48d8393a14
commit 0537088b9d
14 changed files with 1156 additions and 762 deletions
+241
View File
@@ -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, ',')
}