@allkit/shared 0.0.3 → 0.0.5
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.
- package/package.json +21 -26
- package/dist/README.md +0 -1
- package/dist/package.json +0 -21
- package/eslint.config.js +0 -10
- package/scripts/build.mjs +0 -95
- package/skills/SKILL.md +0 -240
- package/skills/examples/usage.ts +0 -88
- package/skills/references/clipboard.md +0 -39
- package/skills/references/cloneDeep.md +0 -60
- package/skills/references/cookie.md +0 -56
- package/skills/references/date.md +0 -466
- package/skills/references/device.md +0 -138
- package/skills/references/element.md +0 -99
- package/skills/references/is.md +0 -415
- package/skills/references/lodash.md +0 -472
- package/skills/references/number.md +0 -248
- package/skills/references/storage.md +0 -113
- package/skills/references/string.md +0 -126
- package/skills/references/timer.md +0 -78
- package/src/clipboard/index.ts +0 -26
- package/src/cloneDeep/__test__/cloneDeep.test.ts +0 -92
- package/src/cloneDeep/index.ts +0 -168
- package/src/constants.ts +0 -27
- package/src/cookie/index.ts +0 -44
- package/src/date/__test__/date-diff.test.ts +0 -23
- package/src/date/__test__/date-duration.test.ts +0 -140
- package/src/date/__test__/date-from.test.ts +0 -64
- package/src/date/__test__/date.test.ts +0 -67
- package/src/date/index.ts +0 -331
- package/src/device/__test__/device.test.ts +0 -138
- package/src/device/index.ts +0 -125
- package/src/element/index.ts +0 -100
- package/src/index.ts +0 -14
- package/src/is/__test__/is.test.ts +0 -320
- package/src/is/index.ts +0 -293
- package/src/lodash/__test__/lodash.test.ts +0 -111
- package/src/lodash/__test__/obj-string.test.ts +0 -107
- package/src/lodash/__test__/uniqueId.test.ts +0 -40
- package/src/lodash/index.ts +0 -396
- package/src/number/__test__/number.test.ts +0 -137
- package/src/number/index.ts +0 -161
- package/src/storage/__test__/storage.test.ts +0 -44
- package/src/storage/index.ts +0 -185
- package/src/string/__test__/string.test.ts +0 -24
- package/src/string/index.ts +0 -49
- package/src/timer/index.ts +0 -15
- package/tsconfig.json +0 -25
- package/types/global.d.ts +0 -13
- package/vite.config.ts +0 -16
- package/vitest.config.ts +0 -28
- /package/{dist/clipboard → clipboard}/index.d.ts +0 -0
- /package/{dist/cloneDeep → cloneDeep}/index.d.ts +0 -0
- /package/{dist/constants.d.ts → constants.d.ts} +0 -0
- /package/{dist/cookie → cookie}/index.d.ts +0 -0
- /package/{dist/date → date}/index.d.ts +0 -0
- /package/{dist/device → device}/index.d.ts +0 -0
- /package/{dist/element → element}/index.d.ts +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/is → is}/index.d.ts +0 -0
- /package/{dist/lodash → lodash}/index.d.ts +0 -0
- /package/{dist/number → number}/index.d.ts +0 -0
- /package/{dist/shared.es.d.ts → shared.es.d.ts} +0 -0
- /package/{dist/shared.es.js → shared.es.js} +0 -0
- /package/{dist/shared.umd.js → shared.umd.js} +0 -0
- /package/{dist/skills → skills/shared}/SKILL.md +0 -0
- /package/{dist/skills → skills/shared}/examples/usage.ts +0 -0
- /package/{dist/skills → skills/shared}/references/clipboard.md +0 -0
- /package/{dist/skills → skills/shared}/references/cloneDeep.md +0 -0
- /package/{dist/skills → skills/shared}/references/cookie.md +0 -0
- /package/{dist/skills → skills/shared}/references/date.md +0 -0
- /package/{dist/skills → skills/shared}/references/device.md +0 -0
- /package/{dist/skills → skills/shared}/references/element.md +0 -0
- /package/{dist/skills → skills/shared}/references/is.md +0 -0
- /package/{dist/skills → skills/shared}/references/lodash.md +0 -0
- /package/{dist/skills → skills/shared}/references/number.md +0 -0
- /package/{dist/skills → skills/shared}/references/storage.md +0 -0
- /package/{dist/skills → skills/shared}/references/string.md +0 -0
- /package/{dist/skills → skills/shared}/references/timer.md +0 -0
- /package/{dist/storage → storage}/index.d.ts +0 -0
- /package/{dist/string → string}/index.d.ts +0 -0
- /package/{dist/timer → timer}/index.d.ts +0 -0
package/src/lodash/index.ts
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
import { isDef, isNullOrUnDef, isObject } from '../is'
|
|
2
|
-
|
|
3
|
-
type OmitUndefined<T> = {
|
|
4
|
-
[K in keyof T]: undefined extends T[K] ? never : T[K]
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 删除对象中的某些键值对
|
|
9
|
-
* @param obj - 源对象
|
|
10
|
-
* @param fields - 需要删除的字段数组
|
|
11
|
-
* @param ignoreEmpty - 是否忽略 undefined | null
|
|
12
|
-
* @returns 返回新的对象
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* const obj = {
|
|
16
|
-
a: 1,
|
|
17
|
-
b: {
|
|
18
|
-
c: 2,
|
|
19
|
-
d: {
|
|
20
|
-
e: 3,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
c: '',
|
|
24
|
-
d: undefined,
|
|
25
|
-
}
|
|
26
|
-
const cloneObj = omit(obj, ['a'])
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export function omit<T extends Record<string, any>, K extends keyof T>(
|
|
30
|
-
obj: T,
|
|
31
|
-
fields: K[] | string[],
|
|
32
|
-
ignoreEmpty = true,
|
|
33
|
-
): Omit<T, K> {
|
|
34
|
-
if (!obj) return {} as Omit<T, K>
|
|
35
|
-
if (!ignoreEmpty) {
|
|
36
|
-
return fields.reduce((result, key) => {
|
|
37
|
-
const { [key]: _, ...rest } = result
|
|
38
|
-
return rest as T
|
|
39
|
-
}, obj)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const result = {} as OmitUndefined<T>
|
|
43
|
-
for (const key in obj) {
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
if (!fields.includes(key) && !isNullOrUnDef(obj[key])) {
|
|
46
|
-
result[key] = obj[key]
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return result
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type Writeable<T> = { -readonly [P in keyof T]: T[P] }
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 从对象中取出指定的键值对
|
|
56
|
-
* @param obj - 源对象
|
|
57
|
-
* @param keys - 需要取出的键值对
|
|
58
|
-
* @param ignoreEmpty - 是否忽略 undefined
|
|
59
|
-
* @returns 返回新的对象
|
|
60
|
-
* @example
|
|
61
|
-
* ```ts
|
|
62
|
-
* const obj = {
|
|
63
|
-
a: 1,
|
|
64
|
-
b: {
|
|
65
|
-
c: 2,
|
|
66
|
-
d: {
|
|
67
|
-
e: 3,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
}
|
|
71
|
-
const cloneObj = pick(obj, ['b'])
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
export function pick<T, U extends keyof T>(
|
|
75
|
-
obj: T,
|
|
76
|
-
keys: ReadonlyArray<U> | string[],
|
|
77
|
-
ignoreUndefined = true,
|
|
78
|
-
) {
|
|
79
|
-
if (!obj) return {} as Writeable<Pick<T, U>>
|
|
80
|
-
return keys.reduce(
|
|
81
|
-
(ret, key) => {
|
|
82
|
-
const value = obj[key as U]
|
|
83
|
-
if (!ignoreUndefined || isDef(value)) {
|
|
84
|
-
ret[key as U] = value
|
|
85
|
-
}
|
|
86
|
-
return ret
|
|
87
|
-
},
|
|
88
|
-
{} as Writeable<Pick<T, U>>,
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 从对象中取出指定的键值对,第二个参数predicate(断言函数)
|
|
94
|
-
* @param obj - 源对象
|
|
95
|
-
* @param predicate - 断言函数,判断为真值的属性会被返回
|
|
96
|
-
* @returns 返回新的对象
|
|
97
|
-
* @example
|
|
98
|
-
* ```ts
|
|
99
|
-
* const obj = {
|
|
100
|
-
a: 1,
|
|
101
|
-
b: {
|
|
102
|
-
c: 2,
|
|
103
|
-
d: {
|
|
104
|
-
e: 3,
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
c: '',
|
|
108
|
-
d: undefined,
|
|
109
|
-
}
|
|
110
|
-
const cloneObj = pickBy(obj, (value) => !!value)
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
export function pickBy<T>(
|
|
114
|
-
obj: T,
|
|
115
|
-
predicate: (val: NonNullable<T>[keyof T], key: keyof T) => boolean,
|
|
116
|
-
) {
|
|
117
|
-
if (!obj) return {} as Partial<T>
|
|
118
|
-
const ret: Partial<T> = {}
|
|
119
|
-
for (const key in obj) {
|
|
120
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
121
|
-
const typedKey = key as keyof T
|
|
122
|
-
const value = obj[typedKey]
|
|
123
|
-
if (predicate(value, typedKey)) {
|
|
124
|
-
ret[typedKey] = value
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return ret
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
type OmitBy<T, F> = { [K in keyof T as F extends T[K] ? never : K]: T[K] }
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* 从对象中去除属性,通过第二个参数predicate(断言函数)
|
|
135
|
-
* 与 {@link pickBy} 反向版
|
|
136
|
-
* @param obj - 源对象
|
|
137
|
-
* @param predicate - 断言函数,判断为真值的属性会被**去除**
|
|
138
|
-
* @returns 返回新的对象
|
|
139
|
-
* @example
|
|
140
|
-
* ```ts
|
|
141
|
-
* const obj = {
|
|
142
|
-
a: 1,
|
|
143
|
-
b: {
|
|
144
|
-
c: 2,
|
|
145
|
-
d: {
|
|
146
|
-
e: 3,
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
c: '',
|
|
150
|
-
d: undefined,
|
|
151
|
-
}
|
|
152
|
-
const cloneObj = omitBy(obj, (value) => !value)
|
|
153
|
-
* ```
|
|
154
|
-
*/
|
|
155
|
-
export function omitBy<T>(
|
|
156
|
-
object: T,
|
|
157
|
-
predicate: (value: T[keyof T], key: keyof T) => boolean,
|
|
158
|
-
): OmitBy<T, T[keyof T]> {
|
|
159
|
-
const result = { ...object }
|
|
160
|
-
for (const key in object) {
|
|
161
|
-
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
|
162
|
-
const typedKey = key as keyof T
|
|
163
|
-
const value = object[typedKey]
|
|
164
|
-
if (predicate(value, typedKey)) {
|
|
165
|
-
delete result[key]
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return result
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 防抖函数
|
|
174
|
-
* @param fn - 需要防抖的函数
|
|
175
|
-
* @param delay - 防抖的时间
|
|
176
|
-
* @example
|
|
177
|
-
* ```ts
|
|
178
|
-
* let handleSearch =()=>{}
|
|
179
|
-
* const debounceSearch = debounce(handleSearch)
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
export const debounce = (fn: Function, delay = 0) => {
|
|
183
|
-
let timer: number
|
|
184
|
-
|
|
185
|
-
return function (this: unknown, ...args: any[]) {
|
|
186
|
-
if (timer) {
|
|
187
|
-
window.clearTimeout(timer)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
timer = window.setTimeout(() => {
|
|
191
|
-
fn.apply(this, args)
|
|
192
|
-
}, delay)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* 节流函数
|
|
198
|
-
* @param fn - 需要节流的函数
|
|
199
|
-
* @param limit - 节流的时间
|
|
200
|
-
* @param exeLastFunc - 是否执行最后一次函数
|
|
201
|
-
* @example
|
|
202
|
-
* ```ts
|
|
203
|
-
* let handleSearch =()=>{}
|
|
204
|
-
* const throttleSearch = throttle(handleSearch)
|
|
205
|
-
* ```
|
|
206
|
-
*/
|
|
207
|
-
export const throttle = <T extends (...args: any) => any>(
|
|
208
|
-
fn: T,
|
|
209
|
-
limit = 200,
|
|
210
|
-
exeLastFunc = true,
|
|
211
|
-
) => {
|
|
212
|
-
let timer: number
|
|
213
|
-
let start = 0
|
|
214
|
-
|
|
215
|
-
return function loop(this: unknown, ...args: Parameters<T>) {
|
|
216
|
-
const now = Date.now()
|
|
217
|
-
const duration = now - start
|
|
218
|
-
if (duration >= limit) {
|
|
219
|
-
fn.apply(this, args)
|
|
220
|
-
// 更新最后执行时间
|
|
221
|
-
start = now
|
|
222
|
-
} else if (exeLastFunc) {
|
|
223
|
-
window.clearTimeout(timer)
|
|
224
|
-
timer = window.setTimeout(() => {
|
|
225
|
-
fn.apply(this, args)
|
|
226
|
-
start = Date.now()
|
|
227
|
-
}, limit - duration)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let counter = 0
|
|
233
|
-
/**
|
|
234
|
-
* 生成当前浏览器唯一id
|
|
235
|
-
* (自增+页面渲染时间+ random随机数)
|
|
236
|
-
* 对于一般的唯一ID 生成来说是足够的
|
|
237
|
-
* @param length -随机数长度 默认16
|
|
238
|
-
* @example
|
|
239
|
-
* ```ts
|
|
240
|
-
* let uid11 = uniqueId(11) //11位
|
|
241
|
-
* let uid16 = uniqueId() //16位
|
|
242
|
-
* let uid19 = uniqueId(19) //19 位
|
|
243
|
-
* let uid32 = uniqueId(32)
|
|
244
|
-
* ```
|
|
245
|
-
* @returns 唯一id
|
|
246
|
-
*/
|
|
247
|
-
export function uniqueId(length: number = 16): string {
|
|
248
|
-
const pNow = new Date().getTime().toString()
|
|
249
|
-
const random = Math.random().toString().replace('.', '').slice(1, 10)
|
|
250
|
-
const padLength = length - pNow.length - counter.toString().length - random.length
|
|
251
|
-
const padding = '0'.repeat(padLength > 0 ? padLength : 0)
|
|
252
|
-
const uniqueId = '' + `${counter}${pNow}${padding}${random}`.slice(0, length)
|
|
253
|
-
counter++
|
|
254
|
-
return uniqueId
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* 将 Blob 对象转换为 base64 字符串
|
|
259
|
-
* @param blob - Blob 对象
|
|
260
|
-
* @param ignorePrefix - 是否忽略 dateUrl 前缀
|
|
261
|
-
* @example
|
|
262
|
-
* ```ts
|
|
263
|
-
* let base64 = blobToBase64(blob)
|
|
264
|
-
* let base64 = blobToBase64(blob,false)
|
|
265
|
-
* ```
|
|
266
|
-
*/
|
|
267
|
-
export function blobToBase64(blob: Blob, ignorePrefix = true): Promise<string> {
|
|
268
|
-
return new Promise((resolve, reject) => {
|
|
269
|
-
const reader = new FileReader()
|
|
270
|
-
reader.onloadend = () => {
|
|
271
|
-
if (typeof reader.result === 'string') {
|
|
272
|
-
if (ignorePrefix) {
|
|
273
|
-
return resolve(reader.result && reader.result.split(',')[1])
|
|
274
|
-
}
|
|
275
|
-
resolve(reader.result)
|
|
276
|
-
} else {
|
|
277
|
-
reject(new Error('Failed to convert Blob to base64'))
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
reader.onerror = reject
|
|
281
|
-
reader.readAsDataURL(blob)
|
|
282
|
-
})
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
type DeepKeyOf<T> = T extends object
|
|
286
|
-
? {
|
|
287
|
-
[K in keyof T]-?: K extends string ? K | `${K}.${DeepKeyOf<T[K]>}` : never
|
|
288
|
-
}[keyof T]
|
|
289
|
-
: never
|
|
290
|
-
|
|
291
|
-
type DeepValueOf<T, K extends string> = K extends `${infer P}.${infer Rest}`
|
|
292
|
-
? P extends keyof T
|
|
293
|
-
? DeepValueOf<T[P], Rest>
|
|
294
|
-
: ''
|
|
295
|
-
: K extends keyof T
|
|
296
|
-
? T[K]
|
|
297
|
-
: ''
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* 从对象中获取指定路径的值
|
|
301
|
-
* @param object -对象
|
|
302
|
-
* @param path - 指定路径
|
|
303
|
-
* @example
|
|
304
|
-
* ```ts
|
|
305
|
-
* const obj = {
|
|
306
|
-
* a: { b: { c: 3 } }
|
|
307
|
-
* }
|
|
308
|
-
* const value = get(obj, 'a.b.c')
|
|
309
|
-
* ```
|
|
310
|
-
*/
|
|
311
|
-
export function get<T, K extends DeepKeyOf<T>>(obj: T, path: K) {
|
|
312
|
-
const keys = (path as string).split('.')
|
|
313
|
-
let result = obj
|
|
314
|
-
|
|
315
|
-
keys.forEach((key) => {
|
|
316
|
-
result = isObject(result) ? (result[key] ?? '') : ''
|
|
317
|
-
})
|
|
318
|
-
return result as DeepValueOf<T, K>
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* 对象转查询字符串
|
|
323
|
-
* @param obj - 对象
|
|
324
|
-
* @returns 查询字符串
|
|
325
|
-
* @example
|
|
326
|
-
* ```ts
|
|
327
|
-
* const str = objToQString({ a: 1, b: 2 })
|
|
328
|
-
* // 输出: 'a=1&b=2'
|
|
329
|
-
* ```
|
|
330
|
-
*/
|
|
331
|
-
export function objToQString(obj: Record<string, any>): string {
|
|
332
|
-
if (!obj || typeof obj !== 'object') return ''
|
|
333
|
-
return Object.entries(obj)
|
|
334
|
-
.map(([key, value]) => {
|
|
335
|
-
if (value === undefined || value === null) return ''
|
|
336
|
-
const strValue = typeof value === 'object' ? JSON.stringify(value) : String(value)
|
|
337
|
-
return `${key}=${encodeURIComponent(strValue)}`
|
|
338
|
-
})
|
|
339
|
-
.filter(Boolean)
|
|
340
|
-
.join('&')
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* 查询字符串转对象
|
|
345
|
-
* @param qString - 查询字符串
|
|
346
|
-
* @returns 对象
|
|
347
|
-
* @example
|
|
348
|
-
* ```ts
|
|
349
|
-
* const obj = qStringToObj('a=1&b=2')
|
|
350
|
-
* // 输出: { a: 1, b: 2 }
|
|
351
|
-
* ```
|
|
352
|
-
*/
|
|
353
|
-
export function qStringToObj(qString: string | null): Record<string, any> {
|
|
354
|
-
if (!qString || typeof qString !== 'string') return {}
|
|
355
|
-
|
|
356
|
-
// 快速检测空字符串
|
|
357
|
-
if (!qString.trim()) return {}
|
|
358
|
-
|
|
359
|
-
// 快速检测完整 URL 且无查询参数
|
|
360
|
-
if (/^https?:\/\/[^?]+$/.test(qString)) return {}
|
|
361
|
-
|
|
362
|
-
// 提取查询参数部分,支持完整 URL 格式
|
|
363
|
-
const queryPart = qString.includes('?') ? qString.split('?')[1] : qString
|
|
364
|
-
if (!queryPart) return {}
|
|
365
|
-
|
|
366
|
-
const result: Record<string, any> = {}
|
|
367
|
-
const pairs = queryPart.split('&')
|
|
368
|
-
|
|
369
|
-
for (const pair of pairs) {
|
|
370
|
-
const [key, value] = pair.split('=')
|
|
371
|
-
if (!key) continue
|
|
372
|
-
|
|
373
|
-
const decodedKey = decodeURIComponent(key)
|
|
374
|
-
const decodedValue = value ? decodeURIComponent(value) : ''
|
|
375
|
-
|
|
376
|
-
// 尝试解析 JSON 值(数组和对象)
|
|
377
|
-
let parsedValue: any = decodedValue
|
|
378
|
-
try {
|
|
379
|
-
parsedValue = JSON.parse(decodedValue)
|
|
380
|
-
} catch {
|
|
381
|
-
// JSON 解析失败,继续处理
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// 尝试转换为数字(如果不是对象或数组)
|
|
385
|
-
if (typeof parsedValue !== 'object' || parsedValue === null) {
|
|
386
|
-
const num = Number(parsedValue)
|
|
387
|
-
if (!isNaN(num)) {
|
|
388
|
-
parsedValue = num
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
result[decodedKey] = parsedValue
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return result
|
|
396
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import { formatNumber, formatMoney } from '../'
|
|
3
|
-
|
|
4
|
-
describe('number.ts - formatNumber', () => {
|
|
5
|
-
test('formatNumber - param is null', () => {
|
|
6
|
-
const num = null
|
|
7
|
-
const expected = ''
|
|
8
|
-
//@ts-ignore
|
|
9
|
-
const formatNum = formatNumber(num)
|
|
10
|
-
expect(expected).toEqual(formatNum)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test('formatNumber - param is NaN', () => {
|
|
14
|
-
const num = '***'
|
|
15
|
-
const expected = '***'
|
|
16
|
-
const formatNum = formatNumber(num)
|
|
17
|
-
expect(expected).toEqual(formatNum)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test('formatNumber - param is number', () => {
|
|
21
|
-
const num = 1000
|
|
22
|
-
const expected = '1,000'
|
|
23
|
-
const formatNum = formatNumber(num)
|
|
24
|
-
expect(expected).toEqual(formatNum)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test('formatNumber - param is number', () => {
|
|
28
|
-
const num = 1234567
|
|
29
|
-
const expected = '1,234,567'
|
|
30
|
-
const formatNum = formatNumber(num)
|
|
31
|
-
expect(expected).toEqual(formatNum)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
test('formatNumber - param is string', () => {
|
|
35
|
-
const num = '1000'
|
|
36
|
-
const expected = '1,000'
|
|
37
|
-
const formatNum = formatNumber(num)
|
|
38
|
-
expect(expected).toEqual(formatNum)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
test('formatNumber - param is string', () => {
|
|
42
|
-
const num = 1234567
|
|
43
|
-
const expected = '1,234,567'
|
|
44
|
-
const formatNum = formatNumber(num)
|
|
45
|
-
expect(expected).toEqual(formatNum)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
test('formatNumber - param is 0', () => {
|
|
49
|
-
const num = 0
|
|
50
|
-
const expected = '0'
|
|
51
|
-
const formatNum = formatNumber(num)
|
|
52
|
-
expect(expected).toEqual(formatNum)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('formatNumber - param is decimal', () => {
|
|
56
|
-
const num = 1234.5678
|
|
57
|
-
const expected = '1,235'
|
|
58
|
-
const formatNum = formatNumber(num)
|
|
59
|
-
expect(expected).toEqual(formatNum)
|
|
60
|
-
})
|
|
61
|
-
test('formatNumber - param is negative', () => {
|
|
62
|
-
const num = -1234.5678
|
|
63
|
-
const expected = '-1,235'
|
|
64
|
-
const formatNum = formatNumber(num)
|
|
65
|
-
expect(expected).toEqual(formatNum)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
test('formatNumber - param is precision eq 2', () => {
|
|
69
|
-
const num = 1234.5678
|
|
70
|
-
const expected = '1,234.57'
|
|
71
|
-
const formatNum = formatNumber(num, { precision: 2 })
|
|
72
|
-
expect(expected).toEqual(formatNum)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('formatNumber - param is precision eq 2', () => {
|
|
76
|
-
const num = 1234
|
|
77
|
-
const expected = '1,234.00'
|
|
78
|
-
const formatNum = formatNumber(num, { precision: 2 })
|
|
79
|
-
expect(expected).toEqual(formatNum)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('number.ts - formatMoney', () => {
|
|
84
|
-
test('formatMoney - 小于等于4位', () => {
|
|
85
|
-
expect(formatMoney(0)).toBe('0')
|
|
86
|
-
expect(formatMoney(1)).toBe('1')
|
|
87
|
-
expect(formatMoney(999)).toBe('999')
|
|
88
|
-
expect(formatMoney(9999)).toBe('9,999')
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
test('formatMoney - 5-8位 (万)', () => {
|
|
92
|
-
expect(formatMoney(10000)).toBe('1万')
|
|
93
|
-
expect(formatMoney(100000)).toBe('10万')
|
|
94
|
-
expect(formatMoney(1000000)).toBe('100万')
|
|
95
|
-
expect(formatMoney(10000000)).toBe('1,000万')
|
|
96
|
-
expect(formatMoney(12345678)).toBe('1,234万5,678')
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test('formatMoney - 9-12位 (亿)', () => {
|
|
100
|
-
expect(formatMoney(100000000)).toBe('1亿')
|
|
101
|
-
expect(formatMoney(1000000000)).toBe('10亿')
|
|
102
|
-
expect(formatMoney(1234567890)).toBe('12亿3,456万7,890')
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test('formatMoney - 大于12位', () => {
|
|
106
|
-
expect(formatMoney(100000000000)).toBe('1,000亿')
|
|
107
|
-
expect(formatMoney(12345678901234)).toBe('123,456亿7890万1234')
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
test('formatMoney - 负数', () => {
|
|
111
|
-
expect(formatMoney(-1234)).toBe('-1,234')
|
|
112
|
-
expect(formatMoney(-10000000)).toBe('-1,000万')
|
|
113
|
-
expect(formatMoney(-100000000)).toBe('-1亿')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
test('formatMoney - 自定义单位', () => {
|
|
117
|
-
expect(formatMoney(123456789, { yi: 'y', wan: 'w' })).toBe('1y2,345w6,789')
|
|
118
|
-
expect(formatMoney(10000000, { yi: 'Y', wan: 'W' })).toBe('1,000W')
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
test('formatMoney - 禁用千位分隔符', () => {
|
|
122
|
-
expect(formatMoney(123456789, { thousandSeparator: '' })).toBe('1亿2345万6789')
|
|
123
|
-
expect(formatMoney(10000000, { thousandSeparator: '' })).toBe('1000万')
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
test('formatMoney - null/undefined/NaN', () => {
|
|
127
|
-
expect(formatMoney(null as any)).toBe('')
|
|
128
|
-
expect(formatMoney(undefined as any)).toBe('')
|
|
129
|
-
expect(formatMoney(NaN)).toBe('')
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
test('formatMoney - 边界值', () => {
|
|
133
|
-
expect(formatMoney(1000)).toBe('1,000')
|
|
134
|
-
expect(formatMoney(9999)).toBe('9,999')
|
|
135
|
-
expect(formatMoney(10000)).toBe('1万')
|
|
136
|
-
})
|
|
137
|
-
})
|
package/src/number/index.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import Big from 'big.js'
|
|
2
|
-
import { isNullOrUnDef } from '../is'
|
|
3
|
-
import type { RoundingMode } from 'big.js'
|
|
4
|
-
|
|
5
|
-
type BigSource = number | string | Big
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 返回一个实例化的Big对象
|
|
9
|
-
* @param n - 待处理的数字/字符串数字
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const value = useNumber('1')
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export function useNumber(n: BigSource): Big {
|
|
16
|
-
return new Big(n)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 4舍5入(精度兼容)
|
|
21
|
-
* @param m - 数字
|
|
22
|
-
* @param dp - 保留位数
|
|
23
|
-
* @param rm - 0: 向下取整 1: 四舍五入 2: 向上取整
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* const value = round(5.23, 1, 1)
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export function round(m: BigSource, dp: number, rm: RoundingMode = 1) {
|
|
30
|
-
return new Big(m).round(dp, rm).toNumber()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface FormatOptions {
|
|
34
|
-
/**
|
|
35
|
-
* 保留小数位数
|
|
36
|
-
*/
|
|
37
|
-
precision?: number
|
|
38
|
-
/**
|
|
39
|
-
* 千位分隔符
|
|
40
|
-
*/
|
|
41
|
-
thousandSeparator?: string
|
|
42
|
-
/**
|
|
43
|
-
* 分割的位数(3位代表千分位分割)
|
|
44
|
-
*/
|
|
45
|
-
bit?: number
|
|
46
|
-
/**
|
|
47
|
-
* 0: 向下取整 1: 四舍五入, 2: roundHalfEven, 3: 向上取整
|
|
48
|
-
*/
|
|
49
|
-
roundMode?: RoundingMode
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* 格式化数字, 默认千位分隔符为","
|
|
54
|
-
* @param val - 要格式化的数字
|
|
55
|
-
* @param options - 配置项
|
|
56
|
-
* @returns 格式化后的数字字符串
|
|
57
|
-
* @example
|
|
58
|
-
* ```ts
|
|
59
|
-
* formatNumber(1000) //输出 "1,000"
|
|
60
|
-
* formatNumber(1234567) //输出 "1,234,567"
|
|
61
|
-
* formatNumber(1234.5678, { precision: 2, thousandSeparator: "-" }) // 输出: "1-234.5678"
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
export function formatNumber(val: number | string, options: FormatOptions = {}): string {
|
|
65
|
-
const { bit = 3, precision = 0, thousandSeparator = ',', roundMode = 1 } = options
|
|
66
|
-
if (isNullOrUnDef(val)) return ''
|
|
67
|
-
if (isNaN(+val)) return val + ''
|
|
68
|
-
let value = useNumber(val).toFixed(precision, roundMode)
|
|
69
|
-
const arr = value.split('.')
|
|
70
|
-
value = arr[0]
|
|
71
|
-
const reg = new RegExp(`(\\d)(?=(\\d{${bit}})+$)`, 'g')
|
|
72
|
-
value = value.replace(reg, `$1${thousandSeparator}`)
|
|
73
|
-
const decimal = arr.length > 1 ? `.${arr?.[1]}` : ''
|
|
74
|
-
return value + decimal
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface MoneyFormatOptions {
|
|
78
|
-
/**
|
|
79
|
-
* 亿的单位字符串,默认 '亿'
|
|
80
|
-
*/
|
|
81
|
-
yi?: string
|
|
82
|
-
/**
|
|
83
|
-
* 万的单位字符串,默认 '万'
|
|
84
|
-
*/
|
|
85
|
-
wan?: string
|
|
86
|
-
/**
|
|
87
|
-
* 千位分隔符,默认 ',',传空字符串或不传则不启用千位分隔
|
|
88
|
-
*/
|
|
89
|
-
thousandSeparator?: string
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const DEFAULT_MONEY_OPTIONS: Required<MoneyFormatOptions> = {
|
|
93
|
-
yi: '亿',
|
|
94
|
-
wan: '万',
|
|
95
|
-
thousandSeparator: ',',
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 格式化金钱(支持万、亿)
|
|
100
|
-
* @param num - 数字
|
|
101
|
-
* @param options - 配置项
|
|
102
|
-
* @returns 格式化后的金钱字符串
|
|
103
|
-
* @example
|
|
104
|
-
* ```ts
|
|
105
|
-
* formatMoney(1234567) // '1,234,567'
|
|
106
|
-
* formatMoney(10000000) // '1,000万'
|
|
107
|
-
* formatMoney(100000000) // '1亿'
|
|
108
|
-
* formatMoney(123456789) // '1亿2,345万6,789'
|
|
109
|
-
* formatMoney(123456789, { yi: 'y', wan: 'w' }) // '1y2,345w6,789'
|
|
110
|
-
* formatMoney(123456789, { thousandSeparator: '' }) // '1亿2345万6789'
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
export function formatMoney(num: number, options: MoneyFormatOptions = {}): string {
|
|
114
|
-
if (isNullOrUnDef(num) || isNaN(num)) return ''
|
|
115
|
-
if (num < 0) return '-' + formatMoney(-num, options)
|
|
116
|
-
|
|
117
|
-
const { yi, wan, thousandSeparator } = {
|
|
118
|
-
...DEFAULT_MONEY_OPTIONS,
|
|
119
|
-
...options,
|
|
120
|
-
}
|
|
121
|
-
const len = String(num).length
|
|
122
|
-
const enableSeparator = !!thousandSeparator
|
|
123
|
-
|
|
124
|
-
const addSeparator = (str: string) => {
|
|
125
|
-
if (!enableSeparator) return str
|
|
126
|
-
return str.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator!)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (len <= 4) return addSeparator(String(num))
|
|
130
|
-
|
|
131
|
-
if (len <= 8) {
|
|
132
|
-
const wanNum = Math.floor(num / 10000)
|
|
133
|
-
const remainder = num % 10000
|
|
134
|
-
return remainder === 0
|
|
135
|
-
? addSeparator(String(wanNum)) + wan
|
|
136
|
-
: addSeparator(String(wanNum)) + wan + addSeparator(remainder.toString().padStart(4, '0'))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (len <= 12) {
|
|
140
|
-
const yiNum = Math.floor(num / 100000000)
|
|
141
|
-
const wanNum = Math.floor((num % 100000000) / 10000)
|
|
142
|
-
const remainder = num % 10000
|
|
143
|
-
|
|
144
|
-
if (yiNum === 0) return formatMoney(num % 100000000, options)
|
|
145
|
-
|
|
146
|
-
const yiPart = addSeparator(String(yiNum)) + yi
|
|
147
|
-
if (wanNum === 0 && remainder === 0) return yiPart
|
|
148
|
-
if (wanNum === 0) return yiPart + addSeparator(remainder.toString())
|
|
149
|
-
if (remainder === 0) return yiPart + addSeparator(String(wanNum)) + wan
|
|
150
|
-
|
|
151
|
-
return yiPart + addSeparator(String(wanNum)) + wan + addSeparator(remainder.toString())
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const yiNum = Math.floor(num / 100000000)
|
|
155
|
-
const remainderFromYi = num % 100000000
|
|
156
|
-
return (
|
|
157
|
-
addSeparator(String(yiNum)) +
|
|
158
|
-
yi +
|
|
159
|
-
formatMoney(remainderFromYi, { ...options, thousandSeparator: '' })
|
|
160
|
-
)
|
|
161
|
-
}
|