react-util-tools 1.0.25 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,527 @@
1
+ /**
2
+ * 字符串工具函数
3
+ */
4
+
5
+ /**
6
+ * 首字母大写
7
+ * @param str 字符串
8
+ * @returns 首字母大写的字符串
9
+ */
10
+ export function capitalize(str: string): string {
11
+ if (!str) return ''
12
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()
13
+ }
14
+
15
+ /**
16
+ * 驼峰命名转换(首字母小写)
17
+ * @param str 字符串
18
+ * @returns 驼峰命名字符串
19
+ */
20
+ export function camelCase(str: string): string {
21
+ if (!str) return ''
22
+ return str
23
+ .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
24
+ .replace(/^[A-Z]/, char => char.toLowerCase())
25
+ }
26
+
27
+ /**
28
+ * 帕斯卡命名转换(首字母大写)
29
+ * @param str 字符串
30
+ * @returns 帕斯卡命名字符串
31
+ */
32
+ export function pascalCase(str: string): string {
33
+ if (!str) return ''
34
+ const camel = camelCase(str)
35
+ return camel.charAt(0).toUpperCase() + camel.slice(1)
36
+ }
37
+
38
+ /**
39
+ * 蛇形命名转换(下划线分隔)
40
+ * @param str 字符串
41
+ * @returns 蛇形命名字符串
42
+ */
43
+ export function snakeCase(str: string): string {
44
+ if (!str) return ''
45
+ return str
46
+ .replace(/([A-Z])/g, '_$1')
47
+ .replace(/[-\s]+/g, '_')
48
+ .replace(/^_/, '')
49
+ .toLowerCase()
50
+ }
51
+
52
+ /**
53
+ * 短横线命名转换(kebab-case)
54
+ * @param str 字符串
55
+ * @returns 短横线命名字符串
56
+ */
57
+ export function kebabCase(str: string): string {
58
+ if (!str) return ''
59
+ return str
60
+ .replace(/([A-Z])/g, '-$1')
61
+ .replace(/[_\s]+/g, '-')
62
+ .replace(/^-/, '')
63
+ .toLowerCase()
64
+ }
65
+
66
+ /**
67
+ * 截断字符串
68
+ * @param str 字符串
69
+ * @param length 最大长度
70
+ * @param suffix 后缀,默认 '...'
71
+ * @returns 截断后的字符串
72
+ */
73
+ export function truncate(str: string, length: number, suffix = '...'): string {
74
+ if (!str || str.length <= length) return str
75
+ return str.slice(0, length) + suffix
76
+ }
77
+
78
+ /**
79
+ * 移除字符串两端空格
80
+ * @param str 字符串
81
+ * @returns 移除空格后的字符串
82
+ */
83
+ export function trim(str: string): string {
84
+ return str ? str.trim() : ''
85
+ }
86
+
87
+ /**
88
+ * 移除字符串左侧空格
89
+ * @param str 字符串
90
+ * @returns 移除空格后的字符串
91
+ */
92
+ export function trimStart(str: string): string {
93
+ return str ? str.trimStart() : ''
94
+ }
95
+
96
+ /**
97
+ * 移除字符串右侧空格
98
+ * @param str 字符串
99
+ * @returns 移除空格后的字符串
100
+ */
101
+ export function trimEnd(str: string): string {
102
+ return str ? str.trimEnd() : ''
103
+ }
104
+
105
+ /**
106
+ * 反转字符串
107
+ * @param str 字符串
108
+ * @returns 反转后的字符串
109
+ */
110
+ export function reverse(str: string): string {
111
+ if (!str) return ''
112
+ return str.split('').reverse().join('')
113
+ }
114
+
115
+ /**
116
+ * 重复字符串
117
+ * @param str 字符串
118
+ * @param count 重复次数
119
+ * @returns 重复后的字符串
120
+ */
121
+ export function repeat(str: string, count: number): string {
122
+ if (!str || count <= 0) return ''
123
+ return str.repeat(count)
124
+ }
125
+
126
+ /**
127
+ * 填充字符串(左侧)
128
+ * @param str 字符串
129
+ * @param length 目标长度
130
+ * @param padStr 填充字符,默认空格
131
+ * @returns 填充后的字符串
132
+ */
133
+ export function padStart(str: string, length: number, padStr = ' '): string {
134
+ if (!str) return padStr.repeat(length)
135
+ return str.padStart(length, padStr)
136
+ }
137
+
138
+ /**
139
+ * 填充字符串(右侧)
140
+ * @param str 字符串
141
+ * @param length 目标长度
142
+ * @param padStr 填充字符,默认空格
143
+ * @returns 填充后的字符串
144
+ */
145
+ export function padEnd(str: string, length: number, padStr = ' '): string {
146
+ if (!str) return padStr.repeat(length)
147
+ return str.padEnd(length, padStr)
148
+ }
149
+
150
+ /**
151
+ * 判断字符串是否以指定字符串开头
152
+ * @param str 字符串
153
+ * @param searchString 搜索字符串
154
+ * @returns 是否以指定字符串开头
155
+ */
156
+ export function startsWith(str: string, searchString: string): boolean {
157
+ if (!str) return false
158
+ return str.startsWith(searchString)
159
+ }
160
+
161
+ /**
162
+ * 判断字符串是否以指定字符串结尾
163
+ * @param str 字符串
164
+ * @param searchString 搜索字符串
165
+ * @returns 是否以指定字符串结尾
166
+ */
167
+ export function endsWith(str: string, searchString: string): boolean {
168
+ if (!str) return false
169
+ return str.endsWith(searchString)
170
+ }
171
+
172
+ /**
173
+ * 判断字符串是否包含指定字符串
174
+ * @param str 字符串
175
+ * @param searchString 搜索字符串
176
+ * @returns 是否包含指定字符串
177
+ */
178
+ export function includes(str: string, searchString: string): boolean {
179
+ if (!str) return false
180
+ return str.includes(searchString)
181
+ }
182
+
183
+ /**
184
+ * 替换字符串中的所有匹配项
185
+ * @param str 字符串
186
+ * @param search 搜索字符串或正则表达式
187
+ * @param replacement 替换字符串
188
+ * @returns 替换后的字符串
189
+ */
190
+ export function replaceAll(
191
+ str: string,
192
+ search: string | RegExp,
193
+ replacement: string
194
+ ): string {
195
+ if (!str) return ''
196
+ if (typeof search === 'string') {
197
+ return str.split(search).join(replacement)
198
+ }
199
+ return str.replace(search, replacement)
200
+ }
201
+
202
+ /**
203
+ * 移除字符串中的 HTML 标签
204
+ * @param str 字符串
205
+ * @returns 移除 HTML 标签后的字符串
206
+ */
207
+ export function stripHtml(str: string): string {
208
+ if (!str) return ''
209
+ return str.replace(/<[^>]*>/g, '')
210
+ }
211
+
212
+ /**
213
+ * 转义 HTML 特殊字符
214
+ * @param str 字符串
215
+ * @returns 转义后的字符串
216
+ */
217
+ export function escapeHtml(str: string): string {
218
+ if (!str) return ''
219
+ const map: Record<string, string> = {
220
+ '&': '&amp;',
221
+ '<': '&lt;',
222
+ '>': '&gt;',
223
+ '"': '&quot;',
224
+ "'": '&#39;'
225
+ }
226
+ return str.replace(/[&<>"']/g, char => map[char])
227
+ }
228
+
229
+ /**
230
+ * 反转义 HTML 特殊字符
231
+ * @param str 字符串
232
+ * @returns 反转义后的字符串
233
+ */
234
+ export function unescapeHtml(str: string): string {
235
+ if (!str) return ''
236
+ const map: Record<string, string> = {
237
+ '&amp;': '&',
238
+ '&lt;': '<',
239
+ '&gt;': '>',
240
+ '&quot;': '"',
241
+ '&#39;': "'"
242
+ }
243
+ return str.replace(/&(amp|lt|gt|quot|#39);/g, entity => map[entity])
244
+ }
245
+
246
+ /**
247
+ * 转换为小写
248
+ * @param str 字符串
249
+ * @returns 小写字符串
250
+ */
251
+ export function toLowerCase(str: string): string {
252
+ return str ? str.toLowerCase() : ''
253
+ }
254
+
255
+ /**
256
+ * 转换为大写
257
+ * @param str 字符串
258
+ * @returns 大写字符串
259
+ */
260
+ export function toUpperCase(str: string): string {
261
+ return str ? str.toUpperCase() : ''
262
+ }
263
+
264
+ /**
265
+ * 单词首字母大写
266
+ * @param str 字符串
267
+ * @returns 单词首字母大写的字符串
268
+ */
269
+ export function titleCase(str: string): string {
270
+ if (!str) return ''
271
+ return str
272
+ .toLowerCase()
273
+ .split(' ')
274
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
275
+ .join(' ')
276
+ }
277
+
278
+ /**
279
+ * 判断是否为空字符串(包括只有空格的字符串)
280
+ * @param str 字符串
281
+ * @returns 是否为空
282
+ */
283
+ export function isEmpty(str: string): boolean {
284
+ return !str || str.trim().length === 0
285
+ }
286
+
287
+ /**
288
+ * 判断是否不为空字符串
289
+ * @param str 字符串
290
+ * @returns 是否不为空
291
+ */
292
+ export function isNotEmpty(str: string): boolean {
293
+ return !isEmpty(str)
294
+ }
295
+
296
+ /**
297
+ * 获取字符串长度(支持 Unicode)
298
+ * @param str 字符串
299
+ * @returns 字符串长度
300
+ */
301
+ export function length(str: string): number {
302
+ if (!str) return 0
303
+ return Array.from(str).length
304
+ }
305
+
306
+ /**
307
+ * 分割字符串为数组
308
+ * @param str 字符串
309
+ * @param separator 分隔符
310
+ * @returns 字符串数组
311
+ */
312
+ export function split(str: string, separator: string | RegExp): string[] {
313
+ if (!str) return []
314
+ return str.split(separator)
315
+ }
316
+
317
+ /**
318
+ * 提取数字
319
+ * @param str 字符串
320
+ * @returns 数字数组
321
+ */
322
+ export function extractNumbers(str: string): number[] {
323
+ if (!str) return []
324
+ const matches = str.match(/\d+(\.\d+)?/g)
325
+ return matches ? matches.map(Number) : []
326
+ }
327
+
328
+ /**
329
+ * 移除所有空格
330
+ * @param str 字符串
331
+ * @returns 移除空格后的字符串
332
+ */
333
+ export function removeSpaces(str: string): string {
334
+ if (!str) return ''
335
+ return str.replace(/\s+/g, '')
336
+ }
337
+
338
+ /**
339
+ * 移除多余空格(保留单个空格)
340
+ * @param str 字符串
341
+ * @returns 移除多余空格后的字符串
342
+ */
343
+ export function normalizeSpaces(str: string): string {
344
+ if (!str) return ''
345
+ return str.replace(/\s+/g, ' ').trim()
346
+ }
347
+
348
+ /**
349
+ * 计算字符串中某个子串出现的次数
350
+ * @param str 字符串
351
+ * @param searchString 搜索字符串
352
+ * @returns 出现次数
353
+ */
354
+ export function countOccurrences(str: string, searchString: string): number {
355
+ if (!str || !searchString) return 0
356
+ return str.split(searchString).length - 1
357
+ }
358
+
359
+ /**
360
+ * 随机字符串生成
361
+ * @param length 长度
362
+ * @param chars 字符集,默认包含字母和数字
363
+ * @returns 随机字符串
364
+ */
365
+ export function randomString(
366
+ length: number,
367
+ chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
368
+ ): string {
369
+ let result = ''
370
+ for (let i = 0; i < length; i++) {
371
+ result += chars.charAt(Math.floor(Math.random() * chars.length))
372
+ }
373
+ return result
374
+ }
375
+
376
+ /**
377
+ * 生成 UUID v4
378
+ * @returns UUID 字符串
379
+ */
380
+ export function uuid(): string {
381
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
382
+ const r = (Math.random() * 16) | 0
383
+ const v = c === 'x' ? r : (r & 0x3) | 0x8
384
+ return v.toString(16)
385
+ })
386
+ }
387
+
388
+ /**
389
+ * 手机号脱敏
390
+ * @param phone 手机号
391
+ * @returns 脱敏后的手机号
392
+ */
393
+ export function maskPhone(phone: string): string {
394
+ if (!phone || phone.length < 11) return phone
395
+ return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
396
+ }
397
+
398
+ /**
399
+ * 身份证号脱敏
400
+ * @param idCard 身份证号
401
+ * @returns 脱敏后的身份证号
402
+ */
403
+ export function maskIdCard(idCard: string): string {
404
+ if (!idCard || idCard.length < 18) return idCard
405
+ return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
406
+ }
407
+
408
+ /**
409
+ * 银行卡号脱敏
410
+ * @param cardNumber 银行卡号
411
+ * @returns 脱敏后的银行卡号
412
+ */
413
+ export function maskBankCard(cardNumber: string): string {
414
+ if (!cardNumber || cardNumber.length < 16) return cardNumber
415
+ return cardNumber.replace(/(\d{4})\d+(\d{4})/, '$1 **** **** $2')
416
+ }
417
+
418
+ /**
419
+ * 姓名脱敏
420
+ * @param name 姓名
421
+ * @returns 脱敏后的姓名
422
+ */
423
+ export function maskName(name: string): string {
424
+ if (!name) return ''
425
+ if (name.length === 2) {
426
+ return name.charAt(0) + '*'
427
+ }
428
+ return name.charAt(0) + '*'.repeat(name.length - 2) + name.charAt(name.length - 1)
429
+ }
430
+
431
+ /**
432
+ * 判断是否为有效的手机号(中国大陆)
433
+ * @param phone 手机号
434
+ * @returns 是否有效
435
+ */
436
+ export function isValidPhone(phone: string): boolean {
437
+ if (!phone) return false
438
+ return /^1[3-9]\d{9}$/.test(phone)
439
+ }
440
+
441
+ /**
442
+ * 判断是否为有效的邮箱
443
+ * @param email 邮箱
444
+ * @returns 是否有效
445
+ */
446
+ export function isValidEmail(email: string): boolean {
447
+ if (!email) return false
448
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
449
+ }
450
+
451
+ /**
452
+ * 判断是否为有效的 URL
453
+ * @param url URL
454
+ * @returns 是否有效
455
+ */
456
+ export function isValidUrl(url: string): boolean {
457
+ if (!url) return false
458
+ try {
459
+ new URL(url)
460
+ return true
461
+ } catch {
462
+ return false
463
+ }
464
+ }
465
+
466
+ /**
467
+ * 判断是否为有效的身份证号(中国大陆)
468
+ * @param idCard 身份证号
469
+ * @returns 是否有效
470
+ */
471
+ export function isValidIdCard(idCard: string): boolean {
472
+ if (!idCard) return false
473
+ return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(idCard)
474
+ }
475
+
476
+ /**
477
+ * 字符串转 Base64
478
+ * @param str 字符串
479
+ * @returns Base64 字符串
480
+ */
481
+ export function toBase64(str: string): string {
482
+ if (!str) return ''
483
+ if (typeof window !== 'undefined' && window.btoa) {
484
+ try {
485
+ return window.btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, p1) => {
486
+ return String.fromCharCode(parseInt(p1, 16))
487
+ }))
488
+ } catch {
489
+ return ''
490
+ }
491
+ }
492
+ // Node.js 环境
493
+ try {
494
+ const BufferClass = (globalThis as any).Buffer
495
+ return BufferClass ? BufferClass.from(str, 'utf-8').toString('base64') : ''
496
+ } catch {
497
+ return ''
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Base64 转字符串
503
+ * @param base64 Base64 字符串
504
+ * @returns 原始字符串
505
+ */
506
+ export function fromBase64(base64: string): string {
507
+ if (!base64) return ''
508
+ if (typeof window !== 'undefined' && window.atob) {
509
+ try {
510
+ const binary = window.atob(base64)
511
+ const bytes = new Uint8Array(binary.length)
512
+ for (let i = 0; i < binary.length; i++) {
513
+ bytes[i] = binary.charCodeAt(i)
514
+ }
515
+ return new TextDecoder().decode(bytes)
516
+ } catch {
517
+ return ''
518
+ }
519
+ }
520
+ // Node.js 环境
521
+ try {
522
+ const BufferClass = (globalThis as any).Buffer
523
+ return BufferClass ? BufferClass.from(base64, 'base64').toString('utf-8') : ''
524
+ } catch {
525
+ return ''
526
+ }
527
+ }