新增错误消息防抖机制,优化错误提示显示并清理冗余代码

This commit is contained in:
WUSIJIAN
2026-01-05 10:05:25 +08:00
parent c5c1c32193
commit 3ee3c90462
14 changed files with 49 additions and 38 deletions

View File

@@ -3,7 +3,6 @@ ENV = 'development'
# 本地环境接口地址
VITE_API_URL = 'http://192.168.3.200:8808/'
# VITE_API_URL = 'http://localhost:8808/'
# VITE_API_URL = 'http://192.168.3.11:8808/'

View File

@@ -6,6 +6,22 @@ import qs from 'qs';
// 标记是否正在处理 token 过期,避免重复弹窗
let isHandlingTokenExpired = false;
// 错误消息防抖:防止短时间内显示多个错误消息
let lastErrorTime = 0;
const ERROR_MESSAGE_INTERVAL = 2000; // 2秒内只显示一个错误
const showErrorMessage = (message: string) => {
const now = Date.now();
// 2秒内只显示一个错误消息不管内容是否相同
if (now - lastErrorTime < ERROR_MESSAGE_INTERVAL) {
return; // 跳过
}
lastErrorTime = now;
ElMessage.error(message);
};
// 配置新建第一个 axios 实例(原来的主服务)
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
@@ -20,9 +36,6 @@ const service: AxiosInstance = axios.create({
// 配置新建第二个 axios 实例(新功能服务)
const newService: AxiosInstance = axios.create({
// baseURL: 'http://192.168.3.95:8000/',
// baseURL: 'http://192.168.3.49:8000/',
// baseURL: 'http://localhost:8000/',
baseURL: 'http://192.168.3.200:8000/',
// baseURL: 'http://192.168.3.11:8000/',
timeout: 50000,
@@ -118,7 +131,7 @@ const responseInterceptor = (response: AxiosResponse) => {
// 业务逻辑错误处理
if (code !== undefined && code !== 0 && code !== 200) {
const errorMsg = message || `请求失败(${code})`;
ElMessage.error(errorMsg);
showErrorMessage(errorMsg);
return Promise.reject(new Error(errorMsg));
}
@@ -130,7 +143,7 @@ const responseErrorHandler = (error: any) => {
console.error('API请求错误:', error);
if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
ElMessage.error('请求超时,请检查网络连接');
showErrorMessage('请求超时,请检查网络连接');
return Promise.reject(new Error('请求超时'));
}
@@ -144,7 +157,8 @@ const responseErrorHandler = (error: any) => {
}
const httpStatus = error.response.status;
const message = error.response.data?.message || error.response.statusText;
// 优先使用返回数据中的 message 字段
const responseMessage = error.response.data?.message;
// 处理 HTTP 错误状态
switch (httpStatus) {
@@ -152,23 +166,23 @@ const responseErrorHandler = (error: any) => {
handleTokenExpired();
break;
case 403:
ElMessage.error('没有权限访问该资源');
showErrorMessage(responseMessage || '没有权限访问该资源');
break;
case 404:
ElMessage.error('请求的资源不存在');
showErrorMessage(responseMessage || '请求的资源不存在');
break;
case 500:
ElMessage.error('服务器内部错误');
showErrorMessage(responseMessage || '服务器内部错误');
break;
case 502:
ElMessage.error('网关错误');
showErrorMessage(responseMessage || '网关错误');
break;
case 503:
ElMessage.error('服务不可用');
showErrorMessage(responseMessage || '服务不可用');
break;
default:
if (httpStatus >= 400) {
ElMessage.error(message || `请求失败(${httpStatus})`);
showErrorMessage(responseMessage || `请求失败(${httpStatus})`);
}
}
@@ -184,4 +198,4 @@ newService.interceptors.response.use(responseInterceptor, responseErrorHandler);
// 导出
export default service;
export { newService };
export { newService, showErrorMessage };

View File

@@ -212,7 +212,6 @@ const fetchDictTypeOptions = () => {
dictLoading.value = true;
getDicts('assets')
.then((res: any) => {
console.log('字典接口返回数据:', res);
const list = res.data?.list ?? [];
// 提取所有字典类型信息
dictTypeOptions.value = list
@@ -220,11 +219,10 @@ const fetchDictTypeOptions = () => {
.filter((info: DictInfo) => info && info.name);
// 保存完整的字典数据列表包含info和values
dictValueOptions.value = list;
console.log('字典类型选项:', dictTypeOptions.value);
console.log('字典值选项:', dictValueOptions.value);
})
.catch((err: any) => {
console.error('获取字典数据失败:', err);
dictTypeOptions.value = [];
dictValueOptions.value = [];
})
@@ -415,8 +413,6 @@ const onSubmit = () => {
updateCategory(submitData)
.then(() => {
ElMessage.success('修改成功');
console.log(submitData,'111');
closeDialog();
emit('getCategoryList');
})

View File

@@ -175,7 +175,7 @@ const onSubmit = async () => {
emit('refresh');
} catch (error) {
console.error('操作失败:', error);
ElMessage.error(state.formData.id ? '修改失败' : '添加失败');
// 错误已由请求拦截器统一处理
} finally {
state.loading = false;
}

View File

@@ -144,10 +144,11 @@ const onSubmit = async () => {
closeDialog();
emit('refresh');
} else {
ElMessage.error(res.message || '配置失败');
// 错误已由请求拦截器统一处理
}
} catch (error: any) {
ElMessage.error(error.message || '配置失败');
console.error('配置失败:', error);
// 错误已由请求拦截器统一处理
} finally {
state.loading = false;
}

View File

@@ -179,7 +179,7 @@ const getList = async () => {
}
} catch (error) {
console.error('获取客服账号列表失败:', error);
ElMessage.error('获取数据失败');
// 错误已由请求拦截器统一处理
tableData.data = [];
tableData.total = 0;
} finally {
@@ -273,7 +273,7 @@ const handleStatusChange = async (row: TableDataItem) => {
return;
}
console.error('状态切换失败:', error);
ElMessage.error('状态切换失败');
// 错误已由请求拦截器统一处理
} finally {
setTimeout(() => {
row.statusLoading = false;

View File

@@ -115,7 +115,7 @@ const openDialog = async (row?: ProductFormData) => {
state.isShowDialog = true;
} catch (error) {
console.error('打开对话框失败:', error);
ElMessage.error('初始化数据失败');
// 错误已由请求拦截器统一处理
}
};
@@ -155,7 +155,7 @@ const onCancel = () => {
*/
const onSubmit = async () => {
if (!formRef.value) {
ElMessage.error('表单引用不存在');
console.error('表单引用不存在');
return;
}
@@ -189,7 +189,7 @@ const onSubmit = async () => {
emit('refresh');
} catch (error) {
console.error('提交失败:', error);
ElMessage.error(state.formData.id === 0 ? '添加失败' : '修改失败');
// 错误已由请求拦截器统一处理
} finally {
state.loading = false;
}

View File

@@ -74,7 +74,7 @@ const handleExport = async () => {
isShowDialog.value = false;
} catch (error: any) {
console.error('导出失败:', error);
ElMessage.error(`导出失败:${error.message || '未知错误'}`);
// 错误已由请求拦截器统一处理
} finally {
loading.value = false;
}

View File

@@ -288,7 +288,7 @@ const handleCustomUpload = async (file: File) => {
success: false,
message: error.message || '导入失败',
};
ElMessage.error(`导入失败:${error.message || '未知错误'}`);
// 错误已由请求拦截器统一处理
}
};
@@ -324,7 +324,8 @@ const handleDownloadTemplate = async () => {
saveAs(zipBlob, filename);
ElMessage.success(`模板下载成功!`);
} catch (error: any) {
ElMessage.error(`模板下载失败${error.message || '未知错误'}`);
console.error('模板下载失败:', error);
// 错误已由请求拦截器统一处理
} finally {
downloadLoading.value = false;
}
@@ -343,7 +344,7 @@ const handleUploadSuccess = (response: any, file: UploadFile, fileList: UploadFi
*/
const handleUploadError = (error: Error, file: UploadFile, fileList: UploadFiles) => {
console.error('上传失败回调', error);
ElMessage.error('文件上传失败');
// 错误已由请求拦截器统一处理
// 手动上传模式下不会执行到这里
};

View File

@@ -173,7 +173,7 @@ const getProductList = async () => {
}
} catch (error) {
console.error('获取产品列表失败:', error);
ElMessage.error('获取产品列表失败');
// 错误已由请求拦截器统一处理,此处不再重复提示
} finally {
tableData.loading = false;
}

View File

@@ -74,7 +74,7 @@ const handleExport = async () => {
isShowDialog.value = false;
} catch (error: any) {
console.error('导出失败:', error);
ElMessage.error(`导出失败:${error.message || '未知错误'}`);
// 错误已由请求拦截器统一处理
} finally {
loading.value = false;
}

View File

@@ -201,7 +201,7 @@ const dataList = async () => {
tableData.total = res.data.total;
} catch (error) {
console.error('获取数据失败:', error);
ElMessage.error('获取数据失败');
// 错误已由请求拦截器统一处理
} finally {
tableData.loading = false;
}

View File

@@ -162,7 +162,7 @@ const onSubmit = async () => {
emit('refresh');
} catch (error) {
console.error('提交失败:', error);
ElMessage.error('操作失败');
// 错误已由请求拦截器统一处理
} finally {
state.loading = false;
}

View File

@@ -197,7 +197,7 @@ const loadTableData = async () => {
tableData.total = total;
} catch (error) {
console.error('加载数据失败:', error);
ElMessage.error('数据加载失败');
// 错误已由请求拦截器统一处理
tableData.data = [];
tableData.total = 0;
} finally {
@@ -238,7 +238,7 @@ const handleDelete = async (row: ScriptItem) => {
} catch (error) {
if (error !== 'cancel') {
console.error('删除失败:', error);
ElMessage.error('删除失败');
// 错误已由请求拦截器统一处理
}
}
};