feat: File list supports right-click operation (#10049)

#5620
This commit is contained in:
蘭
2025-08-19 05:45:14 +00:00
committed by GitHub
parent e9b4ff1932
commit efe7815a7e
2 changed files with 156 additions and 4 deletions
@@ -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');