a2bei4-utils 1.0.2 → 1.0.4
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/LICENSE +21 -21
- package/README.md +2 -2
- package/dist/a2bei4.utils.cjs.js +2080 -1846
- package/dist/a2bei4.utils.cjs.js.map +1 -1
- package/dist/a2bei4.utils.cjs.min.js +1 -1
- package/dist/a2bei4.utils.cjs.min.js.map +1 -1
- package/dist/a2bei4.utils.esm.js +2073 -1843
- package/dist/a2bei4.utils.esm.js.map +1 -1
- package/dist/a2bei4.utils.esm.min.js +1 -1
- package/dist/a2bei4.utils.esm.min.js.map +1 -1
- package/dist/a2bei4.utils.umd.js +2080 -1846
- package/dist/a2bei4.utils.umd.js.map +1 -1
- package/dist/a2bei4.utils.umd.min.js +1 -1
- package/dist/a2bei4.utils.umd.min.js.map +1 -1
- package/dist/arr.cjs +27 -27
- package/dist/arr.cjs.map +1 -1
- package/dist/arr.js +27 -27
- package/dist/arr.js.map +1 -1
- package/dist/audio.cjs +274 -274
- package/dist/audio.cjs.map +1 -1
- package/dist/audio.js +274 -274
- package/dist/audio.js.map +1 -1
- package/dist/browser.cjs +52 -52
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +52 -52
- package/dist/browser.js.map +1 -1
- package/dist/common.cjs +369 -369
- package/dist/common.cjs.map +1 -1
- package/dist/common.js +369 -369
- package/dist/common.js.map +1 -1
- package/dist/date.cjs +421 -188
- package/dist/date.cjs.map +1 -1
- package/dist/date.js +414 -185
- package/dist/date.js.map +1 -1
- package/dist/download.cjs +102 -102
- package/dist/download.cjs.map +1 -1
- package/dist/download.js +102 -102
- package/dist/download.js.map +1 -1
- package/dist/evt.cjs +148 -148
- package/dist/evt.cjs.map +1 -1
- package/dist/evt.js +148 -148
- package/dist/evt.js.map +1 -1
- package/dist/id.cjs +68 -68
- package/dist/id.cjs.map +1 -1
- package/dist/id.js +68 -68
- package/dist/id.js.map +1 -1
- package/dist/timer.cjs +51 -50
- package/dist/timer.cjs.map +1 -1
- package/dist/timer.js +51 -50
- package/dist/timer.js.map +1 -1
- package/dist/tree.cjs +165 -165
- package/dist/tree.cjs.map +1 -1
- package/dist/tree.js +165 -165
- package/dist/tree.js.map +1 -1
- package/dist/webSocket.cjs +403 -403
- package/dist/webSocket.cjs.map +1 -1
- package/dist/webSocket.js +403 -403
- package/dist/webSocket.js.map +1 -1
- package/package.json +1 -1
- package/readme.txt +21 -11
- package/types/date.d.ts +243 -45
- package/types/index.d.ts +244 -47
package/dist/tree.js
CHANGED
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 把嵌套树拍平成 `{ [id]: node }` 映射,同时把原 `children` 置为 `null`。
|
|
3
|
-
*
|
|
4
|
-
* @template T extends Record<PropertyKey, any>
|
|
5
|
-
* @param {T[]} data - 嵌套树森林
|
|
6
|
-
* @param {string} [idKey='id'] - 主键字段
|
|
7
|
-
* @param {string} [childrenKey='children'] - 子节点字段
|
|
8
|
-
* @returns {Record<string, T & { [k in typeof childrenKey]: null }>} id→节点的映射表
|
|
9
|
-
*/
|
|
10
|
-
function nestedTree2IdMap(data, idKey = "id", childrenKey = "children") {
|
|
11
|
-
const retObj = {};
|
|
12
|
-
function fn(nodes) {
|
|
13
|
-
if (Array.isArray(nodes) && nodes.length > 0) {
|
|
14
|
-
nodes.forEach((node) => {
|
|
15
|
-
retObj[node[idKey]] = { ...node };
|
|
16
|
-
retObj[node[idKey]][childrenKey] = null;
|
|
17
|
-
|
|
18
|
-
fn(node[childrenKey]);
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
fn(data);
|
|
23
|
-
return retObj;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* 把**已包含完整父子关系**的扁平节点列表还原成嵌套树(森林)。
|
|
28
|
-
*
|
|
29
|
-
* @template T extends Record<PropertyKey, any>
|
|
30
|
-
* @param {T[]} nodes - 扁平节点列表(必须包含 id / parentId)
|
|
31
|
-
* @param {number | string} [parentId=0] - 根节点标识值
|
|
32
|
-
* @param {Object} [opts] - 字段映射配置
|
|
33
|
-
* @param {string} [opts.idKey='id'] - 节点主键
|
|
34
|
-
* @param {string} [opts.parentKey='parentId'] - 父节点外键
|
|
35
|
-
* @param {string} [opts.childrenKey='children'] - 存放子节点的字段
|
|
36
|
-
* @returns {(T & { [k in typeof childrenKey]: T[] })[]} 嵌套树森林
|
|
37
|
-
*/
|
|
38
|
-
function flatCompleteTree2NestedTree(nodes, parentId = 0, { idKey = "id", parentKey = "parentId", childrenKey = "children" } = {}) {
|
|
39
|
-
const map = new Map(); // id -> node
|
|
40
|
-
const items = []; // 多根森林
|
|
41
|
-
|
|
42
|
-
// 1. 初始化:保证每个节点都有 children,并存入 map
|
|
43
|
-
for (const item of nodes) {
|
|
44
|
-
const node = { ...item, [childrenKey]: [] };
|
|
45
|
-
map.set(item[idKey], node);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 2. 建立父子关系
|
|
49
|
-
for (const item of nodes) {
|
|
50
|
-
const node = map.get(item[idKey]);
|
|
51
|
-
const parentIdVal = item[parentKey];
|
|
52
|
-
|
|
53
|
-
if (parentIdVal === parentId) {
|
|
54
|
-
// 根层
|
|
55
|
-
items.push(node);
|
|
56
|
-
} else {
|
|
57
|
-
// 非根层:找到父节点,把自己挂上去
|
|
58
|
-
const parent = map.get(parentIdVal);
|
|
59
|
-
if (parent) parent[childrenKey].push(node);
|
|
60
|
-
// 如果 parent 不存在,说明数据不完整,可自定义处理
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return items;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 在嵌套树中按 `id` 递归查找节点,并返回其指定属性值。
|
|
69
|
-
*
|
|
70
|
-
* @template T extends Record<PropertyKey, any>
|
|
71
|
-
* @param {string | number} id - 要查找的 id
|
|
72
|
-
* @param {T[]} arr - 嵌套树森林
|
|
73
|
-
* @param {string} [resultKey='name'] - 需要返回的字段
|
|
74
|
-
* @param {string} [idKey='id'] - 主键字段
|
|
75
|
-
* @param {string} [childrenKey='children'] - 子节点字段
|
|
76
|
-
* @returns {any} 找到的值;未找到返回 `undefined`
|
|
77
|
-
*/
|
|
78
|
-
const findObjAttrValueById = function findObjAttrValueByIdFn(id, arr, resultKey = "name", idKey = "id", childrenKey = "children") {
|
|
79
|
-
if (Array.isArray(arr) && arr.length > 0) {
|
|
80
|
-
for (let i = 0; i < arr.length; i++) {
|
|
81
|
-
const item = arr[i];
|
|
82
|
-
if (item[idKey]?.toString() === id?.toString()) {
|
|
83
|
-
return item[resultKey];
|
|
84
|
-
} else if (Array.isArray(item[childrenKey]) && item[childrenKey].length > 0) {
|
|
85
|
-
const result = findObjAttrValueByIdFn(id, item[childrenKey], resultKey, idKey, childrenKey);
|
|
86
|
-
if (result) {
|
|
87
|
-
return result;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 从服务端返回的已选 id 数组里,提取出
|
|
96
|
-
* 1. 叶子节点
|
|
97
|
-
* 2. 所有子节点都被选中的父节点
|
|
98
|
-
* 其余父节点一律丢弃(由前端 Tree 自动算半选)
|
|
99
|
-
*
|
|
100
|
-
* @param {Array} treeData 完整树
|
|
101
|
-
* @param {Array} selectedKeys 后端给的选中 id 数组
|
|
102
|
-
* @param {String} idKey 节点唯一字段
|
|
103
|
-
* @param {String} childrenKey 子节点字段
|
|
104
|
-
* @returns {{checked: string[], halfChecked: string[]}}
|
|
105
|
-
*/
|
|
106
|
-
function extractFullyCheckedKeys(treeData, selectedKeys, idKey = "id", childrenKey = "children") {
|
|
107
|
-
const selectedSet = new Set(selectedKeys);
|
|
108
|
-
const checked = new Set();
|
|
109
|
-
const halfChecked = new Set();
|
|
110
|
-
|
|
111
|
-
/* 返回值含义
|
|
112
|
-
0 - 未选中
|
|
113
|
-
1 - 半选
|
|
114
|
-
2 - 全选
|
|
115
|
-
*/
|
|
116
|
-
function dfs(node) {
|
|
117
|
-
const nodeId = node[idKey];
|
|
118
|
-
const children = node[childrenKey] || [];
|
|
119
|
-
|
|
120
|
-
// 叶子
|
|
121
|
-
if (!children.length) {
|
|
122
|
-
if (selectedSet.has(nodeId)) {
|
|
123
|
-
checked.add(nodeId);
|
|
124
|
-
return 2;
|
|
125
|
-
}
|
|
126
|
-
return 0;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 非叶子
|
|
130
|
-
let allChecked = true;
|
|
131
|
-
let someChecked = false;
|
|
132
|
-
|
|
133
|
-
children.forEach((child) => {
|
|
134
|
-
const childState = dfs(child);
|
|
135
|
-
if (childState !== 2) allChecked = false;
|
|
136
|
-
if (childState >= 1) someChecked = true;
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// 当前节点本身在 selectedKeys 里,但子节点未全选 → 只能算半选
|
|
140
|
-
if (selectedSet.has(nodeId)) {
|
|
141
|
-
if (allChecked) {
|
|
142
|
-
checked.add(nodeId);
|
|
143
|
-
return 2;
|
|
144
|
-
}
|
|
145
|
-
halfChecked.add(nodeId);
|
|
146
|
-
return 1;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 当前节点不在 selectedKeys 里,看子节点
|
|
150
|
-
if (allChecked) {
|
|
151
|
-
checked.add(nodeId);
|
|
152
|
-
return 2;
|
|
153
|
-
}
|
|
154
|
-
if (someChecked) {
|
|
155
|
-
halfChecked.add(nodeId);
|
|
156
|
-
return 1;
|
|
157
|
-
}
|
|
158
|
-
return 0;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
treeData.forEach(dfs);
|
|
162
|
-
return {
|
|
163
|
-
checked: [...checked],
|
|
164
|
-
halfChecked: [...halfChecked]
|
|
165
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* 把嵌套树拍平成 `{ [id]: node }` 映射,同时把原 `children` 置为 `null`。
|
|
3
|
+
*
|
|
4
|
+
* @template T extends Record<PropertyKey, any>
|
|
5
|
+
* @param {T[]} data - 嵌套树森林
|
|
6
|
+
* @param {string} [idKey='id'] - 主键字段
|
|
7
|
+
* @param {string} [childrenKey='children'] - 子节点字段
|
|
8
|
+
* @returns {Record<string, T & { [k in typeof childrenKey]: null }>} id→节点的映射表
|
|
9
|
+
*/
|
|
10
|
+
function nestedTree2IdMap(data, idKey = "id", childrenKey = "children") {
|
|
11
|
+
const retObj = {};
|
|
12
|
+
function fn(nodes) {
|
|
13
|
+
if (Array.isArray(nodes) && nodes.length > 0) {
|
|
14
|
+
nodes.forEach((node) => {
|
|
15
|
+
retObj[node[idKey]] = { ...node };
|
|
16
|
+
retObj[node[idKey]][childrenKey] = null;
|
|
17
|
+
|
|
18
|
+
fn(node[childrenKey]);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
fn(data);
|
|
23
|
+
return retObj;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 把**已包含完整父子关系**的扁平节点列表还原成嵌套树(森林)。
|
|
28
|
+
*
|
|
29
|
+
* @template T extends Record<PropertyKey, any>
|
|
30
|
+
* @param {T[]} nodes - 扁平节点列表(必须包含 id / parentId)
|
|
31
|
+
* @param {number | string} [parentId=0] - 根节点标识值
|
|
32
|
+
* @param {Object} [opts] - 字段映射配置
|
|
33
|
+
* @param {string} [opts.idKey='id'] - 节点主键
|
|
34
|
+
* @param {string} [opts.parentKey='parentId'] - 父节点外键
|
|
35
|
+
* @param {string} [opts.childrenKey='children'] - 存放子节点的字段
|
|
36
|
+
* @returns {(T & { [k in typeof childrenKey]: T[] })[]} 嵌套树森林
|
|
37
|
+
*/
|
|
38
|
+
function flatCompleteTree2NestedTree(nodes, parentId = 0, { idKey = "id", parentKey = "parentId", childrenKey = "children" } = {}) {
|
|
39
|
+
const map = new Map(); // id -> node
|
|
40
|
+
const items = []; // 多根森林
|
|
41
|
+
|
|
42
|
+
// 1. 初始化:保证每个节点都有 children,并存入 map
|
|
43
|
+
for (const item of nodes) {
|
|
44
|
+
const node = { ...item, [childrenKey]: [] };
|
|
45
|
+
map.set(item[idKey], node);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. 建立父子关系
|
|
49
|
+
for (const item of nodes) {
|
|
50
|
+
const node = map.get(item[idKey]);
|
|
51
|
+
const parentIdVal = item[parentKey];
|
|
52
|
+
|
|
53
|
+
if (parentIdVal === parentId) {
|
|
54
|
+
// 根层
|
|
55
|
+
items.push(node);
|
|
56
|
+
} else {
|
|
57
|
+
// 非根层:找到父节点,把自己挂上去
|
|
58
|
+
const parent = map.get(parentIdVal);
|
|
59
|
+
if (parent) parent[childrenKey].push(node);
|
|
60
|
+
// 如果 parent 不存在,说明数据不完整,可自定义处理
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return items;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 在嵌套树中按 `id` 递归查找节点,并返回其指定属性值。
|
|
69
|
+
*
|
|
70
|
+
* @template T extends Record<PropertyKey, any>
|
|
71
|
+
* @param {string | number} id - 要查找的 id
|
|
72
|
+
* @param {T[]} arr - 嵌套树森林
|
|
73
|
+
* @param {string} [resultKey='name'] - 需要返回的字段
|
|
74
|
+
* @param {string} [idKey='id'] - 主键字段
|
|
75
|
+
* @param {string} [childrenKey='children'] - 子节点字段
|
|
76
|
+
* @returns {any} 找到的值;未找到返回 `undefined`
|
|
77
|
+
*/
|
|
78
|
+
const findObjAttrValueById = function findObjAttrValueByIdFn(id, arr, resultKey = "name", idKey = "id", childrenKey = "children") {
|
|
79
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
80
|
+
for (let i = 0; i < arr.length; i++) {
|
|
81
|
+
const item = arr[i];
|
|
82
|
+
if (item[idKey]?.toString() === id?.toString()) {
|
|
83
|
+
return item[resultKey];
|
|
84
|
+
} else if (Array.isArray(item[childrenKey]) && item[childrenKey].length > 0) {
|
|
85
|
+
const result = findObjAttrValueByIdFn(id, item[childrenKey], resultKey, idKey, childrenKey);
|
|
86
|
+
if (result) {
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 从服务端返回的已选 id 数组里,提取出
|
|
96
|
+
* 1. 叶子节点
|
|
97
|
+
* 2. 所有子节点都被选中的父节点
|
|
98
|
+
* 其余父节点一律丢弃(由前端 Tree 自动算半选)
|
|
99
|
+
*
|
|
100
|
+
* @param {Array} treeData 完整树
|
|
101
|
+
* @param {Array} selectedKeys 后端给的选中 id 数组
|
|
102
|
+
* @param {String} idKey 节点唯一字段
|
|
103
|
+
* @param {String} childrenKey 子节点字段
|
|
104
|
+
* @returns {{checked: string[], halfChecked: string[]}}
|
|
105
|
+
*/
|
|
106
|
+
function extractFullyCheckedKeys(treeData, selectedKeys, idKey = "id", childrenKey = "children") {
|
|
107
|
+
const selectedSet = new Set(selectedKeys);
|
|
108
|
+
const checked = new Set();
|
|
109
|
+
const halfChecked = new Set();
|
|
110
|
+
|
|
111
|
+
/* 返回值含义
|
|
112
|
+
0 - 未选中
|
|
113
|
+
1 - 半选
|
|
114
|
+
2 - 全选
|
|
115
|
+
*/
|
|
116
|
+
function dfs(node) {
|
|
117
|
+
const nodeId = node[idKey];
|
|
118
|
+
const children = node[childrenKey] || [];
|
|
119
|
+
|
|
120
|
+
// 叶子
|
|
121
|
+
if (!children.length) {
|
|
122
|
+
if (selectedSet.has(nodeId)) {
|
|
123
|
+
checked.add(nodeId);
|
|
124
|
+
return 2;
|
|
125
|
+
}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 非叶子
|
|
130
|
+
let allChecked = true;
|
|
131
|
+
let someChecked = false;
|
|
132
|
+
|
|
133
|
+
children.forEach((child) => {
|
|
134
|
+
const childState = dfs(child);
|
|
135
|
+
if (childState !== 2) allChecked = false;
|
|
136
|
+
if (childState >= 1) someChecked = true;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// 当前节点本身在 selectedKeys 里,但子节点未全选 → 只能算半选
|
|
140
|
+
if (selectedSet.has(nodeId)) {
|
|
141
|
+
if (allChecked) {
|
|
142
|
+
checked.add(nodeId);
|
|
143
|
+
return 2;
|
|
144
|
+
}
|
|
145
|
+
halfChecked.add(nodeId);
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 当前节点不在 selectedKeys 里,看子节点
|
|
150
|
+
if (allChecked) {
|
|
151
|
+
checked.add(nodeId);
|
|
152
|
+
return 2;
|
|
153
|
+
}
|
|
154
|
+
if (someChecked) {
|
|
155
|
+
halfChecked.add(nodeId);
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
treeData.forEach(dfs);
|
|
162
|
+
return {
|
|
163
|
+
checked: [...checked],
|
|
164
|
+
halfChecked: [...halfChecked]
|
|
165
|
+
};
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
export { extractFullyCheckedKeys, findObjAttrValueById, flatCompleteTree2NestedTree, nestedTree2IdMap };
|
package/dist/tree.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.js","sources":["../src/source/tree.js"],"sourcesContent":["/**\n * 把嵌套树拍平成 `{ [id]: node }` 映射,同时把原 `children` 置为 `null`。\n *\n * @template T extends Record<PropertyKey, any>\n * @param {T[]} data - 嵌套树森林\n * @param {string} [idKey='id'] - 主键字段\n * @param {string} [childrenKey='children'] - 子节点字段\n * @returns {Record<string, T & { [k in typeof childrenKey]: null }>} id→节点的映射表\n */\nexport function nestedTree2IdMap(data, idKey = \"id\", childrenKey = \"children\") {\n const retObj = {};\n function fn(nodes) {\n if (Array.isArray(nodes) && nodes.length > 0) {\n nodes.forEach((node) => {\n retObj[node[idKey]] = { ...node };\n retObj[node[idKey]][childrenKey] = null;\n\n fn(node[childrenKey]);\n });\n }\n }\n fn(data);\n return retObj;\n}\n\n/**\n * 把**已包含完整父子关系**的扁平节点列表还原成嵌套树(森林)。\n *\n * @template T extends Record<PropertyKey, any>\n * @param {T[]} nodes - 扁平节点列表(必须包含 id / parentId)\n * @param {number | string} [parentId=0] - 根节点标识值\n * @param {Object} [opts] - 字段映射配置\n * @param {string} [opts.idKey='id'] - 节点主键\n * @param {string} [opts.parentKey='parentId'] - 父节点外键\n * @param {string} [opts.childrenKey='children'] - 存放子节点的字段\n * @returns {(T & { [k in typeof childrenKey]: T[] })[]} 嵌套树森林\n */\nexport function flatCompleteTree2NestedTree(nodes, parentId = 0, { idKey = \"id\", parentKey = \"parentId\", childrenKey = \"children\" } = {}) {\n const map = new Map(); // id -> node\n const items = []; // 多根森林\n\n // 1. 初始化:保证每个节点都有 children,并存入 map\n for (const item of nodes) {\n const node = { ...item, [childrenKey]: [] };\n map.set(item[idKey], node);\n }\n\n // 2. 建立父子关系\n for (const item of nodes) {\n const node = map.get(item[idKey]);\n const parentIdVal = item[parentKey];\n\n if (parentIdVal === parentId) {\n // 根层\n items.push(node);\n } else {\n // 非根层:找到父节点,把自己挂上去\n const parent = map.get(parentIdVal);\n if (parent) parent[childrenKey].push(node);\n // 如果 parent 不存在,说明数据不完整,可自定义处理\n }\n }\n\n return items;\n}\n\n/**\n * 在嵌套树中按 `id` 递归查找节点,并返回其指定属性值。\n *\n * @template T extends Record<PropertyKey, any>\n * @param {string | number} id - 要查找的 id\n * @param {T[]} arr - 嵌套树森林\n * @param {string} [resultKey='name'] - 需要返回的字段\n * @param {string} [idKey='id'] - 主键字段\n * @param {string} [childrenKey='children'] - 子节点字段\n * @returns {any} 找到的值;未找到返回 `undefined`\n */\nexport const findObjAttrValueById = function findObjAttrValueByIdFn(id, arr, resultKey = \"name\", idKey = \"id\", childrenKey = \"children\") {\n if (Array.isArray(arr) && arr.length > 0) {\n for (let i = 0; i < arr.length; i++) {\n const item = arr[i];\n if (item[idKey]?.toString() === id?.toString()) {\n return item[resultKey];\n } else if (Array.isArray(item[childrenKey]) && item[childrenKey].length > 0) {\n const result = findObjAttrValueByIdFn(id, item[childrenKey], resultKey, idKey, childrenKey);\n if (result) {\n return result;\n }\n }\n }\n }\n};\n\n/**\n * 从服务端返回的已选 id 数组里,提取出\n * 1. 叶子节点\n * 2. 所有子节点都被选中的父节点\n * 其余父节点一律丢弃(由前端 Tree 自动算半选)\n *\n * @param {Array} treeData 完整树\n * @param {Array} selectedKeys 后端给的选中 id 数组\n * @param {String} idKey 节点唯一字段\n * @param {String} childrenKey 子节点字段\n * @returns {{checked: string[], halfChecked: string[]}}\n */\nexport function extractFullyCheckedKeys(treeData, selectedKeys, idKey = \"id\", childrenKey = \"children\") {\n const selectedSet = new Set(selectedKeys);\n const checked = new Set();\n const halfChecked = new Set();\n\n /* 返回值含义\n 0 - 未选中\n 1 - 半选\n 2 - 全选\n */\n function dfs(node) {\n const nodeId = node[idKey];\n const children = node[childrenKey] || [];\n\n // 叶子\n if (!children.length) {\n if (selectedSet.has(nodeId)) {\n checked.add(nodeId);\n return 2;\n }\n return 0;\n }\n\n // 非叶子\n let allChecked = true;\n let someChecked = false;\n\n children.forEach((child) => {\n const childState = dfs(child);\n if (childState !== 2) allChecked = false;\n if (childState >= 1) someChecked = true;\n });\n\n // 当前节点本身在 selectedKeys 里,但子节点未全选 → 只能算半选\n if (selectedSet.has(nodeId)) {\n if (allChecked) {\n checked.add(nodeId);\n return 2;\n }\n halfChecked.add(nodeId);\n return 1;\n }\n\n // 当前节点不在 selectedKeys 里,看子节点\n if (allChecked) {\n checked.add(nodeId);\n return 2;\n }\n if (someChecked) {\n halfChecked.add(nodeId);\n return 1;\n }\n return 0;\n }\n\n treeData.forEach(dfs);\n return {\n checked: [...checked],\n halfChecked: [...halfChecked]\n };\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,WAAW,GAAG,UAAU,EAAE;AAC/E,IAAI,MAAM,MAAM,GAAG,EAAE;
|
|
1
|
+
{"version":3,"file":"tree.js","sources":["../src/source/tree.js"],"sourcesContent":["/**\r\n * 把嵌套树拍平成 `{ [id]: node }` 映射,同时把原 `children` 置为 `null`。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} data - 嵌套树森林\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {Record<string, T & { [k in typeof childrenKey]: null }>} id→节点的映射表\r\n */\r\nexport function nestedTree2IdMap(data, idKey = \"id\", childrenKey = \"children\") {\r\n const retObj = {};\r\n function fn(nodes) {\r\n if (Array.isArray(nodes) && nodes.length > 0) {\r\n nodes.forEach((node) => {\r\n retObj[node[idKey]] = { ...node };\r\n retObj[node[idKey]][childrenKey] = null;\r\n\r\n fn(node[childrenKey]);\r\n });\r\n }\r\n }\r\n fn(data);\r\n return retObj;\r\n}\r\n\r\n/**\r\n * 把**已包含完整父子关系**的扁平节点列表还原成嵌套树(森林)。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} nodes - 扁平节点列表(必须包含 id / parentId)\r\n * @param {number | string} [parentId=0] - 根节点标识值\r\n * @param {Object} [opts] - 字段映射配置\r\n * @param {string} [opts.idKey='id'] - 节点主键\r\n * @param {string} [opts.parentKey='parentId'] - 父节点外键\r\n * @param {string} [opts.childrenKey='children'] - 存放子节点的字段\r\n * @returns {(T & { [k in typeof childrenKey]: T[] })[]} 嵌套树森林\r\n */\r\nexport function flatCompleteTree2NestedTree(nodes, parentId = 0, { idKey = \"id\", parentKey = \"parentId\", childrenKey = \"children\" } = {}) {\r\n const map = new Map(); // id -> node\r\n const items = []; // 多根森林\r\n\r\n // 1. 初始化:保证每个节点都有 children,并存入 map\r\n for (const item of nodes) {\r\n const node = { ...item, [childrenKey]: [] };\r\n map.set(item[idKey], node);\r\n }\r\n\r\n // 2. 建立父子关系\r\n for (const item of nodes) {\r\n const node = map.get(item[idKey]);\r\n const parentIdVal = item[parentKey];\r\n\r\n if (parentIdVal === parentId) {\r\n // 根层\r\n items.push(node);\r\n } else {\r\n // 非根层:找到父节点,把自己挂上去\r\n const parent = map.get(parentIdVal);\r\n if (parent) parent[childrenKey].push(node);\r\n // 如果 parent 不存在,说明数据不完整,可自定义处理\r\n }\r\n }\r\n\r\n return items;\r\n}\r\n\r\n/**\r\n * 在嵌套树中按 `id` 递归查找节点,并返回其指定属性值。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {string | number} id - 要查找的 id\r\n * @param {T[]} arr - 嵌套树森林\r\n * @param {string} [resultKey='name'] - 需要返回的字段\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {any} 找到的值;未找到返回 `undefined`\r\n */\r\nexport const findObjAttrValueById = function findObjAttrValueByIdFn(id, arr, resultKey = \"name\", idKey = \"id\", childrenKey = \"children\") {\r\n if (Array.isArray(arr) && arr.length > 0) {\r\n for (let i = 0; i < arr.length; i++) {\r\n const item = arr[i];\r\n if (item[idKey]?.toString() === id?.toString()) {\r\n return item[resultKey];\r\n } else if (Array.isArray(item[childrenKey]) && item[childrenKey].length > 0) {\r\n const result = findObjAttrValueByIdFn(id, item[childrenKey], resultKey, idKey, childrenKey);\r\n if (result) {\r\n return result;\r\n }\r\n }\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * 从服务端返回的已选 id 数组里,提取出\r\n * 1. 叶子节点\r\n * 2. 所有子节点都被选中的父节点\r\n * 其余父节点一律丢弃(由前端 Tree 自动算半选)\r\n *\r\n * @param {Array} treeData 完整树\r\n * @param {Array} selectedKeys 后端给的选中 id 数组\r\n * @param {String} idKey 节点唯一字段\r\n * @param {String} childrenKey 子节点字段\r\n * @returns {{checked: string[], halfChecked: string[]}}\r\n */\r\nexport function extractFullyCheckedKeys(treeData, selectedKeys, idKey = \"id\", childrenKey = \"children\") {\r\n const selectedSet = new Set(selectedKeys);\r\n const checked = new Set();\r\n const halfChecked = new Set();\r\n\r\n /* 返回值含义\r\n 0 - 未选中\r\n 1 - 半选\r\n 2 - 全选\r\n */\r\n function dfs(node) {\r\n const nodeId = node[idKey];\r\n const children = node[childrenKey] || [];\r\n\r\n // 叶子\r\n if (!children.length) {\r\n if (selectedSet.has(nodeId)) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n return 0;\r\n }\r\n\r\n // 非叶子\r\n let allChecked = true;\r\n let someChecked = false;\r\n\r\n children.forEach((child) => {\r\n const childState = dfs(child);\r\n if (childState !== 2) allChecked = false;\r\n if (childState >= 1) someChecked = true;\r\n });\r\n\r\n // 当前节点本身在 selectedKeys 里,但子节点未全选 → 只能算半选\r\n if (selectedSet.has(nodeId)) {\r\n if (allChecked) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n halfChecked.add(nodeId);\r\n return 1;\r\n }\r\n\r\n // 当前节点不在 selectedKeys 里,看子节点\r\n if (allChecked) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n if (someChecked) {\r\n halfChecked.add(nodeId);\r\n return 1;\r\n }\r\n return 0;\r\n }\r\n\r\n treeData.forEach(dfs);\r\n return {\r\n checked: [...checked],\r\n halfChecked: [...halfChecked]\r\n };\r\n}\r\n"],"names":[],"mappings":"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"}
|