vue-封装函数式触发右键菜单组件
通过使用函数式调用方式去触发右键菜单逻辑
vue文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<script setup lang="ts" name="FillContextMenu">
import { onClickOutside } from '@vueuse/core'
import {nextTick, onMounted, ref, useTemplateRef} from "vue";
import {ElDropdown,ElDropdownItem,ElDropdownMenu} from "element-plus";
import type {DropdownInstance} from "element-plus";
// prop
const props = withDefaults(defineProps<{
position: {x:number,y:number},
index:number,
value:any,
obj:any,
propKey:string,
length:number
}>(),{
position:()=>({x:0,y:0}),
index:()=>0,
value:"",
obj:()=>({}),
propKey:"year_",
length:0
})
// emits
const emits = defineEmits(['close'])
/**
* 初始化
*/
const init = () => {
nextTick(() => {
handleContextmenu()
})
}
/**
* 等值填充
*/
const handleLineFill = () => {
console.log(props.index,props.value,props.obj,props.propKey,props.length)
for(let i = props.index; i < props.length; i++) {
const field = `${props.propKey}${i + 1}`
props.obj[field] = props.value
}
emits('close')
}
// 菜单实例
const fillContextMenuRef = useTemplateRef<HTMLDivElement>("fillContextMenuRef")
/**
* 点击菜单外部关闭菜单
*/
onClickOutside(fillContextMenuRef, () => {
emits('close')
})
// 下拉菜单实例
const dropdownRef = useTemplateRef<DropdownInstance>("dropdownRef")
const position = ref({
top: 0,
left: 0,
bottom: 0,
right: 0,
} as DOMRect)
// 触发元素
const triggerRef = ref({
getBoundingClientRect: () => position.value,
})
/**
* 处理上下文菜单
*/
const handleContextmenu = () => {
dropdownRef.value?.handleClose()
const { x, y } = props.position
position.value = DOMRect.fromRect({
x: x,
y: y,
})
dropdownRef.value?.handleOpen()
}
onMounted(() => {
init()
})
</script>
<template>
<div>
<ElDropdown
ref="dropdownRef"
:virtual-ref="triggerRef"
:show-arrow="false"
:popper-options="{
modifiers: [{ name: 'offset', options: { offset: [0, 0] } }],
}"
virtual-triggering
trigger="contextmenu"
placement="bottom-start"
>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem @click="handleLineFill">按行等值填充</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</div>
</template>
<style scoped lang="scss">
.fill-context-menu {
}
</style>ts文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import {h, render} from 'vue'
import FillContextMenu from './index.vue'
interface ContextMenuOptions {
// 上下文菜单位置
position: { x: number; y: number }
// 上下文菜单索引
index: number
// 上下文菜单值
value: any
// 上下文菜单对象
obj: Record<string, any>
// 上下文菜单属性名
propKey: string
// 上下文菜单长度
length: number
}
// 添加滚轮事件处理函数
const handleWheel = () => {
if (currentCloseMenu) {
currentCloseMenu()
}
}
// 添加中键点击处理函数
const handleMiddleClick = (event: MouseEvent) => {
if (event.button === 1 && currentCloseMenu) { // 1 表示鼠标中键
currentCloseMenu()
}
}
// 添加全局变量保存当前菜单关闭函数
let currentCloseMenu: (() => void) | null = null
export function showFillContextMenu(options: ContextMenuOptions) {
// 关闭已存在的菜单
if (currentCloseMenu) {
currentCloseMenu()
currentCloseMenu = null
}
const container = document.createElement('div')
/**
* 关闭上下文菜单
*/
const closeMenu = () => {
render(null, container)
container.remove()
currentCloseMenu = null // 清除引用
// 移除事件监听
document.removeEventListener('wheel', handleWheel)
document.removeEventListener('mousedown', handleMiddleClick) // 新增移除监听
}
currentCloseMenu = closeMenu // 保存当前实例的关闭方法
// 添加滚轮事件监听
document.addEventListener('wheel', handleWheel, {passive: true})
document.addEventListener('mousedown', handleMiddleClick) // 新增中键监听
const vnode = h(FillContextMenu, {
...options,
onClose: closeMenu
})
document.body.appendChild(container)
render(vnode, container)
return closeMenu
}