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