|
Download dashboard/src/components/folder/README.md from qa1145/astrbbbb: direct link, hf CLI and curl.
- Browser
- Download file 8.42 kB
-
https://huggingface.co/spaces/qa1145/astrbbbb/resolve/main/dashboard/src/components/folder/README.md
- Command line
-
hf download hf://spaces/qa1145/astrbbbb/dashboard/src/components/folder/README.md
-
curl -L -o README.md https://huggingface.co/spaces/qa1145/astrbbbb/resolve/main/dashboard/src/components/folder/README.md
8.42 kB
| # 通用文件夹管理组件库 | |
| 这是一个可复用的文件夹管理 UI 组件库,提供了完整的文件夹树、面包屑导航、拖放操作等功能。可用于管理各种类型的项目,如 Persona、模板、知识库等。 | |
| ## 组件列表 | |
| | 组件 | 说明 | | |
| |------|------| | |
| | `BaseFolderTree` | 文件夹树组件,支持搜索、展开/折叠、右键菜单、拖放 | | |
| | `BaseFolderTreeNode` | 文件夹树节点组件(内部使用) | | |
| | `BaseFolderCard` | 文件夹卡片组件,用于网格布局展示 | | |
| | `BaseFolderBreadcrumb` | 面包屑导航组件 | | |
| | `BaseCreateFolderDialog` | 创建文件夹对话框 | | |
| | `BaseMoveToFolderDialog` | 移动项目到文件夹对话框 | | |
| | `BaseMoveTargetNode` | 移动对话框中的目标文件夹节点(内部使用) | | |
| ## Composable | |
| ### `useFolderManager` | |
| 提供文件夹管理的核心逻辑,包括状态管理、导航、CRUD 操作等。 | |
| ```typescript | |
| import { useFolderManager } from '@/components/folder'; | |
| const { | |
| // 状态 | |
| folderTree, | |
| currentFolderId, | |
| currentFolders, | |
| breadcrumbPath, | |
| expandedFolderIds, | |
| loading, | |
| treeLoading, | |
| // 计算属性 | |
| currentFolderName, | |
| breadcrumbItems, | |
| // 方法 | |
| loadFolderTree, | |
| navigateToFolder, | |
| refreshCurrentFolder, | |
| createFolder, | |
| updateFolder, | |
| deleteFolder, | |
| moveFolder, | |
| toggleFolderExpansion, | |
| setFolderExpansion, | |
| findFolderInTree, | |
| findPathToFolder, | |
| filterTreeBySearch, | |
| } = useFolderManager({ | |
| operations: { | |
| loadFolderTree: async () => { | |
| const response = await axios.get('/api/your-module/folder/tree'); | |
| return response.data.data; | |
| }, | |
| loadSubFolders: async (parentId) => { | |
| const response = await axios.get('/api/your-module/folder/list', { | |
| params: { parent_id: parentId ?? '' } | |
| }); | |
| return response.data.data; | |
| }, | |
| createFolder: async (data) => { | |
| const response = await axios.post('/api/your-module/folder/create', data); | |
| return response.data.data.folder; | |
| }, | |
| updateFolder: async (data) => { | |
| await axios.post('/api/your-module/folder/update', data); | |
| }, | |
| deleteFolder: async (folderId) => { | |
| await axios.post('/api/your-module/folder/delete', { folder_id: folderId }); | |
| }, | |
| }, | |
| rootFolderName: '根目录', | |
| autoLoad: true, | |
| }); | |
| ``` | |
| ## 使用示例 | |
| ### 基础用法 | |
| ```vue | |
| <template> | |
| <div class="folder-manager"> | |
| <!-- 侧边栏 --> | |
| <div class="sidebar"> | |
| <BaseFolderTree | |
| :folder-tree="folderTree" | |
| :current-folder-id="currentFolderId" | |
| :expanded-folder-ids="expandedFolderIds" | |
| :tree-loading="treeLoading" | |
| :accept-drop-types="['item']" | |
| :labels="treeLabels" | |
| @folder-click="navigateToFolder" | |
| @rename-folder="handleRenameFolder" | |
| @move-folder="handleMoveFolder" | |
| @delete-folder="handleDeleteFolder" | |
| @item-dropped="handleItemDropped" | |
| @toggle-expansion="toggleFolderExpansion" | |
| /> | |
| </div> | |
| <!-- 主内容区 --> | |
| <div class="main-content"> | |
| <!-- 面包屑 --> | |
| <BaseFolderBreadcrumb | |
| :breadcrumb-path="breadcrumbPath" | |
| :current-folder-id="currentFolderId" | |
| root-folder-name="根目录" | |
| @navigate="navigateToFolder" | |
| /> | |
| <!-- 文件夹卡片 --> | |
| <v-row> | |
| <v-col v-for="folder in currentFolders" :key="folder.folder_id" cols="3"> | |
| <BaseFolderCard | |
| :folder="folder" | |
| :accept-drop-types="['item']" | |
| :labels="cardLabels" | |
| @click="navigateToFolder(folder.folder_id)" | |
| @open="navigateToFolder(folder.folder_id)" | |
| @rename="handleRenameFolder(folder)" | |
| @move="handleMoveFolder(folder)" | |
| @delete="handleDeleteFolder(folder)" | |
| @item-dropped="handleItemDropped" | |
| /> | |
| </v-col> | |
| </v-row> | |
| </div> | |
| <!-- 创建文件夹对话框 --> | |
| <BaseCreateFolderDialog | |
| v-model="showCreateDialog" | |
| :parent-folder-id="currentFolderId" | |
| :labels="createDialogLabels" | |
| @create="handleCreateFolder" | |
| /> | |
| <!-- 移动对话框 --> | |
| <BaseMoveToFolderDialog | |
| v-model="showMoveDialog" | |
| :folder-tree="folderTree" | |
| :tree-loading="treeLoading" | |
| :current-folder-id="movingFolder?.folder_id" | |
| :item-current-folder-id="movingFolder?.parent_id" | |
| :is-moving-folder="true" | |
| :labels="moveDialogLabels" | |
| @move="handleMove" | |
| /> | |
| </div> | |
| </template> | |
| <script setup lang="ts"> | |
| import { ref } from 'vue'; | |
| import { | |
| BaseFolderTree, | |
| BaseFolderCard, | |
| BaseFolderBreadcrumb, | |
| BaseCreateFolderDialog, | |
| BaseMoveToFolderDialog, | |
| useFolderManager, | |
| } from '@/components/folder'; | |
| const folderManager = useFolderManager({ | |
| operations: { | |
| // ... 实现你的 API 调用 | |
| }, | |
| }); | |
| const { | |
| folderTree, | |
| currentFolderId, | |
| currentFolders, | |
| breadcrumbPath, | |
| expandedFolderIds, | |
| treeLoading, | |
| navigateToFolder, | |
| toggleFolderExpansion, | |
| createFolder, | |
| } = folderManager; | |
| const showCreateDialog = ref(false); | |
| const showMoveDialog = ref(false); | |
| const movingFolder = ref(null); | |
| // 自定义标签 | |
| const treeLabels = { | |
| searchPlaceholder: '搜索文件夹...', | |
| rootFolder: '根目录', | |
| noFolders: '暂无文件夹', | |
| contextMenu: { | |
| open: '打开', | |
| rename: '重命名', | |
| moveTo: '移动到...', | |
| delete: '删除', | |
| }, | |
| }; | |
| const cardLabels = { | |
| open: '打开', | |
| rename: '重命名', | |
| moveTo: '移动到...', | |
| delete: '删除', | |
| }; | |
| const createDialogLabels = { | |
| title: '创建文件夹', | |
| nameLabel: '名称', | |
| descriptionLabel: '描述', | |
| nameRequired: '请输入名称', | |
| cancelButton: '取消', | |
| createButton: '创建', | |
| }; | |
| // 处理函数 | |
| async function handleCreateFolder(data) { | |
| await createFolder(data); | |
| showCreateDialog.value = false; | |
| } | |
| function handleRenameFolder(folder) { | |
| // 打开重命名对话框 | |
| } | |
| function handleMoveFolder(folder) { | |
| movingFolder.value = folder; | |
| showMoveDialog.value = true; | |
| } | |
| function handleDeleteFolder(folder) { | |
| // 确认并删除 | |
| } | |
| function handleItemDropped({ item_id, item_type, target_folder_id }) { | |
| // 处理拖放 | |
| } | |
| async function handleMove(targetFolderId) { | |
| // 执行移动 | |
| showMoveDialog.value = false; | |
| } | |
| </script> | |
| ``` | |
| ## 类型定义 | |
| ```typescript | |
| // 文件夹基础接口 | |
| interface Folder { | |
| folder_id: string; | |
| name: string; | |
| parent_id: string | null; | |
| description?: string | null; | |
| sort_order?: number; | |
| created_at?: string; | |
| updated_at?: string; | |
| } | |
| // 文件夹树节点接口 | |
| interface FolderTreeNode extends Folder { | |
| children: FolderTreeNode[]; | |
| } | |
| // 拖放事件数据 | |
| interface DropEventData { | |
| item_id: string; | |
| item_type: string; | |
| target_folder_id: string | null; | |
| source_data?: any; | |
| } | |
| // 创建文件夹数据 | |
| interface CreateFolderData { | |
| name: string; | |
| parent_id?: string | null; | |
| description?: string; | |
| } | |
| ``` | |
| ## 国际化支持 | |
| 所有组件都支持通过 `labels` prop 自定义文本,方便集成到不同的国际化方案中: | |
| ```vue | |
| <BaseFolderTree | |
| :labels="{ | |
| searchPlaceholder: t('folder.search'), | |
| rootFolder: t('folder.root'), | |
| noFolders: t('folder.empty'), | |
| contextMenu: { | |
| open: t('folder.menu.open'), | |
| rename: t('folder.menu.rename'), | |
| moveTo: t('folder.menu.move'), | |
| delete: t('folder.menu.delete'), | |
| }, | |
| }" | |
| /> | |
| ``` | |
| ## 拖放支持 | |
| 组件内置了拖放支持,可以通过 `acceptDropTypes` 指定接受的拖放类型: | |
| ```vue | |
| <!-- 只接受 'persona' 类型的拖放 --> | |
| <BaseFolderTree | |
| :accept-drop-types="['persona']" | |
| @item-dropped="handleDrop" | |
| /> | |
| <!-- 拖放事件处理 --> | |
| <script setup> | |
| function handleDrop({ item_id, item_type, target_folder_id, source_data }) { | |
| if (item_type === 'persona') { | |
| // 移动 persona 到目标文件夹 | |
| movePersonaToFolder(item_id, target_folder_id); | |
| } | |
| } | |
| </script> | |
| ``` | |
| ## 与 Pinia Store 集成 | |
| 如果你更喜欢使用 Pinia Store 管理状态,可以参考现有的 `personaStore.ts` 实现: | |
| ```typescript | |
| // stores/myFolderStore.ts | |
| import { defineStore } from 'pinia'; | |
| import type { FolderTreeNode, Folder } from '@/components/folder'; | |
| export const useMyFolderStore = defineStore('myFolder', { | |
| state: () => ({ | |
| folderTree: [] as FolderTreeNode[], | |
| currentFolderId: null as string | null, | |
| currentFolders: [] as Folder[], | |
| // ... | |
| }), | |
| actions: { | |
| async loadFolderTree() { | |
| // ... | |
| }, | |
| // ... | |
| }, | |
| }); | |
| ``` | |