diff --git a/src/common/app/app-api.ts b/src/common/app/app-api.ts index eaa4f1b..7a4b013 100644 --- a/src/common/app/app-api.ts +++ b/src/common/app/app-api.ts @@ -1,9 +1,10 @@ import { - download, get, getFileUrl, } from '@/common/utils/http-util.ts' +import { saveFile } from '@/common/app' + interface PresignedUrl extends Record { url?: string bucketName?: string @@ -37,7 +38,7 @@ export default { obtainPresignedUrl(filename: string) { return get('/oss/obtain_presigned_url', {filename}) }, - download(filename: string, params?: any, defaultName: string = '下载的文件') { + ossDownload(filename: string, params?: any) { let url = '' if (filename.startsWith('http') || filename.startsWith('https')) { url = filename @@ -47,26 +48,7 @@ export default { } else { url = '/oss/download/' + filename } - return download(url, params, defaultName) - .then(res => { - // 创建新的URL并指向File对象或者Blob对象的地址 - const blobURL = window.URL.createObjectURL(res.data.data) - // 创建a标签,用于跳转至下载链接 - const tempLink = document.createElement('a') - tempLink.style.display = 'none' - tempLink.href = blobURL - tempLink.setAttribute('download', decodeURI(res.data.filename)) - // 兼容:某些浏览器不支持HTML5的download属性 - if (typeof tempLink.download === 'undefined') { - tempLink.setAttribute('target', '_blank') - } - // 挂载a标签 - document.body.appendChild(tempLink) - tempLink.click() - document.body.removeChild(tempLink) - // 释放blob URL地址 - window.URL.revokeObjectURL(blobURL) - }) + saveFile(get<{ content: Blob, filename: string }>(url, params, {responseType: 'arraybuffer'})) }, fileUrl(filename?: string) { if (filename == null || filename.length <= 0) { diff --git a/src/common/app/index.ts b/src/common/app/index.ts index 38faf72..bbbf88b 100644 --- a/src/common/app/index.ts +++ b/src/common/app/index.ts @@ -3,6 +3,7 @@ import Colls from '@/common/utils/colls.ts' import { useAppSettingStore } from '@/common/app/app-setting-store.ts' import { useAppUserStore } from '@/common/app/app-user-store.ts' import Utils from '@/common/utils' +import type { R } from '@/common/utils/http-util.ts' export const reloadUserInfo = () => { const appSettingStore = useAppSettingStore() @@ -64,3 +65,27 @@ export function isAdmin() { const appUserStore = useAppUserStore() return appUserStore.roles != null && appUserStore.roles.includes('ROLE_ADMIN') } + +export function saveFile(r: Promise>, defaultName: string = '下载的文件') { + return r.then(res => { + const blobURL = window.URL.createObjectURL(res.data.content) + const tempLink = document.createElement('a') + try { + tempLink.style.display = 'none' + tempLink.href = blobURL + tempLink.setAttribute('download', decodeURI(res.data.filename ?? defaultName)) + if (typeof tempLink.download === 'undefined') { + tempLink.setAttribute('target', '_blank') + } + document.body.appendChild(tempLink) + tempLink.click() + return Promise.resolve() + } catch (e) { + console.log('下载失败', e) + return Promise.reject() + } finally { + if (tempLink != null) document.body.removeChild(tempLink) + if (blobURL != null) window.URL.revokeObjectURL(blobURL) + } + }) +} diff --git a/src/common/utils/http-util.ts b/src/common/utils/http-util.ts index a5db4c1..1851bd2 100644 --- a/src/common/utils/http-util.ts +++ b/src/common/utils/http-util.ts @@ -22,9 +22,7 @@ export interface R { headers?: AxiosRequestConfig['headers'] } -type AxiosConfig = Pick -// const closeUrls = closeUrl.split(',') -// type ParamsSerializerType = Extract; +type AxiosConfig = Pick /** * Query 参数处理器 @@ -38,13 +36,11 @@ const paramsSerializer = (params: any) => { /** * 统一错误处理函数 */ -/* function errHandler(r?: R) { - Toast.error(r?.message ?? '操作失败') - } */ const errHandler = Utils.throttle(500, (r?: AxiosResponse, any>) => { console.log('异常处理', r) - ElMessage.error(r?.data?.message ?? '服务器错误') + ElMessage.error(r?.data?.message ?? (r?.data?.msg ?? '服务器错误')) }) + /** * axios 实例 */ @@ -78,20 +74,23 @@ httpUtil.interceptors.request.use( */ httpUtil.interceptors.response.use( response => { - // console.log('HTTP 请求结果', response.config.url, response) // vite 代理失败时 响应码为 200 响应内容为空 - if (response.config.responseType !== 'json') { - return Promise.resolve(response) - } if (response.data == null) { - response.data = {code: 0, success: true, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers} + response.data = {code: 5555, success: false, msg: '无响应内容', message: '无响应内容', data: null} + } else if (response.config.responseType === 'arraybuffer') { + const data = response.data as ArrayBufferView + if (data.byteLength <= 0) { + response.data = {code: 9999, success: false, msg: '文件获取失败', message: '文件获取失败', data: null} + } else { + const contentDisposition = response.headers['Content-Disposition'] ?? response.headers['content-disposition'] + const filename = getFileName(contentDisposition) + // 将二进制流转为blob + const blob = new Blob([ data ]) + response.data = {code: 0, success: true, msg: '成功', message: '文件获取成功', data: {content: blob, filename}} + } } response.data.headers = response.headers - if (response.data.code === 0) { - return Promise.resolve(response) - } else { - return Promise.reject(response) - } + return response.data.code === 0 ? Promise.resolve(response) : Promise.reject(response) }, error => { console.error('HTTP 请求失败', error) @@ -114,17 +113,21 @@ httpUtil.interceptors.response.use( ) /** - * GET 请求(JSON) + * GET 请求 * * @param url 请求地址 * @param params Query 参数 + * @param config 配置 * @param disposeErr 是否处理错误响应,默认-->true */ -export function get(url: string, params?: any, disposeErr: boolean = true) { - /* if (closeUrls.includes(url)) { - return Promise.reject({code: 0, success: true, msg: '', message: '', data: null} as R) - } */ - return httpUtil.get>(url, {params, paramsSerializer, responseType: 'json'}) +export function get(url: string, params?: any, config: AxiosConfig = {}, disposeErr: boolean = true) { + if (config.responseType == null) { + config.responseType = 'json' + } + config.params = params + config.paramsSerializer = paramsSerializer + + return httpUtil.get>(url, config) .then(({data}) => data) .catch(res => { if (disposeErr) errHandler(res) @@ -133,18 +136,21 @@ export function get(url: string, params?: any, disposeErr: boolean = tr } /** - * POST 请求(JSON) + * POST 请求 * * @param url 请求地址 * @param body Body 参数 * @param params 参数 + * @param config 配置 * @param disposeErr 是否处理错误响应,默认-->true */ -export function post(url: string, body?: any, params?: any, disposeErr: boolean = true) { - /* if (closeUrls.includes(url)) { - return Promise.reject({code: 0, success: true, msg: '', message: '', data: null} as R) - } */ - return httpUtil.post>(url, body, {responseType: 'json', params}) +export function post(url: string, body?: any, params?: any, config: AxiosConfig = {}, disposeErr: boolean = true) { + if (config.responseType == null) { + config.responseType = 'json' + } + config.params = params + config.paramsSerializer = paramsSerializer + return httpUtil.post>(url, body, config) .then(({data}) => data) .catch(res => { if (disposeErr) errHandler(res) @@ -222,26 +228,6 @@ function getFileName(contentDisposition: string) { return decodeURIComponent(fileNameWithoutQuotes) } -export function download(url: string, data?: any, params?: any, defaultName: string = '下载的文件', disposeErr: boolean = true) { - return httpUtil.post(url, data, {params, paramsSerializer, responseType: 'arraybuffer'}) - .then(res => { - const data = res.data - if (!data || data.byteLength <= 0) { - // 错误提示 - return Promise.reject({code: 9999, success: false, msg: '文件获取失败', message: '文件获取失败', data: null, headers: res.headers}) - } - const contentDisposition = res.headers['Content-Disposition'] ?? res.headers['content-disposition'] - const filename = getFileName(contentDisposition) ?? defaultName - // 将二进制流转为blob - const blob = new Blob([ data ]) - return Promise.resolve({code: 0, success: true, msg: '成功', message: '文件获取成功', data: {data: blob, filename}, headers: res.headers}) - }) - .catch(res => { - if (disposeErr) errHandler(res) - return Promise.reject(res) - }) -} - export function getFileUrl(path?: string) { if (path == null || path.length <= 0) { return '' @@ -255,7 +241,6 @@ export default { post, postForm, postMltForm, - download, getFileUrl, ins: httpUtil, } diff --git a/src/pages/sys/gen/tpl/tpl-api.ts b/src/pages/sys/gen/tpl/tpl-api.ts index a152ea1..7e1db5b 100644 --- a/src/pages/sys/gen/tpl/tpl-api.ts +++ b/src/pages/sys/gen/tpl/tpl-api.ts @@ -1,8 +1,8 @@ import { - download, get, post, } from '@/common/utils/http-util.ts' +import { saveFile } from '@/common/app' export default { paging(data: TplTypes.SearchTplParam) { @@ -23,30 +23,10 @@ export default { listAll(lang: string) { return get('/tpl/list_all', {lang}) }, - preview(lang: string, tableName: string, data: Record) { return post('/tpl/preview', data, {lang, tableName}) }, download(lang: string, tableName: string, data: Record) { - return download('/tpl/download', data, {lang, tableName}) - .then(res => { - // 创建新的URL并指向File对象或者Blob对象的地址 - const blobURL = window.URL.createObjectURL(res.data.data) - // 创建a标签,用于跳转至下载链接 - const tempLink = document.createElement('a') - tempLink.style.display = 'none' - tempLink.href = blobURL - tempLink.setAttribute('download', decodeURI(res.data.filename)) - // 兼容:某些浏览器不支持HTML5的download属性 - if (typeof tempLink.download === 'undefined') { - tempLink.setAttribute('target', '_blank') - } - // 挂载a标签 - document.body.appendChild(tempLink) - tempLink.click() - document.body.removeChild(tempLink) - // 释放blob URL地址 - window.URL.revokeObjectURL(blobURL) - }) + return saveFile(post('/tpl/download', data, {lang, tableName}, {responseType: 'arraybuffer'})) }, }