jinbi-utils 1.0.19 → 1.0.21

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.
Files changed (81) hide show
  1. package/.babelrc +19 -0
  2. package/.cz-config.js +55 -0
  3. package/.dockerignore +3 -0
  4. package/.editorconfig +12 -0
  5. package/.eslintignore +8 -0
  6. package/.eslintrc.js +54 -0
  7. package/.versionrc.json +9 -0
  8. package/CHUNK_OPTIMIZER_USAGE.md +132 -0
  9. package/Dockerfile +3 -0
  10. package/QUICK_RELEASE.md +85 -0
  11. package/RELEASE_GUIDE.md +243 -0
  12. package/api-extractor.json +15 -0
  13. package/commitlint.config.js +3 -0
  14. package/jest.config.js +15 -0
  15. package/package.json +8 -35
  16. package/rollup.config.chunk-optimizer.js +32 -0
  17. package/rollup.config.js +73 -0
  18. package/src/array/index.ts +85 -0
  19. package/src/build/chunk-optimizer/ARCHITECTURE.md +347 -0
  20. package/src/build/chunk-optimizer/QUICK_START.md +370 -0
  21. package/src/build/chunk-optimizer/README.md +240 -0
  22. package/src/build/chunk-optimizer/core/chunk-generator.ts +166 -0
  23. package/src/build/chunk-optimizer/core/classifier.ts +148 -0
  24. package/src/build/chunk-optimizer/core/dependency-reader.ts +138 -0
  25. package/src/build/chunk-optimizer/examples/basic-usage.ts +234 -0
  26. package/src/build/chunk-optimizer/index.ts +166 -0
  27. package/src/build/chunk-optimizer/rules/common-rules.ts +131 -0
  28. package/src/build/chunk-optimizer/rules/framework-rules.ts +93 -0
  29. package/src/build/chunk-optimizer/rules/index.ts +27 -0
  30. package/src/build/chunk-optimizer/test.ts +94 -0
  31. package/src/build/chunk-optimizer/types.ts +128 -0
  32. package/src/color/index.ts +58 -0
  33. package/src/common/index.ts +353 -0
  34. package/src/constant/common.constant.ts +13 -0
  35. package/src/date/index.ts +143 -0
  36. package/src/dom/index.ts +198 -0
  37. package/src/file/index.ts +319 -0
  38. package/src/http/apiBuilder/README.md +648 -0
  39. package/src/http/apiBuilder/api-builder.ts +502 -0
  40. package/src/http/apiBuilder/example.ts +243 -0
  41. package/src/http/apiBuilder/index.ts +1 -0
  42. package/src/http/apiBuilder//345/277/253/351/200/237/345/217/202/350/200/203.md +199 -0
  43. package/src/http/http.ts +79 -0
  44. package/src/http/httpEnums.ts +61 -0
  45. package/src/iam/index.ts +46 -0
  46. package/src/index.ts +20 -0
  47. package/src/middleware/requestLogger.middware.ts +371 -0
  48. package/src/middleware/requestLoggerUnified.ts +371 -0
  49. package/src/number/index.ts +362 -0
  50. package/src/object/index.ts +54 -0
  51. package/src/print/index.ts +102 -0
  52. package/src/string/index.ts +189 -0
  53. package/src/utils/curl.ts +108 -0
  54. package/src/validate/index.ts +100 -0
  55. package/src/websocket/emitter.ts +39 -0
  56. package/src/websocket/index.ts +6 -0
  57. package/src/websocket/manager.ts +151 -0
  58. package/src/websocket/pinia-store.ts +91 -0
  59. package/src/websocket/service.ts +34 -0
  60. package/src/websocket/types.ts +45 -0
  61. package/test/common/index.test.ts +19 -0
  62. package/test/date/index.test.ts +107 -0
  63. package/test/file/index.test.ts +104 -0
  64. package/test/number/index.test.ts +108 -0
  65. package/test/object/index.test.ts +20 -0
  66. package/test/string/index.test.ts +82 -0
  67. package/tsconfig.json +39 -0
  68. package/typedoc.json +12 -0
  69. package/types/file/index.d.ts +7 -0
  70. package/types/index.d.ts +1 -0
  71. package/types/websocket/emitter.d.ts +16 -0
  72. package/types/websocket/index.d.ts +6 -0
  73. package/types/websocket/manager.d.ts +36 -0
  74. package/types/websocket/pinia-store.d.ts +25 -0
  75. package/types/websocket/service.d.ts +13 -0
  76. package/types/websocket/types.d.ts +34 -0
  77. package/dist/chunk-optimizer.cjs +0 -703
  78. package/dist/index.esm.js +0 -2791
  79. package/dist/index.esm.min.js +0 -15
  80. package/dist/index.umd.js +0 -2899
  81. package/dist/index.umd.min.js +0 -16
@@ -0,0 +1,362 @@
1
+ /**
2
+ * number处理相关
3
+ * @packageDocumentation
4
+ * @module Number
5
+ * @preferred
6
+ */
7
+
8
+ /**
9
+ * 格式化字节大小为可读字符串
10
+ * @param bytes - 字节数
11
+ * @param decimals - 小数位数,默认为 2
12
+ * @returns 格式化后的字符串(如 "1.5 MB")
13
+ */
14
+ export function formatBytes(bytes: number, decimals: number = 2): string {
15
+ if (bytes === 0) return '0 Bytes';
16
+
17
+ const k = 1024;
18
+ const dm = decimals < 0 ? 0 : decimals;
19
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
20
+
21
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
22
+
23
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
24
+ }
25
+
26
+ import { isEmpty } from '../common';
27
+
28
+ /**
29
+ * 货币计算选项接口
30
+ */
31
+ interface MoneyComputeOptions {
32
+ // 操作类型: multiply(乘法) 或 divide(除法)
33
+ operation: 'multiply' | 'divide';
34
+ // 系数,默认为100(分转元)
35
+ factor: number;
36
+ // 小数位数
37
+ precision?: number;
38
+ // 是否格式化为货币格式
39
+ formatAsCurrency?: boolean;
40
+ // 货币符号,默认为¥
41
+ currencySymbol?: string;
42
+ // 自定义格式化函数
43
+ formatter?: (value: number) => string;
44
+ }
45
+
46
+ // 默认货币计算选项
47
+ const DEFAULT_MONEY_OPTIONS: MoneyComputeOptions = {
48
+ operation: 'divide', // 默认为除法,将分转换为元
49
+ factor: 100, // 默认除法因子为100,分转元
50
+ precision: 2, // 默认保留两位小数
51
+ formatAsCurrency: false,
52
+ currencySymbol: '¥' // 人民币符号
53
+ };
54
+ /**
55
+ * 千分位格式化
56
+ #### 使用说明
57
+ ```
58
+ toThousands(1) => 1
59
+ toThousands(12345) => 12,345
60
+ toThousands('12323.12') 返回 '12,323.12'
61
+ ```
62
+ */
63
+ export function toThousands(num: number | string, defaultValue = '-'): string {
64
+ if (isEmpty(num)) {
65
+ return defaultValue;
66
+ }
67
+ let val = num.toString();
68
+
69
+ if (!val.includes('.')) {
70
+ val += '.';
71
+ }
72
+ return val.replace(/\d(?=(\d{3})+\.)/g, $0 => `${$0},`).replace(/\.$/, '');
73
+ }
74
+
75
+ /**
76
+ * 阿拉伯数组转中文数字
77
+ #### 使用说明
78
+ ```
79
+ convertCurrency(0) 返回 '壹拾贰元壹角贰分'
80
+ convertCurrency(1234567809) 返回 '壹拾贰亿叁仟肆佰伍拾陆万柒仟捌佰零玖元整'
81
+ ```
82
+ * @param {number} money 金额
83
+ */
84
+ export function convertCurrency(money = '') {
85
+ const maxNum = 999999999999999.9999; // 最大处理的数字
86
+
87
+ if (isEmpty(money)) {
88
+ return '';
89
+ }
90
+ if (Number(money) >= maxNum) {
91
+ // 超出最大处理数字
92
+ return money;
93
+ }
94
+
95
+ const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
96
+ const cnIntRadice = ['', '拾', '佰', '仟'];
97
+ const cnIntUnits = ['', '万', '亿', '兆']; // 对应整数部分扩展单位
98
+ const cnDecUnits = ['角', '分', '毫', '厘']; // 对应小数部分单位
99
+ const cnInteger = '整'; // 整数金额时后面跟的字符
100
+ const cnIntLast = '元'; // 整型完以后的单位
101
+ let integerNum; // 金额整数部分
102
+ let decimalNum; // 金额小数部分
103
+ let chineseStr = ''; // 输出的中文金额字符串
104
+ let parts; // 分离金额后用的数组,预定义
105
+ money = parseFloat(money).toString();
106
+
107
+ if (money === '0') {
108
+ chineseStr = cnNums[0] + cnIntLast + cnInteger;
109
+ return chineseStr;
110
+ }
111
+ // 转换为字符串
112
+ money = money.toString();
113
+ if (money.indexOf('.') === -1) {
114
+ integerNum = money;
115
+ decimalNum = '';
116
+ } else {
117
+ parts = money.split('.');
118
+ integerNum = parts[0];
119
+ decimalNum = parts[1].substr(0, 4);
120
+ }
121
+ // 获取整型部分转换
122
+ if (parseInt(integerNum, 10) > 0) {
123
+ let zeroCount = 0;
124
+ const intLen = integerNum.length;
125
+ for (let i = 0; i < intLen; i += 1) {
126
+ const n = integerNum.substr(i, 1);
127
+ const p = intLen - i - 1;
128
+ const q = p / 4;
129
+ const m = p % 4;
130
+ if (n === '0') {
131
+ zeroCount += 1;
132
+ } else {
133
+ if (zeroCount > 0) {
134
+ chineseStr += cnNums[0];
135
+ }
136
+ // 归零
137
+ zeroCount = 0;
138
+ chineseStr += cnNums[parseInt(n, 10)] + cnIntRadice[m];
139
+ }
140
+ if (m === 0 && zeroCount < 4) {
141
+ chineseStr += cnIntUnits[q];
142
+ }
143
+ }
144
+ chineseStr += cnIntLast;
145
+ }
146
+ // 小数部分
147
+ if (decimalNum !== '') {
148
+ const decLen = decimalNum.length;
149
+ for (let i = 0; i < decLen; i += 1) {
150
+ const dn = decimalNum.substr(i, 1);
151
+ if (dn !== '0') {
152
+ chineseStr += cnNums[Number(dn)] + cnDecUnits[i];
153
+ }
154
+ }
155
+ }
156
+ if (chineseStr === '') {
157
+ chineseStr += cnNums[0] + cnIntLast + cnInteger;
158
+ } else if (decimalNum === '') {
159
+ chineseStr += cnInteger;
160
+ }
161
+ return chineseStr;
162
+ }
163
+
164
+ /**
165
+ * 格式化小数位
166
+ #### 使用说明
167
+ ```
168
+ formatFloat('1.2345') 返回 '1.23'
169
+ formatFloat('1.2345', 3) 返回 '1.235'
170
+ formatFloat('1', 3) 返回 '1.000'
171
+ ```
172
+ * @param {*} val 小数
173
+ * @param {number} [pos=2] 保留的小数位
174
+ */
175
+ export function formatFloat(val: string | number, pos = 2): string {
176
+ let f = parseFloat(String(val));
177
+ if (Number.isNaN(f)) {
178
+ return '';
179
+ }
180
+ f = Math.round(Number(val) * Math.pow(10, pos)) / Math.pow(10, pos); // pow 幂
181
+ let s = f.toString();
182
+ let rs = s.indexOf('.');
183
+ if (rs < 0) {
184
+ rs = s.length;
185
+ s += '.';
186
+ }
187
+ while (s.length <= rs + pos) {
188
+ s += '0';
189
+ }
190
+ return s;
191
+ }
192
+
193
+ /**
194
+ * 向上取整
195
+ #### 使用说明
196
+ ```
197
+ ceil('1.23') 返回 2
198
+ ceil('1.234', 2) 返回 1.24
199
+ ceil('1.230', 2) 返回 1.23
200
+ ```
201
+ * @param {*} val
202
+ * @param {number} [pos=2] 保留的小数位
203
+ */
204
+ export function ceil(val: string | number, pos = 0) {
205
+ if (isEmpty(val)) {
206
+ return val;
207
+ }
208
+
209
+ const swell = Math.pow(10, pos);
210
+ const swellVal = Number(val) * swell;
211
+ const newVal = Math.ceil(swellVal) / swell;
212
+
213
+ return newVal;
214
+ }
215
+
216
+ /**
217
+ * 向下取整
218
+ #### 使用说明
219
+ ```
220
+ floor('1.23') 返回 1
221
+ floor('1.234', 2) 返回 1.23
222
+ floor('1.230', 2) 返回 1.23
223
+ ```
224
+ * @param {*} val
225
+ * @param {number} [pos=2] 保留的小数位
226
+ */
227
+ export function floor(val: string | number, pos = 0) {
228
+ if (isEmpty(val)) {
229
+ return val;
230
+ }
231
+
232
+ const swell = Math.pow(10, pos);
233
+ const swellVal = Number(val) * swell;
234
+ return Math.floor(swellVal) / swell;
235
+ }
236
+
237
+ /**
238
+ * 格式化货币数字
239
+ #### 使用说明
240
+ ```
241
+ formatMoney(1234) 返回 '12.34'
242
+ formatMoney(1234, { operation: 'multiply', factor: 100 }) 返回 '123400.00'
243
+ formatMoney(1234, { formatAsCurrency: true }) 返回 '¥12.34'
244
+ formatMoney(1234, { formatAsCurrency: true, currencySymbol: '$' }) 返回 '$12.34'
245
+ formatMoney(1234.567, { precision: 3 }) 返回 '12.346'
246
+ formatMoney('1,234.56', { operation: 'multiply', factor: 1 }) 返回 '1234.56'
247
+ ```
248
+ * @param value 要格式化的值
249
+ * @param options 格式化选项
250
+ * @returns 格式化后的字符串
251
+ */
252
+ export function formatMoney(value: number | string | null | undefined, options?: Partial<MoneyComputeOptions>): string {
253
+ // 先计算值
254
+ const numericResult = computeMoney(value, options);
255
+
256
+ // 合并选项
257
+ const mergedOptions: MoneyComputeOptions = {
258
+ ...DEFAULT_MONEY_OPTIONS,
259
+ ...options
260
+ };
261
+
262
+ // 格式化
263
+ if (mergedOptions.formatter) {
264
+ // 使用自定义格式化函数
265
+ return mergedOptions.formatter(numericResult);
266
+ }
267
+
268
+ // 货币格式化
269
+ if (mergedOptions.formatAsCurrency) {
270
+ const formattedNumber = numericResult.toFixed(mergedOptions.precision);
271
+ // 如果数字超过1000,使用千分位格式化
272
+ const formattedWithThousands = numericResult >= 1000 ? toThousands(formattedNumber) : formattedNumber;
273
+ return `${mergedOptions.currencySymbol}${formattedWithThousands}`;
274
+ }
275
+
276
+ // 默认格式化
277
+ return numericResult.toFixed(mergedOptions.precision);
278
+ }
279
+ /**
280
+ * 执行货币计算
281
+ #### 使用说明
282
+ ```
283
+ 分转元(除法)
284
+ computeMoney(1234) 返回 12.34
285
+ 元转分(乘法)
286
+ computeMoney(12.34, { operation: 'multiply', factor: 100 }) 返回 1234
287
+ 自定义精度
288
+ computeMoney(1234.567, { precision: 3 }) 返回 12.346
289
+ 处理带有千分位的字符串
290
+ computeMoney('1,234.56', { operation: 'multiply', factor: 1 }) 返回 1234.56
291
+ 处理空值
292
+ computeMoney(null) 返回 0
293
+ ```
294
+ * @param value 要计算的值
295
+ * @param options 计算选项
296
+ * @returns 计算结果
297
+ */
298
+ export function computeMoney(value: number | string | null | undefined, options?: Partial<MoneyComputeOptions>): number {
299
+ // 合并选项
300
+ const mergedOptions: MoneyComputeOptions = {
301
+ ...DEFAULT_MONEY_OPTIONS,
302
+ ...options
303
+ };
304
+
305
+ // 处理输入值
306
+ let numericValue: number;
307
+
308
+ // 处理可能的各种输入类型
309
+ if (value === null || value === undefined || value === '') {
310
+ return 0;
311
+ } else if (typeof value === 'string') {
312
+ // 尝试将字符串转换为数字,移除非数字字符(保留小数点和负号)
313
+ const cleanedStr = value.replace(/[^\d.-]/g, '');
314
+ numericValue = cleanedStr ? parseFloat(cleanedStr) : 0;
315
+ } else {
316
+ numericValue = value;
317
+ }
318
+
319
+ if (isNaN(numericValue)) {
320
+ return 0;
321
+ }
322
+
323
+ // 执行计算
324
+ let result: number;
325
+ if (mergedOptions.operation === 'multiply') {
326
+ result = numericValue * mergedOptions.factor;
327
+ } else {
328
+ // 防止除以0
329
+ result = mergedOptions.factor !== 0 ? numericValue / mergedOptions.factor : 0;
330
+ }
331
+
332
+ // 处理精度
333
+ if (mergedOptions.precision !== undefined) {
334
+ const multiplier = Math.pow(10, mergedOptions.precision);
335
+ result = Math.round(result * multiplier) / multiplier;
336
+ }
337
+
338
+ return result;
339
+ }
340
+
341
+ /**
342
+ * 将分转换为元并格式化为货币格式
343
+ #### 使用说明
344
+ ```
345
+ formatCentsToYuan(1234) 返回 '¥12.34'
346
+ formatCentsToYuan(123456) 返回 '¥1,234.56'
347
+ formatCentsToYuan(1234, { currencySymbol: '$' }) 返回 '$12.34'
348
+ formatCentsToYuan('1,234', { precision: 3 }) 返回 '¥12.340'
349
+ formatCentsToYuan(null) 返回 '¥0.00'
350
+ ```
351
+ * @param cents 分值
352
+ * @param options 格式化选项
353
+ * @returns 格式化后的字符串
354
+ */
355
+ export function formatCentsToYuan(cents: number | string | null | undefined, options?: Partial<Omit<MoneyComputeOptions, 'operation'>>) {
356
+ return formatMoney(cents, {
357
+ operation: 'divide',
358
+ factor: 100,
359
+ formatAsCurrency: true,
360
+ ...options
361
+ });
362
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * object处理相关
3
+ * @packageDocumentation
4
+ * @module Object
5
+ * @preferred
6
+ */
7
+
8
+ /**
9
+ * 比较两者的值是否相等
10
+ #### 使用说明
11
+ ```
12
+ deepEqual({a: 10}, {a: 10}) 返回 true
13
+ deepEqual({a: 10}, {a: 10, b: 20}) 返回 false
14
+ ```
15
+ * @param {*} x 用来比较的值
16
+ * @param {*} y 另一个用来比较的值
17
+ */
18
+ export function deepEqual(x, y) {
19
+ const ok = Object.keys,
20
+ tx = typeof x,
21
+ ty = typeof y;
22
+ return x && y && tx === 'object' && tx === ty
23
+ ? ok(x).length === ok(y).length && ok(x).every(key => deepEqual(x[key], y[key]))
24
+ : x === y;
25
+ }
26
+ function getNodePath(node, nodeValue, nodeKey, childrenKey, path: any[] = []) {
27
+ path.push(node);
28
+ if (node[nodeKey] === nodeValue) {
29
+ throw 'GOT IT!';
30
+ }
31
+ if (node[childrenKey] && node[childrenKey].length > 0) {
32
+ for (let i = 0; i < node[childrenKey].length; i++) {
33
+ getNodePath(node[childrenKey][i], nodeValue, nodeKey, childrenKey, path);
34
+ }
35
+ }
36
+ path.pop();
37
+ }
38
+ /**
39
+ * 查找树结构节点路径
40
+ * @param {*} nodeValue 节点值
41
+ * @param {string} nodeKey 节点键
42
+ * @param {Array} treeArray 树数据
43
+ * @param {string} children 子集键
44
+ */
45
+ export function findNodePath(nodeValue, nodeKey, treeArray, childrenKey) {
46
+ const path: any[] = [];
47
+ try {
48
+ for (let i = 0; i < treeArray.length; i++) {
49
+ getNodePath(treeArray[i], nodeValue, nodeKey, childrenKey, path);
50
+ }
51
+ } catch (e) {
52
+ return path;
53
+ }
54
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * print处理相关
3
+ * @packageDocumentation
4
+ * @module Print
5
+ * @preferred
6
+ */
7
+
8
+ // https://juejin.im/post/5de4f87cf265da05c7722c20
9
+ /**
10
+ * 获取、设置打印样式
11
+ * @returns
12
+ */
13
+ function getStyle() {
14
+ const styleContent = `#print-container {
15
+ display: none;
16
+ }
17
+ @media print {
18
+ body > :not(.print-container) {
19
+ display: none;
20
+ }
21
+ html,
22
+ body {
23
+ display: block !important;
24
+ }
25
+ #print-container {
26
+ display: block;
27
+ }
28
+ }`;
29
+ const style = document.createElement('style');
30
+ style.innerHTML = styleContent;
31
+ return style;
32
+ }
33
+
34
+ /**
35
+ * 清空打印内容
36
+ */
37
+ function cleanPrint() {
38
+ const div = document.getElementById('print-container');
39
+ if (div) {
40
+ document.querySelector('body')!.removeChild(div);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 新建DOM,将需要打印的内容填充到DOM
46
+ *
47
+ * @param {*} html
48
+ * @returns
49
+ */
50
+ function getContainer(html) {
51
+ cleanPrint();
52
+ const container = document.createElement('div');
53
+ container.setAttribute('id', 'print-container');
54
+ container.innerHTML = html;
55
+ return container;
56
+ }
57
+
58
+ /**
59
+ * 图片完全加载后再调用打印方法
60
+ * @param {*} dom
61
+ * @returns
62
+ */
63
+ function getLoadPromise(dom) {
64
+ let imgs = dom.querySelectorAll('img');
65
+ imgs = [].slice.call(imgs);
66
+
67
+ if (imgs.length === 0) {
68
+ return Promise.resolve();
69
+ }
70
+
71
+ let finishedCount = 0;
72
+ return new Promise((resolve) => {
73
+ function check() {
74
+ finishedCount += 1;
75
+ if (finishedCount === imgs.length) {
76
+ resolve();
77
+ }
78
+ }
79
+ imgs.forEach((img) => {
80
+ img.addEventListener('load', check);
81
+ img.addEventListener('error', check);
82
+ });
83
+ });
84
+ }
85
+
86
+ /**
87
+ * 打印指定 HTML
88
+ * @param {*} html
89
+ */
90
+ export default function printHtml(html) {
91
+ const style = getStyle();
92
+ const container = getContainer(html);
93
+
94
+ document.body.appendChild(style);
95
+ document.body.appendChild(container);
96
+
97
+ getLoadPromise(container).then(() => {
98
+ window.print();
99
+ document.body.removeChild(style);
100
+ document.body.removeChild(container);
101
+ });
102
+ }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * string处理相关
3
+ * @packageDocumentation
4
+ * @module String
5
+ * @preferred
6
+ */
7
+ import { isEmpty } from '../common';
8
+
9
+ /**
10
+ * 空值处理
11
+ */
12
+ export function formatEmptyValue(value: any, defaultValue = '-') {
13
+ if (isEmpty(value)) {
14
+ return defaultValue;
15
+ }
16
+ return value;
17
+ }
18
+
19
+ /**
20
+ * 格式化手机
21
+ #### 使用说明
22
+ * ```
23
+ * formatPhone('18211572781') => '182 1157 2781'
24
+ * formatPhone('18211572781', '-') => '182-1157-2781'
25
+ * ```
26
+ * @param {string | number} phone 手机号
27
+ * @param {string} [separator=' '] 连接符
28
+ */
29
+ export function formatPhone(phone: string | number, separator = ' ', defaultValue = '-') {
30
+ if (isEmpty(phone)) {
31
+ return defaultValue;
32
+ }
33
+ if (phone.toString().length !== 11) {
34
+ return phone;
35
+ }
36
+ const val = phone.toString().replace(/[^\d]/g, '');
37
+ const arr = val.split('');
38
+ let str = '';
39
+ arr.forEach((v, index) => {
40
+ if (index === 3 || index === 7) {
41
+ str += separator;
42
+ }
43
+ str += v;
44
+ });
45
+ return str;
46
+ }
47
+
48
+ /**
49
+ * 隐藏手机号
50
+ #### 使用说明
51
+ * ```
52
+ * formatPhoneHide('18211572781') => '182****2781'
53
+ * formatPhoneHide('', '-') => '-'
54
+ * ```
55
+ * @param {string | number} phone 手机号
56
+ * @param {string} defaultValue 没有值放回的默认值
57
+ */
58
+ export function formatPhoneHide(phone: string | number, defaultValue = '') {
59
+ if (isEmpty(phone)) {
60
+ return defaultValue;
61
+ }
62
+ if (phone.toString().length !== 11) {
63
+ return phone;
64
+ }
65
+ const phoneNumber = phone.toString();
66
+ return `${phoneNumber.substr(0, 3)}****${phoneNumber.substr(7, 11)}`;
67
+ }
68
+
69
+ /**
70
+ * 格式化银行卡 (4位一空格)
71
+ #### 使用说明
72
+ * ```
73
+ * formatBank('6282356862823568123') => '6282 3568 6282 3568 123'
74
+ * formatBank('', '-') => '-'
75
+ * ```
76
+ * @param {strubg | number} val 银行卡号
77
+ * @param {string} defaultValue 没有值放回的默认值
78
+ */
79
+ export function formatBank(val: string | number, defaultValue = '') {
80
+ if (isEmpty(val)) {
81
+ return defaultValue;
82
+ }
83
+ return val.toString().replace(/\s/g, '').replace(/(.{4})/g, '$1 ');
84
+ }
85
+
86
+ /**
87
+ * 生成26个字母列表
88
+ #### 使用说明
89
+ * ```
90
+ * generateEnglishLetters() => ['A', ..., 'Z'];
91
+ * ```
92
+ */
93
+ export function generateEnglishLetters() {
94
+ const englishLettersList: string[] = [];
95
+ for (let i = 65; i < 91; i += 1) {
96
+ englishLettersList.push(String.fromCharCode(i));
97
+ }
98
+ return englishLettersList;
99
+ }
100
+
101
+ /**
102
+ * 驼峰式命名转短横线命名
103
+ *
104
+ * @export
105
+ * @param {*} [string='']
106
+ * @returns
107
+ */
108
+ export function humpTurnDashed(string = '') {
109
+ const rE = /\B([A-Z])/g;
110
+ return string.replace(rE, '-$1').toLowerCase();
111
+ }
112
+
113
+ /**
114
+ * 获取指定分隔符之前的子字符串
115
+ * @param str - 源字符串
116
+ * @param delimiter - 分隔符
117
+ * @returns 分隔符之前的字符串
118
+ */
119
+ export function subBefore(str: string, delimiter: string): string {
120
+ if (!str || typeof delimiter !== 'string') return '';
121
+ return str.substring(0, str.indexOf(delimiter));
122
+ }
123
+
124
+ /**
125
+ * 解析 URL 查询参数为对象
126
+ * @param url - URL 字符串
127
+ * @returns 查询参数对象
128
+ */
129
+ export function getQueryMap(url: string): Record<string, string> {
130
+ const pattern = /^(?:(https?|ftp|rtsp|mms|ws|wss):\/\/)?/i;
131
+ if (!pattern.test(url)) {
132
+ console.error(`${url}不符合超链接规范`);
133
+ return {};
134
+ }
135
+
136
+ const queryIndex = url.indexOf('?');
137
+ if (queryIndex === -1) return {};
138
+
139
+ const queryString = url.slice(queryIndex + 1);
140
+ const params = queryString.split('&');
141
+ const result: Record<string, string> = {};
142
+
143
+ for (const param of params) {
144
+ const [key, value] = param.split('=');
145
+ if (key) {
146
+ result[key] = value || '';
147
+ }
148
+ }
149
+
150
+ return result;
151
+ }
152
+
153
+ /**
154
+ * 隐藏字符串中指定位置的字符
155
+ * @param text - 源字符串或数字
156
+ * @param indexes - 要隐藏的位置索引数组或位置对象数组
157
+ * @param mask - 掩码字符,默认为 '*'
158
+ * @returns 处理后的字符串
159
+ */
160
+ export function hideTextAtIndex(
161
+ text: string | number,
162
+ indexes: number | { start: number; end: number } | (number | { start: number; end: number })[],
163
+ mask: string = '*'
164
+ ): string {
165
+ if (typeof text === 'number') {
166
+ text = text.toString();
167
+ }
168
+
169
+ if (!Array.isArray(indexes)) {
170
+ indexes = [indexes];
171
+ }
172
+
173
+ const chars = text.split('');
174
+
175
+ for (const index of indexes) {
176
+ if (typeof index === 'object' && !Array.isArray(index)) {
177
+ const { start, end } = index;
178
+ if (start >= 0 && start < end && end < chars.length) {
179
+ chars.fill(mask, start, end + 1);
180
+ }
181
+ } else if (typeof index === 'number' && Number.isInteger(index) && index >= 0) {
182
+ if (index < chars.length) {
183
+ chars[index] = mask;
184
+ }
185
+ }
186
+ }
187
+
188
+ return chars.join('');
189
+ }