177 lines
3.9 KiB
Vue
177 lines
3.9 KiB
Vue
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { type FolderTree } from '@/api/folder'
|
||
import { Folder, FolderOpen, ChevronRight, Plus, Edit2, Trash2 } from 'lucide-vue-next'
|
||
|
||
const props = defineProps<{
|
||
folders: FolderTree[]
|
||
selectedId?: string | null
|
||
onSelect: (folder: FolderTree) => void
|
||
onCreate: (parentId: string | null) => void
|
||
onRename: (folder: FolderTree) => void
|
||
onDelete: (folder: FolderTree) => void
|
||
}>()
|
||
|
||
const expandedIds = ref<Set<string>>(new Set())
|
||
|
||
function toggleExpand(id: string) {
|
||
if (expandedIds.value.has(id)) {
|
||
expandedIds.value.delete(id)
|
||
} else {
|
||
expandedIds.value.add(id)
|
||
}
|
||
}
|
||
|
||
function handleContextMenu(e: MouseEvent, folder: FolderTree) {
|
||
e.preventDefault()
|
||
// 显示右键菜单
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="folder-tree">
|
||
<div
|
||
v-for="folder in folders"
|
||
:key="folder.id"
|
||
class="folder-item"
|
||
>
|
||
<div
|
||
class="folder-row"
|
||
:class="{ selected: folder.id === selectedId }"
|
||
@click="props.onSelect(folder)"
|
||
@contextmenu="handleContextMenu($event, folder)"
|
||
>
|
||
<!-- 展开/折叠箭头 -->
|
||
<button
|
||
v-if="folder.children?.length"
|
||
class="expand-btn"
|
||
@click.stop="toggleExpand(folder.id)"
|
||
>
|
||
<ChevronRight
|
||
:size="12"
|
||
:class="{ rotated: expandedIds.has(folder.id) }"
|
||
/>
|
||
</button>
|
||
<span v-else class="expand-placeholder"></span>
|
||
|
||
<!-- 文件夹图标 -->
|
||
<FolderOpen v-if="expandedIds.has(folder.id)" :size="14" class="folder-icon" />
|
||
<Folder v-else :size="14" class="folder-icon" />
|
||
|
||
<!-- 文件夹名称 -->
|
||
<span class="folder-name">{{ folder.name }}</span>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="folder-actions">
|
||
<button @click.stop="props.onCreate(folder.id)" title="添加子文件夹">
|
||
<Plus :size="12" />
|
||
</button>
|
||
<button @click.stop="props.onRename(folder)" title="重命名">
|
||
<Edit2 :size="12" />
|
||
</button>
|
||
<button @click.stop="props.onDelete(folder)" title="删除">
|
||
<Trash2 :size="12" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 子文件夹(递归) -->
|
||
<div
|
||
v-if="folder.children?.length && expandedIds.has(folder.id)"
|
||
class="folder-children"
|
||
>
|
||
<FolderTree
|
||
:folders="folder.children"
|
||
:selected-id="selectedId"
|
||
:on-select="onSelect"
|
||
:on-create="onCreate"
|
||
:on-rename="onRename"
|
||
:on-delete="onDelete"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* sci-fi 风格 */
|
||
.folder-tree {
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.folder-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 6px 8px;
|
||
border-radius: var(--radius-sm);
|
||
cursor: pointer;
|
||
transition: all var(--transition-fast);
|
||
}
|
||
|
||
.folder-row:hover {
|
||
background: rgba(0, 245, 212, 0.04);
|
||
}
|
||
|
||
.folder-row.selected {
|
||
background: var(--accent-cyan-dim);
|
||
border: 1px solid rgba(0, 245, 212, 0.2);
|
||
}
|
||
|
||
.expand-btn {
|
||
background: none;
|
||
border: none;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
color: var(--text-dim);
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.expand-placeholder {
|
||
width: 12px;
|
||
}
|
||
|
||
.folder-icon {
|
||
color: var(--accent-amber);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.folder-name {
|
||
flex: 1;
|
||
color: var(--text-primary);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.folder-actions {
|
||
display: none;
|
||
gap: 2px;
|
||
}
|
||
|
||
.folder-row:hover .folder-actions {
|
||
display: flex;
|
||
}
|
||
|
||
.folder-actions button {
|
||
background: none;
|
||
border: none;
|
||
padding: 2px;
|
||
cursor: pointer;
|
||
color: var(--text-dim);
|
||
border-radius: 3px;
|
||
transition: all var(--transition-fast);
|
||
}
|
||
|
||
.folder-actions button:hover {
|
||
color: var(--accent-cyan);
|
||
background: rgba(0, 245, 212, 0.1);
|
||
}
|
||
|
||
.folder-children {
|
||
padding-left: 16px;
|
||
}
|
||
</style>
|