@cloudcome/utils-core 1.1.1 → 1.2.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 +18 -0
- package/dist/array.cjs +129 -0
- package/dist/array.cjs.map +1 -0
- package/dist/array.d.ts +171 -0
- package/dist/array.mjs +129 -0
- package/dist/array.mjs.map +1 -0
- package/dist/async.cjs +219 -0
- package/dist/async.cjs.map +1 -0
- package/dist/async.d.ts +137 -0
- package/dist/async.mjs +219 -0
- package/dist/async.mjs.map +1 -0
- package/dist/base64.cjs +16 -0
- package/dist/base64.cjs.map +1 -0
- package/dist/base64.d.ts +7 -0
- package/dist/base64.mjs +16 -0
- package/dist/base64.mjs.map +1 -0
- package/dist/cache.cjs +79 -0
- package/dist/cache.cjs.map +1 -0
- package/dist/cache.d.ts +90 -0
- package/dist/cache.mjs +79 -0
- package/dist/cache.mjs.map +1 -0
- package/{src/color/contrast.ts → dist/color/contrast.d.ts} +2 -12
- package/dist/color/distance.d.ts +8 -0
- package/dist/color/helpers.d.ts +2 -0
- package/dist/color/hex-hsl.d.ts +3 -0
- package/{src/color/hex-hsv.ts → dist/color/hex-hsv.d.ts} +3 -11
- package/{src/color/hex-hwb.ts → dist/color/hex-hwb.d.ts} +3 -11
- package/dist/color/hex-rgb.d.ts +18 -0
- package/{src/color/hsl-lighten.ts → dist/color/hsl-lighten.d.ts} +2 -7
- package/{src/color/hsv-brighten.ts → dist/color/hsv-brighten.d.ts} +2 -7
- package/{src/color/luminance.ts → dist/color/luminance.d.ts} +2 -9
- package/{src/color/mix.ts → dist/color/mix.d.ts} +2 -10
- package/dist/color/rgb-hsl.d.ts +23 -0
- package/{src/color/rgb-hsv.ts → dist/color/rgb-hsv.d.ts} +3 -30
- package/dist/color/rgb-hwb.d.ts +29 -0
- package/{src/color/rgb-lab.ts → dist/color/rgb-lab.d.ts} +3 -11
- package/dist/color/rgb-whiter.d.ts +12 -0
- package/dist/color/rgb-xyz.d.ts +22 -0
- package/{src/color/types.ts → dist/color/types.d.ts} +30 -12
- package/{src/color/xyz-lab.ts → dist/color/xyz-lab.d.ts} +3 -32
- package/dist/color.cjs +250 -0
- package/dist/color.cjs.map +1 -0
- package/dist/color.mjs +250 -0
- package/dist/color.mjs.map +1 -0
- package/dist/const.cjs +14 -0
- package/dist/const.cjs.map +1 -0
- package/dist/const.mjs +15 -0
- package/dist/const.mjs.map +1 -0
- package/dist/core.cjs +250 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.mjs +251 -0
- package/dist/core.mjs.map +1 -0
- package/dist/crypto/md5.d.mts +1 -0
- package/dist/crypto/sha1.d.mts +1 -0
- package/dist/crypto/sha256.d.mts +1 -0
- package/dist/crypto/sha512.d.mts +1 -0
- package/dist/crypto.cjs +812 -0
- package/dist/crypto.cjs.map +1 -0
- package/{src/crypto.ts → dist/crypto.d.ts} +4 -20
- package/dist/crypto.mjs +812 -0
- package/dist/crypto.mjs.map +1 -0
- package/dist/date/const.d.ts +6 -0
- package/dist/date/core.d.ts +52 -0
- package/dist/date/days.d.ts +23 -0
- package/{src/date/is.ts → dist/date/is.d.ts} +8 -102
- package/dist/date/relative.d.ts +44 -0
- package/dist/date/start-end.d.ts +73 -0
- package/dist/date/timezone.d.ts +67 -0
- package/dist/date/weeks.d.ts +72 -0
- package/dist/date.cjs +239 -0
- package/dist/date.cjs.map +1 -0
- package/dist/date.mjs +241 -0
- package/dist/date.mjs.map +1 -0
- package/dist/dict.cjs +2 -0
- package/dist/dict.cjs.map +1 -0
- package/dist/dict.mjs +2 -0
- package/dist/dict.mjs.map +1 -0
- package/dist/each.cjs +18 -0
- package/dist/each.cjs.map +1 -0
- package/dist/each.mjs +19 -0
- package/dist/each.mjs.map +1 -0
- package/dist/easing.cjs +151 -0
- package/dist/easing.cjs.map +1 -0
- package/dist/easing.d.ts +46 -0
- package/dist/easing.mjs +151 -0
- package/dist/easing.mjs.map +1 -0
- package/dist/emitter.cjs +94 -0
- package/dist/emitter.cjs.map +1 -0
- package/dist/emitter.d.ts +68 -0
- package/dist/emitter.mjs +94 -0
- package/dist/emitter.mjs.map +1 -0
- package/dist/enum.cjs +58 -0
- package/dist/enum.cjs.map +1 -0
- package/dist/enum.d.ts +68 -0
- package/dist/enum.mjs +58 -0
- package/dist/enum.mjs.map +1 -0
- package/dist/env.cjs +28 -0
- package/dist/env.cjs.map +1 -0
- package/{src/env.ts → dist/env.d.ts} +6 -30
- package/dist/env.mjs +28 -0
- package/dist/env.mjs.map +1 -0
- package/dist/error.cjs +12 -0
- package/dist/error.cjs.map +1 -0
- package/{src/error.ts → dist/error.d.ts} +3 -12
- package/dist/error.mjs +12 -0
- package/dist/error.mjs.map +1 -0
- package/dist/exception.cjs +22 -0
- package/dist/exception.cjs.map +1 -0
- package/dist/exception.d.ts +31 -0
- package/dist/exception.mjs +22 -0
- package/dist/exception.mjs.map +1 -0
- package/dist/fn.cjs +76 -0
- package/dist/fn.cjs.map +1 -0
- package/dist/fn.d.ts +102 -0
- package/dist/fn.mjs +76 -0
- package/dist/fn.mjs.map +1 -0
- package/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +5 -0
- package/dist/index.mjs.map +1 -0
- package/dist/merge.cjs +87 -0
- package/dist/merge.cjs.map +1 -0
- package/dist/merge.mjs +88 -0
- package/dist/merge.mjs.map +1 -0
- package/dist/number.cjs +14 -0
- package/dist/number.cjs.map +1 -0
- package/dist/number.d.ts +153 -0
- package/dist/number.mjs +14 -0
- package/dist/number.mjs.map +1 -0
- package/{src/object/each.ts → dist/object/each.d.ts} +3 -23
- package/dist/object/get-set.d.ts +111 -0
- package/dist/object/is.d.ts +32 -0
- package/dist/object/merge.d.ts +72 -0
- package/{src/object/process.ts → dist/object/process.d.ts} +4 -38
- package/dist/object.cjs +130 -0
- package/dist/object.cjs.map +1 -0
- package/dist/object.mjs +130 -0
- package/dist/object.mjs.map +1 -0
- package/dist/path.cjs +77 -0
- package/dist/path.cjs.map +1 -0
- package/dist/path.d.ts +82 -0
- package/dist/path.mjs +77 -0
- package/dist/path.mjs.map +1 -0
- package/dist/promise.cjs +62 -0
- package/dist/promise.cjs.map +1 -0
- package/{src/promise.ts → dist/promise.d.ts} +6 -67
- package/dist/promise.mjs +62 -0
- package/dist/promise.mjs.map +1 -0
- package/dist/qs.cjs +47 -0
- package/dist/qs.cjs.map +1 -0
- package/{src/qs.ts → dist/qs.d.ts} +3 -60
- package/dist/qs.mjs +47 -0
- package/dist/qs.mjs.map +1 -0
- package/dist/regexp.cjs +66 -0
- package/dist/regexp.cjs.map +1 -0
- package/dist/regexp.d.ts +65 -0
- package/dist/regexp.mjs +66 -0
- package/dist/regexp.mjs.map +1 -0
- package/dist/string.cjs +16 -0
- package/dist/string.cjs.map +1 -0
- package/dist/string.d.ts +80 -0
- package/dist/string.mjs +16 -0
- package/dist/string.mjs.map +1 -0
- package/dist/string2.cjs +157 -0
- package/dist/string2.cjs.map +1 -0
- package/dist/string2.mjs +158 -0
- package/dist/string2.mjs.map +1 -0
- package/dist/time/from.d.ts +14 -0
- package/dist/time/to.d.ts +38 -0
- package/dist/time.cjs +82 -0
- package/dist/time.cjs.map +1 -0
- package/dist/time.mjs +82 -0
- package/dist/time.mjs.map +1 -0
- package/dist/timer.cjs +119 -0
- package/dist/timer.cjs.map +1 -0
- package/dist/timer.d.ts +96 -0
- package/{src/timer.ts → dist/timer.mjs} +17 -124
- package/dist/timer.mjs.map +1 -0
- package/dist/tree.cjs +125 -0
- package/dist/tree.cjs.map +1 -0
- package/dist/tree.d.ts +210 -0
- package/dist/tree.mjs +125 -0
- package/dist/tree.mjs.map +1 -0
- package/dist/type.cjs +78 -0
- package/dist/type.cjs.map +1 -0
- package/{src/type.ts → dist/type.d.ts} +20 -96
- package/dist/type.mjs +78 -0
- package/dist/type.mjs.map +1 -0
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +12 -33
- package/dist/types.mjs +2 -0
- package/dist/types.mjs.map +1 -0
- package/dist/unique.cjs +46 -0
- package/dist/unique.cjs.map +1 -0
- package/dist/unique.d.ts +22 -0
- package/dist/unique.mjs +46 -0
- package/dist/unique.mjs.map +1 -0
- package/dist/url.cjs +37 -0
- package/dist/url.cjs.map +1 -0
- package/dist/url.d.ts +53 -0
- package/dist/url.mjs +37 -0
- package/dist/url.mjs.map +1 -0
- package/dist/version.cjs +33 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.d.ts +32 -0
- package/dist/version.mjs +33 -0
- package/dist/version.mjs.map +1 -0
- package/package.json +8 -2
- package/CHANGELOG.md +0 -52
- package/src/array.ts +0 -312
- package/src/async.ts +0 -379
- package/src/base64.ts +0 -20
- package/src/cache.ts +0 -146
- package/src/color/distance.ts +0 -28
- package/src/color/helpers.ts +0 -23
- package/src/color/hex-hsl.ts +0 -11
- package/src/color/hex-rgb.ts +0 -39
- package/src/color/rgb-hsl.ts +0 -53
- package/src/color/rgb-hwb.ts +0 -56
- package/src/color/rgb-whiter.ts +0 -22
- package/src/color/rgb-xyz.ts +0 -62
- package/src/crypto/md5.mjs +0 -357
- package/src/crypto/sha1.mjs +0 -300
- package/src/crypto/sha256.mjs +0 -310
- package/src/crypto/sha512.mjs +0 -459
- package/src/date/const.ts +0 -6
- package/src/date/core.ts +0 -162
- package/src/date/days.ts +0 -51
- package/src/date/relative.ts +0 -92
- package/src/date/start-end.ts +0 -246
- package/src/date/timezone.ts +0 -220
- package/src/date/weeks.ts +0 -100
- package/src/dts/global.d.ts +0 -27
- package/src/easing.ts +0 -166
- package/src/emitter.ts +0 -117
- package/src/enum.ts +0 -171
- package/src/exception.ts +0 -68
- package/src/fn.ts +0 -197
- package/src/index.ts +0 -1
- package/src/number.ts +0 -236
- package/src/object/get-set.ts +0 -273
- package/src/object/is.ts +0 -128
- package/src/object/merge.ts +0 -180
- package/src/path.ts +0 -188
- package/src/regexp.ts +0 -156
- package/src/string.ts +0 -146
- package/src/time/from.ts +0 -57
- package/src/time/to.ts +0 -106
- package/src/tree.ts +0 -394
- package/src/unique.ts +0 -77
- package/src/url.ts +0 -93
- package/src/version.ts +0 -71
- package/test/array.test.ts +0 -332
- package/test/async-real.test.ts +0 -39
- package/test/async.test.ts +0 -375
- package/test/base64.test.ts +0 -32
- package/test/cache.test.ts +0 -83
- package/test/color.test.ts +0 -163
- package/test/crypto.test.ts +0 -34
- package/test/date-tz.test.ts +0 -206
- package/test/date.test.ts +0 -353
- package/test/easing.test.ts +0 -33
- package/test/emitter.test.ts +0 -71
- package/test/enum.test.ts +0 -113
- package/test/env.test.ts +0 -69
- package/test/error.test.ts +0 -58
- package/test/exception.test.ts +0 -43
- package/test/fn.test.ts +0 -263
- package/test/helpers.ts +0 -23
- package/test/index.test.ts +0 -6
- package/test/number.test.ts +0 -213
- package/test/object.test.ts +0 -309
- package/test/path.test.ts +0 -156
- package/test/promise.test.ts +0 -199
- package/test/qs.test.ts +0 -79
- package/test/regexp.test.ts +0 -97
- package/test/string.test.ts +0 -150
- package/test/time.test.ts +0 -214
- package/test/timer.test.ts +0 -114
- package/test/tree.test.ts +0 -348
- package/test/type.test.ts +0 -226
- package/test/unique.test.ts +0 -71
- package/test/url.test.ts +0 -136
- package/test/version.test.ts +0 -52
- package/tsconfig.json +0 -31
- package/vite.config.mts +0 -114
- /package/{src/color.ts → dist/color.d.ts} +0 -0
- /package/{src/date.ts → dist/date.d.ts} +0 -0
- /package/{src/dict.ts → dist/dict.d.ts} +0 -0
- /package/{src/object.ts → dist/object.d.ts} +0 -0
- /package/{src/time.ts → dist/time.d.ts} +0 -0
package/src/string.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { numberConvert, randomNumber } from './number';
|
|
2
|
-
import { isFunction, isNullish, isNumber, isObject, isString, isUndefined } from './type';
|
|
3
|
-
|
|
4
|
-
export const STRING_ARABIC_NUMERALS = '0123456789';
|
|
5
|
-
export const STRING_HEXADECIMALS = '0123456789abcdef';
|
|
6
|
-
export const STRING_LOWERCASE_ALPHA = 'abcdefghijklmnopqrstuvwxyz';
|
|
7
|
-
export const STRING_UPPERCASE_ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
8
|
-
export const STRING_DICT = `${STRING_ARABIC_NUMERALS + STRING_UPPERCASE_ALPHA + STRING_LOWERCASE_ALPHA}`;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 将字符串转换为驼峰格式
|
|
12
|
-
* @param {string} string - 要转换的字符串
|
|
13
|
-
* @param {boolean} [bigger] - 是否大写第一个字母,默认为 false
|
|
14
|
-
* @returns {string} - 转换后的驼峰格式字符串
|
|
15
|
-
*/
|
|
16
|
-
export function stringCamelCase(string: string, bigger?: boolean): string {
|
|
17
|
-
const string2 = string.replace(/[\s_-](.)/g, (_, char) => (char as string).toUpperCase());
|
|
18
|
-
return bigger ? string2.slice(0, 1).toUpperCase() + string2.slice(1) : string2;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 将字符串转换为连字格式
|
|
23
|
-
* @param {string} string - 要转换的字符串
|
|
24
|
-
* @param {string} [separator] - 分隔符,默认是 "-"(短横线)
|
|
25
|
-
* @returns {string} - 转换后的连字格式字符串
|
|
26
|
-
*/
|
|
27
|
-
export function stringKebabCase(string: string, separator = '-'): string {
|
|
28
|
-
return string.replace(/[A-Z]/g, (origin) => `${separator}${origin.toLowerCase()}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* 生成随机字符串
|
|
33
|
-
* @param {number} length - 生成的随机字符串长度
|
|
34
|
-
* @param {string} [dict] - 用于生成随机字符串的字符字典,默认为数字、小写字母和大写字母的组合
|
|
35
|
-
* @returns {string} - 生成的随机字符串
|
|
36
|
-
* @example
|
|
37
|
-
* randomString(10); // 生成一个长度为 10 的随机字符串
|
|
38
|
-
* randomString(8, 'ABCDEF'); // 生成一个长度为 8 的随机字符串,仅包含字符 'ABCDEF'
|
|
39
|
-
*/
|
|
40
|
-
export function randomString(length: number, dict?: string): string {
|
|
41
|
-
const dictFinal = dict || STRING_DICT;
|
|
42
|
-
const dictLength = dictFinal.length;
|
|
43
|
-
|
|
44
|
-
let result = '';
|
|
45
|
-
|
|
46
|
-
for (let i = 0; i < length; i++) {
|
|
47
|
-
result += dictFinal.charAt(randomNumber(0, dictLength - 1));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 简单的模板引擎,类似于 Python 的 `.format()` 方法
|
|
55
|
-
* 支持通过索引或对象/名称的方式传递变量
|
|
56
|
-
* 当使用对象/名称方式时,可以传递一个回退值作为第三个参数
|
|
57
|
-
*
|
|
58
|
-
* @category 字符串
|
|
59
|
-
* @example
|
|
60
|
-
* ```
|
|
61
|
-
* // 索引方式
|
|
62
|
-
* const result = stringFormat(
|
|
63
|
-
* '你好 {0}!我的名字是 {1}。',
|
|
64
|
-
* '张三',
|
|
65
|
-
* '李四'
|
|
66
|
-
* ); // 你好 张三!我的名字是 李四。
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```
|
|
71
|
-
* // 对象方式
|
|
72
|
-
* const result = stringFormat(
|
|
73
|
-
* '{greet}!我的名字是 {name}。',
|
|
74
|
-
* { greet: '你好', name: '王五' }
|
|
75
|
-
* ); // 你好!我的名字是 王五。
|
|
76
|
-
* ```
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* ```
|
|
80
|
-
* // 带回退值的对象方式
|
|
81
|
-
* const result = stringFormat(
|
|
82
|
-
* '{greet}!我的名字是 {name}。',
|
|
83
|
-
* { greet: '你好' }, // name 未传递,因此会使用回退值
|
|
84
|
-
* '未知'
|
|
85
|
-
* ); // 你好!我的名字是 未知。
|
|
86
|
-
* ```
|
|
87
|
-
*/
|
|
88
|
-
export function stringFormat(
|
|
89
|
-
str: string,
|
|
90
|
-
object: Record<string | number, unknown>,
|
|
91
|
-
fallback?: string | ((key: string) => string),
|
|
92
|
-
): string;
|
|
93
|
-
export function stringFormat(str: string, ...args: (string | number | bigint | undefined | null)[]): string;
|
|
94
|
-
export function stringFormat(str: string, ...args: unknown[]): string {
|
|
95
|
-
const [firstArg, fallback] = args;
|
|
96
|
-
|
|
97
|
-
if (isObject(firstArg) || isUndefined(firstArg)) {
|
|
98
|
-
const vars = firstArg || {};
|
|
99
|
-
return str.replace(/\{(\w+)\}/g, (_, key) => vars[key] ?? (isFunction(fallback) ? fallback(key) : fallback) ?? key);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return str.replace(/\{(\d+)\}/g, (_, key) => {
|
|
103
|
-
const index = Number(key);
|
|
104
|
-
if (Number.isNaN(index)) return key;
|
|
105
|
-
return args[index];
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* 生成符合 [RFC 4122](https://www.ietf.org/rfc/rfc4122.txt) 版本 4 的 UUID 字符串
|
|
111
|
-
* @returns {string} - 生成的 UUID 字符串
|
|
112
|
-
* @example
|
|
113
|
-
* const uuid = randomUUID4();
|
|
114
|
-
* console.log(uuid); // 输出类似 '123e4567-e89b-12d3-a456-426614174000' 的 UUID 字符串
|
|
115
|
-
*/
|
|
116
|
-
export function randomUUID4(): string {
|
|
117
|
-
const template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
|
|
118
|
-
let result = '';
|
|
119
|
-
|
|
120
|
-
for (let i = 0; i < template.length; i++) {
|
|
121
|
-
const t = template[i];
|
|
122
|
-
|
|
123
|
-
if (t === '-' || t === '4') {
|
|
124
|
-
result += t;
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (t === 'y') {
|
|
129
|
-
result += randomString(1, '89ab');
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
result += randomString(1, STRING_HEXADECIMALS);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* 将值转换为字符串,若值为 null 或 undefined 则返回空字符串
|
|
141
|
-
* @param {unknown} value - 需要转换的值
|
|
142
|
-
* @returns {string} 转换后的字符串结果
|
|
143
|
-
*/
|
|
144
|
-
export function stringify(value: unknown) {
|
|
145
|
-
return isNullish(value) ? '' : String(value);
|
|
146
|
-
}
|
package/src/time/from.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// @rer https://day.js.org/docs/en/durations/creating
|
|
2
|
-
|
|
3
|
-
import { DATE_DAY_MS, DATE_HOUR_MS, DATE_MINUTE_MS, DATE_MONTH_MS, DATE_SECOND_MS, DATE_YEAR_MS } from '@/date';
|
|
4
|
-
import { isString } from '@/type';
|
|
5
|
-
import type { TTimeDuration } from './to';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 时间转换规则数组
|
|
9
|
-
* @type {Array<[RegExp, (match: RegExpMatchArray) => number]>}
|
|
10
|
-
* @property {RegExp} 0 - 匹配时间单位正则表达式
|
|
11
|
-
* @property {function} 1 - 将匹配结果转换为毫秒数的函数
|
|
12
|
-
*/
|
|
13
|
-
const rules: [key: keyof TTimeDuration, time: number][] = [
|
|
14
|
-
['years', DATE_YEAR_MS],
|
|
15
|
-
['months', DATE_MONTH_MS],
|
|
16
|
-
['days', DATE_DAY_MS],
|
|
17
|
-
['hours', DATE_HOUR_MS],
|
|
18
|
-
['minutes', DATE_MINUTE_MS],
|
|
19
|
-
['seconds', DATE_SECOND_MS],
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 将时间持续时间字符串或对象转换为毫秒数
|
|
24
|
-
*
|
|
25
|
-
* @param duration - 可以是时间持续时间字符串(如 '1d2h')或 TTimeDuration 对象
|
|
26
|
-
* @returns 计算得到的总毫秒数
|
|
27
|
-
*/
|
|
28
|
-
export function timeFrom(duration: string | TTimeDuration) {
|
|
29
|
-
const td = isString(duration) ? timeParse(duration) : duration;
|
|
30
|
-
return rules.reduce((acc, [key, time]) => acc + (td[key] || 0) * time, 0);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const durationMatchRules: [RegExp, key: keyof TTimeDuration][] = [
|
|
34
|
-
[/(\d+)y/i, 'years'],
|
|
35
|
-
[/(\d+)M/, 'months'],
|
|
36
|
-
[/(\d+)d/i, 'days'],
|
|
37
|
-
[/(\d+)h/i, 'hours'],
|
|
38
|
-
[/(\d+)m/, 'minutes'],
|
|
39
|
-
[/(\d+)s/, 'seconds'],
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 将时长字符串解析为时间对象
|
|
44
|
-
* @param duration - 时长字符串(例如 "1h30m")
|
|
45
|
-
* @returns 包含解析后时间单位的对象(小时、分钟等)
|
|
46
|
-
*/
|
|
47
|
-
export function timeParse(duration: string) {
|
|
48
|
-
const result = {} as TTimeDuration;
|
|
49
|
-
|
|
50
|
-
for (const [regex, key] of durationMatchRules) {
|
|
51
|
-
const match = duration.match(regex);
|
|
52
|
-
if (match) result[key] = Number(match[1]);
|
|
53
|
-
else result[key] = 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return result;
|
|
57
|
-
}
|
package/src/time/to.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { DATE_DAY_MS, DATE_HOUR_MS, DATE_MINUTE_MS, DATE_SECOND_MS } from '../date';
|
|
2
|
-
|
|
3
|
-
export type TTimeDuration = {
|
|
4
|
-
years: number;
|
|
5
|
-
months: number;
|
|
6
|
-
/** 天数 */
|
|
7
|
-
days: number;
|
|
8
|
-
/** 小时数 */
|
|
9
|
-
hours: number;
|
|
10
|
-
/** 分钟数 */
|
|
11
|
-
minutes: number;
|
|
12
|
-
/** 秒数 */
|
|
13
|
-
seconds: number;
|
|
14
|
-
/** 毫秒数 */
|
|
15
|
-
milliseconds: number;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type _TTimeParsePoint = 'D' | 'h' | 'm' | 's' | 'S';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 解析时间毫秒数为绝对时间对象
|
|
22
|
-
* @param timeMs 时间毫秒数
|
|
23
|
-
* @param maxPoint 最大时间单位(决定分解的起始单位)
|
|
24
|
-
* @returns 包含时间单位分解结果的对象
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* // 默认以天为最大单位分解
|
|
28
|
-
* timeInDay(123456789);
|
|
29
|
-
* // { days: 1, hours: 10, minutes: 17, seconds: 36, milliseconds: 789 }
|
|
30
|
-
*
|
|
31
|
-
* // 指定最大单位为分钟,分解到分钟及以下单位
|
|
32
|
-
* timeInMinute(123456789);
|
|
33
|
-
* // { days: 0, hours: 0, minutes: 2057, seconds: 36, milliseconds: 789 }
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
function _timeAbsolute(timeMs: number, maxPoint: _TTimeParsePoint): TTimeDuration {
|
|
37
|
-
const minPoint: _TTimeParsePoint = 'S';
|
|
38
|
-
|
|
39
|
-
const defines: [point: _TTimeParsePoint, key: keyof TTimeDuration, base: number][] = [
|
|
40
|
-
['D', 'days', DATE_DAY_MS],
|
|
41
|
-
['h', 'hours', DATE_HOUR_MS],
|
|
42
|
-
['m', 'minutes', DATE_MINUTE_MS],
|
|
43
|
-
['s', 'seconds', DATE_SECOND_MS],
|
|
44
|
-
['S', 'milliseconds', 1],
|
|
45
|
-
] as const;
|
|
46
|
-
|
|
47
|
-
const minIndex = defines.findIndex((item) => item[0] === maxPoint);
|
|
48
|
-
const maxIndex = defines.findIndex((item) => item[0] === minPoint);
|
|
49
|
-
|
|
50
|
-
let timeMsFinal = timeMs;
|
|
51
|
-
const dao: TTimeDuration = {
|
|
52
|
-
years: 0,
|
|
53
|
-
months: 0,
|
|
54
|
-
days: 0,
|
|
55
|
-
hours: 0,
|
|
56
|
-
minutes: 0,
|
|
57
|
-
seconds: 0,
|
|
58
|
-
milliseconds: 0,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
for (let i = minIndex; i <= maxIndex; i++) {
|
|
62
|
-
const mode = defines[i];
|
|
63
|
-
const base = mode[2];
|
|
64
|
-
const value = Math.floor(timeMsFinal / base);
|
|
65
|
-
timeMsFinal = timeMsFinal - value * base;
|
|
66
|
-
dao[mode[1]] = value;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return dao;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 将时间毫秒数解析为以天为最大单位的绝对时间对象
|
|
74
|
-
* @param timeMs 时间毫秒数
|
|
75
|
-
* @returns 包含天/小时/分钟/秒/毫秒分解结果的对象
|
|
76
|
-
*/
|
|
77
|
-
export function timeToDays(timeMs: number) {
|
|
78
|
-
return _timeAbsolute(timeMs, 'D');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 将时间毫秒数解析为以小时为最大单位的绝对时间对象
|
|
83
|
-
* @param timeMs 时间毫秒数
|
|
84
|
-
* @returns 包含小时/分钟/秒/毫秒分解结果的对象
|
|
85
|
-
*/
|
|
86
|
-
export function timeToHours(timeMs: number) {
|
|
87
|
-
return _timeAbsolute(timeMs, 'h');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* 将时间毫秒数解析为以分钟为最大单位的绝对时间对象
|
|
92
|
-
* @param timeMs 时间毫秒数
|
|
93
|
-
* @returns 包含分钟/秒/毫秒分解结果的对象
|
|
94
|
-
*/
|
|
95
|
-
export function timeToMinutes(timeMs: number) {
|
|
96
|
-
return _timeAbsolute(timeMs, 'm');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 将时间毫秒数解析为以秒为最大单位的绝对时间对象
|
|
101
|
-
* @param timeMs 时间毫秒数
|
|
102
|
-
* @returns 包含秒/毫秒分解结果的对象
|
|
103
|
-
*/
|
|
104
|
-
export function timeToSeconds(timeMs: number) {
|
|
105
|
-
return _timeAbsolute(timeMs, 's');
|
|
106
|
-
}
|
package/src/tree.ts
DELETED
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
import { arrayEach } from './array';
|
|
2
|
-
import { isArray, isNullish, isUndefined } from './type';
|
|
3
|
-
import type { AnyObject } from './types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 表示深度遍历中的节点对象,包含子节点列表。
|
|
7
|
-
*/
|
|
8
|
-
export type TTreeItem = AnyObject & {
|
|
9
|
-
/**
|
|
10
|
-
* 子节点列表。
|
|
11
|
-
*/
|
|
12
|
-
children?: TTreeItem[];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 表示深度遍历中的节点列表。
|
|
17
|
-
*
|
|
18
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
19
|
-
*/
|
|
20
|
-
export type TTreeList<I extends TTreeItem> = I[];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 表示深度遍历中的遍历器状态。
|
|
24
|
-
*
|
|
25
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
26
|
-
*/
|
|
27
|
-
export type TTreeWalker<I extends TTreeItem> = {
|
|
28
|
-
/**
|
|
29
|
-
* 当前层级的节点列表。
|
|
30
|
-
*/
|
|
31
|
-
list: TTreeList<I>;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 当前节点的父节点,如果为根节点则为 `null`。
|
|
35
|
-
*/
|
|
36
|
-
parent: I | null;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 当前节点的层级,从 1 开始计数。
|
|
40
|
-
*/
|
|
41
|
-
level: number;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 从根节点到当前节点的路径。
|
|
45
|
-
*/
|
|
46
|
-
path: TTreeList<I>;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 表示深度遍历中的节点信息。
|
|
51
|
-
*
|
|
52
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
53
|
-
*/
|
|
54
|
-
export type TTreeInfo<I extends TTreeItem> = TTreeWalker<I> & {
|
|
55
|
-
/**
|
|
56
|
-
* 当前节点。
|
|
57
|
-
*/
|
|
58
|
-
item: I;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* 当前节点在 `list` 中的索引。
|
|
62
|
-
*/
|
|
63
|
-
index: number;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 深度遍历的同步迭代器函数类型。
|
|
68
|
-
*
|
|
69
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
70
|
-
* @param info - 当前节点的信息。
|
|
71
|
-
* @returns 如果返回 `false`,则提前终止遍历。
|
|
72
|
-
*/
|
|
73
|
-
export type TreeEachIterator<I extends TTreeItem> = (info: TTreeInfo<I>) => false | unknown;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 深度遍历的异步迭代器函数类型。
|
|
77
|
-
*
|
|
78
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
79
|
-
* @param info - 当前节点的信息。
|
|
80
|
-
* @returns 如果返回 `false`,则提前终止遍历。
|
|
81
|
-
*/
|
|
82
|
-
export type TreeEachIteratorAsync<I extends TTreeItem> = (info: TTreeInfo<I>) => Promise<boolean | unknown>;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 深度遍历的同步遍历器函数类型。
|
|
86
|
-
*
|
|
87
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
88
|
-
* @param walker - 遍历器状态。
|
|
89
|
-
* @returns 遍历结果。
|
|
90
|
-
*/
|
|
91
|
-
export type TreeWalk<I extends TTreeItem> = (walker: TTreeWalker<I>) => unknown;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* 深度遍历的异步遍历器函数类型。
|
|
95
|
-
*
|
|
96
|
-
* @template I - 节点对象的类型,必须继承自 `TreeItem`。
|
|
97
|
-
* @param walker - 遍历器状态。
|
|
98
|
-
* @returns 异步遍历结果。
|
|
99
|
-
*/
|
|
100
|
-
export type TreeWalkAsync<I extends TTreeItem> = (walker: TTreeWalker<I>) => Promise<unknown>;
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* 深度遍历数组中的每个元素,并对每个元素执行提供的回调函数。
|
|
104
|
-
*
|
|
105
|
-
* @param treeList - 要遍历的深度数组。
|
|
106
|
-
* @param iterator - 对每个元素执行的回调函数。如果回调函数返回 `false`,则提前终止遍历。
|
|
107
|
-
* @param breadthFist - 是否使用广度优先遍历,默认为 `false`(深度优先)。
|
|
108
|
-
* @returns 无返回值。
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* ```typescript
|
|
112
|
-
* const treeList = [
|
|
113
|
-
* { value: 1, children: [{ value: 2 }, { value: 3 }] },
|
|
114
|
-
* { value: 4 }
|
|
115
|
-
* ];
|
|
116
|
-
*
|
|
117
|
-
* treeEach(treeList, (item) => {
|
|
118
|
-
* console.log(item.value);
|
|
119
|
-
* if (item.value === 2) return false; // 提前终止遍历
|
|
120
|
-
* });
|
|
121
|
-
* ```
|
|
122
|
-
*/
|
|
123
|
-
export function treeEach<I extends TTreeItem = TTreeItem>(
|
|
124
|
-
treeList: TTreeList<I>,
|
|
125
|
-
iterator: TreeEachIterator<I>,
|
|
126
|
-
breadthFist = false,
|
|
127
|
-
): void {
|
|
128
|
-
const treeInfoList: TTreeInfo<I>[] = [];
|
|
129
|
-
let returnFalse = false;
|
|
130
|
-
|
|
131
|
-
const iterate = (info: TTreeInfo<I>) => {
|
|
132
|
-
if (iterator(info) === false) {
|
|
133
|
-
returnFalse = true;
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const next = (info: TTreeInfo<I>, walk: TreeWalk<I>) => {
|
|
139
|
-
const { item, level, parent, path } = info;
|
|
140
|
-
const { children } = item;
|
|
141
|
-
|
|
142
|
-
if (isArray(children)) {
|
|
143
|
-
returnFalse =
|
|
144
|
-
walk({
|
|
145
|
-
...info,
|
|
146
|
-
parent: item,
|
|
147
|
-
list: children as TTreeList<I>,
|
|
148
|
-
level: level + 1,
|
|
149
|
-
}) === false;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const walk: TreeWalk<I> = (walker) => {
|
|
154
|
-
const { list, level, parent, path } = walker;
|
|
155
|
-
|
|
156
|
-
const path2 = [...path];
|
|
157
|
-
while (parent !== null && path2.length > 0 && path2[path2.length - 1] !== parent) {
|
|
158
|
-
path2.pop();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
arrayEach(list, (item, index) => {
|
|
162
|
-
if (returnFalse) return false;
|
|
163
|
-
|
|
164
|
-
const info: TTreeInfo<I> = {
|
|
165
|
-
...walker,
|
|
166
|
-
item,
|
|
167
|
-
index,
|
|
168
|
-
path: [...path2, item],
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// 广度优先
|
|
172
|
-
if (breadthFist) {
|
|
173
|
-
treeInfoList.push(info);
|
|
174
|
-
}
|
|
175
|
-
// 深度优先
|
|
176
|
-
else {
|
|
177
|
-
iterate(info);
|
|
178
|
-
if (returnFalse) return false;
|
|
179
|
-
next(info, walk);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
if (breadthFist) {
|
|
184
|
-
while (!returnFalse) {
|
|
185
|
-
const info = treeInfoList.shift();
|
|
186
|
-
if (!info) break;
|
|
187
|
-
|
|
188
|
-
iterate(info);
|
|
189
|
-
if (returnFalse) break;
|
|
190
|
-
|
|
191
|
-
// 内部也会进入 walk
|
|
192
|
-
next(info, walk);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return !returnFalse;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
walk({
|
|
200
|
-
list: treeList,
|
|
201
|
-
level: 1,
|
|
202
|
-
parent: null,
|
|
203
|
-
path: [],
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* 在深度数组中查找满足条件的第一个节点信息。
|
|
209
|
-
*
|
|
210
|
-
* @param treeList - 要查找的深度数组。
|
|
211
|
-
* @param predicate - 判断节点是否满足条件的回调函数。
|
|
212
|
-
* @param breadthFist - 是否使用广度优先查找,默认为 `false`(深度优先)。
|
|
213
|
-
* @returns 如果找到满足条件的节点,则返回该节点的信息;否则返回 `undefined`。
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* ```typescript
|
|
217
|
-
* const treeList = [
|
|
218
|
-
* { value: 1, children: [{ value: 2 }, { value: 3 }] },
|
|
219
|
-
* { value: 4 }
|
|
220
|
-
* ];
|
|
221
|
-
*
|
|
222
|
-
* const found = treeFind(treeList, (info) => info.item.value === 3);
|
|
223
|
-
* console.log(found);
|
|
224
|
-
* // {
|
|
225
|
-
* // item: { value: 3 },
|
|
226
|
-
* // index: 1,
|
|
227
|
-
* // list: [{ value: 2 }, { value: 3 }],
|
|
228
|
-
* // parent: { value: 1, children: [{ value: 2 }, { value: 3 }] },
|
|
229
|
-
* // level: 2,
|
|
230
|
-
* // path: [{ value: 1, children: [{ value: 2 }, { value: 3 }] }, { value: 3 }]
|
|
231
|
-
* // }
|
|
232
|
-
* ```
|
|
233
|
-
*/
|
|
234
|
-
export function treeFind<I extends TTreeItem>(
|
|
235
|
-
treeList: TTreeList<I>,
|
|
236
|
-
predicate: (info: TTreeInfo<I>) => boolean,
|
|
237
|
-
breadthFist = false,
|
|
238
|
-
): TTreeInfo<I> | undefined {
|
|
239
|
-
let found: TTreeInfo<I> | undefined;
|
|
240
|
-
|
|
241
|
-
treeEach(
|
|
242
|
-
treeList,
|
|
243
|
-
(info) => {
|
|
244
|
-
if (predicate(info)) {
|
|
245
|
-
found = info;
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
breadthFist,
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
return found;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* 将深度嵌套的树形结构扁平化为一维数组,并对每个节点执行指定的转换函数。
|
|
257
|
-
*
|
|
258
|
-
* @template I - 树节点的类型,必须继承自 `TreeItem`。
|
|
259
|
-
* @template T - 转换后的数据类型。
|
|
260
|
-
* @param {TTreeList<I>} deepList - 要扁平化的深度嵌套树形结构。
|
|
261
|
-
* @param {(info: TTreeInfo<I>) => T} flatten - 对每个节点执行的转换函数,返回转换后的数据。
|
|
262
|
-
* @param {boolean} [breadthFist=false] - 是否使用广度优先遍历,默认为 `false`(深度优先)。
|
|
263
|
-
* @returns {T[]} - 转换后的一维数组。
|
|
264
|
-
* @example
|
|
265
|
-
* ```typescript
|
|
266
|
-
* const treeList = [
|
|
267
|
-
* { value: 1, children: [{ value: 2 }, { value: 3 }] },
|
|
268
|
-
* { value: 4 }
|
|
269
|
-
* ];
|
|
270
|
-
*
|
|
271
|
-
* const flattened = deepFlat(treeList, (info) => info.item.value);
|
|
272
|
-
* console.log(flattened); // [1, 2, 3, 4]
|
|
273
|
-
* ```
|
|
274
|
-
*/
|
|
275
|
-
export function deepFlat<I extends TTreeItem, T>(
|
|
276
|
-
deepList: TTreeList<I>,
|
|
277
|
-
flatten: (info: TTreeInfo<I>) => T,
|
|
278
|
-
breadthFist = false,
|
|
279
|
-
): T[] {
|
|
280
|
-
const list2: T[] = [];
|
|
281
|
-
|
|
282
|
-
treeEach(
|
|
283
|
-
deepList,
|
|
284
|
-
(info) => {
|
|
285
|
-
list2.push(flatten(info));
|
|
286
|
-
},
|
|
287
|
-
breadthFist,
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
return list2;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
type FromItemInfo<I> = {
|
|
294
|
-
selfKey: unknown;
|
|
295
|
-
parentKey: unknown;
|
|
296
|
-
item: I;
|
|
297
|
-
index: number;
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
export type TTreeFromOptions<I extends TTreeItem> = {
|
|
301
|
-
getSelfKey: (item: I, index: number) => unknown;
|
|
302
|
-
getParentKey: (item: I, index: number) => unknown;
|
|
303
|
-
appendChild: (parentInfo: FromItemInfo<I>, info: FromItemInfo<I>) => unknown;
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* 从扁平列表构建树形结构。
|
|
308
|
-
*
|
|
309
|
-
* @template I - 节点对象的类型,必须继承自 `AnyObject`。
|
|
310
|
-
* @param {I[]} list - 扁平化的节点列表。
|
|
311
|
-
* @param {TTreeFromOptions<I>} options - 构建树形结构的配置选项。
|
|
312
|
-
* @param {function} options.getSelfKey - 获取节点自身唯一标识的函数。
|
|
313
|
-
* @param {function} options.getParentKey - 获取节点父节点唯一标识的函数。
|
|
314
|
-
* @param {function} options.appendChild - 将子节点添加到父节点的函数。
|
|
315
|
-
* @returns {I | undefined} - 构建的树形结构的根节点,如果未找到根节点则返回 `undefined`。
|
|
316
|
-
*
|
|
317
|
-
* @example
|
|
318
|
-
* ```typescript
|
|
319
|
-
* const list = [
|
|
320
|
-
* { id: 1, parentId: null, name: 'Root' },
|
|
321
|
-
* { id: 2, parentId: 1, name: 'Child 1' },
|
|
322
|
-
* { id: 3, parentId: 1, name: 'Child 2' }
|
|
323
|
-
* ];
|
|
324
|
-
*
|
|
325
|
-
* const tree = treeFrom(list, {
|
|
326
|
-
* getSelfKey: (item) => item.id,
|
|
327
|
-
* getParentKey: (item) => item.parentId,
|
|
328
|
-
* appendChild: (parent, info) => {
|
|
329
|
-
* if (!parent.children) parent.children = [];
|
|
330
|
-
* parent.children.push(info.item);
|
|
331
|
-
* }
|
|
332
|
-
* });
|
|
333
|
-
*
|
|
334
|
-
* console.log(tree);
|
|
335
|
-
* // {
|
|
336
|
-
* // id: 1,
|
|
337
|
-
* // parentId: null,
|
|
338
|
-
* // name: 'Root',
|
|
339
|
-
* // children: [
|
|
340
|
-
* // { id: 2, parentId: 1, name: 'Child 1' },
|
|
341
|
-
* // { id: 3, parentId: 1, name: 'Child 2' }
|
|
342
|
-
* // ]
|
|
343
|
-
* // }
|
|
344
|
-
* ```
|
|
345
|
-
*/
|
|
346
|
-
export function treeFrom<I extends TTreeItem>(list: I[], options: TTreeFromOptions<I>): TTreeList<I> | undefined {
|
|
347
|
-
const keyMap = new Map<unknown, FromItemInfo<I>>();
|
|
348
|
-
const freeSet = new Set<FromItemInfo<I>>();
|
|
349
|
-
const roots: TTreeList<I> = [];
|
|
350
|
-
|
|
351
|
-
// 分配节点
|
|
352
|
-
const assign = (info: FromItemInfo<I>, isFirst = false) => {
|
|
353
|
-
const { selfKey, parentKey, item, index } = info;
|
|
354
|
-
|
|
355
|
-
// 父级指向为空
|
|
356
|
-
if (isNullish(parentKey)) {
|
|
357
|
-
roots.push(item);
|
|
358
|
-
}
|
|
359
|
-
// 父级指向不为空
|
|
360
|
-
else {
|
|
361
|
-
const parent = keyMap.get(parentKey);
|
|
362
|
-
|
|
363
|
-
// 未找到父级节点
|
|
364
|
-
if (isUndefined(parent)) {
|
|
365
|
-
// 游离节点
|
|
366
|
-
if (isFirst) freeSet.add(info);
|
|
367
|
-
}
|
|
368
|
-
// 已找到父级节点
|
|
369
|
-
else {
|
|
370
|
-
options.appendChild(parent, info);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
// 构建 map
|
|
376
|
-
arrayEach(list, (item, index) => {
|
|
377
|
-
const selfKey = options.getSelfKey(item, index);
|
|
378
|
-
const parentKey = options.getParentKey(item, index);
|
|
379
|
-
|
|
380
|
-
if (isNullish(selfKey)) return;
|
|
381
|
-
|
|
382
|
-
const info = { selfKey, parentKey, item, index };
|
|
383
|
-
keyMap.set(selfKey, info);
|
|
384
|
-
|
|
385
|
-
assign(info, true);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// 处理游离节点
|
|
389
|
-
for (const info of freeSet.values()) {
|
|
390
|
-
assign(info);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return roots;
|
|
394
|
-
}
|