Skip to content

Commit d3654c8

Browse files
committed
feat: 右键菜单组件
1 parent 45bd231 commit d3654c8

File tree

19 files changed

+402
-70
lines changed

19 files changed

+402
-70
lines changed

components.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ export {}
88
/* prettier-ignore */
99
declare module 'vue' {
1010
export interface GlobalComponents {
11+
ElBadge: typeof import('element-plus/es')['ElBadge']
1112
ElIcon: typeof import('element-plus/es')['ElIcon']
1213
ElLink: typeof import('element-plus/es')['ElLink']
1314
ElText: typeof import('element-plus/es')['ElText']
1415
FluentCheckBox: typeof import('./src/components/fluent-ui/FluentCheckBox.vue')['default']
1516
FluentInput: typeof import('./src/components/fluent-ui/FluentInput.vue')['default']
1617
NavBar: typeof import('./src/components/mainwindow/NavBar.vue')['default']
18+
PopMenu: typeof import('./src/components/common/PopMenu/PopMenu.vue')['default']
19+
PopMenuItem: typeof import('./src/components/common/PopMenu/PopMenuItem.vue')['default']
1720
RouterLink: typeof import('vue-router')['RouterLink']
1821
RouterView: typeof import('vue-router')['RouterView']
1922
SideBar: typeof import('./src/components/mainwindow/SideBar.vue')['default']

public/assets/svg/esc_16.svg

Lines changed: 1 addition & 0 deletions
Loading

public/assets/svg/esc_24.svg

Lines changed: 1 addition & 0 deletions
Loading

public/assets/svg/question_16.svg

Lines changed: 1 addition & 0 deletions
Loading

public/assets/svg/question_24.svg

Lines changed: 1 addition & 0 deletions
Loading

public/assets/svg/update_16.svg

Lines changed: 1 addition & 0 deletions
Loading

public/assets/svg/update_24.svg

Lines changed: 1 addition & 0 deletions
Loading
201 KB
Loading
221 KB
Loading
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<!--
2+
description: 右键菜单
3+
author: 程旭
4+
date: 2025-04-10
5+
-->
6+
<template>
7+
<div class="pop-menu flex-column-all-center" ref="popMenuRef" v-if="props.attribute.visible">
8+
<PopMenuItem v-for="(item, index) in props.attribute.items" :key="index" :item="item" />
9+
</div>
10+
</template>
11+
<script setup lang="ts">
12+
import { watch, ref, nextTick } from 'vue';
13+
import PopMenuItem from './PopMenuItem.vue';
14+
15+
import { PopMenuAttributeInterface } from '../PopMenu/index';
16+
17+
import { useMouse } from '@vueuse/core'
18+
19+
const { x, y } = useMouse();
20+
21+
const props = defineProps<{
22+
attribute: PopMenuAttributeInterface;
23+
}>();
24+
25+
const emit = defineEmits<{
26+
(e: 'close'): void;
27+
}>();
28+
29+
const popMenuRef = ref<HTMLElement | null>(null);
30+
31+
// 监听点击外部关闭
32+
const handleClickOutside = (_e: MouseEvent) => {
33+
if (popMenuRef.value /*&& !popMenuRef.value.contains(e.target as Node)*/) {
34+
emit('close');
35+
}
36+
};
37+
38+
const setPosition = (x: number, y: number) => {
39+
const popMenu = popMenuRef.value;
40+
// if (!popMenu) {
41+
// return;
42+
// }
43+
const windowWidth = window.innerWidth;
44+
const windowHeight = window.innerHeight;
45+
const popMenuWidth = popMenu.offsetWidth;
46+
const popMenuHeight = popMenu.offsetHeight;
47+
48+
if (x + popMenuWidth > windowWidth) {
49+
popMenu.style.left = `${windowWidth - popMenuWidth -10}px`;
50+
} else {
51+
popMenu.style.left = `${x}px`;
52+
53+
}
54+
55+
if (y + popMenuHeight > windowHeight) {
56+
popMenu.style.top = `${windowHeight - popMenuHeight -10}px`;
57+
} else {
58+
popMenu.style.top = `${y}px`;
59+
60+
}
61+
}
62+
63+
64+
watch(() => props.attribute.visible, async (newVal) => {
65+
console.log('visible', newVal);
66+
if (newVal) {
67+
await nextTick();
68+
if (props.attribute.autoPositioning) {
69+
setPosition(x.value, y.value);
70+
} else {
71+
if (!props.attribute.position) {
72+
setPosition(x.value, y.value);
73+
} else {
74+
setPosition(props.attribute.position.x, props.attribute.position.y);
75+
}
76+
}
77+
setTimeout(() => {
78+
document.addEventListener('click', handleClickOutside);
79+
}, 100);
80+
} else {
81+
document.removeEventListener('click', handleClickOutside);
82+
}
83+
}, { deep: true, immediate: true });
84+
85+
86+
</script>
87+
<style lang="scss" scoped>
88+
.pop-menu {
89+
min-width: 180px;
90+
border-radius: 10px;
91+
background-color: var(--pop-menu-background-color);
92+
transition: top 0.15s ease, left 0.15s ease, opacity 0.2s ease;
93+
opacity: 1;
94+
padding: 5px 5px;
95+
box-shadow: var(--pop-menu-box-shadow);
96+
backdrop-filter: blur(5px);
97+
position: absolute;
98+
z-index: 9999;
99+
gap:1px;
100+
border: 1px solid var(--pop-menu-border-color);
101+
}
102+
</style>

0 commit comments

Comments
 (0)