项目原因,需要把一个扁平/线性数组转换成树形数组(符合 el-tree 数据要求)
const resData = [
{ id: '1', label: '动物', parentId: '', icon: '' },
{ id: '2', label: '狗', parentId: '1', icon: 'icon-chongwutubiao13' },
{ id: '3', label: '哈士奇', parentId: '2', icon: 'icon-hashiqi' },
{ id: '4', label: '柯基', parentId: '2', icon: 'icon-keji-' },
{ id: '6', label: '猫', parentId: '1', icon: 'icon-chongwutubiao04' },
{ id: '7', label: '植物', parentId: '', icon: '' },
{ id: '8', label: '微生物', parentId: '', icon: '' },
]
JS 代码:扁平数组转换成树形数组
直接上代码,不需要递归
function transformData(data) {
// 深拷贝一份(以防连续调用出错)
const cData = JSON.parse(JSON.stringify(data))
const map = {}
const tData = []
// 注意:这里item的引用地址指向cData
cData.forEach(item => (map[item.id] = item))
cData.forEach(child => {
const mapItem = map[child.parentId]
if (mapItem) {
// 注意:这里mapItem引用地址指向也是指向cData
if (!mapItem.children) mapItem.children = []
mapItem.children.push(child)
} else {
// 顶级节点
tData.push(child)
}
})
return tData
}
项目应用:el-tree 制作一个树形多级嵌套菜单栏
Vue 代码
<template>
<el-scrollbar wrap-class="scrollbar-wrapper" style="height: 100%">
<el-tree
ref="tree"
v-loading="treeDataLoading"
node-key="id"
:data="treeData"
:show-checkbox="showCheckBox"
:default-expand-all="defaultExpandAll"
style="height: 100%; padding-bottom: 20px"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
{{ node.label }}
<template v-if="data.icon">
<el-tooltip effect="dark" :content="data.label" placement="top-start">
<i :class="['iconfont', data.icon]"></i>
</el-tooltip>
</template>
</span>
</el-tree>
</el-scrollbar>
</template>
<script>
export default {
props: {
// 节点是否可被选择(显示前面的复选框)
showCheckBox: {
type: Boolean,
default: false,
},
// 是否默认展开所有节点
defaultExpandAll: {
type: Boolean,
default: false,
},
},
data() {
return {
treeData: [],
treeDataLoading: false,
resData: [
{ id: '1', label: '动物', parentId: '', icon: '' },
{ id: '2', label: '狗', parentId: '1', icon: 'icon-chongwutubiao13' },
{ id: '3', label: '哈士奇', parentId: '2', icon: 'icon-hashiqi' },
{ id: '4', label: '柯基', parentId: '2', icon: 'icon-keji-' },
{ id: '6', label: '猫', parentId: '1', icon: 'icon-chongwutubiao04' },
{ id: '7', label: '植物', parentId: '', icon: '' },
{ id: '8', label: '微生物', parentId: '', icon: '' },
],
}
},
methods: {
init() {
this.treeDataLoading = true
// 发送数据请求(这里不发送请求以resData模拟)
new Promise(res => {
const tree = this.resData
this.treeData = this.transformData(tree)
res()
})
.catch(() => {})
.finally(() => {
this.treeDataLoading = false
})
},
transformData(data, params = {}) {
const cData = JSON.parse(JSON.stringify(data))
const map = {}
const tData = []
const attr = {
id: 'id',
parentId: 'parentId',
}
const arg = Object.assign({}, attr, params)
cData.forEach(item => (map[item[arg.id]] = item))
cData.forEach(child => {
const mapItem = map[child[arg.parentId]]
if (mapItem) {
if (!mapItem.children) mapItem.children = []
mapItem.children.push(child)
} else {
tData.push(child)
}
})
return tData
},
// 全选
checkAll() {
this.$refs.tree.setCheckedNodes(this.treeData)
},
// 取消全选
cancelAll() {
this.$refs.tree.setCheckedKeys([])
},
},
}
</script>
<style lang="less" scoped>
.scrollbar-wrapper {
overflow-x: hidden !important;
}
.custom-tree-node {
font-size: 16px;
}
</style>
父组件调用
<template>
<div>
<tree-select ref="tree" defaultExpandAll />
</div>
</template>
<script>
import TreeSelect from './components/TreeSelect'
export default {
components: {
TreeSelect,
},
mounted() {
this.$refs['tree'].init()
},
}
</script>
js实现无限层级树形数据结构(创新算法)
由于做项目的需要,把一个线性数组转成树形数组,在网上查了很多文章,觉得他们写的太复杂了,于是自己写了一个,在折腾了一下午终于把它写出来啦(激动.gif),用两个filter过滤器就搞定了,代码简洁明了,数据结构小白都能看懂。
js代码:把扁平数据转成树形数据
function setTreeData(source){
let cloneData = JSON.parse(JSON.stringify(source)) // 对源数据深度克隆
return cloneData.filter(father=>{ // 循环所有项,并添加children属性
let branchArr = cloneData.filter(child=> father.id == child.parentId); // 返回每一项的子级数组
branchArr.length>0 ? father.children=branchArr : '' //给父级添加一个children属性,并赋值
return father.parentId==0; //返回第一层
});
}
封装函数:
function treeData(source, id, parentId, children){
let cloneData = JSON.parse(JSON.stringify(source))
return cloneData.filter(father=>{
let branchArr = cloneData.filter(child => father[id] == child[parentId]);
branchArr.length>0 ? father[children] = branchArr : ''
return father[parentId] == 0 // 如果第一层不是parentId=0,请自行修改
})
}
// 调用时,字段名以字符串的形式传参,如treeData(source, 'id', 'parentId', 'children')
vue组件:
<template>
<el-tree
:data="treeData"
:props="defaultProps"
accordion
@node-click="handleNodeClick">
</el-tree>
</template>
<script>
export default {
name: "Test",
data(){
return {
data : [
{id:1,parentId:0,name:"一级菜单A",rank:1},
{id:2,parentId:0,name:"一级菜单B",rank:1},
{id:3,parentId:0,name:"一级菜单C",rank:1},
{id:4,parentId:1,name:"二级菜单A-A",rank:2},
{id:5,parentId:1,name:"二级菜单A-B",rank:2},
{id:6,parentId:2,name:"二级菜单B-A",rank:2},
{id:7,parentId:4,name:"三级菜单A-A-A",rank:3},
{id:8,parentId:7,name:"四级菜单A-A-A-A",rank:4},
{id:9,parentId:8,name:"五级菜单A-A-A-A-A",rank:5},
{id:10,parentId:9,name:"六级菜单A-A-A-A-A-A",rank:6},
{id:11,parentId:10,name:"七级菜单A-A-A-A-A-A-A",rank:7},
{id:12,parentId:11,name:"八级菜单A-A-A-A-A-A-A-A",rank:8},
{id:13,parentId:12,name:"九级菜单A-A-A-A-A-A-A-A-A",rank:9},
{id:14,parentId:13,name:"十级菜单A-A-A-A-A-A-A-A-A-A",rank:10},
],
defaultProps: {
children: 'children',
label: 'name'
}
}
},
computed:{
treeData(){
let cloneData = JSON.parse(JSON.stringify(this.data)) // 对源数据深度克隆
return cloneData.filter(father=>{
let branchArr = cloneData.filter(child=>father.id == child.parentId) //返回每一项的子级数组
branchArr.length>0 ? father.children = branchArr : '' //如果存在子级,则给父级添加一个children属性,并赋值
return father.parentId==0; //返回第一层
});
}
},
methods:{
handleNodeClick(data){
// console.log(data)
console.log(this.treeData)
}
},
mounted(){
}
}
</script>
<style scoped>
</style>