zsy-recycling-supervision-a.../src/common/utils/http-util.ts

270 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import {
downloadBaseUrl,
serverBaseUrl,
serverTimeout
} from '@/common'
import axios, { AxiosRequestConfig } from 'axios'
import * as qs from 'qs'
import Toast from '@/components/toast'
import { useAppUserStore } from '@/common/app/app-user-store.ts'
import { throttle } from '@/common/utils/index.ts'
/**
* HTTP 统一响应结构
*/
export interface R<T = any> {
code: number;
msg: string;
message: any;
data: T;
headers?: AxiosRequestConfig['headers']
}
export enum ContentType {
FORM = 'application/x-www-form-urlencoded',
MLT_FORM = 'multipart/form-data',
JSON = 'application/json',
BINARY = 'application/octet-stream',
}
type AxiosConfig = Pick<AxiosRequestConfig, 'headers' | 'params' | 'responseType'>
// type ParamsSerializerType = Extract<AxiosRequestConfig['paramsSerializer'], any>;
/**
* Query 参数处理器
*
* @param params 参数内容
*/
const paramsSerializer = (params: any) => {
return qs.stringify(params, {indices: false, allowDots: true})
}
/**
* 统一错误处理函数
*/
/* function errHandler(r?: R) {
Toast.error(r?.message ?? '操作失败')
} */
const errHandler = throttle(500, (r?: R) => {
Toast.error(r?.message ?? '服务器错误')
})
/**
* axios 实例
*/
const httpUtil = axios.create({
timeout: serverTimeout,
baseURL: serverBaseUrl,
headers: {
Accept: ContentType.JSON,
},
})
/**
* 配置请求拦截器
*/
httpUtil.interceptors.request.use(
config => {
const appUserStore = useAppUserStore()
config.headers.Authorization = appUserStore.token
config.formSerializer = {indexes: null}
return config
},
error => {
// TODO 请求失败日志
console.error('HTTP 请求发送失败', error)
return Promise.reject(error)
},
)
/**
* 配置响应拦截器
*/
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, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers}
}
response.data.headers = response.headers
if (response.data.code === 0) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
error => {
console.error('HTTP 请求失败', error)
if (error.response != null) {
error.response.data = {...error.response.data, headers: error.response.headers}
} else if (error.request != null) {
error.response = {
data: {code: 9999, msg: '网络异常', message: '网络异常', data: null},
}
} else {
error.response = {
data: {code: 5555, msg: '请求发送失败', message: '请求发送失败', data: null},
}
}
return Promise.reject(error.response.data)
},
)
/**
* GET 请求
*
* @param url 请求地址
* @param params Query 参数
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function get<T = any>(url: string, params?: any, disposeErr: boolean = true) {
return httpUtil.get<R<T>>(url, {params, paramsSerializer, responseType: 'json'})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
/**
* POST 请求(通用)
*
* @param url 请求地址
* @param body Body 参数
* @param config Axios 配置
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function post<T>(url: string, body?: any, config?: AxiosConfig, disposeErr: boolean = true) {
return httpUtil.post<R<T>>(url, body, {...config, responseType: 'json'})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
/**
* POST 请求(编码表单)
*
* @param url 请求地址
* @param body Body 参数
* @param config Axios 配置
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function postForm<T>(url: string, body: any, config?: AxiosConfig, disposeErr: boolean = true) {
return httpUtil.postForm<R<T>>(url, paramsSerializer(body),
{
headers: {
...(config?.headers ?? {}),
'Content-Type': ContentType.FORM,
},
params: config?.params,
responseType: config?.responseType ?? 'json',
})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
/**
* POST 请求(多部分表单)
*
* @param url 请求地址
* @param body Body 参数
* @param config Axios 配置
* @param disposeErr 是否处理错误响应,默认-->true
*/
export function postMltForm<T>(url: string, body: any, config?: AxiosConfig, disposeErr: boolean = true) {
return httpUtil.postForm<R<T>>(url, body,
{
headers: {
...(config?.headers ?? {}),
'Content-Type': ContentType.MLT_FORM,
},
params: config?.params,
responseType: config?.responseType ?? 'json',
})
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
function getFileName(contentDisposition: string) {
// 检查content-disposition是否存在
if (!contentDisposition) {
return null
}
// 查找filename=部分
const match = contentDisposition.match(/filename=(.+)/)
if (!match || match.length < 2) {
return null
}
// 提取并解码文件名
const fileNameEncoded = match[1].trim()
// 移除可能存在的引号
const fileNameWithoutQuotes = fileNameEncoded.replace(/["']/g, '')
// 解码URL编码的字符串
return decodeURIComponent(fileNameWithoutQuotes)
}
export function download(url: string, params?: any, disposeErr: boolean = true) {
return httpUtil.get(url, {params, paramsSerializer, responseType: 'arraybuffer'})
.then(res => {
let data = res.data
if (!data || data.byteLength <= 0) {
// 错误提示
return
}
const contentDisposition = res.headers['Content-Disposition'] ?? res.headers['content-disposition']
const fileName = getFileName(contentDisposition) ?? '下载的文件'
// 将二进制流转为blob
const blob = new Blob([ data ])
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(blob)
// 创建a标签用于跳转至下载链接
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', decodeURI(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)
})
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
export function getFileUrl(filename: string) {
const appUserStore = useAppUserStore()
return downloadBaseUrl + filename + '?authorization=' + appUserStore.token
}
export default {
get,
post,
postForm,
postMltForm,
download,
getFileUrl,
}