25-Vue 2.x (比如ELEMENT-UI 树形控件)右键菜单组件,菜单内容可以随意自定义

 原文:

element-ui 的树形控件本身是不支持右键的,官方文档上推荐把操作按钮展示在对应的项上,但是如果操作项比较多,或者本身就有信息需要展示在项上,再加上这些操作按钮就会比较凌乱。正好公司业务上有这个需求,因此我在网上搜了一圈,找到比较好的解决方式。

整体思路就是:当右键时,调用左键的 node-click 方法,通过 node-click 将获取到的数据储存起来,然后展示右键菜单,获取到数据后,怎么处理就可以自由发挥了。

右键菜单我使用了 vue-context-menu 插件,引入方式可看 README

Demo

下面是具体代码:

<div id="app">
<el-tree :data="data" id="el-tree" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
<context-menu class="right-menu"
:target="contextMenuTarget"
:show="contextMenuVisible"
@update:show="(show) => contextMenuVisible = show">
<a href="javascript:;" @click="handleClick">操作1</a>
<a href="javascript:;" @click="handleClick">操作2</a>
<a href="javascript:;" @click="handleClick">操作3</a>
</context-menu>
当前右键获取到的节点为:{{JSON.stringify(nodeData)}}
</div>
export default {
data() {
return {
contextMenuTarget: null,
contextMenuVisible: false,
nodeData: {},
data: [{
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'
}]
}]
}, {
label: '一级 3',
children: [{
label: '二级 3-1',
children: [{
label: '三级 3-1-1'
}]
}, {
label: '二级 3-2',
children: [{
label: '三级 3-2-1'
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
}
},
mounted() {
this.$nextTick(() => {
// vue-context-menu 需要传入一个触发右键事件的元素,等页面 dom 渲染完毕后才可获取
this.contextMenuTarget = document.querySelector('#el-tree')
// 获取所有的 treeitem,循环监听右键事件
const tree = document.querySelectorAll('#el-tree [role="treeitem"]')
tree.forEach(i => {
i.addEventListener('contextmenu',event => {
// 如果右键了,则模拟点击这个treeitem
event.target.click()
})
})
})
},
methods: {
handleNodeClick(data) {
console.log(data)
this.nodeData = data
},
handleClick(){
this.contextMenuVisible = false
}
}
}
html,
body {
height: 100%;
font-size: 14px;
}
#app {
width: 800px;
margin: 0 auto;
font-family: 'Microsoft Yahei', 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
height: 100%;
}
// 右键会选中文字,为了美观将它禁用
#el-tree{
user-select:none;
}
.right-menu {
position: fixed;
background: #fff;
border: solid 1px rgba(0, 0, 0, .2);
border-radius: 3px;
z-index: 999;
display: none;
}
.right-menu a {
width: 75px;
height: 28px;
line-height: 28px;
text-align: center;
display: block;
color: #1a1a1a;
}
.right-menu a:hover {
background: #eee;
color: #fff;
}
.right-menu {
border: 1px solid #eee;
box-shadow: 0 0.5em 1em 0 rgba(0,0,0,.1);
border-radius: 1px;
}
a {
text-decoration: none;
}
.right-menu a {
padding: 2px;
}
.right-menu a:hover {
background: #42b983;
}

以上就是完整代码。

当然还有另外一种思路。el-tree 自带有 node-contextmenu 事件,如果使用了这个事件,原生的右键事件都会被阻止,因此上面方法不能与这个事件一起使用,会造成右键插件无法正常显示。但是 node-contextmenu 事件返回了非常详细的参数:依次为 event、传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。由于可以获取到 event,里面带有鼠标右击的坐标,因此完全可以自己写一个右键菜单,而且这个事件的回调参数更为丰富。

 

补充:上面的示例是把右键出菜单这个功能绑定到了

this.contextMenuTarget = document.querySelector('#el-tree')

这个下面的所有的树节点上,并通过下面的

const tree = document.querySelectorAll('#el-tree [role="treeitem"]')

通过标签选择器,为el-tree下的所有role="treeitem"监听了右键事件,但是如有有需求,不想给树的所有层级都添加右键功能,也就是说是否可以指定层级绑定这个功能呢?

是可以的,上面的逻辑坐下微调,比如只想给业务线这个二级树添加这个功能,那么将上面的标签选择器,选择为儿子一代,而不是在所有的子孙中找符合的元素

    async initTreeData() {
      await getTreeData().then(response => {
        this.data = response.data;
        this.handleNodeClick(this.data[0])  //初始化展示的节点
        }).catch(error => {
          console.log(error.response);
        });

        this.$nextTick(() => {
          // vue-context-menu 需要传入一个触发右键事件的元素,等页面 dom 渲染完毕后才可获取
          this.contextMenuTarget = document.querySelector('#el-tree')
          // 获取所有的 treeitem,循环监听右键事件
          const tree = document.querySelectorAll('#el-tree > [role="treeitem"]')
          tree.forEach(i => {
            this.contextMenuTarget_list.push(i)
            i.addEventListener('contextmenu',event => {
              // 如果右键了,则模拟点击这个treeitem
              event.target.click()
            })
          })
        })
    },

同时时间是通过模拟点击这个treeitem实现的,所以可以在点击这个里面做判断,只有符合规则的节点才展示菜单

<context-menu 
          class="right-menu"
          :target="contextMenuTarget"
          :show="contextMenuVisible"    //即通过相关逻辑控制contextMenuVisible的值为true或者false
> <a href="javascript:;" @click="handleTransfer">{{operation[node_type]}}</a> </context-menu>

官方文档https://github.com/xunleif2e/vue-context-menu 是通过 @update:show来控制的,我们可以不用这个,直接用节点的点击事件即可,如下

handleNodeClick(data) {
  if (data.key.split('.').length == 2) {
    this.contextMenuVisible = true
  } else {
    this.contextMenuVisible = false
  }
}

实现的效果,如下

 

 

 

 

其他参考:

https://evolly.one/2019/03/29/57-el-tree-contextmenu/

https://github.com/xunleif2e/vue-context-menu

posted @ 2019-08-09 19:19  番茄土豆西红柿  阅读(756)  评论(0编辑  收藏  举报
TOP