260 lines
7.0 KiB
TypeScript

import { type ClassValue, clsx } from 'clsx';
// 合并 CSS 类名
export function cn(...inputs: ClassValue[]) {
return clsx(inputs);
}
// 格式化货币
export function formatCurrency(amount: number, currency: 'CNY' | 'USD' = 'CNY') {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: currency,
}).format(amount);
}
// 格式化时间
export function formatTime(date: string | Date) {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
}).format(new Date(date));
}
// 格式化相对时间
export function formatRelativeTime(date: string | Date) {
const now = new Date();
const target = new Date(date);
const diffInSeconds = Math.floor((now.getTime() - target.getTime()) / 1000);
if (diffInSeconds < 60) {
return `${diffInSeconds}秒前`;
} else if (diffInSeconds < 3600) {
return `${Math.floor(diffInSeconds / 60)}分钟前`;
} else if (diffInSeconds < 86400) {
return `${Math.floor(diffInSeconds / 3600)}小时前`;
} else if (diffInSeconds < 2592000) {
return `${Math.floor(diffInSeconds / 86400)}天前`;
} else {
return formatTime(date);
}
}
// 格式化通话时长
export function formatDuration(seconds: number) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
}
}
// 格式化文件大小
export function formatFileSize(bytes: number) {
const sizes = ['B', 'KB', 'MB', 'GB'];
if (bytes === 0) return '0 B';
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}
// 生成随机ID
export function generateId() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
// 防抖函数
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);
};
}
// 节流函数
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);
}
};
}
// 深拷贝
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 any;
if (obj instanceof Array) return obj.map(item => deepClone(item)) as any;
if (typeof obj === 'object') {
const clonedObj = {} as any;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clonedObj[key] = deepClone(obj[key]);
}
}
return clonedObj;
}
return obj;
}
// 验证邮箱
export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// 验证手机号
export function isValidPhone(phone: string): boolean {
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phone);
}
// 获取通话状态的中文描述
export function getCallStatusText(status: string): string {
const statusMap: Record<string, string> = {
pending: '等待中',
active: '通话中',
ended: '已结束',
cancelled: '已取消',
failed: '失败',
};
return statusMap[status] || status;
}
// 获取通话模式的中文描述
export function getCallModeText(mode: string): string {
const modeMap: Record<string, string> = {
ai_voice: 'AI语音',
ai_video: 'AI视频',
sign_language: '手语翻译',
human_interpreter: '真人翻译',
};
return modeMap[mode] || mode;
}
// 获取用户类型的中文描述
export function getUserTypeText(type: string): string {
const typeMap: Record<string, string> = {
individual: '个人用户',
enterprise: '企业用户',
};
return typeMap[type] || type;
}
// 获取订单状态的中文描述
export function getOrderStatusText(status: string): string {
const statusMap: Record<string, string> = {
pending: '待处理',
completed: '已完成',
cancelled: '已取消',
refunded: '已退款',
};
return statusMap[status] || status;
}
// 获取文档翻译状态的中文描述
export function getDocumentStatusText(status: string): string {
const statusMap: Record<string, string> = {
uploaded: '已上传',
processing: '处理中',
completed: '已完成',
failed: '失败',
};
return statusMap[status] || status;
}
// 计算通话费用(按分钟向上取整)
export function calculateCallCost(durationInSeconds: number, ratePerMinute: number): number {
const minutes = Math.ceil(durationInSeconds / 60);
return minutes * ratePerMinute;
}
// 检查余额是否足够
export function checkBalanceSufficient(balance: number, requiredAmount: number): boolean {
return balance >= requiredAmount;
}
// 获取颜色类名基于状态
export function getStatusColor(status: string): string {
const colorMap: Record<string, string> = {
active: 'text-green-600 bg-green-100',
pending: 'text-yellow-600 bg-yellow-100',
ended: 'text-gray-600 bg-gray-100',
cancelled: 'text-red-600 bg-red-100',
failed: 'text-red-600 bg-red-100',
completed: 'text-green-600 bg-green-100',
processing: 'text-blue-600 bg-blue-100',
uploaded: 'text-purple-600 bg-purple-100',
};
return colorMap[status] || 'text-gray-600 bg-gray-100';
}
// 安全地解析JSON
export function safeJsonParse<T>(str: string, fallback: T): T {
try {
return JSON.parse(str);
} catch (error) {
return fallback;
}
}
// 生成头像URL
export function generateAvatarUrl(name: string): string {
const firstChar = name.charAt(0).toUpperCase();
return `https://ui-avatars.com/api/?name=${encodeURIComponent(firstChar)}&background=random&color=fff&size=40`;
}
// 导出所有状态文本映射
export const STATUS_TEXTS = {
CALL_STATUS: {
pending: '等待中',
active: '通话中',
ended: '已结束',
cancelled: '已取消',
failed: '失败',
},
CALL_MODE: {
ai_voice: 'AI语音',
ai_video: 'AI视频',
sign_language: '手语翻译',
human_interpreter: '真人翻译',
},
USER_TYPE: {
individual: '个人用户',
enterprise: '企业用户',
},
ORDER_STATUS: {
pending: '待处理',
completed: '已完成',
cancelled: '已取消',
refunded: '已退款',
},
DOCUMENT_STATUS: {
uploaded: '已上传',
processing: '处理中',
completed: '已完成',
failed: '失败',
},
NOTIFICATION_TYPE: {
info: '信息',
warning: '警告',
error: '错误',
success: '成功',
},
} as const;