lis8.0-vue3/src/utils/request.js
2025-10-29 09:41:21 +08:00

304 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
createVNode,
render
} from 'vue';
import axios from 'axios'
import {
ElNotification,
ElMessageBox,
ElMessage,
ElLoading
} from 'element-plus'
import {
getToken
} from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import {
tansParams,
blobValidate
} from '@/utils/ruoyi'
import cache from '@/plugins/cache'
import {
saveAs
} from 'file-saver'
import useUserStore from '@/store/modules/user'
let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = {
show: false
};
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 60000
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小
const limitSize = 5 * 1024 * 1024; // 限制存放数据5M
if (requestSize >= limitSize) {
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
return config;
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
// console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = (() => {
const rawCode = res.data.code;
// 如果是 null/undefined,返回默认值 200
if (rawCode === null || rawCode === undefined) {
return 200;
}
// 如果是字符串,尝试转换为数字
if (typeof rawCode === 'string') {
const numericCode = Number(rawCode);
return isNaN(numericCode) ? 200 : numericCode;
}
// 如果已经是数字,直接返回
if (typeof rawCode === 'number') {
return rawCode;
}
// 其他情况,返回默认值
return 200;
})();
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
isRelogin.show = false;
useUserStore().logOut().then(() => {
location.href = '/index';
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
ElMessage({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} else if (code === 601) {
ElMessage({
message: msg,
type: 'warning'
})
return Promise.reject(new Error(msg))
} else if (code === 3) {
//提示并成功
ElMessage({
message: msg,
type: 'success'
})
return Promise.resolve(res.data)
} else if (code === 2) {
//选择是否后续操作
ElMessageBox.confirm(msg, '系统提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'warning'
}).then(() => {
return Promise.resolve(res.data);
})
return Promise.reject('error');
} else if (code === 1) {
//提示并失败
ElNotification({
title: '提示',
message: msg,
type: 'error',
duration: 0,
})
return Promise.resolve(res.data)
} else if (code === 4) {
//返回特定值,需要后续处理,表示当前操作需要处理后再调用,problemId是步骤关键参数,需要再次调用时引入
return Promise.resolve(res.data)
} else if (code === 5) {
// 1. 提取后端返回的问题ID(根据实际字段名调整)
const problemId = res.data.problemId;
if (!problemId) {
ElMessage.error('缺少问题ID,无法继续操作');
return Promise.reject('缺少问题ID');
}
// 2. 限制递归次数,避免无限循环
const retryCount = (res.config.retryCount || 0) + 1;
const maxRetry = 10; // 最大重试次数
if (retryCount > maxRetry) {
ElMessage.error('超过最大操作次数,请稍后再试');
return Promise.reject('超过最大重试次数');
}
// 3. 询问用户,确认后携带问题ID发起新请求
return ElMessageBox.confirm(msg, '系统提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'warning'
}).then(() => {
const {
url,
method,
data: data0,
params: params0
} = res.data;
// 4. 构造新请求参数,携带问题ID(根据后端要求决定传递方式)
const newParams = {
...params0,
problemId: problemId // 作为URL参数传递
};
const newData = {
...data0,
problemId: problemId // 作为请求体数据传递(根据method调整)
};
// 5. 发起新请求,携带重试次数和问题ID
return service.request({
url,
method,
data: method.toLowerCase() === 'get' ? undefined : newData, // get请求不发data
params: method.toLowerCase() === 'get' ? newParams : params0,
retryCount, // 传递重试次数
problemId // 可选:在config中携带,方便后续跟踪
});
}).catch(() => {
// 用户取消操作时,携带问题ID告知后端(可选)
return Promise.reject({
message: '用户取消操作',
problemId: problemId
});
});
} else if (code !== 200 && code !== 0) {
ElNotification.error({
title: msg
})
return Promise.reject('error')
} else {
return Promise.resolve(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) + "异常";
}
if (message.includes("数据正在处理,请勿重复提交")) {
return
} else {
ElMessage({
message: message,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject(error)
}
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({
text: "正在下载数据,请稍候",
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'
},
responseType: 'blob',
...config
}).then(async (data) => {
const isBlob = blobValidate(data);
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close();
})
}
export default service