Spaces:
Sleeping
Sleeping
| <template> | |
| <div class="folder-select"> | |
| <!-- 面包屑导航 --> | |
| <div class="folder-select__nav"> | |
| <van-cell :border="false" class="nav-cell"> | |
| <template #title> | |
| <div class="nav-breadcrumb"> | |
| <van-icon name="wap-home-o" class="home-icon" @click="handleHomeClick" /> | |
| <template v-for="(path, index) in currentFolderPath" :key="path.cid"> | |
| <van-icon v-if="index !== 0" name="arrow" /> | |
| <span | |
| class="path-item" | |
| :class="{ 'is-active': index === currentFolderPath.length - 1 }" | |
| @click="handleFolderClick(path, index)" | |
| > | |
| {{ path.name }} | |
| </span> | |
| </template> | |
| </div> | |
| </template> | |
| </van-cell> | |
| </div> | |
| <!-- 文件夹列表 --> | |
| <div class="folder-select__list"> | |
| <div v-if="resourceStore.loadTree" class="folder-select__loading"> | |
| <van-loading type="spinner" vertical>加载中...</van-loading> | |
| </div> | |
| <van-empty v-if="!resourceStore.loadTree && !folders.length" description="暂无文件夹" /> | |
| <van-cell-group v-if="!resourceStore.loadTree && folders.length" :border="false"> | |
| <van-cell | |
| v-for="folder in folders" | |
| :key="folder.cid" | |
| :border="false" | |
| clickable | |
| @click="getList(folder)" | |
| > | |
| <template #icon> | |
| <van-icon name="folder-o" class="folder-icon" /> | |
| </template> | |
| <template #title> | |
| <span class="folder-name">{{ folder.name }}</span> | |
| </template> | |
| <template #right-icon> | |
| <van-icon name="arrow" /> | |
| </template> | |
| </van-cell> | |
| </van-cell-group> | |
| </div> | |
| </div> | |
| </template> | |
| <script setup lang="ts"> | |
| import { ref, defineProps, onBeforeUnmount } from "vue"; | |
| import { cloud115Api } from "@/api/cloud115"; | |
| import { quarkApi } from "@/api/quark"; | |
| import type { Folder } from "@/types"; | |
| import { type RequestResult } from "@/types/response"; | |
| import { useResourceStore } from "@/stores/resource"; | |
| import { showNotify } from "vant"; | |
| const props = defineProps({ | |
| cloudType: { | |
| type: String, | |
| required: true, | |
| }, | |
| }); | |
| const resourceStore = useResourceStore(); | |
| const folders = ref<Folder[]>([]); | |
| const currentFolderPath = ref<Folder[]>([]); | |
| const emit = defineEmits<{ | |
| (e: "select", currentFolderPath: Folder[] | null): void; | |
| (e: "close"): void; | |
| }>(); | |
| const cloudTypeApiMap = { | |
| pan115: cloud115Api, | |
| quark: quarkApi, | |
| }; | |
| // 返回根目录 | |
| const handleHomeClick = () => { | |
| currentFolderPath.value = []; | |
| getList(); | |
| }; | |
| const handleFolderClick = (folder: Folder, index: number) => { | |
| currentFolderPath.value = currentFolderPath.value.slice(0, index + 1); | |
| getList(folder); | |
| }; | |
| const getList = async (data?: Folder) => { | |
| const api = cloudTypeApiMap[props.cloudType as keyof typeof cloudTypeApiMap]; | |
| try { | |
| resourceStore.setLoadTree(true); | |
| const res: RequestResult<Folder[]> = await api.getFolderList?.(data?.cid || "0"); | |
| if (res?.code === 0) { | |
| folders.value = res.data || []; | |
| if (!data) { | |
| currentFolderPath.value = [ | |
| { | |
| name: "根目录", | |
| cid: "0", | |
| }, | |
| ]; | |
| } else if (!currentFolderPath.value.find((p) => p.cid === data.cid)) { | |
| currentFolderPath.value.push(data); | |
| } | |
| emit("select", currentFolderPath.value); | |
| } else { | |
| throw new Error(res.message); | |
| } | |
| } catch (error) { | |
| showNotify({ | |
| type: "danger", | |
| message: error instanceof Error ? error.message : "获取目录失败", | |
| }); | |
| currentFolderPath.value = []; | |
| folders.value = []; | |
| emit("select", null); | |
| emit("close"); | |
| } finally { | |
| resourceStore.setLoadTree(false); | |
| } | |
| }; | |
| // 初始化加载 | |
| getList(); | |
| // 组件销毁前重置状态 | |
| onBeforeUnmount(() => { | |
| currentFolderPath.value = []; | |
| folders.value = []; | |
| emit("select", null); | |
| }); | |
| </script> | |
| <style lang="scss" scoped> | |
| .folder-select { | |
| position: relative; | |
| height: 100%; | |
| background: var(--theme-other_background); | |
| display: flex; | |
| flex-direction: column; | |
| &__nav { | |
| flex-shrink: 0; | |
| border-bottom: 0.5px solid #f5f5f5; | |
| background: var(--theme-other_background); | |
| .nav-cell { | |
| padding: 12px 16px; | |
| min-height: 24px; | |
| } | |
| .nav-breadcrumb { | |
| display: flex; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 4px; | |
| font-size: 14px; | |
| line-height: 1.4; | |
| min-height: 20px; | |
| } | |
| .home-icon { | |
| font-size: 16px; | |
| color: var(--theme-theme); | |
| margin-right: 4px; | |
| } | |
| .path-item { | |
| color: #666; | |
| padding: 2px 4px; | |
| border-radius: 4px; | |
| &.is-active { | |
| color: var(--theme-theme); | |
| font-weight: 500; | |
| } | |
| &:active { | |
| background-color: #f5f5f5; | |
| } | |
| } | |
| } | |
| &__list { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 8px 0; | |
| position: relative; | |
| min-height: 200px; | |
| display: flex; | |
| flex-direction: column; | |
| .van-empty { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| } | |
| .van-cell-group { | |
| flex: 1; | |
| } | |
| } | |
| &__loading { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| right: 0; | |
| bottom: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: rgba(255, 255, 255, 0.9); | |
| z-index: 0; | |
| .van-loading { | |
| padding: 16px 24px; | |
| background: rgba(0, 0, 0, 0.7); | |
| border-radius: 8px; | |
| color: #fff; | |
| } | |
| } | |
| .folder-icon { | |
| font-size: 20px; | |
| color: var(--theme-theme); | |
| margin-right: 8px; | |
| } | |
| .folder-name { | |
| font-size: 15px; | |
| color: var(--theme-color); | |
| } | |
| } | |
| // 深度修改 Vant 组件样式 | |
| :deep(.van-cell) { | |
| padding: 12px 16px; | |
| &::after { | |
| display: none; | |
| } | |
| &:active { | |
| background-color: #f5f5f5; | |
| } | |
| } | |
| :deep(.van-empty) { | |
| padding: 32px 0; | |
| margin: 0; | |
| } | |
| </style> | |