index.ts
5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// 工具函数集合
/**
* 格式化日期
* @param date 日期
* @param format 格式
* @returns 格式化后的日期字符串
*/
export function formatDate(date: Date | string | number, format = 'YYYY-MM-DD HH:mm:ss'): string {
if (!date) return ''
const d = new Date(date)
if (isNaN(d.getTime())) return ''
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const hours = String(d.getHours()).padStart(2, '0')
const minutes = String(d.getMinutes()).padStart(2, '0')
const seconds = String(d.getSeconds()).padStart(2, '0')
return format
.replace('YYYY', String(year))
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds)
}
/**
* 防抖函数
* @param func 要防抖的函数
* @param wait 等待时间
* @returns 防抖后的函数
*/
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: number | null = null
return function executedFunction(...args: Parameters<T>) {
const later = () => {
timeout = null
func(...args)
}
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(later, 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 function executedFunction(...args: Parameters<T>) {
if (!inThrottle) {
func(...args)
inThrottle = true
setTimeout(() => (inThrottle = false), limit)
}
}
}
/**
* 深拷贝
* @param obj 要拷贝的对象
* @returns 拷贝后的对象
*/
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
}
/**
* 生成唯一ID
* @returns 唯一ID字符串
*/
export function generateId(): string {
return Date.now().toString(36) + Math.random().toString(36).substr(2)
}
/**
* 获取文件扩展名
* @param filename 文件名
* @returns 扩展名
*/
export function getFileExtension(filename: string): string {
return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2)
}
/**
* 格式化文件大小
* @param bytes 字节数
* @returns 格式化后的文件大小
*/
export function formatFileSize(bytes: number): string {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
/**
* 验证邮箱格式
* @param email 邮箱地址
* @returns 是否有效
*/
export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
/**
* 验证手机号格式
* @param phone 手机号
* @returns 是否有效
*/
export function isValidPhone(phone: string): boolean {
const phoneRegex = /^1[3-9]\d{9}$/
return phoneRegex.test(phone)
}
/**
* 验证URL格式
* @param url URL地址
* @returns 是否有效
*/
export function isValidUrl(url: string): boolean {
try {
new URL(url)
return true
} catch {
return false
}
}
/**
* 获取URL参数
* @param name 参数名
* @returns 参数值
*/
export function getUrlParam(name: string): string | null {
const urlParams = new URLSearchParams(window.location.search)
return urlParams.get(name)
}
/**
* 设置URL参数
* @param name 参数名
* @param value 参数值
*/
export function setUrlParam(name: string, value: string): void {
const url = new URL(window.location.href)
url.searchParams.set(name, value)
window.history.replaceState({}, '', url.toString())
}
/**
* 移除URL参数
* @param name 参数名
*/
export function removeUrlParam(name: string): void {
const url = new URL(window.location.href)
url.searchParams.delete(name)
window.history.replaceState({}, '', url.toString())
}
/**
* 数组去重
* @param array 数组
* @param key 去重的键名(可选)
* @returns 去重后的数组
*/
export function uniqueArray<T>(array: T[], key?: keyof T): T[] {
if (!key) {
return [...new Set(array)]
}
const seen = new Set()
return array.filter(item => {
const value = item[key]
if (seen.has(value)) {
return false
}
seen.add(value)
return true
})
}
/**
* 数组分组
* @param array 数组
* @param key 分组的键名
* @returns 分组后的对象
*/
export function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {
return array.reduce((groups, item) => {
const group = String(item[key])
groups[group] = groups[group] || []
groups[group].push(item)
return groups
}, {} as Record<string, T[]>)
}
/**
* 睡眠函数
* @param ms 毫秒数
* @returns Promise
*/
export function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* 重试函数
* @param fn 要重试的函数
* @param retries 重试次数
* @param delay 重试间隔
* @returns Promise
*/
export async function retry<T>(
fn: () => Promise<T>,
retries = 3,
delay = 1000
): Promise<T> {
try {
return await fn()
} catch (error) {
if (retries > 0) {
await sleep(delay)
return retry(fn, retries - 1, delay)
}
throw error
}
}