import { ref } from 'vue';
import type { DirectoryNode, FileStore } from '../types';
import {
checkPathExists,
loadDirectoryStructure,
extractDirectoryPath,
extractFileName,
openDirectoryDialog,
} from '../fileUtils';
import { useDirectoryStore } from '../../store';
import { mergeSimilarDirectories } from '../../utils/path';
const MAX_DIRECTORY_COUNT = 10;
const FULL_TREE_DEPTH = 64;
* 目录管理composable
*/
export function useDirectoryManager(fileStore: FileStore) {
const directoryStore = useDirectoryStore();
const recentDirectories = ref<DirectoryNode[]>([]);
* 合并相似目录路径
* 例如:将 D:\A\doc, doc, D:\A\doc\dir 合并为 D:\A\doc
*
* 算法逻辑:
* 1. 分离绝对路径和相对路径
* 2. 按路径长度从短到长排序(父目录优先)
* 3. 对于每个路径,检查是否已有父路径存在
* 4. 对于相对路径,检查是否已有对应的绝对路径
*/
const toggleDirectory = async (dirPath: string, node?: DirectoryNode): Promise<void> => {
let directory: DirectoryNode | undefined;
if (node) {
directory = node;
} else {
directory = recentDirectories.value.find((dir) => dir.path === dirPath);
}
if (!directory) return;
directory.expanded = !directory.expanded;
directoryStore.setExpanded(dirPath, directory.expanded);
if (directory.expanded) {
if (!directory.children || directory.children.length === 0) {
const result = await loadDirectoryStructure(dirPath, 1, FULL_TREE_DEPTH);
if (result.success && result.data) {
directory.children = result.data;
}
}
}
};
* 获取最近访问的目录列表
*/
const getRecentDirectories = async (): Promise<void> => {
try {
const directories = new Set<string>();
fileStore.sortedRecentFiles.forEach((file) => {
const dirPath = extractDirectoryPath(file.path);
if (dirPath) directories.add(dirPath);
});
const mergedDirectories = mergeSimilarDirectories(Array.from(directories)).slice(0, MAX_DIRECTORY_COUNT);
const directoryResults = await Promise.all(
mergedDirectories.map(async (dirPath) => {
const dirExists = await checkPathExists(dirPath);
if (!dirExists) return null;
const cached = directoryStore.items.find((it) => it.path === dirPath);
return {
path: dirPath,
name: extractFileName(dirPath),
type: 'directory' as const,
expanded: cached?.expanded ?? false,
children: [] as DirectoryNode[],
};
}),
);
const validDirectories = directoryResults.filter(Boolean) as DirectoryNode[];
const cachedOnly = directoryStore.items
.filter((item) => !validDirectories.some((dir) => dir.path === item.path))
.map((item) => ({
path: item.path,
name: extractFileName(item.path),
type: 'directory' as const,
expanded: item.expanded,
children: [] as DirectoryNode[],
}));
recentDirectories.value = [...validDirectories, ...cachedOnly].slice(0, MAX_DIRECTORY_COUNT);
const loadPromises = recentDirectories.value
.filter((dir) => dir.expanded)
.map(async (currentDir) => {
const result = await loadDirectoryStructure(currentDir.path, 1, FULL_TREE_DEPTH);
if (result.success && result.data) {
const updatedDir = recentDirectories.value.find((dir) => dir.path === currentDir.path);
if (updatedDir) {
updatedDir.children = result.data;
}
}
});
await Promise.all(loadPromises);
directoryStore.setItems(
recentDirectories.value.map((dir) => ({ path: dir.path, expanded: dir.expanded ?? false })),
);
} catch (error) {
console.error('获取目录列表失败:', error);
}
};
* 打开目录对话框并添加到列表
*/
const openDirectory = async (): Promise<void> => {
try {
const result = await openDirectoryDialog();
if (!result.success || !result.data) return;
const dirPath = result.data;
const existingDir = recentDirectories.value.find((dir) => dir.path === dirPath);
if (existingDir) {
await toggleDirectory(dirPath);
return;
}
const newDir: DirectoryNode = {
path: dirPath,
name: extractFileName(dirPath),
type: 'directory',
expanded: true,
children: [],
};
directoryStore.upsertDirectory(dirPath, true);
const loadResult = await loadDirectoryStructure(dirPath, 1, FULL_TREE_DEPTH);
if (loadResult.success && loadResult.data) {
newDir.children = loadResult.data;
}
recentDirectories.value = [newDir, ...recentDirectories.value].slice(0, MAX_DIRECTORY_COUNT);
directoryStore.setItems(
recentDirectories.value.map((dir) => ({ path: dir.path, expanded: dir.expanded ?? false })),
);
} catch (error) {
console.error('打开目录失败:', error);
}
};
const refreshDirectories = async (): Promise<void> => {
const currentExpansionState = new Map<string, boolean>();
recentDirectories.value.forEach((dir) => {
currentExpansionState.set(dir.path, dir.expanded || false);
});
await getRecentDirectories();
recentDirectories.value.forEach((currentDir) => {
const savedState = currentExpansionState.get(currentDir.path);
if (savedState !== undefined) {
const updatableDir = recentDirectories.value.find((d) => d.path === currentDir.path);
if (updatableDir) {
updatableDir.expanded = savedState;
}
if (savedState && currentDir.children && currentDir.children.length === 0) {
loadDirectoryStructure(currentDir.path, 1, FULL_TREE_DEPTH).then((result) => {
if (result.success && result.data) {
const updatedDir = recentDirectories.value.find((d) => d.path === currentDir.path);
if (updatedDir) {
updatedDir.children = result.data;
}
}
});
}
}
});
directoryStore.setItems(
recentDirectories.value.map((dir) => ({ path: dir.path, expanded: dir.expanded ?? false })),
);
};
return {
recentDirectories,
toggleDirectory,
openDirectory,
refreshDirectories,
getRecentDirectories,
};
}