import { clsx, type ClassValue } from 'clsx'; /** * 合并 className */ export function cn(...inputs: ClassValue[]): string { return clsx(inputs); } /** * 延迟函数 */ export function delay(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * 防抖函数 */ export function debounce) => ReturnType>( func: T, wait: number ): (...args: Parameters) => void { let timeoutId: ReturnType | null = null; return function (this: ThisParameterType, ...args: Parameters) { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { func.apply(this, args); }, wait); }; } /** * 节流函数 */ export function throttle) => ReturnType>( func: T, limit: number ): (...args: Parameters) => void { let inThrottle: boolean; return function (this: ThisParameterType, ...args: Parameters) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => (inThrottle = false), limit); } }; } /** * 深拷贝 */ export function deepClone(obj: T): T { if (obj === null || typeof obj !== 'object') { return obj; } return JSON.parse(JSON.stringify(obj)); } /** * 生成唯一 ID */ export function generateId(): string { return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; } /** * 获取对象的嵌套值 */ export function getNestedValue(obj: Record, path: string): T | undefined { const keys = path.split('.'); let result: unknown = obj; for (const key of keys) { if (result === null || result === undefined) { return undefined; } result = (result as Record)[key]; } return result as T; } /** * 将对象转为查询字符串 */ export function objectToQueryString(obj: Record): string { const params = new URLSearchParams(); Object.entries(obj).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { params.append(key, String(value)); } }); return params.toString(); } /** * 从查询字符串解析对象 */ export function queryStringToObject(query: string): Record { const params = new URLSearchParams(query); const result: Record = {}; params.forEach((value, key) => { result[key] = value; }); return result; } /** * 数组去重 */ export function uniqueArray(arr: T[], key?: keyof T): T[] { if (key) { const seen = new Set(); return arr.filter((item) => { const k = item[key]; if (seen.has(k)) { return false; } seen.add(k); return true; }); } return [...new Set(arr)]; } /** * 数组分组 */ export function groupBy(arr: T[], key: keyof T): Record { return arr.reduce( (groups, item) => { const groupKey = String(item[key]); if (!groups[groupKey]) { groups[groupKey] = []; } groups[groupKey].push(item); return groups; }, {} as Record ); } /** * 复制文本到剪贴板 */ export async function copyToClipboard(text: string): Promise { try { await navigator.clipboard.writeText(text); return true; } catch { // 降级方案 const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); const success = document.execCommand('copy'); document.body.removeChild(textarea); return success; } } /** * 下载 Blob 文件 */ export function downloadBlob(blob: Blob, filename: string): void { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } /** * 检查是否为移动设备 */ export function isMobile(): boolean { if (typeof window === 'undefined') return false; return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } /** * 获取颜色列表(用于图表) */ export function getChartColors(): string[] { return [ '#1565C0', '#4CAF50', '#F5A623', '#E53935', '#9C27B0', '#00BCD4', '#FF9800', '#795548', '#607D8B', '#3F51B5', ]; }