🐛 修复导入错误提示重复弹出并丢失业务文案
- axios 拦截器:blob 请求(导入/导出)在业务校验失败时后端返回的是 JSON(Content-Type: application/json),而 axios 在 responseType='blob' 下把响应体解析成 Blob,直接读 res.data.msg 取不到任何字段, status 退回 res.status、文案落到「系统错误」兜底,用户误以为系统崩溃。 现按 Content-Type 判断:JSON 才解析回对象,真·文件流保持原样不动。 - 统一提示出口 notifiedError():弹窗后给 Error 打 _notified 标记, 业务层据此避免同一条错误被弹两次。 - import-excel:拦截器已提示过的错误不再重复 $message.error; >500/网络异常等仍由 resolveErrorMessage 从 Blob 中取 msg 兜底。
This commit is contained in:
+52
-11
@@ -70,11 +70,44 @@ const redirectToLogin = () => {
|
||||
store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }));
|
||||
};
|
||||
|
||||
/**
|
||||
* 统一弹出错误提示并返回带标记的 Error。
|
||||
* `_notified` 标记用于让业务层(批量导入等)识别「拦截器已经提示过」,
|
||||
* 避免同一条错误被弹两次。
|
||||
*/
|
||||
const notifiedError = message => {
|
||||
ElMessage({
|
||||
message: message,
|
||||
type: 'error',
|
||||
});
|
||||
const error = new Error(message);
|
||||
error._notified = true;
|
||||
return error;
|
||||
};
|
||||
|
||||
axios.defaults.timeout = 10000;
|
||||
//返回其他状态码
|
||||
axios.defaults.validateStatus = function (status) {
|
||||
return status >= 200 && status <= 500; // 默认的
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断响应是否为 JSON。
|
||||
* 用于 blob 请求(导入/导出)的兜底:后端在业务校验失败时返回的是 JSON
|
||||
* (Content-Type: application/json),而不是文件流,需要解析后才能取到 msg。
|
||||
*/
|
||||
const isJsonContentType = res => {
|
||||
const headers = res.headers || {};
|
||||
const contentType = String(headers['content-type'] || res.data?.type || '').toLowerCase();
|
||||
return contentType.includes('application/json') || contentType.includes('text/json');
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断响应体是否已是解析好的对象(无需再解析)。
|
||||
* 注意 Blob 也是 object,必须排除,否则会把待解析的文件流误判为已完成。
|
||||
*/
|
||||
const isPlainObject = value =>
|
||||
value !== null && typeof value === 'object' && !(value instanceof Blob);
|
||||
//跨域请求,允许保存cookie
|
||||
axios.defaults.withCredentials = true;
|
||||
// NProgress Configuration
|
||||
@@ -140,7 +173,7 @@ axios.interceptors.request.use(
|
||||
|
||||
//http response拦截
|
||||
axios.interceptors.response.use(
|
||||
res => {
|
||||
async res => {
|
||||
NProgress.done();
|
||||
//获取配置信息
|
||||
const config = res.config;
|
||||
@@ -149,6 +182,22 @@ axios.interceptors.response.use(
|
||||
if (cryptoData) {
|
||||
res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey));
|
||||
}
|
||||
// blob 请求(文件下载、导入)的响应体是 Blob,直接读 res.data.code / res.data.msg
|
||||
// 拿不到任何字段:状态码会退回 res.status、提示文案会落到「系统错误」兜底,
|
||||
// 用户就会以为系统崩了——而后端其实返回了明确的业务提示(如「未读取到数据行…」)。
|
||||
// 这里按 Content-Type 判断:JSON 才解析回对象;真·文件流(Excel 等)保持原样不动。
|
||||
// 兼容 Blob(浏览器 responseType='blob')与 string(非浏览器环境)两种形态。
|
||||
if (isJsonContentType(res) && !isPlainObject(res.data)) {
|
||||
try {
|
||||
const text = res.data instanceof Blob ? await res.data.text() : res.data;
|
||||
const parsed = typeof text === 'string' && text ? JSON.parse(text) : null;
|
||||
if (parsed && typeof parsed === 'object') {
|
||||
res.data = parsed;
|
||||
}
|
||||
} catch (ignored) {
|
||||
// 声明是 JSON 却解析失败:保持原样,交给下游按文件处理
|
||||
}
|
||||
}
|
||||
//获取状态信息
|
||||
const status = res.data.error_code || res.data.code || res.status;
|
||||
const statusWhiteList = website.statusWhiteList || [];
|
||||
@@ -204,20 +253,12 @@ axios.interceptors.response.use(
|
||||
|
||||
// OAuth2 业务错误码提示
|
||||
if (status > 2000 && !validatenull(res.data.error_description)) {
|
||||
ElMessage({
|
||||
message: message,
|
||||
type: 'error',
|
||||
});
|
||||
return Promise.reject(new Error(message));
|
||||
return Promise.reject(notifiedError(message));
|
||||
}
|
||||
|
||||
// 其余非 200 响应统一提示
|
||||
if (status !== 200) {
|
||||
ElMessage({
|
||||
message: message,
|
||||
type: 'error',
|
||||
});
|
||||
return Promise.reject(new Error(message));
|
||||
return Promise.reject(notifiedError(message));
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
@@ -153,7 +153,11 @@ export const handleImportExcel = async (vm, option, column, businessName, onSucc
|
||||
option.onSuccess(res);
|
||||
}
|
||||
} catch (error) {
|
||||
vm.$message.error(await resolveErrorMessage(error));
|
||||
// 拦截器已提示过的错误(如后端 400 返回的业务提示)不再重复弹一次,
|
||||
// 只保留一次清晰的中文提示。
|
||||
if (!error?._notified) {
|
||||
vm.$message.error(await resolveErrorMessage(error));
|
||||
}
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user