
工作中需要一种树形菜单组件,经过两天的构思最终通过作用域插槽实现: 此组件将每个节点(插槽名为 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, }, ]}, ] },] );
使用示例(VTreeNodeDemo.vue)
<template> <VTreeNode :list="list" :level="level" > <template #node="slotprops"> <div> {{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> .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> 


网友评论文明上网理性发言 已有0人参与
发表评论: