初步添加FileTree功能

This commit is contained in:
2025-07-27 01:41:14 +08:00
parent bb7d069e06
commit e3707dab5c
3 changed files with 303 additions and 27 deletions

View File

@@ -0,0 +1,108 @@
<template>
<div class="file-tree-item">
<div
class="flex items-center py-1 px-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors"
:class="{
'bg-blue-50 dark:bg-blue-900/20': item.highlighted,
'text-green-600 dark:text-green-400': item.diff === 'addition',
'text-red-600 dark:text-red-400': item.diff === 'deletion'
}"
@click="toggleFolder"
>
<!-- 缩进 -->
<div class="flex items-center" :style="{ marginLeft: level * 16 + 'px' }">
<!-- 箭头仅文件夹且有子项时显示 -->
<Icon
v-if="showArrow && item.isFolder && item.children && item.children.length > 0"
:name="isExpanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
class="mr-1 text-gray-400 w-4 h-4 transition-transform"
:class="{ 'rotate-90': isExpanded }"
/>
<div v-else-if="showArrow" class="w-4 mr-1"></div>
<!-- 图标 -->
<Icon
v-if="showIcon"
:name="item.icon || 'i-lucide-file'"
class="mr-2 text-gray-500 w-4 h-4"
:class="{
'text-green-500': item.icon?.includes('vue'),
'text-blue-500': item.icon?.includes('typescript') || item.icon?.includes('javascript'),
'text-orange-500': item.icon?.includes('markdown'),
'text-yellow-500': item.icon?.includes('json')
}"
/>
<!-- 标题 -->
<span
class="text-sm font-mono"
:class="{
'font-semibold': item.isFolder,
'underline': item.highlighted
}"
>
{{ item.title }}
</span>
</div>
</div>
<!-- 子项 -->
<div v-if="item.children && item.children.length > 0 && isExpanded" class="ml-4">
<FileTreeItem
v-for="child in item.children"
:key="child.title"
:item="child"
:level="level + 1"
:show-arrow="showArrow"
:show-icon="showIcon"
/>
</div>
</div>
</template>
<script setup lang="ts">
type FileTreeItemDiff = 'none' | 'addition' | 'deletion';
interface FileTreeItem {
title: string;
icon?: string;
children?: FileTreeItem[];
highlighted?: boolean;
diff?: FileTreeItemDiff;
isFolder?: boolean;
}
const props = defineProps<{
item: FileTreeItem;
level: number;
showArrow: boolean;
showIcon: boolean;
}>();
const expandedState = inject('expandedState', ref(new Set<string>()));
const itemKey = computed(() => `${props.item.title}-${props.level}`);
const isExpanded = computed({
get: () => expandedState.value.has(itemKey.value),
set: (value: boolean) => {
if (value) {
expandedState.value.add(itemKey.value);
} else {
expandedState.value.delete(itemKey.value);
}
}
});
// 初始化时展开所有文件夹
onMounted(() => {
if (props.item.isFolder && props.item.children && props.item.children.length > 0) {
isExpanded.value = true;
}
});
function toggleFolder() {
if (props.item.isFolder && props.item.children && props.item.children.length > 0) {
isExpanded.value = !isExpanded.value;
}
}
</script>