135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
import { type ClassValue, clsx } from 'clsx';
|
|
import { twMerge } from 'tailwind-merge';
|
|
|
|
/**
|
|
* 合并 Tailwind CSS 类名的工具函数
|
|
* @param inputs - 类名数组或对象
|
|
* @returns 合并后的类名字符串
|
|
*/
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
|
|
/**
|
|
* 格式化日期
|
|
* @param date - 日期对象或字符串
|
|
* @param options - 格式化选项
|
|
* @returns 格式化后的日期字符串
|
|
*/
|
|
export function formatDate(
|
|
date: Date | string,
|
|
options: Intl.DateTimeFormatOptions = {}
|
|
): string {
|
|
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
|
return new Intl.DateTimeFormat('zh-CN', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
...options,
|
|
}).format(dateObj);
|
|
}
|
|
|
|
/**
|
|
* 格式化数字
|
|
* @param num - 数字
|
|
* @param options - 格式化选项
|
|
* @returns 格式化后的数字字符串
|
|
*/
|
|
export function formatNumber(
|
|
num: number,
|
|
options: Intl.NumberFormatOptions = {}
|
|
): string {
|
|
return new Intl.NumberFormat('zh-CN', options).format(num);
|
|
}
|
|
|
|
/**
|
|
* 生成随机 ID
|
|
* @param length - ID 长度
|
|
* @returns 随机 ID 字符串
|
|
*/
|
|
export function generateId(length: number = 8): string {
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
let result = '';
|
|
for (let i = 0; i < length; i++) {
|
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 防抖函数
|
|
* @param func - 要防抖的函数
|
|
* @param wait - 等待时间(毫秒)
|
|
* @returns 防抖后的函数
|
|
*/
|
|
export function debounce<T extends (...args: any[]) => any>(
|
|
func: T,
|
|
wait: number
|
|
): (...args: Parameters<T>) => void {
|
|
let timeout: NodeJS.Timeout;
|
|
return (...args: Parameters<T>) => {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => func(...args), wait);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 节流函数
|
|
* @param func - 要节流的函数
|
|
* @param limit - 时间限制(毫秒)
|
|
* @returns 节流后的函数
|
|
*/
|
|
export function throttle<T extends (...args: any[]) => any>(
|
|
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);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 检查是否为移动设备
|
|
* @returns 是否为移动设备
|
|
*/
|
|
export function isMobile(): boolean {
|
|
if (typeof window === 'undefined') return false;
|
|
return window.innerWidth < 768;
|
|
}
|
|
|
|
/**
|
|
* 检查是否为暗色模式
|
|
* @returns 是否为暗色模式
|
|
*/
|
|
export function isDarkMode(): boolean {
|
|
if (typeof window === 'undefined') return false;
|
|
return document.documentElement.classList.contains('dark');
|
|
}
|
|
|
|
/**
|
|
* 切换暗色模式
|
|
*/
|
|
export function toggleDarkMode(): void {
|
|
if (typeof window === 'undefined') return;
|
|
document.documentElement.classList.toggle('dark');
|
|
localStorage.setItem('darkMode', document.documentElement.classList.contains('dark').toString());
|
|
}
|
|
|
|
/**
|
|
* 初始化暗色模式
|
|
*/
|
|
export function initDarkMode(): void {
|
|
if (typeof window === 'undefined') return;
|
|
const saved = localStorage.getItem('darkMode');
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
|
|
if (saved === 'true' || (saved === null && prefersDark)) {
|
|
document.documentElement.classList.add('dark');
|
|
}
|
|
}
|