| import type { RcFile } from 'antd/es/upload'; |
| |
| // 将文件转换为Base64 |
| export const getBase64 = (file: RcFile): Promise<string> => |
| new Promise((resolve, reject) => { |
| const reader = new FileReader(); |
| reader.readAsDataURL(file); |
| reader.onload = () => resolve(reader.result as string); |
| reader.onerror = (error) => reject(error); |
| }); |
| |
| // 验证图片文件 |
| interface ValidationResult { |
| valid: boolean; |
| message?: string; |
| } |
| |
| export const validateImageFile = (file: RcFile): ValidationResult => { |
| const isImage = file.type.startsWith('image/'); |
| if (!isImage) { |
| return { valid: false, message: '只能上传图片文件!' }; |
| } |
| |
| const isLt5M = file.size / 1024 / 1024 < 5; |
| if (!isLt5M) { |
| return { valid: false, message: '图片大小不能超过 5MB!' }; |
| } |
| |
| return { valid: true }; |
| }; |
| |
| // 验证种子文件 |
| export const validateSeedFile = (file: RcFile): ValidationResult => { |
| const isLt100M = file.size / 1024 / 1024 < 100; |
| if (!isLt100M) { |
| return { valid: false, message: '种子文件大小不能超过 100MB!' }; |
| } |
| |
| return { valid: true }; |
| }; |
| |
| // 格式化文件大小 |
| export const formatFileSize = (bytes: number): string => { |
| 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]; |
| }; |
| |
| // 防抖函数 - 修复版本 |
| export const debounce = <T extends (...args: unknown[]) => unknown>( |
| func: T, |
| wait: number |
| ): ((...args: Parameters<T>) => void) => { |
| let timeout: ReturnType<typeof setTimeout>; |
| |
| return (...args: Parameters<T>) => { |
| clearTimeout(timeout); |
| timeout = setTimeout(() => func(...args), wait); |
| }; |
| }; |
| |
| // 节流函数 - 修复版本 |
| export const throttle = <T extends (...args: unknown[]) => unknown>( |
| func: T, |
| limit: number |
| ): ((...args: Parameters<T>) => void) => { |
| let inThrottle: boolean; |
| |
| return (...args: Parameters<T>) => { |
| if (!inThrottle) { |
| func(...args); |
| inThrottle = true; |
| setTimeout(() => inThrottle = false, limit); |
| } |
| }; |
| }; |
| // 生成唯一ID |
| export const generateId = (): string => { |
| return Date.now().toString(36) + Math.random().toString(36).substr(2); |
| }; |
| |
| // 安全的JSON解析 |
| export const safeJsonParse = <T>(str: string, defaultValue: T): T => { |
| try { |
| return JSON.parse(str) as T; |
| } catch { |
| return defaultValue; |
| } |
| }; |
| |
| // 检查是否为空值 |
| export const isEmpty = (value: unknown): boolean => { |
| if (value === null || value === undefined) return true; |
| if (typeof value === 'string') return value.trim() === ''; |
| if (Array.isArray(value)) return value.length === 0; |
| if (typeof value === 'object') return Object.keys(value).length === 0; |
| return false; |
| }; |
| |
| // URL参数解析 |
| export const parseUrlParams = (url: string): Record<string, string> => { |
| const params: Record<string, string> = {}; |
| const urlObj = new URL(url); |
| |
| urlObj.searchParams.forEach((value, key) => { |
| params[key] = value; |
| }); |
| |
| return params; |
| }; |
| |
| // 时间格式化 |
| export const formatDate = (date: Date | string | number, format: string = 'YYYY-MM-DD HH:mm:ss'): string => { |
| const d = new Date(date); |
| |
| 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.toString()) |
| .replace('MM', month) |
| .replace('DD', day) |
| .replace('HH', hours) |
| .replace('mm', minutes) |
| .replace('ss', seconds); |
| }; |
| |
| // 相对时间格式化(例如:2小时前) |
| export const formatRelativeTime = (date: Date | string | number): string => { |
| const now = new Date(); |
| const target = new Date(date); |
| const diff = now.getTime() - target.getTime(); |
| |
| const seconds = Math.floor(diff / 1000); |
| const minutes = Math.floor(seconds / 60); |
| const hours = Math.floor(minutes / 60); |
| const days = Math.floor(hours / 24); |
| const months = Math.floor(days / 30); |
| const years = Math.floor(months / 12); |
| |
| if (years > 0) return `${years}年前`; |
| if (months > 0) return `${months}个月前`; |
| if (days > 0) return `${days}天前`; |
| if (hours > 0) return `${hours}小时前`; |
| if (minutes > 0) return `${minutes}分钟前`; |
| return '刚刚'; |
| }; |