212 lines
4.7 KiB
TypeScript
212 lines
4.7 KiB
TypeScript
import { clsx, type ClassValue } from 'clsx';
|
|
|
|
/**
|
|
* 合并 className
|
|
*/
|
|
export function cn(...inputs: ClassValue[]): string {
|
|
return clsx(inputs);
|
|
}
|
|
|
|
/**
|
|
* 延迟函数
|
|
*/
|
|
export function delay(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
/**
|
|
* 防抖函数
|
|
*/
|
|
export function debounce<T extends (...args: Parameters<T>) => ReturnType<T>>(
|
|
func: T,
|
|
wait: number
|
|
): (...args: Parameters<T>) => void {
|
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
timeoutId = setTimeout(() => {
|
|
func.apply(this, args);
|
|
}, wait);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 节流函数
|
|
*/
|
|
export function throttle<T extends (...args: Parameters<T>) => ReturnType<T>>(
|
|
func: T,
|
|
limit: number
|
|
): (...args: Parameters<T>) => void {
|
|
let inThrottle: boolean;
|
|
|
|
return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
|
if (!inThrottle) {
|
|
func.apply(this, args);
|
|
inThrottle = true;
|
|
setTimeout(() => (inThrottle = false), limit);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 深拷贝
|
|
*/
|
|
export function deepClone<T>(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<T>(obj: Record<string, unknown>, 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<string, unknown>)[key];
|
|
}
|
|
|
|
return result as T;
|
|
}
|
|
|
|
/**
|
|
* 将对象转为查询字符串
|
|
*/
|
|
export function objectToQueryString(obj: Record<string, unknown>): 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<string, string> {
|
|
const params = new URLSearchParams(query);
|
|
const result: Record<string, string> = {};
|
|
|
|
params.forEach((value, key) => {
|
|
result[key] = value;
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 数组去重
|
|
*/
|
|
export function uniqueArray<T>(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<T>(arr: T[], key: keyof T): Record<string, T[]> {
|
|
return arr.reduce(
|
|
(groups, item) => {
|
|
const groupKey = String(item[key]);
|
|
if (!groups[groupKey]) {
|
|
groups[groupKey] = [];
|
|
}
|
|
groups[groupKey].push(item);
|
|
return groups;
|
|
},
|
|
{} as Record<string, T[]>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 复制文本到剪贴板
|
|
*/
|
|
export async function copyToClipboard(text: string): Promise<boolean> {
|
|
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',
|
|
];
|
|
}
|