feat(FileTree): 改进文件树交互并添加主题支持
- 修复目录加载逻辑,避免空数组误判为已加载 - 改进新建/重命名操作的父目录路径计算 - 为文件树节点添加悬停提示和主题样式适配 - 在工具栏添加设置按钮 - 扩展主题系统以支持暗色模式
This commit is contained in:
parent
6b26049909
commit
4a7bd83835
|
|
@ -49,7 +49,8 @@ window.services = {
|
|||
name: item.name,
|
||||
path: path.join(dirPath, item.name),
|
||||
type: item.isDirectory() ? "directory" : "file",
|
||||
children: item.isDirectory() ? [] : undefined, // 目录初始化为空数组,标记为可展开
|
||||
// children 不设置或设为 undefined,表示未加载;如果设为 [],组件会认为已加载且为空
|
||||
children: undefined,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("读取目录失败:", error);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
>
|
||||
<template #title="{ dataRef }">
|
||||
<a-dropdown :trigger="['contextmenu']">
|
||||
<span class="tree-node-title">{{ dataRef.name }}</span>
|
||||
<span class="tree-node-title" :title="dataRef.name">{{ dataRef.name }}</span>
|
||||
<template #overlay>
|
||||
<a-menu @click="(e) => handleMenuClick(e, dataRef)">
|
||||
<a-menu-item key="open" v-if="dataRef.type === 'file'">打开</a-menu-item>
|
||||
|
|
@ -101,11 +101,12 @@ const handleRefresh = async () => {
|
|||
|
||||
const onLoadData = (treeNode) => {
|
||||
return new Promise(async (resolve) => {
|
||||
if (treeNode.dataRef.children) {
|
||||
// 使用 key (path) 来加载
|
||||
if (treeNode.dataRef.children && treeNode.dataRef.children.length > 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
await loadDirectory(treeNode.eventKey); // eventKey is path
|
||||
await loadDirectory(treeNode.key || treeNode.eventKey);
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
|
@ -116,7 +117,7 @@ const onSelect = (keys, { node }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const isMdFile = (name) => name.toLowerCase().endsWith('.md');
|
||||
const isMdFile = (name) => name && name.toLowerCase().endsWith('.md');
|
||||
|
||||
const isPinned = (path) => config.pinnedFiles.includes(path);
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ const handleMenuClick = async ({ key }, node) => {
|
|||
if (window.services?.deleteItem) {
|
||||
await window.services.deleteItem(node.path);
|
||||
message.success('删除成功');
|
||||
await refresh(); // 刷新可能会重置展开状态,理想情况是只刷新父目录
|
||||
await refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -192,7 +193,23 @@ const handleModalOk = async () => {
|
|||
}
|
||||
|
||||
const node = currentNode.value;
|
||||
const parentPath = node.type === 'directory' ? node.path : node.path.substring(0, node.path.lastIndexOf(window.utools.isWindows() ? '\\' : '/'));
|
||||
// 如果是新建,父目录是 node.path;如果是重命名,父目录是 node 的父级
|
||||
// 这里逻辑有点小问题,如果右键的是文件,新建应该是在同级目录?
|
||||
// Ant Design Tree 右键的 node 是当前节点。
|
||||
// 如果是 directory,新建在其中。
|
||||
// 如果是 file,新建在同级。
|
||||
|
||||
let parentPath = '';
|
||||
if (currentAction.value === 'newFile' || currentAction.value === 'newFolder') {
|
||||
if (node.type === 'directory') {
|
||||
parentPath = node.path;
|
||||
} else {
|
||||
parentPath = node.path.substring(0, node.path.lastIndexOf(window.utools.isWindows() ? '\\' : '/'));
|
||||
}
|
||||
} else {
|
||||
// rename
|
||||
parentPath = node.path.substring(0, node.path.lastIndexOf(window.utools.isWindows() ? '\\' : '/'));
|
||||
}
|
||||
|
||||
try {
|
||||
if (currentAction.value === 'rename') {
|
||||
|
|
@ -200,14 +217,13 @@ const handleModalOk = async () => {
|
|||
await window.services.renameItem(node.path, newPath);
|
||||
message.success('重命名成功');
|
||||
} else if (currentAction.value === 'newFile') {
|
||||
// 这里的 node 应该是目录
|
||||
const separator = window.utools.isWindows() ? '\\' : '/';
|
||||
const newPath = `${node.path}${separator}${modalInputValue.value}`;
|
||||
const newPath = `${parentPath}${separator}${modalInputValue.value}`;
|
||||
await window.services.writeFile(newPath, ''); // 创建空文件
|
||||
message.success('创建文件成功');
|
||||
} else if (currentAction.value === 'newFolder') {
|
||||
const separator = window.utools.isWindows() ? '\\' : '/';
|
||||
const newPath = `${node.path}${separator}${modalInputValue.value}`;
|
||||
const newPath = `${parentPath}${separator}${modalInputValue.value}`;
|
||||
await window.services.createDirectory(newPath);
|
||||
message.success('创建文件夹成功');
|
||||
}
|
||||
|
|
@ -225,6 +241,8 @@ const handleModalOk = async () => {
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--card-background, #fff);
|
||||
color: var(--text-color, #000);
|
||||
}
|
||||
|
||||
.tree-header {
|
||||
|
|
@ -232,7 +250,7 @@ const handleModalOk = async () => {
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-bottom: 1px solid var(--border-color, #f0f0f0);
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
@ -245,6 +263,17 @@ const handleModalOk = async () => {
|
|||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* 覆盖 Ant Design Tree 的一些样式以美化 */
|
||||
:deep(.ant-tree) {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-treenode) {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
.empty-tip {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
|
|
@ -252,7 +281,30 @@ const handleModalOk = async () => {
|
|||
}
|
||||
|
||||
.tree-node-title {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1; /* allow it to take remaining space but not force width */
|
||||
margin-left: 4px; /* spacing between icon and text */
|
||||
}
|
||||
|
||||
:deep(.ant-tree-node-content-wrapper) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
min-height: 28px; /* improve touch target / spacing */
|
||||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-node-content-wrapper:hover) {
|
||||
background-color: var(--hover-background, rgba(0, 0, 0, 0.04));
|
||||
}
|
||||
|
||||
:deep(.ant-tree-node-selected .ant-tree-node-content-wrapper) {
|
||||
background-color: var(--primary-color-bg, #e6f7ff) !important;
|
||||
color: var(--primary-color, #1890ff);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@
|
|||
<a-button type="text" size="small" @click="$emit('toggle-git')" title="Git 操作">
|
||||
<template #icon><GithubOutlined /></template>
|
||||
</a-button>
|
||||
|
||||
<a-button type="text" size="small" @click="$emit('toggle-settings')" title="设置">
|
||||
<template #icon><SettingOutlined /></template>
|
||||
</a-button>
|
||||
|
||||
<a-dropdown>
|
||||
<a-button type="text" size="small" title="导出">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,24 @@ export function useTheme() {
|
|||
const updateCSSVariables = () => {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--primary-color', primaryColor.value);
|
||||
// Ant Design uses generated colors, but we can set the main one for our custom CSS
|
||||
|
||||
// 生成一个浅色背景用于选中态
|
||||
// 简单变淡处理,或者直接使用 opacity
|
||||
root.style.setProperty('--primary-color-bg', primaryColor.value + '1A'); // 10% opacity
|
||||
|
||||
if (isDark.value) {
|
||||
root.style.setProperty('--card-background', '#1f1f1f');
|
||||
root.style.setProperty('--text-color', 'rgba(255, 255, 255, 0.85)');
|
||||
root.style.setProperty('--border-color', '#303030');
|
||||
root.style.setProperty('--hover-background', 'rgba(255, 255, 255, 0.08)');
|
||||
root.style.setProperty('--text-color-secondary', 'rgba(255, 255, 255, 0.45)');
|
||||
} else {
|
||||
root.style.setProperty('--card-background', '#ffffff');
|
||||
root.style.setProperty('--text-color', 'rgba(0, 0, 0, 0.85)');
|
||||
root.style.setProperty('--border-color', '#f0f0f0');
|
||||
root.style.setProperty('--hover-background', 'rgba(0, 0, 0, 0.04)');
|
||||
root.style.setProperty('--text-color-secondary', 'rgba(0, 0, 0, 0.45)');
|
||||
}
|
||||
};
|
||||
|
||||
const getAntdTheme = () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue