业务场景
数组生成的标签,使用右键打开菜单,快捷删除目标选中的标签、或者关闭除选中之外的剩余标签,或者全部关闭
实现思路
右键时屏蔽原生事件,把自定义右键菜单浮框封装为组件,传入事件中$event中鼠标的x,y轴位置来显示
开始封装浮框菜单组件
组件接收3个参数
visible // 显示控制
position 坐标轴位置
mark 父组件传入的数据标识,用于操作数据
当点击菜单按钮时,发送回调事件给父组件,由父组件决定数据操作
<template>
<div ref="rightMenu" v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="rightMenu">
<span @click="clickMenu('close')">关闭</span>
<span @click="clickMenu('closeOther')">关闭其他</span>
<span @click="clickMenu('closeAll')">全部关闭</span>
</div>
</template>
<script>
export default {
props: {
visible: { // 显示控制
type: Boolean,
required: true,
},
position: { // 坐标轴位置
type: Object,
default: () => ({ clientX: 0, clientY: 0 })
},
mark: { // 父组件传入的数据标识,用于操作数据
type: [String, Number],
default: ''
}
},
data() {
return {
left: 0, // x轴坐标位置,默认为0,浮框显示时再计算位置
top: 0, // y轴坐标位置,默认为0,浮框显示时再计算位置
menu: { // 浮框菜单高宽信息
width: 0,
height: 0
}
}
},
watch: {
// 监听body点击以及鼠标滚轮事件
visible(newVal) {
if (newVal) { // 显示
document.documentElement.addEventListener('click', this.closeMenu)
document.documentElement.addEventListener('wheel', this.closeMenu)
} else { // 隐藏
document.documentElement.removeEventListener('click', this.closeMenu)
document.documentElement.removeEventListener('wheel', this.closeMenu)
}
},
position(newVal) {
this.setPosition(newVal)
}
},
methods: {
// 设置位置信息
async setPosition({ clientX, clientY }) {
let positionX = clientX // x轴坐标
let positionY = clientY // y轴坐标
await this.$nextTick() // dom更新后再获取ref
this.getRightMenu() // 获取浮框菜单高宽信息
// 这里获取窗口屏幕高度不要使用 document.body 不等于 游览器窗口高度,会造成计算错误
if (positionY + this.menu.height > window.innerHeight) { // y轴超出边界处理 (y轴上边距坐标+元素高度 > 窗口屏幕高度)
positionY = document.documentElement.offsetHeight - this.menu.height
}
if (positionX + this.menu.width > window.innerWidth) { // x轴超出边界处理(x轴左边距坐标+元素宽度 > 窗口屏幕宽度)
positionX = document.documentElement.offsetWidth - this.menu.width
}
this.left = positionX
this.top = positionY
},
// 获取浮框菜单高宽信息
getRightMenu() {
console.log(this.$refs.rightMenu)
this.menu.width = this.$refs.rightMenu.offsetWidth // 选择器宽度(如果是vue组件要加上$el)
this.menu.height = this.$refs.rightMenu.offsetHeight // 选择器高度(如果是vue组件要加上$el)
},
// 关闭右键菜单浮框
closeMenu() {
this.$emit('update:visible', false)
this.$emit('update:position', { clientX: 0, clientY: 0 })
this.menu = { // 选择器高宽信息
width: 0,
height: 0
}
},
// 点击操作按钮
clickMenu(target) {
this.$emit(target, this.mark) // 传回事件名和数据标识
// 点击后会自动关闭菜单,已在打开时绑定事件 document.documentElement.addEventListener('click', this.closeMenu)
}
}
}
</script>
<style lang="less" scoped>
.rightMenu{
display: flex;
flex-direction: column;
position: fixed;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
border-radius: 6px;
padding: 5px;
z-index: 9999;
span{
border-bottom: 1px solid #EBEEF5;
padding: 10px 20px;
text-align: center;
cursor: pointer;
}
span:hover{
background: #eee;
}
span:last-child{
border-bottom: 0;
}
}
</style>
在进行位置的高宽计算,绑定事件时要注意,不要使用document.body,body是根据文档内容大小撑开的高宽,导致没有占满全屏,发生错误
应当使用 document.documentElement,指整个文档流的根节点,其占据的大小应当等于浏览器视口的大小。
在vue中使用
在item子元素使用 contextmenu 右键事件并加上修饰符阻止默认事件
根据不同的回调事件进行数据操作
<template>
<div id="app">
<div class="box">
<el-tag
v-for="item in tags"
:key="item.id"
@contextmenu.prevent.native="openMenu(item, $event)"
>
{{ item.title }}
</el-tag>
</div>
<RightMenu
:visible.sync="menu.visible"
:position.sync="menu.position"
:mark="menu.mark"
@close="close"
@closeOther="closeOther"
@closeAll="closeAll"
/>
</div>
</template>
<script>
import RightMenu from '@/components/RightMenu/index.vue'
export default {
name: "App",
components: { RightMenu },
data() {
return {
tags: [
{
id: 1,
title: '标签1'
},
{
id: 2,
title: '标签2'
},
{
id: 3,
title: '标签3'
},
{
id: 4,
title: '标签4'
},
{
id: 5,
title: '标签5'
},
],
menu: {
visible: false,
position: {
clientX: 0,
clientY: 0
}
}
};
},
methods: {
// 打开菜单组件
openMenu(item, e) {
this.menu = {
visible: true,
mark: item.id, // 把当前id传入
position: {
clientX: e.clientX,
clientY: e.clientY
}
}
},
// 关闭当前
close(id) {
const index = this.tags.findIndex(item => item.id === id)
index !== -1 && this.tags.splice(index, 1)
},
// 关闭其他
closeOther(id) {
const index = this.tags.findIndex(item => item.id === id)
const currentItem = [this.tags[index]]
this.tags = currentItem
},
// 关闭所有
closeAll() {
this.tags = []
}
}
};
</script>
<style lang="less">
#app{
display: flex;
align-items: center;
}
.box{
display: flex;
.el-tag{
margin-right: 20px;
font-size: 22px;
padding: 10px;
height: auto;
cursor: pointer;
}
}
</style>
实现效果
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END