在现代Web开发中,树形结构(Tree)是一种常见的数据展示方式,尤其是在需要展示层级关系的数据时,树形结构能够非常直观地呈现数据之间的关系。ElementUI作为一款基于Vue.js的UI组件库,提供了丰富的组件来帮助开发者快速构建用户界面。其中,el-tree
组件是一个非常强大的树形结构组件,支持多种功能,如节点展开/折叠、节点选择、节点拖拽等。
然而,ElementUI的el-tree
组件默认并不支持右键菜单功能,这在某些场景下可能会限制其使用。本文将详细介绍如何在Vue项目中使用ElementUI的el-tree
组件,并通过自定义右键菜单实现节点的增删改功能。
el-tree
是ElementUI提供的一个树形结构组件,支持以下功能:
el-tree
组件的基本用法如下:
<template>
<el-tree :data="treeData" :props="defaultProps"></el-tree>
</template>
<script>
export default {
data() {
return {
treeData: [
{
label: '一级 1',
children: [
{
label: '二级 1-1',
children: [
{
label: '三级 1-1-1'
}
]
}
]
},
{
label: '一级 2',
children: [
{
label: '二级 2-1',
children: [
{
label: '三级 2-1-1'
}
]
},
{
label: '二级 2-2',
children: [
{
label: '三级 2-2-1'
}
]
}
]
}
],
defaultProps: {
children: 'children',
label: 'label'
}
};
}
};
</script>
在这个例子中,我们定义了一个简单的树形结构,并通过el-tree
组件将其渲染出来。
在开始实现右键菜单功能之前,我们需要先搭建一个Vue项目,并引入ElementUI库。
首先,使用Vue CLI创建一个新的Vue项目:
vue create vue-tree-demo
在创建项目时,选择默认配置即可。
进入项目目录,并安装ElementUI:
cd vue-tree-demo
npm install element-ui --save
在src/main.js
中引入ElementUI:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
render: h => h(App),
}).$mount('#app');
在src/components
目录下创建一个新的组件TreeDemo.vue
,并在App.vue
中引入该组件。
src/components/TreeDemo.vue
:
<template>
<div class="tree-demo">
<el-tree :data="treeData" :props="defaultProps"></el-tree>
</div>
</template>
<script>
export default {
data() {
return {
treeData: [
{
label: '一级 1',
children: [
{
label: '二级 1-1',
children: [
{
label: '三级 1-1-1'
}
]
}
]
},
{
label: '一级 2',
children: [
{
label: '二级 2-1',
children: [
{
label: '三级 2-1-1'
}
]
},
{
label: '二级 2-2',
children: [
{
label: '三级 2-2-1'
}
]
}
]
}
],
defaultProps: {
children: 'children',
label: 'label'
}
};
}
};
</script>
<style scoped>
.tree-demo {
padding: 20px;
}
</style>
src/App.vue
:
<template>
<div id="app">
<TreeDemo />
</div>
</template>
<script>
import TreeDemo from './components/TreeDemo.vue';
export default {
components: {
TreeDemo
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
在项目根目录下运行以下命令启动项目:
npm run serve
打开浏览器,访问http://localhost:8080
,你应该能够看到一个简单的树形结构。
在上一节中,我们已经创建了一个简单的树形结构。接下来,我们将进一步探索el-tree
组件的其他功能。
el-tree
组件支持节点的单选和多选。我们可以通过show-checkbox
属性来启用多选功能。
<template>
<div class="tree-demo">
<el-tree :data="treeData" :props="defaultProps" show-checkbox></el-tree>
</div>
</template>
el-tree
组件默认支持节点的展开和折叠。我们可以通过default-expand-all
属性来设置所有节点默认展开。
<template>
<div class="tree-demo">
<el-tree :data="treeData" :props="defaultProps" default-expand-all></el-tree>
</div>
</template>
el-tree
组件支持节点的拖拽功能。我们可以通过draggable
属性来启用拖拽功能。
<template>
<div class="tree-demo">
<el-tree :data="treeData" :props="defaultProps" draggable></el-tree>
</div>
</template>
el-tree
组件允许我们自定义节点的内容。我们可以通过scoped slot
来实现自定义节点内容。
<template>
<div class="tree-demo">
<el-tree :data="treeData" :props="defaultProps">
<span slot-scope="{ node, data }" class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button type="text" size="mini" @click="append(data)">添加</el-button>
<el-button type="text" size="mini" @click="remove(node, data)">删除</el-button>
</span>
</span>
</el-tree>
</div>
</template>
<script>
export default {
methods: {
append(data) {
const newChild = { label: '新节点', children: [] };
if (!data.children) {
this.$set(data, 'children', []);
}
data.children.push(newChild);
},
remove(node, data) {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex(d => d.id === data.id);
children.splice(index, 1);
}
}
};
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>
在这个例子中,我们为每个节点添加了两个按钮:“添加”和“删除”。点击“添加”按钮可以在当前节点下添加一个新的子节点,点击“删除”按钮可以删除当前节点。
虽然el-tree
组件提供了丰富的功能,但它默认并不支持右键菜单。为了实现右键菜单功能,我们需要借助Vue的自定义指令和ElementUI的el-menu
组件。
首先,我们创建一个新的组件ContextMenu.vue
,用于显示右键菜单。
src/components/ContextMenu.vue
:
<template>
<div v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="context-menu">
<el-menu @select="handleSelect">
<el-menu-item index="add">添加</el-menu-item>
<el-menu-item index="edit">编辑</el-menu-item>
<el-menu-item index="delete">删除</el-menu-item>
</el-menu>
</div>
</template>
<script>
export default {
data() {
return {
visible: false,
left: 0,
top: 0,
selectedNode: null
};
},
methods: {
showMenu(event, node) {
this.visible = true;
this.left = event.clientX;
this.top = event.clientY;
this.selectedNode = node;
},
hideMenu() {
this.visible = false;
},
handleSelect(index) {
this.$emit('select', index, this.selectedNode);
this.hideMenu();
}
}
};
</script>
<style scoped>
.context-menu {
position: fixed;
z-index: 1000;
background-color: #fff;
border: 1px solid #ebeef5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>
在这个组件中,我们定义了一个右键菜单,包含“添加”、“编辑”和“删除”三个选项。菜单的显示位置通过left
和top
属性来控制。
接下来,我们在TreeDemo.vue
中使用ContextMenu
组件,并为其绑定事件。
src/components/TreeDemo.vue
:
<template>
<div class="tree-demo">
<el-tree
:data="treeData"
:props="defaultProps"
@node-contextmenu="handleNodeContextMenu"
></el-tree>
<ContextMenu ref="contextMenu" @select="handleMenuSelect"></ContextMenu>
</div>
</template>
<script>
import ContextMenu from './ContextMenu.vue';
export default {
components: {
ContextMenu
},
data() {
return {
treeData: [
{
label: '一级 1',
children: [
{
label: '二级 1-1',
children: [
{
label: '三级 1-1-1'
}
]
}
]
},
{
label: '一级 2',
children: [
{
label: '二级 2-1',
children: [
{
label: '三级 2-1-1'
}
]
},
{
label: '二级 2-2',
children: [
{
label: '三级 2-2-1'
}
]
}
]
}
],
defaultProps: {
children: 'children',
label: 'label'
}
};
},
methods: {
handleNodeContextMenu(event, data, node, component) {
event.preventDefault();
this.$refs.contextMenu.showMenu(event, node);
},
handleMenuSelect(index, node) {
switch (index) {
case 'add':
this.append(node);
break;
case 'edit':
this.edit(node);
break;
case 'delete':
this.remove(node);
break;
}
},
append(node) {
const newChild = { label: '新节点', children: [] };
if (!node.data.children) {
this.$set(node.data, 'children', []);
}
node.data.children.push(newChild);
},
edit(node) {
this.$prompt('请输入节点名称', '编辑节点', {
inputValue: node.data.label
}).then(({ value }) => {
node.data.label = value;
});
},
remove(node) {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex(d => d.id === node.data.id);
children.splice(index, 1);
}
}
};
</script>
<style scoped>
.tree-demo {
padding: 20px;
}
</style>
在这个例子中,我们通过@node-contextmenu
事件监听树节点的右键点击事件,并在事件处理函数中调用ContextMenu
组件的showMenu
方法来显示右键菜单。当用户选择菜单项时,handleMenuSelect
方法会根据选择的菜单项执行相应的操作。
在上一节中,我们已经实现了右键菜单的基本功能。接下来,我们将进一步完善节点的增删改功能。
在append
方法中,我们已经在当前节点下添加了一个新的子节点。为了确保新节点的唯一性,我们可以为每个节点生成一个唯一的ID。
append(node) {
const newChild = { id: this.generateId(), label: '新节点', children: [] };
if (!node.data.children) {
this.$set(node.data, 'children', []);
}
node.data.children.push(newChild);
},
generateId() {
return '_' + Math.random().toString(36).substr(2, 9);
}
在edit
方法中,我们使用el-prompt
组件来弹出一个输入框,允许用户编辑节点的名称。
edit(node) {
this.$prompt('请输入节点名称', '编辑节点', {
inputValue: node.data.label
}).then(({ value }) => {
node.data.label = value;
});
}
在remove
方法中,我们已经实现了删除节点的功能。为了确保删除操作的安全性,我们可以在删除前弹出一个确认对话框。
remove(node) {
this.$confirm('确定要删除该节点吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex(d => d.id === node.data.id);
children.splice(index, 1);
});
}
以下是完整的TreeDemo.vue
和ContextMenu.vue
代码示例。
src/components/TreeDemo.vue
:
”`vue