提交1 bmad搭建与项目启动 - ok
This commit is contained in:
218
crop-x/src/lib/utils.ts
Normal file
218
crop-x/src/lib/utils.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
// 合并 Tailwind CSS 类名的工具函数
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
export function formatDate(date: Date | string | number): string {
|
||||
const d = new Date(date)
|
||||
return d.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
export function formatTime(date: Date | string | number): string {
|
||||
const d = new Date(date)
|
||||
return d.toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
export function formatDateTime(date: Date | string | number): string {
|
||||
return `${formatDate(date)} ${formatTime(date)}`
|
||||
}
|
||||
|
||||
// 相对时间格式化
|
||||
export function formatRelativeTime(date: Date | string | number): string {
|
||||
const now = new Date()
|
||||
const target = new Date(date)
|
||||
const diffMs = now.getTime() - target.getTime()
|
||||
const diffSecs = Math.floor(diffMs / 1000)
|
||||
const diffMins = Math.floor(diffSecs / 60)
|
||||
const diffHours = Math.floor(diffMins / 60)
|
||||
const diffDays = Math.floor(diffHours / 24)
|
||||
|
||||
if (diffSecs < 60) {
|
||||
return '刚刚'
|
||||
} else if (diffMins < 60) {
|
||||
return `${diffMins}分钟前`
|
||||
} else if (diffHours < 24) {
|
||||
return `${diffHours}小时前`
|
||||
} else if (diffDays < 7) {
|
||||
return `${diffDays}天前`
|
||||
} else {
|
||||
return formatDate(date)
|
||||
}
|
||||
}
|
||||
|
||||
// 数字格式化
|
||||
export function formatNumber(num: number, precision = 2): string {
|
||||
return num.toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: precision,
|
||||
maximumFractionDigits: precision,
|
||||
})
|
||||
}
|
||||
|
||||
// 货币格式化
|
||||
export function formatCurrency(amount: number): string {
|
||||
return new Intl.NumberFormat('zh-CN', {
|
||||
style: 'currency',
|
||||
currency: 'CNY',
|
||||
}).format(amount)
|
||||
}
|
||||
|
||||
// 百分比格式化
|
||||
export function formatPercentage(value: number, precision = 1): string {
|
||||
return `${(value * 100).toFixed(precision)}%`
|
||||
}
|
||||
|
||||
// 文件大小格式化
|
||||
export function formatFileSize(bytes: number): string {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let size = bytes
|
||||
let unitIndex = 0
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024
|
||||
unitIndex++
|
||||
}
|
||||
|
||||
return `${formatNumber(size, unitIndex === 0 ? 0 : 2)} ${units[unitIndex]}`
|
||||
}
|
||||
|
||||
// 农机状态映射
|
||||
export const machineryStatusMap = {
|
||||
running: { label: '运行中', color: 'status-running' },
|
||||
idle: { label: '空闲中', color: 'status-idle' },
|
||||
maintenance: { label: '维护中', color: 'status-maintenance' },
|
||||
error: { label: '故障中', color: 'status-error' },
|
||||
offline: { label: '离线', color: 'status-offline' },
|
||||
} as const
|
||||
|
||||
// 获取农机状态信息
|
||||
export function getMachineryStatus(status: keyof typeof machineryStatusMap) {
|
||||
return machineryStatusMap[status] || { label: '未知', color: 'status-idle' }
|
||||
}
|
||||
|
||||
// 农作物类型映射
|
||||
export const cropTypeMap = {
|
||||
rice: { label: '水稻', icon: '🌾' },
|
||||
wheat: { label: '小麦', icon: '🌾' },
|
||||
corn: { label: '玉米', icon: '🌽' },
|
||||
soybean: { label: '大豆', icon: '🫘' },
|
||||
vegetable: { label: '蔬菜', icon: '🥬' },
|
||||
fruit: { label: '水果', icon: '🍎' },
|
||||
} as const
|
||||
|
||||
// 获取农作物信息
|
||||
export function getCropInfo(type: keyof typeof cropTypeMap) {
|
||||
return cropTypeMap[type] || { label: '未知', icon: '🌱' }
|
||||
}
|
||||
|
||||
// 防抖函数
|
||||
export function debounce<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timeout: NodeJS.Timeout | null = null
|
||||
|
||||
return (...args: Parameters<T>) => {
|
||||
if (timeout !== null) {
|
||||
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 = false
|
||||
|
||||
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 T
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item)) as T
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const clonedObj = {} as T
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
clonedObj[key] = deepClone(obj[key])
|
||||
}
|
||||
}
|
||||
return clonedObj
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// 生成随机ID
|
||||
export function generateId(length = 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
|
||||
}
|
||||
|
||||
// 验证邮箱
|
||||
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 daysBetween(date1: Date | string, date2: Date | string): number {
|
||||
const d1 = new Date(date1)
|
||||
const d2 = new Date(date2)
|
||||
const diffTime = Math.abs(d2.getTime() - d1.getTime())
|
||||
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
// 获取季节
|
||||
export function getSeason(date: Date | string = new Date()): string {
|
||||
const d = new Date(date)
|
||||
const month = d.getMonth() + 1
|
||||
|
||||
if (month >= 3 && month <= 5) return '春季'
|
||||
if (month >= 6 && month <= 8) return '夏季'
|
||||
if (month >= 9 && month <= 11) return '秋季'
|
||||
return '冬季'
|
||||
}
|
||||
Reference in New Issue
Block a user