lanan-system-vue/src/utils/request.js

272 lines
9.0 KiB
JavaScript
Raw Normal View History

2024-07-30 15:07:56 +08:00
import axios from 'axios'
2024-10-23 23:26:01 +08:00
import {Message, MessageBox, Notification,Loading} from 'element-ui'
2024-07-30 15:07:56 +08:00
import store from '@/store'
import {getAccessToken, getRefreshToken, getTenantId, setToken} from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import {getPath, getTenantEnable} from "@/utils/ruoyi";
import {refreshToken} from "@/api/login";
2024-10-23 23:26:01 +08:00
import { saveAs } from 'file-saver'
2024-07-30 15:07:56 +08:00
// 需要忽略的提示。忽略后,自动 Promise.reject('error')
const ignoreMsgs = [
"无效的刷新令牌", // 刷新令牌被删除时,不用提示
"刷新令牌已过期" // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401无法跳转到登出界面
]
// 是否显示重新登录
export let isRelogin = { show: false };
// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
// 请求队列
let requestList = []
// 是否正在刷新中
let isRefreshToken = false
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API + '/admin-api/', // 此处的 /admin-api/ 地址,原因是后端的基础路径为 /admin-api/
// 超时
timeout: 30000,
// 禁用 Cookie 等信息
withCredentials: false,
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getAccessToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// 设置租户
if (getTenantEnable()) {
const tenantId = getTenantId();
if (tenantId) {
config.headers['tenant-id'] = tenantId;
}
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?';
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
const part = encodeURIComponent(propName) + '='
if (value !== null && typeof(value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']';
const subPart = encodeURIComponent(params) + '='
url += subPart + encodeURIComponent(value[key]) + "&";
}
} else {
url += part + encodeURIComponent(value) + "&";
}
}
}
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(async res => {
let { data } = res
// 未设置状态码则默认成功状态
// 二进制数据则直接返回,例如说 Excel 导出
if (
res.request.responseType === 'blob' ||
res.request.responseType === 'arraybuffer'
) {
// 注意:如果导出的响应为 json说明可能失败了不直接返回进行下载
if (res.data.type !== 'application/json') {
return res.data
}
data = await new Response(res.data).json()
}
const code = data.code || 200;
// 获取错误信息
const msg = data.msg || errorCode[code] || errorCode['default']
if (ignoreMsgs.indexOf(msg) !== -1) { // 如果是忽略的错误码,直接返回 msg 异常
return Promise.reject(msg)
} else if (code === 401) {
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true;
// 1. 如果获取不到刷新令牌,则只能执行登出操作
if (!getRefreshToken()) {
return handleAuthorized();
}
// 2. 进行刷新访问令牌
try {
const refreshTokenRes = await refreshToken()
// 2.1 刷新成功,则回放队列的请求 + 当前请求
setToken(refreshTokenRes.data)
requestList.forEach(cb => cb())
return service(res.config)
} catch (e) {// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
requestList.forEach(cb => cb())
// 提示是否要登出。即不回放当前请求!不然会形成递归
return handleAuthorized();
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise(resolve => {
requestList.push(() => {
res.config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(service(res.config))
})
})
}
} else if (code === 500) {
Message({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} else if (code === 501) {
Message({
type: 'error',
duration: 0,
message: msg
})
return Promise.reject(new Error(msg))
} else if (code === 901) {
Message({
type: 'error',
duration: 0,
dangerouslyUseHTMLString: true,
message: '<div>演示模式,无法进行写操作</div>'
+ '<div> &nbsp; </div>'
+ '<div>参考 https://doc.iocoder.cn/ 教程</div>'
+ '<div> &nbsp; </div>'
+ '<div>5 分钟搭建本地环境</div>',
})
return Promise.reject(new Error(msg))
} else if (code !== 200) {
if (msg === '无效的刷新令牌') { // hard coding忽略这个提示直接登出
console.log(msg)
} else {
Notification.error({
title: msg
})
}
return Promise.reject('error')
} else {
return res.data
}
}, error => {
console.log('err' + error)
let {message} = error;
if (message === "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export function getBaseHeader() {
return {
'Authorization': "Bearer " + getAccessToken(),
'tenant-id': getTenantId(),
}
}
function handleAuthorized() {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = getPath('/index');
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
}
2024-08-13 09:08:22 +08:00
let downloadLoadingInstance;
2024-10-23 23:26:01 +08:00
// 通用下载方法
2024-08-13 09:08:22 +08:00
// 通用下载方法
export function download(url, params, filename, config) {
2024-10-23 23:26:01 +08:00
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
2024-08-13 09:08:22 +08:00
responseType: 'blob',
...config
}).then(async (data) => {
2024-10-23 23:26:01 +08:00
const isBlob = blobValidate(data);
2024-08-13 09:08:22 +08:00
if (isBlob) {
2024-10-23 23:26:01 +08:00
const blob = new Blob([data])
saveAs(blob, filename)
2024-08-13 09:08:22 +08:00
} else {
2024-10-23 23:26:01 +08:00
const resText = await data.text();
2024-08-13 09:08:22 +08:00
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
2024-10-23 23:26:01 +08:00
Message.error(errMsg);
2024-08-13 09:08:22 +08:00
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
2024-10-23 23:26:01 +08:00
Message.error('下载文件出现错误,请联系管理员!')
2024-08-13 09:08:22 +08:00
downloadLoadingInstance.close();
})
}
2024-10-23 23:26:01 +08:00
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
}
}
} else {
result += part + encodeURIComponent(value) + "&";
}
}
}
return result
}
2024-07-30 15:07:56 +08:00
2024-10-23 23:26:01 +08:00
// 验证是否为blob格式
export function blobValidate(data) {
return data.type !== 'application/json'
}
2024-07-30 15:07:56 +08:00
export default service