This commit is contained in:
Zy
2026-04-06 14:45:50 +08:00
commit f414026af8
373 changed files with 30667 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
<template>
<Teleport to="body">
<div v-show="show" ref="menuRef" class="context-menu" :style="{ left: `${x}px`, top: `${y}px`, zIndex: 999999 }">
<div class="item" v-for="item in menu" :key="item.label" @click="handleClick(item)">
<div class="item-list">
{{ item.label }}
</div>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const props = defineProps<{
menu: Array<{ label: string; value: any }>;
}>();
const emit = defineEmits<{
select: [value: any];
}>();
const show = ref(false);
const x = ref(0);
const y = ref(0);
const menuRef = ref();
// 打开菜单
const open = async (e: MouseEvent, value: any) => {
e.preventDefault();
x.value = e.clientX;
y.value = e.clientY;
// 先显示一次,才能获取 DOM 尺寸
show.value = true;
await nextTick();
// 🔥 这里用 ref 拿 DOM,100% 拿到
if (!menuRef.value) return;
const rect = menuRef.value.getBoundingClientRect();
let left = e.clientX;
let top = e.clientY;
// 右边界碰撞
if (left + rect.width > window.innerWidth) {
left = window.innerWidth - rect.width - 8;
}
console.log('window', window.innerWidth);
// 下边界碰撞
if (top + rect.height > window.innerHeight) {
top = window.innerHeight - rect.height - 8;
}
// 上边界不能小于0
if (top < 8) top = 8;
if (left < 8) left = 8;
x.value = left;
y.value = top;
setTimeout(() => {
show.value = true;
document.addEventListener('click', close);
document.addEventListener('contextmenu', close);
}, 10);
};
// 关闭
const close = () => {
show.value = false;
console.log('close');
document.removeEventListener('click', close);
document.removeEventListener('contextmenu', close);
};
// 点击选项
const handleClick = (item) => {
emit('select', item.value);
close();
};
// 暴露方法给父组件调用
defineExpose({ open });
</script>
<style scoped>
.context-menu {
position: fixed;
background: #fff;
border: 1px solid #eee;
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
min-width: 100px;
}
.item {
cursor: pointer;
font-size: 14px;
color: rgba(26, 117, 255, 1);
.item-list {
width: 100%;
padding: 15px;
}
}
.item:hover {
background: #f5f7fa;
color: #409eff;
}
</style>