@allkit/shared 0.0.1
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/README.md +1 -0
- package/dist/README.md +1 -0
- package/dist/clipboard/index.d.ts +9 -0
- package/dist/cloneDeep/index.d.ts +47 -0
- package/dist/constants.d.ts +26 -0
- package/dist/cookie/index.d.ts +31 -0
- package/dist/date/index.d.ts +133 -0
- package/dist/device/index.d.ts +70 -0
- package/dist/element/index.d.ts +12 -0
- package/dist/index.d.ts +13 -0
- package/dist/is/index.d.ts +181 -0
- package/dist/lodash/index.d.ts +184 -0
- package/dist/number/index.d.ts +84 -0
- package/dist/package.json +21 -0
- package/dist/shared.es.d.ts +2 -0
- package/dist/shared.es.js +1367 -0
- package/dist/shared.umd.js +1 -0
- package/dist/storage/index.d.ts +144 -0
- package/dist/string/index.d.ts +40 -0
- package/dist/timer/index.d.ts +9 -0
- package/eslint.config.js +10 -0
- package/package.json +26 -0
- package/scripts/build.mjs +92 -0
- package/skill/SKILL.md +240 -0
- package/skill/examples/usage.ts +67 -0
- package/skill/references/clipboard.md +39 -0
- package/skill/references/cloneDeep.md +60 -0
- package/skill/references/cookie.md +56 -0
- package/skill/references/date.md +466 -0
- package/skill/references/device.md +138 -0
- package/skill/references/element.md +99 -0
- package/skill/references/is.md +415 -0
- package/skill/references/lodash.md +472 -0
- package/skill/references/number.md +248 -0
- package/skill/references/storage.md +113 -0
- package/skill/references/string.md +126 -0
- package/skill/references/timer.md +78 -0
- package/src/clipboard/index.ts +26 -0
- package/src/cloneDeep/__test__/cloneDeep.test.ts +92 -0
- package/src/cloneDeep/index.ts +168 -0
- package/src/constants.ts +27 -0
- package/src/cookie/index.ts +44 -0
- package/src/date/__test__/date-diff.test.ts +23 -0
- package/src/date/__test__/date-duration.test.ts +140 -0
- package/src/date/__test__/date-from.test.ts +64 -0
- package/src/date/__test__/date.test.ts +67 -0
- package/src/date/index.ts +331 -0
- package/src/device/__test__/device.test.ts +138 -0
- package/src/device/index.ts +125 -0
- package/src/element/index.ts +100 -0
- package/src/index.ts +14 -0
- package/src/is/__test__/is.test.ts +320 -0
- package/src/is/index.ts +293 -0
- package/src/lodash/__test__/lodash.test.ts +111 -0
- package/src/lodash/__test__/obj-string.test.ts +107 -0
- package/src/lodash/__test__/uniqueId.test.ts +40 -0
- package/src/lodash/index.ts +396 -0
- package/src/number/__test__/number.test.ts +137 -0
- package/src/number/index.ts +161 -0
- package/src/storage/__test__/storage.test.ts +44 -0
- package/src/storage/index.ts +185 -0
- package/src/string/__test__/string.test.ts +24 -0
- package/src/string/index.ts +49 -0
- package/src/timer/index.ts +15 -0
- package/tsconfig.json +25 -0
- package/types/global.d.ts +13 -0
- package/vite.config.ts +16 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# storage
|
|
2
|
+
|
|
3
|
+
本地存储工具函数,自动处理 JSON 序列化,支持 TypeScript 类型推断
|
|
4
|
+
|
|
5
|
+
## Functions
|
|
6
|
+
|
|
7
|
+
### setLocal
|
|
8
|
+
|
|
9
|
+
设置 LocalStorage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
function setLocal<T>(key: string, content: T): void
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### getLocal
|
|
16
|
+
|
|
17
|
+
获取 LocalStorage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
function getLocal<T>(key: string): T
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### removeLocal
|
|
24
|
+
|
|
25
|
+
删除 LocalStorage
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
function removeLocal(key: string): void
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### clearLocal
|
|
32
|
+
|
|
33
|
+
清空 LocalStorage
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
function clearLocal(): void
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### setSession
|
|
40
|
+
|
|
41
|
+
设置 SessionStorage
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
function setSession<T>(key: string, content: T): void
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### getSession
|
|
48
|
+
|
|
49
|
+
获取 SessionStorage
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
function getSession<T>(key: string): T
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### removeSession
|
|
56
|
+
|
|
57
|
+
删除 SessionStorage
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
function removeSession(key: string): void
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### clearSession
|
|
64
|
+
|
|
65
|
+
清空 SessionStorage
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
function clearSession(): void
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### setStore
|
|
72
|
+
|
|
73
|
+
设置本地存储
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
function setStore<T>(key: string, value: T, type?: 'local' | 'session'): void
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### getStore
|
|
80
|
+
|
|
81
|
+
获取本地存储
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
function getStore<T>(key: string, type?: 'local' | 'session'): T
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### removeStore
|
|
88
|
+
|
|
89
|
+
删除本地存储
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
function removeStore(key: string, type?: 'local' | 'session'): void
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Example
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { setLocal, getLocal, setSession, getSession } from '@allkit/shared'
|
|
99
|
+
|
|
100
|
+
// LocalStorage
|
|
101
|
+
setLocal('token', '123')
|
|
102
|
+
const token = getLocal<string>('token') // '123'
|
|
103
|
+
removeLocal('token')
|
|
104
|
+
|
|
105
|
+
// SessionStorage
|
|
106
|
+
setSession('userInfo', { name: '张三', age: 18 })
|
|
107
|
+
const userInfo = getSession<{ name: string; age: number }>('userInfo')
|
|
108
|
+
removeSession('userInfo')
|
|
109
|
+
|
|
110
|
+
// 通用方法
|
|
111
|
+
setStore('token', '123', 'local')
|
|
112
|
+
const token2 = getStore<string>('token', 'local')
|
|
113
|
+
```
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# string
|
|
2
|
+
|
|
3
|
+
字符串操作工具函数
|
|
4
|
+
|
|
5
|
+
## Functions
|
|
6
|
+
|
|
7
|
+
### camelize
|
|
8
|
+
|
|
9
|
+
中划线转小驼峰
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
function camelize(str: string): string
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Example**
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
camelize('user-info') // 'userInfo'
|
|
19
|
+
camelize('hello-world-test') // 'helloWorldTest'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### kebabCase
|
|
23
|
+
|
|
24
|
+
驼峰转中划线
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
function kebabCase(str: string): string
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Example**
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
kebabCase('userInfo') // 'user-info'
|
|
34
|
+
kebabCase('helloWorldTest') // 'hello-world-test'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### snakeCase
|
|
38
|
+
|
|
39
|
+
驼峰转下划线
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
function snakeCase(str: string): string
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Example**
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
snakeCase('userInfo') // 'user_info'
|
|
49
|
+
snakeCase('helloWorldTest') // 'hello_world_test'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### lowerFirst
|
|
53
|
+
|
|
54
|
+
首字母转小写
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
function lowerFirst(str: string): string
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Example**
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
lowerFirst('UserInfo') // 'userInfo'
|
|
64
|
+
lowerFirst('Hello') // 'hello'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### upperFirst
|
|
68
|
+
|
|
69
|
+
首字母转大写
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
function upperFirst(str: string): string
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Example**
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
upperFirst('userInfo') // 'UserInfo'
|
|
79
|
+
upperFirst('hello') // 'Hello'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### trim
|
|
83
|
+
|
|
84
|
+
去除字符串首尾空白
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
function trim(str: string, chars?: string): string
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Example**
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
trim(' hello ') // 'hello'
|
|
94
|
+
trim('--hello--', '-') // 'hello'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### repeat
|
|
98
|
+
|
|
99
|
+
重复字符串
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
function repeat(str: string, count: number): string
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Example**
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
repeat('a', 3) // 'aaa'
|
|
109
|
+
repeat('ab', 2) // 'abab'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### padStart / padEnd
|
|
113
|
+
|
|
114
|
+
字符串补全
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
function padStart(str: string, length: number, chars?: string): string
|
|
118
|
+
function padEnd(str: string, length: number, chars?: string): string
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Example**
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
padStart('5', 2, '0') // '05'
|
|
125
|
+
padEnd('hi', 4, '!') // 'hi!!'
|
|
126
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# timer
|
|
2
|
+
|
|
3
|
+
定时器工具函数
|
|
4
|
+
|
|
5
|
+
## Functions
|
|
6
|
+
|
|
7
|
+
### sleep
|
|
8
|
+
|
|
9
|
+
睡眠函数
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
function sleep(timer: number): Promise<void>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Example**
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
await sleep(1000) // 等待 1 秒
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### raf
|
|
22
|
+
|
|
23
|
+
监听下一动画帧(Promise)
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
function raf(): Promise<void>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Example**
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
raf().then(() => {
|
|
33
|
+
console.log('执行下一帧')
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### doubleRaf
|
|
38
|
+
|
|
39
|
+
监听双帧动画帧(Promise)
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
function doubleRaf(): Promise<void>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Example**
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
doubleRaf().then(() => {
|
|
49
|
+
console.log('执行双帧后')
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### clearTimer
|
|
54
|
+
|
|
55
|
+
清除定时器
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
function clearTimer(timerId: number | undefined): void
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { sleep, raf, doubleRaf } from '@allkit/shared'
|
|
65
|
+
|
|
66
|
+
// 睡眠
|
|
67
|
+
await sleep(100)
|
|
68
|
+
|
|
69
|
+
// 下一帧
|
|
70
|
+
raf().then(() => {
|
|
71
|
+
console.log('执行下一帧')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// 双帧
|
|
75
|
+
doubleRaf().then(() => {
|
|
76
|
+
console.log('执行双帧后')
|
|
77
|
+
})
|
|
78
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 复制文本到剪贴板
|
|
3
|
+
* @param value - 文本内容
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* let isResult= copyTextToClipboard('test')
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export function copyTextToClipboard(value: string = '') {
|
|
10
|
+
// 动态创建 textarea 标签
|
|
11
|
+
const textarea = document.createElement('textarea')
|
|
12
|
+
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
|
|
13
|
+
textarea.readOnly = true
|
|
14
|
+
textarea.style.position = 'absolute'
|
|
15
|
+
textarea.style.left = '-9999px'
|
|
16
|
+
// 将要 copy 的值赋给 textarea 标签的 value 属性
|
|
17
|
+
textarea.value = value
|
|
18
|
+
// 将 textarea 插入到 body 中
|
|
19
|
+
document.body.appendChild(textarea)
|
|
20
|
+
// 选中值并复制
|
|
21
|
+
textarea.select()
|
|
22
|
+
textarea.setSelectionRange(0, textarea.value.length)
|
|
23
|
+
const result = document.execCommand('Copy')
|
|
24
|
+
document.body.removeChild(textarea)
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { cloneDeep } from '../'
|
|
3
|
+
describe('cloneDeep', () => {
|
|
4
|
+
test('simple object with no nested objects', () => {
|
|
5
|
+
const source = { a: 1, b: 'test' }
|
|
6
|
+
const result = cloneDeep(source)
|
|
7
|
+
expect(result).toEqual(source)
|
|
8
|
+
expect(result).not.toBe(source)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('object with nested objects', () => {
|
|
12
|
+
const source = { a: 1, b: { c: 2 } }
|
|
13
|
+
const result = cloneDeep(source)
|
|
14
|
+
expect(result).toEqual(source)
|
|
15
|
+
expect(result.b).not.toBe(source.b)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('object with multiple levels of nested objects', () => {
|
|
19
|
+
const source = { a: 1, b: { c: { d: 2 } } }
|
|
20
|
+
const result = cloneDeep(source)
|
|
21
|
+
expect(result).toEqual(source)
|
|
22
|
+
expect(result.b).not.toBe(source.b)
|
|
23
|
+
expect(result.b.c).not.toBe(source.b.c)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('object with arrays and nested arrays', () => {
|
|
27
|
+
const source = { a: [1, [2]], b: { c: [3, 4] } }
|
|
28
|
+
const result = cloneDeep(source)
|
|
29
|
+
expect(result).toEqual(source)
|
|
30
|
+
expect(result.a).not.toBe(source.a)
|
|
31
|
+
expect(result.a[1]).not.toBe(source.a[1])
|
|
32
|
+
expect(result.b.c).not.toBe(source.b.c)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('object with complex types of nested objects', () => {
|
|
36
|
+
const source = { a: { b: new Date(), c: /test/g, d: () => {} } }
|
|
37
|
+
const result = cloneDeep(source)
|
|
38
|
+
expect(result).toEqual(source)
|
|
39
|
+
expect(result).not.toBe(source)
|
|
40
|
+
expect(result.a).not.toBe(source.a)
|
|
41
|
+
expect(result.a.b).toBe(source.a.b)
|
|
42
|
+
expect(result.a.c).toBe(source.a.c)
|
|
43
|
+
expect(result.a.d).toBe(source.a.d)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('object with non-enumerable properties', () => {
|
|
47
|
+
const source = { a: 1 }
|
|
48
|
+
Object.defineProperty(source, 'b', { value: 2, enumerable: false })
|
|
49
|
+
const result = cloneDeep(source)
|
|
50
|
+
expect(result).toEqual(source)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('object containing Set and Map instances', () => {
|
|
54
|
+
const source = {
|
|
55
|
+
a: new Set([{ a: 1 }, { a: 2 }]),
|
|
56
|
+
b: new Map([[{ b: 1 }, { b: 2 }]]),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = cloneDeep(source)
|
|
60
|
+
expect(result).toEqual(source)
|
|
61
|
+
|
|
62
|
+
expect(result.a).not.toBe(source.a)
|
|
63
|
+
expect(result.b).not.toBe(source.b)
|
|
64
|
+
|
|
65
|
+
// Objects inside sets are cloned.
|
|
66
|
+
expect([...result.a].every((item) => !source.a.has(item))).toBe(true)
|
|
67
|
+
|
|
68
|
+
// Object keys in maps are *not* cloned.
|
|
69
|
+
// Object values in maps are cloned.
|
|
70
|
+
expect(
|
|
71
|
+
[...result.b].every(([key, value]) => source.b.has(key) && source.b.get(key) !== value),
|
|
72
|
+
).toBe(true)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('handle circular references', () => {
|
|
76
|
+
const source: any = { a: 1 }
|
|
77
|
+
source.b = source
|
|
78
|
+
const result = cloneDeep(source)
|
|
79
|
+
expect(result).not.toBe(source)
|
|
80
|
+
expect(result).toEqual(source)
|
|
81
|
+
expect(result.b).toBe(result)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('avoid cloning an object more than once', () => {
|
|
85
|
+
const source: any = { a1: { b: 1 } }
|
|
86
|
+
source.a2 = source.a1
|
|
87
|
+
const result = cloneDeep(source)
|
|
88
|
+
expect(result).toEqual(source)
|
|
89
|
+
expect(result.a1).toBe(result.a2)
|
|
90
|
+
expect(result.a1).not.toBe(source.a1)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { isArray, isMap, isPlainObject, isSet, isUnDef } from '../is'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A strategy for cloning objects with `cloneDeep`.
|
|
5
|
+
*
|
|
6
|
+
* Methods **must** call the `track` function with the new parent
|
|
7
|
+
* object **before** looping over the input object's
|
|
8
|
+
* properties/elements for cloning purposes. This protects against
|
|
9
|
+
* circular references.
|
|
10
|
+
*
|
|
11
|
+
* Methods may return the input object to indicate that cloning should
|
|
12
|
+
* be skipped.
|
|
13
|
+
*
|
|
14
|
+
* Methods may return null to indicate that the default cloning logic
|
|
15
|
+
* should be used.
|
|
16
|
+
*/
|
|
17
|
+
export interface CloningStrategy {
|
|
18
|
+
cloneMap: <K, V>(
|
|
19
|
+
parent: Map<K, V>,
|
|
20
|
+
track: (newParent: Map<K, V>) => Map<K, V>,
|
|
21
|
+
clone: <T>(value: T) => T,
|
|
22
|
+
) => Map<K, V> | null
|
|
23
|
+
cloneSet: <T>(
|
|
24
|
+
parent: Set<T>,
|
|
25
|
+
track: (newParent: Set<T>) => Set<T>,
|
|
26
|
+
clone: <T>(value: T) => T,
|
|
27
|
+
) => Set<T> | null
|
|
28
|
+
cloneArray: <T>(
|
|
29
|
+
parent: readonly T[],
|
|
30
|
+
track: (newParent: T[]) => T[],
|
|
31
|
+
clone: <T>(value: T) => T,
|
|
32
|
+
) => T[] | null
|
|
33
|
+
cloneObject: <T extends object>(
|
|
34
|
+
parent: T,
|
|
35
|
+
track: (newParent: T) => T,
|
|
36
|
+
clone: <T>(value: T) => T,
|
|
37
|
+
) => T | null
|
|
38
|
+
cloneOther: <T>(parent: T, track: (newParent: T) => T, clone: <T>(value: T) => T) => T | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const DefaultCloningStrategy: CloningStrategy = {
|
|
42
|
+
cloneMap<K, V>(
|
|
43
|
+
input: Map<K, V>,
|
|
44
|
+
track: (newParent: Map<K, V>) => Map<K, V>,
|
|
45
|
+
clone: <T>(value: T) => T,
|
|
46
|
+
): Map<K, V> {
|
|
47
|
+
const output = track(new Map())
|
|
48
|
+
for (const [key, value] of input) {
|
|
49
|
+
output.set(key, clone(value))
|
|
50
|
+
}
|
|
51
|
+
return output
|
|
52
|
+
},
|
|
53
|
+
cloneSet<T>(
|
|
54
|
+
input: Set<T>,
|
|
55
|
+
track: (newParent: Set<T>) => Set<T>,
|
|
56
|
+
clone: <T>(value: T) => T,
|
|
57
|
+
): Set<T> {
|
|
58
|
+
const output = track(new Set())
|
|
59
|
+
for (const value of input) {
|
|
60
|
+
output.add(clone(value))
|
|
61
|
+
}
|
|
62
|
+
return output
|
|
63
|
+
},
|
|
64
|
+
cloneArray<T>(
|
|
65
|
+
input: readonly T[],
|
|
66
|
+
track: (newParent: T[]) => T[],
|
|
67
|
+
clone: <T>(value: T) => T,
|
|
68
|
+
): T[] {
|
|
69
|
+
// Use .forEach for correct handling of sparse arrays
|
|
70
|
+
const output = track(new Array(input.length))
|
|
71
|
+
input.forEach((value, index) => {
|
|
72
|
+
output[index] = clone(value)
|
|
73
|
+
})
|
|
74
|
+
return output
|
|
75
|
+
},
|
|
76
|
+
cloneObject<T extends object>(
|
|
77
|
+
input: T,
|
|
78
|
+
track: (newParent: T) => T,
|
|
79
|
+
clone: <T>(value: T) => T,
|
|
80
|
+
): T {
|
|
81
|
+
const output = track(Object.create(Object.getPrototypeOf(input)))
|
|
82
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
83
|
+
// By copying the property descriptors, we preserve computed
|
|
84
|
+
// properties and non-enumerable properties.
|
|
85
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key)!
|
|
86
|
+
if ('value' in descriptor) {
|
|
87
|
+
descriptor.value = clone(descriptor.value)
|
|
88
|
+
}
|
|
89
|
+
Object.defineProperty(output, key, descriptor)
|
|
90
|
+
}
|
|
91
|
+
return output
|
|
92
|
+
},
|
|
93
|
+
cloneOther<T>(input: T, track: (newParent: T) => T): T {
|
|
94
|
+
return track(input)
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 深拷贝
|
|
100
|
+
*
|
|
101
|
+
* 默认情况下,唯一被克隆的对象是普通对象、类实例、数组、`Set`实例和`Map`,`Date`
|
|
102
|
+
* 其他的场景,通过第二个参数自定义Strategy,实现拷贝逻辑(copy Radashi)
|
|
103
|
+
*
|
|
104
|
+
* ```ts
|
|
105
|
+
* const obj = { a: 1, b: { c: 2 } }
|
|
106
|
+
* const clone = cloneDeep(obj)
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export function cloneDeep<T extends object>(root: T, customStrategy?: Partial<CloningStrategy>): T {
|
|
110
|
+
if (isUnDef(root)) {
|
|
111
|
+
return root
|
|
112
|
+
}
|
|
113
|
+
const strategy = { ...DefaultCloningStrategy, ...customStrategy }
|
|
114
|
+
|
|
115
|
+
const tracked = new Map<unknown, unknown>()
|
|
116
|
+
const track = (parent: unknown, newParent: unknown) => {
|
|
117
|
+
tracked.set(parent, newParent)
|
|
118
|
+
return newParent
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const clone = <T>(value: T): T =>
|
|
122
|
+
value && typeof value === 'object'
|
|
123
|
+
? ((tracked.get(value) ?? cloneDeep(value, strategy)) as T)
|
|
124
|
+
: value
|
|
125
|
+
|
|
126
|
+
const cloneDeep = (parent: unknown, strategy: CloningStrategy): unknown => {
|
|
127
|
+
const cloneParent = (
|
|
128
|
+
isPlainObject(parent)
|
|
129
|
+
? strategy.cloneObject
|
|
130
|
+
: isArray(parent)
|
|
131
|
+
? strategy.cloneArray
|
|
132
|
+
: isMap(parent)
|
|
133
|
+
? strategy.cloneMap
|
|
134
|
+
: isSet(parent)
|
|
135
|
+
? strategy.cloneSet
|
|
136
|
+
: strategy.cloneOther
|
|
137
|
+
) as (
|
|
138
|
+
newParent: unknown,
|
|
139
|
+
track: (newParent: unknown) => unknown,
|
|
140
|
+
clone: (value: unknown) => unknown,
|
|
141
|
+
) => unknown
|
|
142
|
+
|
|
143
|
+
const newParent = cloneParent(parent, track.bind(null, parent), clone)
|
|
144
|
+
if (!newParent) {
|
|
145
|
+
// Use the default strategy if null is returned.
|
|
146
|
+
return cloneDeep(parent, DefaultCloningStrategy)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
tracked.set(parent, newParent)
|
|
150
|
+
return newParent
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return cloneDeep(root, strategy) as T
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @deprecated - 请使用cloneDeep
|
|
158
|
+
*
|
|
159
|
+
* 深拷贝(与cloneDeep完成相同,历史原因2个名字)
|
|
160
|
+
* 默认情况下,唯一被克隆的对象是普通对象、类实例、数组、`Set`实例和`Map`,`Date`
|
|
161
|
+
* 其他的场景,通过第二个参数自定义Strategy,实现拷贝逻辑
|
|
162
|
+
*
|
|
163
|
+
* ```ts
|
|
164
|
+
* const obj = { a: 1, b: { c: 2 } }
|
|
165
|
+
* const clone = deepClone(obj)
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export const deepClone = cloneDeep
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 普通是与否枚举
|
|
3
|
+
*/
|
|
4
|
+
export enum EnumYesNo {
|
|
5
|
+
/**
|
|
6
|
+
* 是('1')
|
|
7
|
+
*/
|
|
8
|
+
YES = 1,
|
|
9
|
+
/**
|
|
10
|
+
* 否('0')
|
|
11
|
+
*/
|
|
12
|
+
NO = 0,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 普通是与否枚举(10,20)
|
|
17
|
+
*/
|
|
18
|
+
export enum EnumYesNoPlus {
|
|
19
|
+
/**
|
|
20
|
+
* 是('10')
|
|
21
|
+
*/
|
|
22
|
+
YES = '10',
|
|
23
|
+
/**
|
|
24
|
+
* 否('20')
|
|
25
|
+
*/
|
|
26
|
+
NO = '20',
|
|
27
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import Cookies from 'js-cookie'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 设置cookie
|
|
5
|
+
* @param name - cookie的key
|
|
6
|
+
* @param value - cookie的内容
|
|
7
|
+
* @param options - cookie的配置
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* setCookie('name', 'value')
|
|
11
|
+
* setCookie('name', 'value', { expires: 30 })
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function setCookie(
|
|
15
|
+
name: string,
|
|
16
|
+
value: string,
|
|
17
|
+
options: Parameters<typeof Cookies.set>[2] = { expires: 30 },
|
|
18
|
+
) {
|
|
19
|
+
Cookies.set(name, value, options)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 获取指定name的cookie值
|
|
24
|
+
* @param name - cookie的key
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* getCookie('name')
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function getCookie(name: string) {
|
|
31
|
+
return Cookies.get(name)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 删除指定name的cookie
|
|
36
|
+
* @param name - cookie的key
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* removeCookie('name')
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function removeCookie(name: string) {
|
|
43
|
+
Cookies.remove(name)
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { dateDiffFormat } from '../'
|
|
3
|
+
|
|
4
|
+
describe('date.ts', () => {
|
|
5
|
+
test('dateDiffFormat - YY年MM个月DD天', () => {
|
|
6
|
+
const start = '2024-12-18'
|
|
7
|
+
const _date = dateDiffFormat(start, 1743762046095)
|
|
8
|
+
expect(_date).toEqual('0年3个月17天')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('dateFormat - param is Date', () => {
|
|
12
|
+
const start = '2024-12-18'
|
|
13
|
+
const _date = dateDiffFormat(start, 1743762046095, 'MM个月DD天')
|
|
14
|
+
|
|
15
|
+
expect(_date).toEqual('3个月17天')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('dateFormat - param is Date', () => {
|
|
19
|
+
const start = '2023-12-18'
|
|
20
|
+
const _date = dateDiffFormat(start, '2026-12-18', 'MM个月DD天')
|
|
21
|
+
expect(_date).toEqual('36个月0天')
|
|
22
|
+
})
|
|
23
|
+
})
|