Parcourir la source

perf(文件下载、主页): 更新文件下载、游戏选择栏默认过滤不活跃游戏

文件下载现在可以批量下载及下载文件夹了
fxs il y a 1 jour
Parent
commit
cc89355d11
2 fichiers modifiés avec 103 ajouts et 21 suppressions
  1. 96 16
      src/views/FileManage/FileList.vue
  2. 7 5
      src/views/IndexView.vue

+ 96 - 16
src/views/FileManage/FileList.vue

@@ -3,6 +3,7 @@ import DownloadTipModal from '@/components/common/DownloadTipModal.vue'
 import CommonFileUpload from '@/components/form/CommonFileUpload.vue'
 
 import { useRequest } from '@/hooks/useRequest.ts'
+import type { ResponseInfo } from '@/types/res.ts'
 
 import { FilterType, type QueryInfo } from '@/types/table.ts'
 import axiosInstance from '@/utils/axios/axiosInstance.ts'
@@ -321,9 +322,9 @@ const handleCreate = async () => {
   const res = (await axiosInstance.post(AllApi.makeDir, {
     dir: getFullPath(),
     dirName: makeDirDialogConfig.value.formData.name
-  })) as { code: number }
+  })) as { code: number; msg: string }
   if (res.code !== 0) {
-    ElMessage.error('创建文件夹失败')
+    ElMessage.error(`创建文件夹失败,${res.msg}`)
     return
   }
   ElMessage.success('创建文件夹成功')
@@ -409,17 +410,83 @@ const downloadSingleFile = async (url: string) => {
 /**
  * 批量下载文件
  * @param urls 文件下载地址列表
+ * @param folderDownloadPaths 文件下载地址列表
  */
-const handleBatchDownload = async (urls: string[]) => {
+// const handleBatchDownload = async (urls: string[], folderDownloadPaths: any) => {
+//   console.log(folderDownloadPaths)
+//   const zip = new JSZip()
+//
+//   const promises = urls.map(async (url, i) => {
+//     const response = await fetch(url)
+//     const blob = await response.blob()
+//     const fileName = url.split('/').pop()?.split('?')[0] ?? `文件${i}`
+//     zip.file(fileName, blob)
+//   })
+//   await Promise.all(promises)
+//   const content = await zip.generateAsync({ type: 'blob' })
+//   FileSaver.saveAs(content, `${new Date().getTime()}.zip`)
+// }
+
+const handleBatchDownload = async (urls: string[], folderDownloadPaths: any) => {
   const zip = new JSZip()
+  const promises: Promise<void>[] = []
+
+  // 辅助函数:从URL提取文件名
+  const getFileNameFromUrl = (url: string): string => {
+    return url.split('/').pop()?.split('?')[0] ?? ''
+  }
+
+  // 辅助函数:下载文件并添加到ZIP
+  const fetchFileAndAddToZip = async (url: string, filename: string) => {
+    try {
+      const response = await fetch(url)
+      const blob = await response.blob()
+      zip.file(filename, blob)
+    } catch (error) {
+      console.error(`下载失败: ${url}`, error)
+    }
+  }
 
-  const promises = urls.map(async (url, i) => {
-    const response = await fetch(url)
-    const blob = await response.blob()
-    const fileName = url.split('/').pop()?.split('?')[0] ?? `文件${i}`
-    zip.file(fileName, blob)
+  // 处理普通URL列表
+  urls.forEach((url) => {
+    const filename = getFileNameFromUrl(url)
+    if (filename) {
+      promises.push(fetchFileAndAddToZip(url, filename))
+    }
   })
+
+  // 递归处理嵌套结构
+  const processNestedFolder = (content: any, basePath: string) => {
+    if (typeof content === 'string') {
+      promises.push(fetchFileAndAddToZip(content, basePath))
+    } else if (typeof content === 'object' && content !== null) {
+      Object.entries(content).forEach(([key, value]) => {
+        const newPath = basePath ? `${basePath}/${key}` : key
+        processNestedFolder(value, newPath)
+      })
+    }
+  }
+
+  // 处理文件夹结构
+  if (Array.isArray(folderDownloadPaths)) {
+    folderDownloadPaths.forEach((item) => {
+      if (typeof item === 'string') {
+        const filename = getFileNameFromUrl(item)
+        if (filename) {
+          promises.push(fetchFileAndAddToZip(item, filename))
+        }
+      } else if (typeof item === 'object' && item !== null) {
+        Object.entries(item).forEach(([folderName, content]) => {
+          processNestedFolder(content, folderName)
+        })
+      }
+    })
+  }
+
+  // 等待所有文件下载完成
   await Promise.all(promises)
+
+  // 生成并保存ZIP文件
   const content = await zip.generateAsync({ type: 'blob' })
   FileSaver.saveAs(content, `${new Date().getTime()}.zip`)
 }
@@ -433,8 +500,26 @@ const downloadFiles = async (singleFileDownloadUrl?: string) => {
   try {
     const info = getSelectedInfo()
     // 文件夹需要单独去请求一下里面包含的所有url,然后合并到fileDownloadPaths中
-    // const folderId = info.filter((item) => item.type === 2).map((item) => item.id) // 筛选出文件夹ID
+    const folderDirs = info
+      .filter((item) => item.type === 2)
+      .map((item) => item.dir + item.file_name + '/') // 筛选出文件夹ID
+
+    const downloadQueue: Promise<ResponseInfo>[] = []
+    folderDirs.forEach((item) => {
+      downloadQueue.push(
+        axiosInstance.post(AllApi.downloadFolder, {
+          dir: item
+        })
+      )
+    })
+    const folderDownloadPaths = await Promise.all(downloadQueue).then((res) => {
+      return res.map((item) => item.data)
+    })
+
     const fileDownloadPaths = info.filter((item) => item.type === 1).map((item) => item.cos_path)
+    if (singleFileDownloadUrl) {
+      fileDownloadPaths.push(singleFileDownloadUrl)
+    }
 
     const totalNums = fileDownloadPaths.length
     // 总的文件下载数量,单个或者传入了单个文件的下载地址的话直接下,否则打包下
@@ -442,17 +527,12 @@ const downloadFiles = async (singleFileDownloadUrl?: string) => {
 
     downloadProgressRef.value?.show()
 
-    if (isSingle) {
+    if (isSingle && folderDownloadPaths.length === 0) {
       const url = singleFileDownloadUrl || fileDownloadPaths[0]
       await downloadSingleFile(url)
       return
     }
-    // 这里防止意外情况
-    if (fileDownloadPaths.length === 0) {
-      ElMessage.warning('请至少选择一个文件进行下载')
-      return
-    }
-    await handleBatchDownload(fileDownloadPaths)
+    await handleBatchDownload(fileDownloadPaths, folderDownloadPaths)
 
     ElNotification({
       title: 'Success',

+ 7 - 5
src/views/IndexView.vue

@@ -147,7 +147,7 @@ const blobUrlInfo = reactive<Record<string, string>>({})
 const basePath = ref<string | undefined>()
 
 // 是否过滤不活跃的游戏
-const isFilterNotActiveGame = ref<boolean>(false)
+const isFilterNotActiveGame = ref<boolean>(true)
 
 // 游戏选择框的加载状态
 const gameSelectLoading = ref<boolean>(false)
@@ -345,9 +345,11 @@ onMounted(() => {
   })
   selectedGame.value = selectInfo.gid
 
-  updateUserInfo({
-    ...userInfo
-  })
+  if (userInfo) {
+    updateUserInfo({
+      ...userInfo
+    })
+  }
 })
 </script>
 
@@ -382,7 +384,7 @@ onMounted(() => {
                 <el-checkbox
                   v-model="isFilterNotActiveGame"
                   @change="updateNavbarGameSelect('', true)"
-                  >是否过滤不活跃游戏
+                  >过滤不活跃游戏
                 </el-checkbox>
               </template>
               <el-option