Browse Source

模板文件下载

CzRger 3 days ago
parent
commit
b9822a8c05

+ 8 - 3
web/app/components/datasets/documents/mould/index.tsx

@@ -10,10 +10,9 @@ import FileInput from '@/app/components/base/file-uploader/file-input'
 import { FileContextProvider, useStore } from '@/app/components/base/file-uploader/store'
 import { useFile } from '@/app/components/base/file-uploader/hooks'
 import useSWR from 'swr'
-import { addMouldFile, delMouldFile, fetchMoulds } from '@/service/common'
+import { addMouldFile, delMouldFile, downloadMouldFile, fetchMoulds } from '@/service/common'
 import { useDebounceFn } from 'ahooks'
 import Confirm from '@/app/components/base/confirm'
-import { downloadFile } from '@/app/components/base/file-uploader/utils'
 
 const DetailModel = ({
   datasetId,
@@ -106,6 +105,12 @@ const DetailModel = ({
     }
     catch (e) { }
   }, [delId])
+  const handleDownload = async (file) => {
+    await downloadMouldFile({
+      url: `/datasets/template/${file.id}`,
+      fileName: file.name,
+    })
+  }
   return (
     <>
       <div className={cn(s.wrap)}>
@@ -165,7 +170,7 @@ const DetailModel = ({
                   <div className="ml-2 text-sm">{file.name}</div>
                   <div className="ml-auto cursor-pointer" onClick={(e) => {
                     e.stopPropagation()
-                    downloadFile(`/files/${file.id}/file-preview?timestamp=${new Date().getTime()}` || '', file.name)
+                    handleDownload(file)
                   }}>
                     <img src="/imgs/download.png"/>
                   </div>

+ 6 - 0
web/service/base.ts

@@ -68,6 +68,7 @@ export type IOtherOptions = {
   bodyStringify?: boolean
   needAllResponseContent?: boolean
   deleteContentType?: boolean
+  fileName?: string
   silent?: boolean
   onData?: IOnData // for stream
   onThought?: IOnThought
@@ -582,3 +583,8 @@ export const patch = <T>(url: string, options = {}, otherOptions?: IOtherOptions
 export const patchPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
   return patch<T>(url, options, { ...otherOptions, isPublicAPI: true })
 }
+
+// request methods
+export const download = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
+  return request<T>(url, Object.assign({}, options, { method: 'GET', headers: { 'content-type': ContentType.download } }), otherOptions)
+}

+ 6 - 1
web/service/common.ts

@@ -1,5 +1,5 @@
 import type { Fetcher } from 'swr'
-import { del, get, patch, post, put, upload } from './base'
+import { del, download, get, patch, post, put, upload } from './base'
 import type {
   AccountIntegrate,
   ApiBasedExtension,
@@ -234,6 +234,11 @@ export const delMouldFile = ({ url, body }: any) => {
   return del(url)
 }
 
+export const downloadMouldFile = ({ url, fileName }: any) => {
+  console.log('下载模板文件', url, fileName)
+  return download(url, {}, { fileName })
+}
+
 export const handleExamine = ({ url, body }: any) => {
   // return post<InvitationResponse>(url, { body })
   console.log('处理上下线审核', body)

+ 30 - 2
web/service/fetch.ts

@@ -12,6 +12,7 @@ export const ContentType = {
   audio: 'audio/mpeg',
   form: 'application/x-www-form-urlencoded; charset=UTF-8',
   download: 'application/octet-stream', // for download
+  downloadDocument: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // for download
   downloadZip: 'application/zip', // for download
   upload: 'multipart/form-data', // for upload
 }
@@ -138,6 +139,7 @@ async function base<T>(url: string, options: FetchOptionType = {}, otherOptions:
     needAllResponseContent,
     deleteContentType,
     getAbortController,
+    fileName,
   } = otherOptions
 
   const base
@@ -195,9 +197,35 @@ async function base<T>(url: string, options: FetchOptionType = {}, otherOptions:
   const contentType = res.headers.get('content-type')
   if (
     contentType
-    && [ContentType.download, ContentType.audio, ContentType.downloadZip].includes(contentType)
-  )
+    && [ContentType.download, ContentType.audio, ContentType.downloadZip, ContentType.downloadDocument].includes(contentType)
+  ) {
+    if (fileName) {
+      let filename
+      // 尝试从Content-Disposition获取文件名
+      const contentDisposition = res.headers.get('content-disposition')
+      console.log(contentDisposition)
+      if (contentDisposition) {
+        const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/)
+        if (fileNameMatch && fileNameMatch[1])
+          filename = fileNameMatch[1]
+      }
+      const blob = await res.blob()
+      // 创建下载链接
+      const downloadUrl = window.URL.createObjectURL(blob)
+      const a = document.createElement('a')
+      a.href = downloadUrl
+      a.download = fileName || filename || 'download'
+      document.body.appendChild(a)
+      a.click()
+      // 清理
+      setTimeout(() => {
+        document.body.removeChild(a)
+        window.URL.revokeObjectURL(downloadUrl)
+      }, 100)
+      return blob as T
+    }
     return await res.blob() as T
+  }
 
   return await res.json() as T
 }