功能
- 传入一个普通对象,生成菜单
- 能够代开和关闭某个节点。
效果
实现后效果如下
原理
重点在于Vue组件可以嵌套使用。
思路
首先实现一个组件TreeNavItrem
,它表示树中的其他子树或叶子结点。然后实现TreeNav
组件,它表示树的根节点,根节点没有内容,只作为一个容器。
代码
注意,使用了阿里的图标iconfont。
TreeNavItem.vue
<template>
<div class="nav-tree-item-group">
<div class="nav-tree-item-name" @click="toggleTree">
<i class="iconfont icon-arrow-right" :class="{ 'icon-arrow-right-open': isOpen }" v-if="isTree"></i>
{{treeData.name}}
</div>
<ul class="nav-tree-item-subTree" v-show="isOpen" v-if="isTree">
<li v-for="(item,index) in treeData.subTree" :key="index">
<TreeNavItem :treeData="item"></TreeNavItem>
</li>
</ul>
</div>
</template>
<script>
import "@/assets/icon/iconfont/iconfont.css"
/**
* 树状导航栏中的一项
* tree item结构:
* 普通树:
{
name:"title"
}
子树:
{
name:"subtitle 1",
subTree:[
{name:"title 1.1"},
{name:"title 1.2"}
]
}
*/
export default {
name:"TreeNavItem",
data:function(){
return {
isOpen:false
}
},
props:{
/**
* 树的数据
*/
treeData:{
type:Object,
default:[{name:"title"}]
}
},
computed:{
/**
* 是树?即非叶子节点
*/
isTree(){
return this.treeData.subTree && this.treeData.subTree.length>0;
}
},
methods:{
/**
* 数打开与关闭状态的切换
* 前提非叶子节点
*/
toggleTree(){
if(this.isTree){
this.isOpen=!this.isOpen;
}
}
}
}
</script>
<style>
.nav-tree-item-subTree{
list-style-type: none;
padding-left:0;
}
.nav-tree-item-subTree > li{
margin-bottom:0.2rem;
margin-left:1rem;
}
.nav-tree-item-name{
margin-left: 1rem;
position: relative;
}
.icon-arrow-right{
display:inline-block;
position: absolute;
left: -1rem;
font-size: 0.5rem;
color: green;
font-weight: bolder;
top: 50%;
transform: translateY(-50%);
transition: transform 0.5s;
}
.icon-arrow-right-open{
transform: translateY(-50%) rotate(90deg);
}
</style>
TreeNav.vue
<template>
<div class="nav-tree-group">
<ul class="nav-tree">
<li class="nav-tree-item" v-for="(item,index) in treeData" :key="index">
<TreeNavItem :treeData="item"></TreeNavItem>
</li>
</ul>
</div>
</template>
<script>
import TreeNavItem from "./TreeNavItem"
/**
* 树状导航栏
* 数据格式如下所示:
let treeData=[
{name:"title 1"},
{name:"title 2"},
{
name:"title 3",
subTree:[
{name:"title 3.1"},
{name:"tile 3.2"}
]
},
{
name:"title 4",
subTree:[
{name:"title 4.1"},
{name:"title 4.2"},
{
name:"title 4.3",
subTree:[
{name:"title 4.3.1"},
{name:"title 4.3.2"},
{name:"title 4.3.3"}
]
}
]
}
]
*/
export default {
name:"TreeNav",
props:{
/**
* 树的数据
*/
treeData:{
type:Array,
default:[{name:"title"}]
}
},
components:{
TreeNavItem
}
}
</script>
<style>
.nav-tree-group{
background-color: white;
padding:0.5rem;
}
.nav-tree{
list-style-type: none;
padding-left:0;
}
.nav-tree-item{
margin-bottom:0.2rem;
}
</style>
用于展示效果的App.vue
<template>
<div id="app">
<TreeNav class="nav-sidebar" :treeData="titles"></TreeNav>
</div>
</template>
<script>
import MarkdownEditor from "@/components/MarkdownEditor"
import TreeNav from "@/components/Tree/TreeNav"
export default {
name: 'app',
data:function(){
return {
titles:[
{name:"用户管理"},
{name:"订单管理"},
{
name:"商品管理",
subTree:[
{name:"添加商品"},
{name:"查看商品"}
]
},
{
name:"个人信息",
subTree:[
{name:"修改信息"},
{name:"添加信息"},
{
name:"聊天记录",
subTree:[
{name:"聊天"},
{name:"删除好友"},
{name:"添加好友"}
]
}
]
}
]
}
},
components:{
MarkdownEditor,
TreeNav
}
}
</script>
<style>
#app{
position: fixed;
top:0;
bottom:0;
left:0;
right:0;
background-color: #eee;
}
.nav-sidebar{
width:8rem;
}
</style>