-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/rzx007/nav
- Loading branch information
Showing
11 changed files
with
968 additions
and
714 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<!-- 基于giscus的评论 --> | ||
<!-- 参考: https://tricker39.github.io/programming-knowledge/home/giscus --> | ||
<template> | ||
<div class="comment"> | ||
<component | ||
:is="'script'" | ||
src="https://giscus.app/client.js" | ||
data-repo="rzx007/nav" | ||
data-repo-id="R_kgDOKFJWVg" | ||
data-category="Show and tell" | ||
data-category-id="DIC_kwDOKFJWVs4CclyE" | ||
data-mapping="pathname" | ||
data-strict="0" | ||
data-reactions-enabled="1" | ||
data-emit-metadata="0" | ||
data-input-position="bottom" | ||
:data-theme="!isDark ? 'light' : 'dark'" | ||
data-lang="zh-CN" | ||
crossorigin="anonymous" | ||
data-loading="eager" | ||
async | ||
></component> | ||
</div> | ||
</template> | ||
<script setup lang="ts"> | ||
import { useData } from 'vitepress' | ||
// 获取当前配色方案 | ||
const { isDark } = useData() | ||
</script> | ||
<style lang="scss" scoped> | ||
.comment { | ||
margin-top: 16px; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
interface Node { | ||
id: string; // 节点的唯一标识符 | ||
children?: Array<Node>; // 节点的子节点数组 | ||
pid?: string; // 节点的父节点标识符 | ||
} | ||
interface Options { | ||
idKey?: string; // 自定义 id 字段的名称 | ||
pidKey?: string; // 自定义 pid 字段的名称 | ||
childrenKey?: string; // 自定义 children 字段的名称 | ||
} | ||
|
||
export const arrayToTree = ( | ||
array: Array<Node | undefined>, | ||
options: Options = {} | ||
) => { | ||
if (!Array.isArray(array)) { | ||
throw new Error('The first argument must be an array.'); | ||
} | ||
// 解构 options 对象来获取自定义字段名称或默认值 | ||
const { idKey = 'id', pidKey = 'pid', childrenKey = 'children' } = options; | ||
// 创建节点 id 到节点的映射 | ||
const map = array.reduce((acc: Record<string, Node>, node: any) => { | ||
acc[node[idKey]] = { ...node, [childrenKey]: [] }; // 初始化节点并添加到映射中 | ||
return acc; | ||
}, {}); | ||
|
||
Object.values(map).forEach((node: any) => { | ||
// 遍历所有节点 | ||
const parentId = node[pidKey]; | ||
if (parentId) { | ||
// 如果存在父节点 | ||
const parent: any = map[parentId]; // 获取当前节点的父节点 | ||
if (!parent[childrenKey]) { | ||
// 如果父节点没有 children 属性 | ||
parent[childrenKey] = []; // 初始化 children 属性 | ||
} | ||
parent[childrenKey].push(node); // 将当前节点添加到父节点的 children 数组中 | ||
} | ||
}); | ||
|
||
// 找到所有根节点(没有父节点的节点)并构建树 | ||
const tree = Object.values(map).filter((node: any) => !node[pidKey]); | ||
|
||
return tree; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
interface Item { | ||
// Add any existing properties | ||
children?: Item[]; | ||
// Define the new property | ||
id: string; | ||
} | ||
export const generateTree = (depth: number, width: number): Item => { | ||
const node: Item = { | ||
id: Math.floor(Math.random() * 1000000).toString() | ||
}; | ||
if (depth > 0) { | ||
node.children = []; | ||
for (let i = 0; i < width; i++) { | ||
node.children.push(generateTree(depth - 1, width)); | ||
} | ||
} | ||
return node; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// 节点类型 Node Type | ||
interface Node { | ||
[key: string]: any; | ||
children?: Array<Node>; | ||
} | ||
|
||
// 入参类型 | ||
interface TreeToArrayOptions { | ||
// 子节点的键名称,默认为'children' | ||
childrenKey?: string; | ||
// 需要忽略的字段名列表,默认为空列表 | ||
ignoreFields?: Array<string>; | ||
// 需要添加的字段名及其对应属性值的计算方式列表,默认为空列表 | ||
addFields?: Array<{ fieldName: string; callback: (item: Node) => any }>; | ||
// 子节点是否需要父节点的id,默认为true | ||
needParentId?: boolean; | ||
} | ||
|
||
// 根据树状结构以数组形式返回所有节点(包括子孙节点) | ||
export const treeToArray = ( | ||
tree: Array<Node>, | ||
options: TreeToArrayOptions = {} | ||
): Array<Node> => { | ||
const { | ||
// 子节点的键名称 | ||
childrenKey = 'children', | ||
// 要忽略的字段名列表 | ||
ignoreFields = [], | ||
// 需要添加的字段名及其对应属性值的计算方式列表 | ||
addFields = [], | ||
// 子节点是否需要父节点的id,默认为true | ||
needParentId = true | ||
} = options; | ||
const nodes: Array<Node> = []; | ||
// stack用于迭代树结构中的子节点和子孙子节点 | ||
const stack: Array<{ | ||
// 正在处理的节点,包括该节点的信息、父节点的id、以及该节点的所有子节点 | ||
node: Node | null; | ||
children: Array<Node>; | ||
parentId: string | null; | ||
}> = []; | ||
// 将整个树的所有节点压入栈,其中root节点的parentId为空 | ||
stack.push({ | ||
node: null, | ||
children: tree, | ||
parentId: null | ||
}); | ||
while (stack.length) { | ||
const { node, children, parentId } = stack.pop()!; | ||
if (node) { | ||
// 存储该节点的所有属性到newNode中,除去childrenKey所指定的子节点信息 | ||
const { [childrenKey]: subChildren, ...rest } = node; | ||
const newNode = { ...rest }; | ||
if (needParentId) { | ||
// 如果needParentId为true,则将父节点的id添加到该节点的属性中 | ||
newNode['parentId'] = parentId; | ||
} | ||
if (addFields.length) { | ||
// 如果需要添加属性值,则遍历addFields列表,计算对应的属性值 | ||
for (let i = 0; i < addFields.length; i++) { | ||
newNode[addFields[i].fieldName] = addFields[i].callback(node); | ||
} | ||
} | ||
if (ignoreFields.length) { | ||
// 如果需要忽略某些属性,则遍历ignoreFields列表,将对应的属性从newNode中删除 | ||
for (let i = 0; i < ignoreFields.length; i++) { | ||
delete newNode[ignoreFields[i]]; | ||
} | ||
} | ||
// 将newNode存入nodes数组中 | ||
nodes.push(newNode); | ||
} | ||
if (children) { | ||
// 将该节点的所有子节点压入栈中,继续循环直到stack为空 | ||
for (let i = children.length - 1; i >= 0; i--) { | ||
stack.push({ | ||
node: children[i], | ||
children: children[i][childrenKey] || [], | ||
parentId: node?.id || '' | ||
}); | ||
} | ||
} | ||
} | ||
// 返回以数组形式储存的所有节点(包括子孙节点) | ||
return nodes; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
# 树形数据相互转换 | ||
|
||
## 把树形数据转换成扁平数组 | ||
|
||
::: details 查看代码 | ||
<<< @/code/tree/treeToArray.ts{ts} | ||
::: | ||
|
||
### 参数 | ||
|
||
接受两个参数: | ||
|
||
- **Tree**: 树形结构数组 | ||
|
||
- **Options**: 一个可选的参数对象,用于配置转换方法的具体行为 | ||
|
||
| 属性 | 描述 | 类型 | 默认值 | | ||
| ------------ | ------------------------------------------------ | ----------------------------------------------- | ---------- | | ||
| addFields | 需要添加的字段名称及其对应的属性值计算方法的列表 | [{ fieldName: string;callback: (item) => any }] | [] | | ||
| childrenKey | 子节点的键名 | string | 'children' | | ||
| ignoreFields | 要忽略的字段名称列表 | string[] | [] | | ||
| needParentId | 是否添加节点信息的父节点 ID | boolean | true | | ||
|
||
### 示例 | ||
|
||
```javascript | ||
const treeArray = [ | ||
{ | ||
id: '1', | ||
name: 'Node 1', | ||
list: [ | ||
{ | ||
id: '2', | ||
name: 'Node 2', | ||
list: [ | ||
{ | ||
id: '3', | ||
name: 'Node 3' | ||
} | ||
] | ||
}, | ||
{ | ||
id: '4', | ||
name: 'Node 4' | ||
} | ||
] | ||
} | ||
] | ||
const calculateDepth = (node) => { | ||
let depth = 0 | ||
let parent = node | ||
while (parent) { | ||
depth++ | ||
parent = parent['parentId'] && treeArray.find((n) => n.id === parent['parentId']) | ||
} | ||
return depth | ||
} | ||
const options = { | ||
childrenKey: 'list', | ||
ignoreFields: [], | ||
addFields: [ | ||
{ | ||
fieldName: 'hasChildren', // 添加新字段 'hasChildren',用于判断是否有子节点 | ||
callback: (node) => Boolean(node['children']) | ||
}, | ||
{ | ||
fieldName: 'depth', // 添加新字段 'depth',用于记录节点深度 | ||
callback: calculateDepth | ||
} | ||
], | ||
needParentId: true | ||
} | ||
|
||
const flatArray = treeToArray(treeArray, options) | ||
|
||
console.log(flatArray) | ||
``` | ||
结果如下: | ||
```json | ||
[ | ||
{ "id": "1", "name": "Node 1", "parentId": "", "hasChildren": false, "depth": 1 }, | ||
{ "id": "2", "name": "Node 2", "parentId": "1", "hasChildren": false, "depth": 1 }, | ||
{ "id": "3", "name": "Node 3", "parentId": "2", "hasChildren": false, "depth": 1 }, | ||
{ "id": "4", "name": "Node 4", "parentId": "1", "hasChildren": false, "depth": 1 } | ||
] | ||
``` | ||
|
||
## 把扁平数组转换成树形数据 | ||
|
||
::: details 查看代码 | ||
<<< @/code/tree/arrayToTree.ts{ts} | ||
::: | ||
|
||
### 参数 | ||
|
||
它接受两个参数: | ||
|
||
- **Array**: 扁平的节点数组 | ||
- **Options**: 一个可选的参数对象,用于配置转换方法的具体行为 | ||
|
||
| 参数 | 描述 | 类型 | 默认值 | | ||
| ----------- | ---------------------------- | ------ | ---------- | | ||
| childrenKey | 自定义节点 children 字段名称 | string | 'children' | | ||
| idKey | 自定义节点 ID 字段名称 | string | 'id' | | ||
| pidKey | 自定义节点父 ID 字段名称 | string | 'pid' | | ||
|
||
### 示例 | ||
|
||
```javascript | ||
const flatArray = [ | ||
{ uid: '1', name: 'node1', pid: null }, | ||
{ uid: '2', name: 'node2', pid: '1' }, | ||
{ uid: '3', name: 'node3', pid: '1' }, | ||
{ uid: '4', name: 'node4', pid: '2' }, | ||
{ uid: '5', name: 'node5', pid: '2' }, | ||
{ uid: '6', name: 'node6', pid: '3' } | ||
] | ||
|
||
const options = { | ||
idKey: 'id', | ||
pidKey: 'pid', | ||
childrenKey: 'children' | ||
} | ||
|
||
const treeArray = arrayToTree(flatArray, options) | ||
``` | ||
|
||
### 生成随机树形数据 | ||
|
||
<<< @/code/tree/generateTree.ts{ts} |
Oops, something went wrong.