简略效果图.png
目录及数据结构
-
目录结构
- Tree.vue 树形控件父组件
- TreeItem.vue 树形控件递归组件
-
树形控件数据结构
treeData: [ { id: 1, text: '父级', // 显示的文字 expand: false, // 默认是否展开 children: [{ // 子节点 id: 2, text: '子级1', expand: false }, { id: 3, text: '子级2', expand: false, children: [{ id: 4, text: '孙级1', expand: false }, { id: 5, text: '孙级2', expand: false }] }] } ]
解决组件如何才能递归调用
在组件模板内调用自身必须明确定义组件的name属性,并且递归调用时组件名称就是name属性。如在TreeItem.vue组件中组件的name名称为'TreeItem',那么在template中调用时组件名称就必须是<TreeItem>。
当然也可以全局注册组件,具体可以查看vue官方文档 递归组件
TreeItem.vue 代码
<template>
<div>
<div class="tree">
<!-- 展开、收缩 -->
<img :class="{'down': true,'expand_right': treeItemData.expand,'no-child': !treeItemData.children || treeItemData.children.length === 0}"
src="../assets/down.png"
alt=""
@click="treeItemData.expand = !treeItemData.expand">
<!-- 文本 -->
<span v-if="!updateFlag"
class="text">{{treeItemData.text}}</span>
<input v-else
type="text"
ref="inputText"
v-model="treeItemData.text"
@blur="updateFlag = false" />
<!-- 修改 -->
<img class="operation_img"
src="../assets/update.png"
alt=""
@click="updateFlag = !updateFlag">
<!-- 增加 -->
<img class="operation_img"
src="../assets/add.png"
alt=""
@click="append">
<!-- 删除 -->
<img class="operation_img"
src="../assets/yoai_h5_sub.png"
alt=""
@click="del">
</div>
<div style="margin-left: 30px;"
v-if="!treeItemData.expand">
<!-- 递归 -->
<TreeItem v-for="item in treeItemData.children"
:key="item.id"
:treeItemData="item"
:parent="treeItemData.children"></TreeItem>
</div>
</div>
</template>
<script>
let id = 1000;
export default {
name: "TreeItem",
props: {
parent: { // 父级数据
type: Array,
default () {
return [];
}
},
treeItemData: { // 本节点数据
type: Object,
default () {
return {};
}
}
},
watch: {
// 使文本框聚焦
updateFlag (val) {
this.$nextTick(() => {
val && this.$refs.inputText.focus();
})
}
},
data () {
return {
updateFlag: false, // input显示控制
}
},
methods: {
// 添加
append () {
const newChild = { id: id++, text: '新增节点', children: [] };
if (!this.treeItemData.children) {
this.$set(this.treeItemData, 'children', []);
}
this.treeItemData.children.push(newChild);
},
// 删除
del () {
const index = this.parent.findIndex(item => item.id === this.treeItemData.id);
this.parent.splice(index, 1);
}
}
}
</script>
<style lang="less" scoped>
.tree {
display: flex;
align-items: center;
font-size: 16px;
line-height: 40px;
.down {
width: 10px;
height: 10px;
margin-left: -10px;
transition: all 0.2s linear;
&.expand_right {
transform: rotate(-90deg);
}
&.no-child {
display: none;
}
}
.text {
margin: 0 10px;
}
.operation_img {
width: 18px;
height: 18px;
margin-left: 10px;
}
input {
padding: 5px 10px;
width: 100px;
}
}
</style>>
Tree.vue 代码
<template>
<div style="margin-left: 20px;">
<TreeItem v-for="item in treeData"
:key="item.id"
:treeItemData="item"
:parent="treeData"></TreeItem>
</div>
</template>
<script>
import TreeItem from "./TreeItem";
export default {
name: "Tree",
components: {
TreeItem
},
props: {
treeData: { // 树形控件数据
type: Array,
default () {
return [];
}
}
}
}
</script>
使用Tree组件
<template>
<div>
<button v-if="treeData.length === 0"
@click="addNode">添加</button>
<Tree v-else
:tree-data="treeData"></Tree>
</div>
</template>
<script>
import Tree from "./Tree";
export default {
components: {
Tree
},
data () {
return {
treeData: [
{
id: 1,
text: '父级',
expand: false,
children: [{
id: 2,
text: '子级1',
expand: false,
},
{
id: 3,
text: '子级2',
expand: false,
children: [{
id: 4,
text: '孙级1',
expand: false,
},
{
id: 5,
text: '孙级2',
expand: false,
}]
}]
}
]
}
},
methods: {
addNode () {
this.treeData.push({
id: 1,
text: '父级',
expand: false
})
}
}
}
</script>