feat(FileTree): 为文件树空白区域添加上下文菜单

新增对文件树空白区域的右键菜单支持,包含新建文件/文件夹、刷新和打开资源管理器选项
This commit is contained in:
cfq 2026-01-30 15:55:56 +08:00
parent 1bf39910dd
commit 7f6d445305
1 changed files with 65 additions and 36 deletions

View File

@ -23,45 +23,56 @@
<a-empty description="请选择目录" /> <a-empty description="请选择目录" />
</div> </div>
<div v-else class="tree-content" ref="treeContentRef"> <a-dropdown v-else :trigger="['contextmenu']">
<a-directory-tree ref="treeRef" v-model:expandedKeys="state.expandedKeys" v-model:selectedKeys="selectedKeys" :tree-data="treeData" :field-names="{ title: 'name', key: 'path', children: 'children' }" :load-data="onLoadData" @select="onSelect"> <div class="tree-content" ref="treeContentRef">
<template #icon="{ dataRef, expanded }"> <a-directory-tree ref="treeRef" v-model:expandedKeys="state.expandedKeys" v-model:selectedKeys="selectedKeys" :tree-data="treeData" :field-names="{ title: 'name', key: 'path', children: 'children' }" :load-data="onLoadData" @select="onSelect">
<component :is="getFileIcon(dataRef.name, dataRef.type, expanded)" :style="{ color: getFileIconColor(dataRef.name, dataRef.type), fontSize: '18px', marginRight: '6px' }" /> <template #icon="{ dataRef, expanded }">
</template> <component :is="getFileIcon(dataRef.name, dataRef.type, expanded)" :style="{ color: getFileIconColor(dataRef.name, dataRef.type), fontSize: '18px', marginRight: '6px' }" />
<template #title="{ dataRef }"> </template>
<a-dropdown :trigger="['contextmenu']"> <template #title="{ dataRef }">
<span class="tree-node-title" :title="dataRef.name">{{ dataRef.name }}</span> <a-dropdown :trigger="['contextmenu']">
<template #overlay> <span class="tree-node-title" :title="dataRef.name">{{ dataRef.name }}</span>
<a-menu @click="(e) => handleMenuClick(e, dataRef)"> <template #overlay>
<a-menu-item key="open" v-if="dataRef.type === 'file'">打开</a-menu-item> <a-menu @click="(e) => handleMenuClick(e, dataRef)">
<a-menu-item key="open" v-if="dataRef.type === 'file'">打开</a-menu-item>
<template v-if="isMdFile(dataRef.name)"> <template v-if="isMdFile(dataRef.name)">
<a-menu-item key="pin"> <a-menu-item key="pin">
{{ isPinned(dataRef.path) ? "取消置顶" : "置顶" }} {{ isPinned(dataRef.path) ? "取消置顶" : "置顶" }}
</a-menu-item> </a-menu-item>
</template> </template>
<a-menu-divider />
<!-- 文件夹操作 -->
<template v-if="dataRef.type === 'directory'">
<a-menu-item key="newFile">新建文件</a-menu-item>
<a-menu-item key="newFolder">新建文件夹</a-menu-item>
<a-menu-divider /> <a-menu-divider />
</template>
<a-menu-item key="rename">重命名</a-menu-item> <!-- 文件夹操作 -->
<a-menu-item key="delete" danger>删除</a-menu-item> <template v-if="dataRef.type === 'directory'">
<a-menu-item key="newFile">新建文件</a-menu-item>
<a-menu-item key="newFolder">新建文件夹</a-menu-item>
<a-menu-divider />
</template>
<a-menu-divider /> <a-menu-item key="rename">重命名</a-menu-item>
<a-menu-item key="copyPath">复制路径</a-menu-item> <a-menu-item key="delete" danger>删除</a-menu-item>
<a-menu-item key="openInExplorer">在资源管理器打开</a-menu-item>
</a-menu> <a-menu-divider />
</template> <a-menu-item key="copyPath">复制路径</a-menu-item>
</a-dropdown> <a-menu-item key="openInExplorer">在资源管理器打开</a-menu-item>
</template> </a-menu>
</a-directory-tree> </template>
</div> </a-dropdown>
</template>
</a-directory-tree>
</div>
<template #overlay>
<a-menu @click="handleBackgroundMenuClick">
<a-menu-item key="newFile">新建文件</a-menu-item>
<a-menu-item key="newFolder">新建文件夹</a-menu-item>
<a-menu-divider />
<a-menu-item key="refresh">刷新</a-menu-item>
<a-menu-item key="openInExplorer">在资源管理器打开</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<!-- 重命名/新建弹窗 --> <!-- 重命名/新建弹窗 -->
<a-modal v-model:open="modalVisible" :title="modalTitle" @ok="handleModalOk"> <a-modal v-model:open="modalVisible" :title="modalTitle" @ok="handleModalOk">
@ -362,6 +373,26 @@ const handleMenuClick = async ({ key }, node) => {
} }
}; };
const handleBackgroundMenuClick = ({ key }) => {
if (key === 'refresh') {
handleRefresh();
return;
}
//
const separator = window.utools.isWindows() ? "\\" : "/";
// C:\pop() state.rootPath
const rootName = state.rootPath.split(separator).pop() || state.rootPath;
const rootNode = {
name: rootName,
path: state.rootPath,
type: "directory"
};
handleMenuClick({ key }, rootNode);
};
const handleModalOk = async () => { const handleModalOk = async () => {
if (!modalInputValue.value) { if (!modalInputValue.value) {
message.warning("请输入内容"); message.warning("请输入内容");
@ -493,10 +524,8 @@ const handleModalOk = async () => {
} }
:deep(.ant-dropdown-trigger) { :deep(.ant-dropdown-trigger) {
display: flex;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
align-items: center;
} }
:deep(.ant-tree-node-content-wrapper:hover) { :deep(.ant-tree-node-content-wrapper:hover) {