vue递归组件封装
上效果(递归生成树形菜单,无限层数都可以!)
- 首先需要使用递归方法把数据变成树形结构
//后端传过来的数据长这样子 而且是变化的不确定有多少层的数据
list = [
{ id:1, name: '张三', parent_id: 0, children: [] },
{ id:2, name: '李四', parent_id: 0, children: [] },
{ id:3, name: '王五', parent_id: 1, children: [] },
{ id:4, name: '六六', parent_id: 3, children: [] },
{ id:4, name: '七七', parent_id: 2, children: [] },
]
// 这个时候就需要用到递归来处理这种数据
// 递归生成树形结构
export function initTree (arr, parent_id, i = 0) {
// arr 变量数据
// 第一次以后:根据id去查询parent_id相同的(相同为子数据)
// 第一次:查找所有parent_id为0的数据组成第一级
const child = arr.filter(item => item.parent_id == parent_id)
// 第一次:循环parent_id为0的数组
if (child.length !== 0) {
let level = i + 1
return child.map(item => ({
...item,
level,
// 当前存在id(id与parent_id应该是必须有的)调用initTree() 查找所有parent_id为本id的数据
// childs字段写入
children: initTree(arr, item.id, level)
}))
}
}
- 最后生成的树形结构为这样子
// 第一个参数是数组 第二个是最顶级的parent_id
const treeList = initTree (list, 0)
treeList = [
{ id:1, name: '张三', parent_id: 0,
children: [
{ id:3, name: '王五', parent_id: 1,
children: [
{ id:4, name: '六六', parent_id: 3, children: [] },
]
},
]
},
{ id:2, name: '李四', parent_id: 0,
children: [
{ id:4, name: '七七', parent_id: 2, children: [] },
]
},
]
- 生成递归组件(自己调自己)
<template>
<ul class="menu">
<li v-for="item in menuList" :key="item.name" :class="['menu-item', item.selectName]">
<div :class="['menu-item-content', item.children && item.children.length ? 'parent': ['animated', 'bounceInLeft']]" :style="{'padding-left': item.level * 25 + 'px'}" @click="toggleShow(item)">
<img src="../../assets/image/close.png" alt="" v-show="item.children && item.children.length && !item.showChild">
<img src="../../assets/image/open.png" alt="" v-show="item.children && item.children.length && item.showChild">
<van-icon name="arrow" class="leftIcon" v-if="item.children && item.children.length" />
<span class="menu-item-name" :id="`span${item.id}`" :ref="`ref${item.id}`">
{{item.name}}
<van-icon name="passed" class="nowFill" v-show="!item.children && item.nowFill" />
<van-icon name="passed" class="nowFill" style="right: -30px;" v-show="item.children && item.nowFill" />
<van-icon name="warn-o" class="nowFill minusIcon" v-show="item.children && !item.nowFill" />
<!-- <van-icon name="contact" style="right: -30px;" class="nowFill" v-show="item.is_staff && !icon" />
<van-icon name="cluster-o" style="right: -30px;" class="nowFill" v-show="!item.is_staff && !icon && type===1" /> -->
</span>
</div>
<Menu v-if="item.showChild && item.children && item.children.length" :list="item.children" @clickOne="clickOne"></Menu>
</li>
</ul>
</template>
<script>
import _ from 'lodash'
import '../../utils/animate.min.css'
export default {
name: 'Menu',
props: ['list'],
watch: {
list: {
immediate: true,
deep: true,
handler (newV) {
this.menuList = _.cloneDeep(newV)
this.menuList = this.menuList.map(item => {
item.showChild = true
return item
})
}
}
},
data () {
return {
menuList: [],
beforeItem: {},
beforeDom: null
}
},
methods: {
toggleShow(item) {
if (item.children && item.children.length) {
item.showChild = !item.showChild
this.$forceUpdate()
} else {
this.$emit('clickOne', item)
if (sessionStorage.getItem('dom')) {
console.log(sessionStorage.getItem('dom'))
this.beforeDom = document.getElementById(sessionStorage.getItem('dom'))
this.beforeDom.style.color = 'white'
}
let currentDom = document.getElementById(`span${item.id}`)
currentDom.style.color = '#05f5a5'
sessionStorage.setItem('dom', `span${item.id}`)
}
},
clickOne (item) {
this.$emit('clickOne', item)
}
},
destroyed () {
sessionStorage.removeItem('dom')
}
}
</script>
<style lang="scss">
.menu {
color: white;
width: 100%;
height: 100%;
font-size: 14px;
margin-top: 10px;
.menu-item {
width: 100%;
.menu-item-content {
height: 35px;
line-height: 35px;
display: flex;
align-items: center;
position: relative;
img {
width: 30px;
height: 30px;
}
}
.parent {
background: rgba(255, 255, 255, 0.247);
margin-bottom: 5px;
}
.menu-item-name {
margin-left: 5px;
position: relative;
}
}
}
.selected {
color: #05f5a5 !important;
}
.leftIcon {
position: absolute !important;
top: 50%;
transform: translate(0, -50%);
right: 20px;
color: white !important;
}
.nowFill {
position: absolute !important;
top: 50%;
transform: translate(0, -50%);
color: #05f5a5 !important;
right: -40px;
font-size: 18px !important;
}
.minusIcon {
color: red !important;
right: -30px;
}
</style>
可直接使用 传一个list就行 icon用的vant的。