mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-07 00:00:23 +00:00
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<ul class="context-menu" ref="menuRef" :style="{ top: `${adjustedY}px`, left: `${adjustedX}px` }" @click.stop>
|
||||
<li
|
||||
v-for="(btn, index) in buttons"
|
||||
:key="index"
|
||||
:class="[{ disabled: isDisabled(btn) }, { divided: btn.divided }]"
|
||||
@click="!isDisabled(btn) && onClick(btn)"
|
||||
>
|
||||
{{ btn.label }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
x: number;
|
||||
y: number;
|
||||
row: any;
|
||||
buttons: {
|
||||
label: string;
|
||||
click: (row: any) => void;
|
||||
disabled?: boolean | ((row: any) => boolean);
|
||||
divided?: boolean;
|
||||
}[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
const menuRef = ref<HTMLElement | null>(null);
|
||||
const onClick = (btn: any) => {
|
||||
btn.click?.(props.row);
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const isDisabled = computed(() => {
|
||||
return function (btn: any) {
|
||||
return typeof btn.disabled === 'function' ? btn.disabled(props.row) : btn.disabled;
|
||||
};
|
||||
});
|
||||
|
||||
const adjustedX = ref(props.x);
|
||||
const adjustedY = ref(props.y);
|
||||
|
||||
watch(
|
||||
() => [props.x, props.y],
|
||||
async () => {
|
||||
await nextTick(); // 确保菜单渲染完成
|
||||
if (!menuRef.value) return;
|
||||
|
||||
const menuRect = menuRef.value.getBoundingClientRect();
|
||||
const windowWidth = window.innerWidth;
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
// 修正横向
|
||||
if (props.x + menuRect.width > windowWidth) {
|
||||
adjustedX.value = windowWidth - menuRect.width - 4; // 留点边距
|
||||
} else {
|
||||
adjustedX.value = props.x;
|
||||
}
|
||||
|
||||
// 修正纵向
|
||||
if (props.y + menuRect.height > windowHeight) {
|
||||
adjustedY.value = windowHeight - menuRect.height - 4;
|
||||
} else {
|
||||
adjustedY.value = props.y;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const handleClickOutside = () => emit('close');
|
||||
|
||||
onMounted(() => document.addEventListener('click', () => handleClickOutside));
|
||||
onUnmounted(() => document.removeEventListener('click', () => handleClickOutside));
|
||||
</script>
|
||||
<style scoped>
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
background: var(--panel-main-bg-color-9);
|
||||
border: 1px solid var(--el-border-color);
|
||||
color: var(--el-color-primary);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
padding: 4px 0;
|
||||
margin: 0;
|
||||
z-index: 9999;
|
||||
min-width: 120px;
|
||||
}
|
||||
.context-menu li {
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.context-menu li:hover {
|
||||
background-color: var(--panel-menu-bg-color);
|
||||
}
|
||||
.context-menu li.disabled {
|
||||
color: var(--el-border-color);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.context-menu li.divided {
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div @dragover="handleDragover" @drop="handleDrop" @dragleave="handleDragleave">
|
||||
<div
|
||||
@dragover="handleDragover"
|
||||
@drop="handleDrop"
|
||||
@dragleave="handleDragleave"
|
||||
@contextmenu.prevent="onRightClick"
|
||||
@click="onRightClick"
|
||||
>
|
||||
<div class="flex sm:flex-row flex-col justify-start gap-y-2 items-center gap-x-4" ref="toolRef">
|
||||
<div class="flex-shrink-0 flex sm:w-min w-full items-center justify-start">
|
||||
<el-tooltip :content="$t('file.back')" placement="top">
|
||||
@@ -388,6 +394,7 @@
|
||||
@cell-mouse-enter="showFavorite"
|
||||
@cell-mouse-leave="hideFavorite"
|
||||
:heightDiff="300"
|
||||
@row-contextmenu="openRight"
|
||||
>
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column
|
||||
@@ -535,6 +542,14 @@
|
||||
<VscodeOpenDialog ref="dialogVscodeOpenRef" />
|
||||
<Preview ref="previewRef" />
|
||||
<TerminalDialog ref="dialogTerminalRef" />
|
||||
<RightContextMenu
|
||||
v-if="menuVisible"
|
||||
:x="menuPosition.x"
|
||||
:y="menuPosition.y"
|
||||
:row="currentRow"
|
||||
:buttons="buttons"
|
||||
@close="hideRightMenu"
|
||||
/>
|
||||
</LayoutContent>
|
||||
</div>
|
||||
</template>
|
||||
@@ -865,6 +880,7 @@ const top = () => {
|
||||
};
|
||||
|
||||
const jump = async (url: string) => {
|
||||
hideRightMenu();
|
||||
history.splice(pointer + 1);
|
||||
history.push(url);
|
||||
pointer = history.length - 1;
|
||||
@@ -1308,7 +1324,8 @@ const buttons = [
|
||||
openDownload(row);
|
||||
},
|
||||
disabled: (row: File.File) => {
|
||||
return row.isDir;
|
||||
debugger;
|
||||
return row?.isDir;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1366,8 +1383,8 @@ const buttons = [
|
||||
{
|
||||
label: i18n.global.t('file.addFavorite'),
|
||||
click: (row: File.File) => {
|
||||
if (row.favoriteID > 0) {
|
||||
remove(row.favoriteID);
|
||||
if (row?.favoriteID > 0) {
|
||||
remove(row?.favoriteID);
|
||||
} else {
|
||||
addToFavorite(row);
|
||||
}
|
||||
@@ -1424,6 +1441,36 @@ const handleDragleave = (event: { preventDefault: () => void }) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const menuVisible = ref(false);
|
||||
const noMenuVisible = ref(false);
|
||||
const menuPosition = reactive({ x: 0, y: 0 });
|
||||
const currentRow = ref<File.File>({} as File.File);
|
||||
|
||||
function openRight(row: File.File, _column: any, event: MouseEvent) {
|
||||
noMenuVisible.value = true;
|
||||
if (row.name === currentRow.value?.name && menuVisible.value) {
|
||||
menuVisible.value = false;
|
||||
} else {
|
||||
event.preventDefault();
|
||||
currentRow.value = row;
|
||||
menuPosition.x = event.clientX;
|
||||
menuPosition.y = event.clientY;
|
||||
menuVisible.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function hideRightMenu() {
|
||||
menuVisible.value = false;
|
||||
}
|
||||
|
||||
function onRightClick() {
|
||||
if (noMenuVisible.value) {
|
||||
noMenuVisible.value = false;
|
||||
} else {
|
||||
menuVisible.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (localStorage.getItem('show-hidden') === null) {
|
||||
localStorage.setItem('show-hidden', 'true');
|
||||
|
||||
Reference in New Issue
Block a user