目标
右键点击树组件中的节点,弹出增删改的菜单,要求菜单总是在点击位置的附近
实现步骤
1. 添加一个树
<template> <div> <el-tree <!-- 绑定数据 --> :data="tree" highlight-current node-key="id" :props="{ children: 'children', label: 'name' }" ref="tree" > </el-tree> </div> </template>
2. 添加数据
export default { data() { return { tree: [ { id: 1, name: '北京', children: [ { id: 5, name: '朝阳', children: [ { id: 17, name: '双塔', children: [] }, { id: 18, name: '龙城', children: [] } ] }, { id: 6, name: '丰台', children: [ { id: 19, name: '新村', children: [] }, { id: 20, name: '大红门', children: [] }, { id: 21, name: '长辛店', children: [ { id: 22, name: '东山坡', children: [] }, { id: 23, name: '北关', children: [] }, { id: 24, name: '光明里', children: [] }, { id: 25, name: '赵辛店', children: [] }, { id: 26, name: '西峰寺', children: [] } ] } ] }, { id: 7, name: '海淀', children: [] }, { id: 8, name: '房山', children: [] }, { id: 10, name: '顺义', children: [] } ] }, { id: 2, name: '上海', children: [ { id: 11, name: '黄埔', children: [] }, { id: 12, name: '徐汇', children: [] } ] }, { id: 3, name: '广州', children: [ { id: 13, name: '荔湾', children: [] }, { id: 14, name: '白云', children: [] }, { id: 15, name: '越秀', children: [] }, { id: 16, name: '南沙', children: [] } ] } ] } } }
3. 实现效果
4. 添加右键菜单
组件的官网
Element - The world's most popular Vue UI framework
事件 解析 参数 node-contextmenu
当某一节点被鼠标右键点击时会触发该事件 共四个参数,依次为:event、传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身 node-click
节点被点击时的回调 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身 node-expand
节点被展开时触发的事件 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身 node-collapse
节点被关闭时触发的事件 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身
5. 添加菜单部分代码
注:菜单默认不显示,最好不要嵌套太深,并且记得设置 z-index 避免被覆盖,其他样式根据自己需求都可以调整
<div v-show="showTreeMenu" class="treeMenu"> <div @click="addOrg"> <i class="el-icon-plus" style="color: #1e9fff"></i> 增加子分组 </div> <div @click="editOrg"> <i class="el-icon-edit" style="color: #fcc465"></i> 修改分组名称 </div> <div @click="deleteOrg"> <i class="el-icon-delete" style="color: red"></i> 删除分组及子分组 </div> </div> <style lang="less" scoped> .treeMenu { font-size: 14px; position: fixed; padding: 2px 0px 5px; z-index: 99999; // 为了显示再最上层 top: 50%; left: 50%; background-color: rgba(255, 255, 255, 0.9); overflow: hidden; // 溢出的部分隐藏 border-radius: 5px; // 圆角 border: 1px solid #e6ebf5; box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); div { padding: 2px 10px; box-sizing: border-box; text-align: left; } div:hover { background-color: #eee; cursor: pointer; } } </style>
6. 添加事件部分
<script> export default { methods: { addOrg() { console.log('添加子分组') }, editOrg() { console.log('修改分组名称') }, deleteOrg() { this.$confirm(`删除 [${this.contextNode.name}] 分组、下级子分组 <br>是否继续?`, '提示', { dangerouslyUseHTMLString: true, confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', center: true }).then(console.log('删除分组及下级子分组')) } } } </script>
7. 给树组件添加事件和对应的数据
<template> <div> <el-tree :data="tree" highlight-current node-key="id" :props="{ children: 'children', label: 'name' }" <!-- 添加打开节点的事件 --> @node-expand="openTreeNode" <!-- 添加关闭节点的事件 --> @node-collapse="closeTreeNode" ref="tree" <!-- 添加右键的事件 --> @node-contextmenu="openTreeMenu" > </el-tree> </div> </template> <script> export default { methods: { openTreeMenu(event, data, node, target) { console.log(event, data, node, target) this.showTreeMenu = true // 显示菜单 this.contextNode = data // 存储数据 document .querySelector('.treeMenu') .setAttribute('style', `top:${event.clientY + 10}px;left:${event.clientX + 10}px;`) document.addEventListener('click', this.closeTreeMenu) document.addEventListener('contextmenu', this.closeTreeMenu) }, closeTreeMenu() { this.showTreeMenu = false // 关闭菜单 document.removeEventListener('click', this.closeTreeMenu) document.removeEventListener('contextmenu', this.closeTreeMenu) }, openTreeNode(data) { console.log('节点被展开') this.showTreeMenu = false console.log(data) }, closeTreeNode(data) { console.log('节点被关闭') this.showTreeMenu = false console.log(data) } }, data() { return { showTreeMenu: false, contextNode: {}, tree: [ { id: 1, name: '北京', children: [ { id: 5, name: '朝阳', children: [ { id: 17, name: '双塔', children: [] }, { id: 18, name: '龙城', children: [] } ] }, { id: 6, name: '丰台', children: [ { id: 19, name: '新村', children: [] }, { id: 20, name: '大红门', children: [] }, { id: 21, name: '长辛店', children: [ { id: 22, name: '东山坡', children: [] }, { id: 23, name: '北关', children: [] }, { id: 24, name: '光明里', children: [] }, { id: 25, name: '赵辛店', children: [] }, { id: 26, name: '西峰寺', children: [] } ] } ] }, { id: 7, name: '海淀', children: [] }, { id: 8, name: '房山', children: [] }, { id: 10, name: '顺义', children: [] } ] }, { id: 2, name: '上海', children: [ { id: 11, name: '黄埔', children: [] }, { id: 12, name: '徐汇', children: [] } ] }, { id: 3, name: '广州', children: [ { id: 13, name: '荔湾', children: [] }, { id: 14, name: '白云', children: [] }, { id: 15, name: '越秀', children: [] }, { id: 16, name: '南沙', children: [] } ] } ] } } } </script>
实现效果
完整代码
<template> <div> <el-tree :data="tree" highlight-current node-key="id" :props="{ children: 'children', label: 'name' }" @node-expand="openTreeNode" @node-collapse="closeTreeNode" ref="tree" @node-contextmenu="openTreeMenu" > </el-tree> <div v-show="showTreeMenu" class="treeMenu"> <div @click="addOrg"> <i class="el-icon-plus" style="color: #1e9fff"></i> 增加子分组 </div> <div @click="editOrg"> <i class="el-icon-edit" style="color: #fcc465"></i> 修改分组名称 </div> <div @click="deleteOrg"> <i class="el-icon-delete" style="color: red"></i> 删除分组及子分组 </div> </div> </div> </template> <script> export default { methods: { addOrg() { console.log('添加子分组', this.contextNode) }, editOrg() { console.log('修改分组名称', this.contextNode) }, deleteOrg() { this.$confirm(`删除 [${this.contextNode.name}] 分组、下级子分组 <br>是否继续?`, '提示', { dangerouslyUseHTMLString: true, confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', center: true }).then(console.log('删除分组及下级子分组', this.contextNode)) }, openTreeMenu(event, data, node, target) { console.log(event, data, node, target) this.showTreeMenu = true // 显示菜单 this.contextNode = data // 存储数据 document .querySelector('.treeMenu') .setAttribute('style', `top:${event.clientY + 10}px;left:${event.clientX + 10}px;`) document.addEventListener('click', this.closeTreeMenu) document.addEventListener('contextmenu', this.closeTreeMenu) }, closeTreeMenu() { this.showTreeMenu = false // 关闭菜单 document.removeEventListener('click', this.closeTreeMenu) document.removeEventListener('contextmenu', this.closeTreeMenu) }, openTreeNode(data) { console.log('节点被展开') this.showTreeMenu = false console.log(data) }, closeTreeNode(data) { console.log('节点被关闭') this.showTreeMenu = false console.log(data) } }, data() { return { showTreeMenu: false, contextNode: {}, tree: [ { id: 1, name: '北京', children: [ { id: 5, name: '朝阳', children: [ { id: 17, name: '双塔', children: [] }, { id: 18, name: '龙城', children: [] } ] }, { id: 6, name: '丰台', children: [ { id: 19, name: '新村', children: [] }, { id: 20, name: '大红门', children: [] }, { id: 21, name: '长辛店', children: [ { id: 22, name: '东山坡', children: [] }, { id: 23, name: '北关', children: [] }, { id: 24, name: '光明里', children: [] }, { id: 25, name: '赵辛店', children: [] }, { id: 26, name: '西峰寺', children: [] } ] } ] }, { id: 7, name: '海淀', children: [] }, { id: 8, name: '房山', children: [] }, { id: 10, name: '顺义', children: [] } ] }, { id: 2, name: '上海', children: [ { id: 11, name: '黄埔', children: [] }, { id: 12, name: '徐汇', children: [] } ] }, { id: 3, name: '广州', children: [ { id: 13, name: '荔湾', children: [] }, { id: 14, name: '白云', children: [] }, { id: 15, name: '越秀', children: [] }, { id: 16, name: '南沙', children: [] } ] } ] } } } </script> <style lang="less" scoped> .treeMenu { font-size: 14px; position: fixed; padding: 2px 0px 5px; z-index: 99999; top: 50%; left: 50%; background-color: rgba(255, 255, 255, 0.9); overflow: hidden; border-radius: 5px; border: 1px solid #e6ebf5; box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); div { padding: 2px 10px; box-sizing: border-box; text-align: left; } div:hover { background-color: #eee; cursor: pointer; } } </style>