vue递归组件

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的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值