1. 效果
一共分为三级,一级标题默认显示,一级和二级标题点击可展开:
需要提供以下格式的数据titles:
data() {
return {
titles: [
{
first: '一级标题1',
seconds: [
{
second: '二级标题1',
thirds: ['三级标题1', '三级标题2', '三级标题3']
},
{
second: '二级标题2',
thirds: ['三级标题4', '三级标题5', '三级标题6']
}
]
},
{
first: '一级标题2',
seconds: [
{
second: '二级标题3',
thirds: ['三级标题7', '三级标题8', '三级标题9']
},
{
second: '二级标题4',
thirds: ['三级标题10', '三级标题11', '三级标题12']
}
]
},
{ first: '一级标题3', seconds: [] }
]
}
},
2. 上代码
Tree.vue里的代码:
<template>
<div id="tree">
<div class="wrapper">
<div v-for="(item) in newTitles" :key="item.ID">
<div @click="titleChange(item.ID)" class="titleFirst">
<tree-svg :Isshow="item.IsOpen" class="treesvg"></tree-svg>
<span>{{item.first}}</span>
</div>
<div v-for="item2 in item.seconds" :key="item2.ID">
<div v-if="item.IsOpen" @click="title2Change(item2.ID)" class="titleSecond">
<tree-svg :Isshow="item2.IsOpen" class="treesvg2"></tree-svg>
<span>{{item2.second}}</span>
</div>
<div class="titleThird" v-for="(item3,index) in item2.thirds" :key="index">
<div v-if="item2.IsOpen&&item.IsOpen">{{item3}}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
newTitles: [] // 重构后的数据结构
}
},
props: ['titles'], // 传入原始数据结构
methods: {
titleChange(ID) { // 一级标题点击事件
this.newTitles.forEach((item, index) => {
if (item.ID === ID) {
this.newTitles[index].IsOpen = !this.newTitles[index].IsOpen
}
})
},
title2Change(ID) { // 二级标题点击事件
this.newTitles.forEach((item, index) => {
item.seconds.forEach((item2, index2) => {
if (item2.ID === ID) {
this.newTitles[index].seconds[index2].IsOpen = !this.newTitles[
index
].seconds[index2].IsOpen
}
})
})
}
},
components: {
TreeSvg: () => import('./tree/TreeSvg.vue')
},
mounted() { // 对数组进行重构
this.newTitles = this.titles.map((item, index) => {
const newSeconds = item.seconds.map((item2, index2) => {
return {
...item2,
IsOpen: false,
ID: index2
}
})
return {
first: item.first,
seconds: newSeconds,
IsOpen: false,
ID: index
}
})
}
}
</script>
<style lang="less" scoped>
#tree {
.titleFirst {
font-size: 20px;
}
.treesvg {
display: inline-block;
}
.titleSecond {
font-size: 18px;
margin-left: 10px;
.treesvg2 {
display: inline-block;
margin-right: 2px;
}
}
.titleThird {
margin-left: 35px;
}
}
</style>
显示箭头方向的组件TreeSvg.vue:
<template>
<div id="treesvg">
<svg v-if="!Isshow"> // 方向向右的svg
<polyline points="2,2.5 9.5,10 2,17.5" style="fill:none;stroke:black;stroke-width:2" />
</svg>
<svg v-if="Isshow"> // 方向向下的svg
<polyline points="2,7 10,14 18,7" style="fill:none;stroke:black;stroke-width:2" />
</svg>
</div>
</template>
<script>
export default {
props: ['Isshow']
}
</script>
<style lang="less" scoped>
#treesvg {
svg {
width: 20px;
height: 20px;
}
}
</style>
引用组件如下,其中的titles的数据结构已在上方演示:
<Tree :titles="titles"></Tree>
3. 分析
- 利用的vue的用数据操作dom的思想,定义了数组newTitles,dom的渲染依据的就是newTitle这个数组。这个数组里面每一级标题的内容、是否展开、下级标题的信息都储存在标题对象里。
- 实现的核心是对原始数组titles的重构,用到了数组的map方法,为原始数组的每个标题对象添加了是否展开这样一个属性,而当点击相应的标题时,则修改对应标题对象的这个属性即可。
- 这个组件的实现用到了许多vue基础相关的东西,对vue基础的使用和js基本算法的都有很大的帮助。
- 代码可能有些复杂和繁琐,但只要逻辑思路清晰,实现起来基本没有问题。