网络编程
位置:首页>> 网络编程>> JavaScript>> Vue3 通过作用域插槽实现树形菜单嵌套组件

Vue3 通过作用域插槽实现树形菜单嵌套组件

作者:SoaringHeart  发布时间:2024-05-28 15:42:28 

标签:Vue3,树形菜单,嵌套组件,作用域,插槽

一、需求来源

工作中需要一种树形菜单组件,经过两天的构思最终通过作用域插槽实现: 此组件将每个节点(插槽名为 node)暴露出来。

通过插槽的 attributes 向当前插槽节点传递子项 item(数据对象)和level(层深)参数,在保持组件内部极简的同时支持在数据模型中扩展性。基本达到比较好的封装颗粒度,大家可以在此基础上无限扩展封装具体的业务逻辑。

二、效果图

let list = reactive(
 [{
   name:'1 一级菜单',
   isExpand: true,//是否展开子项
   enabled: false,//是否可以响应事件
   child:[
     { name:'1.1 二级菜单',    
       isExpand: true,
       child:[
         { name:'1.1.1 * 菜单', isExpand: true, },
       ]
     },
     { name:'1.2 二级菜单', isExpand: true, },
   ]
 },
 {
   name:'1.1 一级菜单',
   isExpand: true,
   child:[
     { name:'1.1.1 二级菜单', isExpand: true, },
     { name:'1.1.2 二级菜单',
       isExpand: false,
       child:[
         { name:'1.1.2.1 * 菜单', isExpand: true, },
       ]},
   ]
 },]
);

Vue3 通过作用域插槽实现树形菜单嵌套组件

三、使用示例(VTreeNodeDemo.vue)

<template>
 <VTreeNode
   :list="list"
   :level="level"
 >
   <template #node="slotProps">
     <div class="tree-node">
       {{prefix(slotProps.level)}}{{slotProps.item.name}}{{sufix(slotProps.item)}}
     </div>
   </template>
 </VTreeNode>
</template>
<script setup>
import VTreeNode from '@/components/VTreeNode/VTreeNode.vue';
import { ref, reactive, watch, onMounted, } from 'vue';
let list = reactive(
 [{
   name:'1 一级菜单',
   isExpand: true,//是否展开子项
   enabled: false,//是否可以响应事件
   child:[
     { name:'1.1 二级菜单',    
       isExpand: true,
       child:[
         { name:'1.1.1 * 菜单', isExpand: true, },
       ]
     },
     { name:'1.2 二级菜单', isExpand: true, },
   ]
 },
 {
   name:'1.1 一级菜单',
   isExpand: true,
   child:[
     { name:'1.1.1 二级菜单', isExpand: true, },
     { name:'1.1.2 二级菜单',
       isExpand: false,
       child:[
         { name:'1.1.2.1 * 菜单', isExpand: true, },
       ]},
   ]
 },]
);
const level = ref(0);
const prefix = (count) => {
 return '__'.repeat(count);
};
const sufix = (item) => {
 if (!Reflect.has(item, 'child')) {
   return '';
 }
 return ` (${item.child.length}子项)`;
};
</script>
<style scoped lang='scss'>
.tree-node{
 height: 45px;
 display: flex;
 justify-self: center;
 align-items: center;
 // background-color: green;
 border-bottom: 1px solid #e4e4e4;
}
</style>

四、源码(VTreeNode.vue):

<template>
 <!-- <div> -->
   <div v-for="(item,index) in list" :key="index">
     <slot name="node" :item="item" :level="levelRef">
       <div>{{ item.name }}</div>
     </slot>
     <div v-show="item.child && canExpand(item)" >
       <VTreeNode :list="item.child" :level="levelRef">
         <template #node="slotProps">
           <slot name="node" :item="slotProps.item" :level="slotProps.level">
             <div>{{ slotProps.item.name }}</div>
           </slot>
         </template>
       </VTreeNode>
     </div>
   </div>
 <!-- </div> -->
</template>
<script setup>
import { ref, reactive, watch, computed, onMounted, } from 'vue';
const props = defineProps({
 list: {
   type: Array,
   default: () => [],
   validator: (val) => {
     return Array.isArray(val) && val.every(e => Reflect.has(e, 'name'));
   }
 },
 level: {
   type: Number,
   default: 0,
 }
});
const emit = defineEmits(['update:level', ])
const levelRef = computed({
 set: (newVal) => {
   if (props.level !== newVal) {
     emit("update:level", newVal);
   }
 },
 get: () => {
   const tmp = props.level + 1;
   return tmp;
 },
});
const canExpand = (item) => {
 return Reflect.has(item, 'isExpand') && item.isExpand;
};
// onMounted(() => {
//   console.log(`levelRef:${levelRef.value}`);
// });
</script>

VTreeNode.vue

VTreeNodeDemo.vue

来源:https://juejin.cn/post/7165359183582199815

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com