vue封装可实现选中、新增、修改、删除功能的树组件
程序员文章站
2024-03-17 20:26:22
...
我们在开发项目的时候经常会遇到一些树形结构的数据结构,比如部门、班级、关系等等层级结构的数据,通常常规的展示方式一般是通过树的结构来展示出来,现在我们自己就来实现一个可以新增、修改、删除的树。
实现的过程很简单,树一般都会采用递归思路,然后将数据展示出来。
<div class="tree" id="Tree" ref="TreeNode">
<ul>
<li v-for="(v, n) in treeData" :key="n">
<span @click="nodeClick(v, $event)" :class="currentId === v.id ? 'active' : ''">
<strong v-if="!v.isExpend" @click.stop="expendNode(v)">+</strong>
<strong v-if="v.isExpend" @click.stop="expendNode(v)">-</strong>
{{v.name}}
<em @click.stop="addNode(v)" class="del-btn add-btn" v-if="isEdit">新增</em>
<em @click.stop="editNode(v)" class="del-btn edit-btn" v-if="isEdit">编辑</em>
<em @click.stop="delNode(v)" class="del-btn del" v-if="isEdit">删除</em>
</span>
<Tree
:treeData="v.children"
:isEdit="isEdit"
v-if="v.children"
v-show="v.isExpend"
@nodeClick="nodeClick"
@expendNode="expendNode"
@delNode="delNode"
@addNode="addNode"
@editNode="editNode"
/>
</li>
</ul>
</div>
然后抛出对应的节点,通过递归的方式获取到对应的数据结构的节点,然后就可以对节点进行增加,修改,查询了。
// 递归操作根据id获取节点操作
getNode (data, id, type) {
let that = this;
data.map((item, index) => {
if (item.id == id) {
that.currentNode = item; // 结果赋值
// 删除操作
if (type === 'del') {
if (window.confirm('确定要删除节点?')) {
data.splice(index, 1);
alert('删除成功!');
}
}
// 展开收缩
if (type === 'expend') {
item.isExpend = !item.isExpend;
}
// 编辑操作
if (type === 'edit') {
}
// 新增操作
if (type === 'add') {
if (!item.children) {
that.$set(item, 'children', []);
}
item.children.push({
id: new Date().getTime(), // 用时间戳标识唯一id,具体情况可以根据自己定一个唯一标识
name: '新增节点' + new Date().getTime(),
isExpend: false
});
// 当没有子节点的时候新增一个节点后展开节点
if (item.children.length > 0) {
item.isExpend = true;
}
}
} else {
if (item.children) {
this.getNode(item.children, id, type);
}
}
});
}
整个过程主要的思路就是递归遍历输出树,然后通过点击节点递归获取树中对应的节点。
整个代码如下:
Tree组件部分:
<template>
<div class="tree" id="Tree" ref="TreeNode">
<ul>
<li v-for="(v, n) in treeData" :key="n">
<span @click="nodeClick(v, $event)" :class="currentId === v.id ? 'active' : ''">
<strong v-if="!v.isExpend" @click.stop="expendNode(v)">+</strong>
<strong v-if="v.isExpend" @click.stop="expendNode(v)">-</strong>
{{v.name}}
<em @click.stop="addNode(v)" class="del-btn add-btn" v-if="isEdit">新增</em>
<em @click.stop="editNode(v)" class="del-btn edit-btn" v-if="isEdit">编辑</em>
<em @click.stop="delNode(v)" class="del-btn del" v-if="isEdit">删除</em>
</span>
<Tree
:treeData="v.children"
:isEdit="isEdit"
v-if="v.children"
v-show="v.isExpend"
@nodeClick="nodeClick"
@expendNode="expendNode"
@delNode="delNode"
@addNode="addNode"
@editNode="editNode"
/>
</li>
</ul>
</div>
</template>
<script>
import Tree from './tree.vue';
export default {
name: 'Tree',
data () {
return {
currentId: 1 // 高亮当前节点标识
};
},
computed: {
},
props: ['treeData', 'isEdit'],
components: { Tree },
created () {
},
mounted () {
},
methods: {
// 删除节点
delNode (v) {
this.$emit('delNode', v);
},
// 新增节点
addNode (v) {
this.$emit('addNode', v);
},
// 修改节点
editNode (v) {
this.$emit('editNode', v);
},
// dom方法设置当前节点高亮显示
setCurrentNode (node) {
document.querySelector('#Tree span.active').classList.remove('active');
node.classList.add('active');
},
// 点击当前节点
nodeClick (v, $event) {
this.setCurrentNode($event.target);
this.$emit('nodeClick', v, $event);
},
// 收缩展开节点
expendNode (v) {
this.$emit('expendNode', v);
}
}
};
</script>
<style lang="less" scoped>
.tree {
user-select: none;
ul li {
padding:15px 0 0 15px;
span {
&.active {
color:red;
font-weight: bolder;
}
&:hover {
cursor: pointer;
}
display:block;
font-size: 20px;
padding: 15px 0;
color: #fff;
strong {
margin-right: 20px;
display:inline-block;
width: 20px;
height: 20px;
background:#909090;
color:#333;
font-size: 20px;
text-align:center;
line-height:20px;
border-radius:10px;
}
em {
margin-left: 15px;
font-style:normal;
color:#1a63de;
}
.add-btn {
color: green;
}
em.del {
color: red;
}
}
}
}
</style>
外层引入:
<template>
<div id="app">
<div class="loading" v-if="this.$store.state.loadingFlag">
<img src="static/images/loading.gif" />
</div>
<div id="mainBox">
<Tree
:treeData="treeData"
:isEdit="isEdit"
@nodeClick="nodeClick"
@expendNode="expendNode"
@delNode="delNode"
@addNode="addNode"
@editNode="editNode"
/>
<router-view />
</div>
</div>
</template>
<script>
import Tree from '@/pages/tree/Tree.vue';
export default {
name: 'App',
data () {
return {
isEdit: true,
currentNode: null, // 当前点击的节点
treeData: [
{
id: 1,
name: '一级标题 1',
isExpend: true,
children: [
{
id: 2,
name: '二级标题 2-1',
isExpend: true,
children: [
{
id: 5,
name: '三级标题 3-1',
isExpend: true,
children: [
{
id: 7,
name: '四级标题 4-1',
isExpend: false
},
{
id: 8,
name: '四级标题 4-2',
isExpend: false
}
]
},
{
id: 6,
name: '三级标题 3-1',
isExpend: false
}
]
},
{
id: 3,
name: '一级标题 2-1',
isExpend: false
},
{
id: 4,
name: '一级标题 2-1',
isExpend: false
}
]
}
]
};
},
components: { Tree },
methods: {
// 递归操作根据id获取节点操作
getNode (data, id, type) {
let that = this;
data.map((item, index) => {
if (item.id == id) {
that.currentNode = item; // 结果赋值
// 删除操作
if (type === 'del') {
if (window.confirm('确定要删除节点?')) {
data.splice(index, 1);
alert('删除成功!');
}
}
// 展开收缩
if (type === 'expend') {
item.isExpend = !item.isExpend;
}
// 编辑操作
if (type === 'edit') {
}
// 新增操作
if (type === 'add') {
if (!item.children) {
that.$set(item, 'children', []);
}
item.children.push({
id: new Date().getTime(), // 用时间戳标识唯一id,具体情况可以根据自己定一个唯一标识
name: '新增节点' + new Date().getTime(),
isExpend: false
});
// 当没有子节点的时候新增一个节点后展开节点
if (item.children.length > 0) {
item.isExpend = true;
}
}
} else {
if (item.children) {
this.getNode(item.children, id, type);
}
}
});
},
expendNode (v) {
this.getNode(this.treeData, v.id, 'expend');
},
nodeClick (v) {
console.log(v);
},
delNode (v) {
this.getNode(this.treeData, v.id, 'del');
},
addNode (v) {
this.getNode(this.treeData, v.id, 'add');
alert('新增成功!');
},
editNode (v) {
console.log(v);
}
},
created () {
},
watch: {
$route (to) {
console.log(to);
if (to.path.includes('login')) {
this.hidden = false;
} else {
this.hidden = true;
}
}
}
};
</script>
<style lang="less">
@import "style/common/reset";
@import "style/common/base";
</style>
<style lang="less" scoped>
#app {
position: absolute;
height: 100%;
width: 100%;
-webkit-overflow-scrolling: touch;
padding-bottom: 120px;
overflow-y: auto;
background: url("static/images/back.png");
background-size: 100%;
background-image: linear-gradient(#030303, #170403);
.loading {
position: fixed;
z-index: 999999;
width: 100%;
height: 100%;
text-align: center;
background: rgba(0, 0, 0, 0.75);
img {
position: relative;
top: 50%;
transform: translateY(-50%);
height: 150px;
}
}
}
</style>
结果如下:
编辑部分我没有具体去一一实现,原理都是一样的传参进行修改,还有一天就要上班了,最近冠状病毒太厉害了,回去的时候200多个确诊,现在已经达到35000多个了,搞得人心慌慌,真有点像生化危机的感觉,希望病毒尽快消失,生活尽快回复正常。
结束语: 努力,努力,再努力! ( ̄_ ̄ )
上一篇: 非常有意思的SQL优化经历:从30248.271s到0.001s
下一篇: 记一次有意思的文件上传