2025-04-11 10:31:26 +08:00
|
|
|
|
import axios from 'axios'
|
|
|
|
|
|
import errorCode from './errorCode'
|
|
|
|
|
|
import { Notification, MessageBox, Message } from 'element-ui'
|
|
|
|
|
|
import loadingManager from './loadingManager';
|
2025-04-18 14:45:39 +08:00
|
|
|
|
import errorNotificationManager from './errorNotificationManager';
|
|
|
|
|
|
|
2025-04-11 10:31:26 +08:00
|
|
|
|
// 创建axios实例
|
|
|
|
|
|
const service = axios.create({
|
|
|
|
|
|
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
|
|
|
|
|
baseURL: process.env.VUE_APP_BASE_API,
|
|
|
|
|
|
// 超时
|
|
|
|
|
|
timeout: 10000,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 网络错误相关配置
|
|
|
|
|
|
const NETWORK_ERROR_THROTTLE_TIME = 5000; // 错误提示节流时间
|
|
|
|
|
|
const RETRY_DELAY = 2000; // 重试间隔时间
|
|
|
|
|
|
const MAX_RETRY_TIMES = 3; // 最大重试次数
|
|
|
|
|
|
const RETRY_WINDOW = 60000; // 60秒重试窗口
|
|
|
|
|
|
let lastNetworkErrorTime = 0; // 上次网络错误提示时间
|
|
|
|
|
|
let pendingRequests = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-04-18 14:45:39 +08:00
|
|
|
|
// 网络状态监听器
|
|
|
|
|
|
// 网络状态最后提示时间
|
|
|
|
|
|
let lastNetworkStatusTime = {
|
|
|
|
|
|
online: 0,
|
|
|
|
|
|
offline: 0
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建一个全局标志,确保每次网络恢复只显示一次提示
|
|
|
|
|
|
let networkRecoveryInProgress = false;
|
|
|
|
|
|
|
2025-04-11 10:31:26 +08:00
|
|
|
|
// 网络状态监听器
|
|
|
|
|
|
window.addEventListener('online', () => {
|
|
|
|
|
|
const now = Date.now();
|
2025-04-18 14:45:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 避免短时间内多次触发
|
|
|
|
|
|
if (networkRecoveryInProgress) {
|
|
|
|
|
|
console.log('[网络] 网络恢复处理已在进行中,忽略重复事件');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
networkRecoveryInProgress = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 严格检查是否应该显示提示
|
|
|
|
|
|
if (now - lastNetworkStatusTime.online > 30000) { // 30秒内不重复提示
|
|
|
|
|
|
lastNetworkStatusTime.online = now;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (window.vm && window.vm.$message) {
|
|
|
|
|
|
// 确保消息只显示一次
|
|
|
|
|
|
window.vm.$message({
|
|
|
|
|
|
message: window.vm.$i18n.t('home.networkReconnected') || '网络已重新连接,正在恢复数据...',
|
|
|
|
|
|
type: 'success',
|
|
|
|
|
|
duration: 5000,
|
|
|
|
|
|
showClose: true,
|
|
|
|
|
|
});
|
|
|
|
|
|
console.log('[网络] 显示网络恢复提示, 时间:', new Date().toLocaleTimeString());
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('[网络] 显示网络恢复提示失败:', e);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('[网络] 抑制重复的网络恢复提示, 间隔过短:', now - lastNetworkStatusTime.online + 'ms');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 网络恢复时,重试所有待处理的请求
|
2025-04-11 10:31:26 +08:00
|
|
|
|
const pendingPromises = [];
|
|
|
|
|
|
|
|
|
|
|
|
pendingRequests.forEach(async (request, key) => {
|
|
|
|
|
|
if (now - request.timestamp <= RETRY_WINDOW) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取新的响应数据
|
|
|
|
|
|
const response = await service(request.config);
|
|
|
|
|
|
pendingPromises.push(response);
|
|
|
|
|
|
|
|
|
|
|
|
// 执行请求特定的回调
|
|
|
|
|
|
if (request.callback && typeof request.callback === 'function') {
|
|
|
|
|
|
request.callback(response);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理特定类型的请求
|
|
|
|
|
|
if (window.vm) {
|
|
|
|
|
|
// 处理图表数据请求
|
|
|
|
|
|
if (request.config.url.includes('getPoolPower') && response && response.data) {
|
|
|
|
|
|
// 触发图表更新事件
|
|
|
|
|
|
window.dispatchEvent(new CustomEvent('chart-data-updated', {
|
|
|
|
|
|
detail: { type: 'poolPower', data: response.data }
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (request.config.url.includes('getNetPower') && response && response.data) {
|
|
|
|
|
|
window.dispatchEvent(new CustomEvent('chart-data-updated', {
|
|
|
|
|
|
detail: { type: 'netPower', data: response.data }
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (request.config.url.includes('getBlockInfo') && response && response.rows) {
|
|
|
|
|
|
window.dispatchEvent(new CustomEvent('chart-data-updated', {
|
|
|
|
|
|
detail: { type: 'blockInfo', data: response.rows }
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pendingRequests.delete(key);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('重试请求失败:', error);
|
|
|
|
|
|
pendingRequests.delete(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
pendingRequests.delete(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 等待所有请求完成
|
|
|
|
|
|
Promise.allSettled(pendingPromises).then(() => {
|
|
|
|
|
|
// 重置所有 loading 状态
|
2025-04-18 14:45:39 +08:00
|
|
|
|
if (loadingManager) {
|
|
|
|
|
|
loadingManager.resetAllLoadingStates();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 手动重置一些关键的 loading 状态
|
|
|
|
|
|
if (window.vm) {
|
|
|
|
|
|
// 常见的加载状态
|
|
|
|
|
|
const commonLoadingProps = [
|
|
|
|
|
|
'minerChartLoading', 'reportBlockLoading', 'apiPageLoading',
|
|
|
|
|
|
'MiningLoading', 'miniLoading'
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
commonLoadingProps.forEach(prop => {
|
|
|
|
|
|
if (typeof window.vm[prop] !== 'undefined') {
|
|
|
|
|
|
window.vm[prop] = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-11 10:31:26 +08:00
|
|
|
|
// 触发网络重试完成事件
|
|
|
|
|
|
window.dispatchEvent(new CustomEvent('network-retry-complete'));
|
2025-04-18 14:45:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 重置网络恢复标志
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
networkRecoveryInProgress = false;
|
|
|
|
|
|
}, 5000); // 5秒后允许再次处理网络恢复
|
2025-04-11 10:31:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-04-18 14:45:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 使用错误提示管理器控制网络断开提示
|
2025-04-11 10:31:26 +08:00
|
|
|
|
window.addEventListener('offline', () => {
|
2025-04-18 14:45:39 +08:00
|
|
|
|
if (window.vm && window.vm.$message && errorNotificationManager.canShowError('networkOffline')) {
|
2025-04-11 10:31:26 +08:00
|
|
|
|
window.vm.$message({
|
|
|
|
|
|
message: window.vm.$i18n.t('home.networkOffline') || '网络连接已断开,系统将在恢复连接后自动重试',
|
2025-04-18 14:45:39 +08:00
|
|
|
|
type: 'error',
|
|
|
|
|
|
duration: 5000,
|
|
|
|
|
|
showClose: true,
|
2025-04-11 10:31:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
service.defaults.retry = 2;// 重试次数
|
|
|
|
|
|
service.defaults.retryDelay = 2000;
|
|
|
|
|
|
service.defaults.shouldRetry = (error) => true
|
|
|
|
|
|
|
|
|
|
|
|
localStorage.setItem('superReportError', "")
|
|
|
|
|
|
let superReportError = localStorage.getItem('superReportError')
|
|
|
|
|
|
window.addEventListener("setItem", () => {
|
|
|
|
|
|
superReportError = localStorage.getItem('superReportError')
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// request拦截器
|
|
|
|
|
|
service.interceptors.request.use(config => {
|
|
|
|
|
|
superReportError = ""
|
|
|
|
|
|
// retryCount =0
|
|
|
|
|
|
localStorage.setItem('superReportError', "")
|
|
|
|
|
|
// 是否需要设置 token
|
|
|
|
|
|
let token
|
|
|
|
|
|
try {
|
|
|
|
|
|
token = JSON.parse(localStorage.getItem('token'))
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.log(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (token) {
|
|
|
|
|
|
config.headers['Authorization'] = token
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (config.method == 'get' && config.data) {
|
|
|
|
|
|
config.params = config.data
|
|
|
|
|
|
}
|
|
|
|
|
|
// 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];
|
|
|
|
|
|
var part = encodeURIComponent(propName) + "=";
|
|
|
|
|
|
if (value !== null && typeof (value) !== "undefined") {
|
|
|
|
|
|
if (typeof value === 'object') {
|
|
|
|
|
|
for (const key of Object.keys(value)) {
|
|
|
|
|
|
if (value[key] !== null && typeof (value[key]) !== 'undefined') {
|
|
|
|
|
|
let params = propName + '[' + key + ']';
|
|
|
|
|
|
let 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 => {
|
|
|
|
|
|
Promise.reject(error)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 响应拦截器
|
|
|
|
|
|
service.interceptors.response.use(res => {
|
|
|
|
|
|
// 未设置状态码则默认成功状态
|
|
|
|
|
|
const code = res.data.code || 200;
|
|
|
|
|
|
// 获取错误信息
|
|
|
|
|
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
|
|
|
|
|
if (code === 421) {
|
|
|
|
|
|
localStorage.removeItem('token')
|
|
|
|
|
|
// 系统状态已过期,请重新点击SUPPORT按钮进入
|
|
|
|
|
|
superReportError = localStorage.getItem('superReportError')
|
|
|
|
|
|
if (!superReportError) {
|
|
|
|
|
|
superReportError = 421
|
|
|
|
|
|
localStorage.setItem('superReportError', superReportError)
|
|
|
|
|
|
MessageBox.confirm(window.vm.$i18n.t(`user.loginExpired`), window.vm.$i18n.t(`user.overduePrompt`), {
|
|
|
|
|
|
distinguishCancelAndClose: true,
|
|
|
|
|
|
confirmButtonText: window.vm.$i18n.t(`user.login`),
|
|
|
|
|
|
cancelButtonText: window.vm.$i18n.t(`user.Home`),
|
|
|
|
|
|
// showCancelButton: false, // 隐藏取消按钮
|
|
|
|
|
|
closeOnClickModal: false, // 点击空白处不关闭对话框
|
|
|
|
|
|
showClose: false, // 隐藏关闭按钮
|
|
|
|
|
|
type: 'warning'
|
|
|
|
|
|
}
|
|
|
|
|
|
).then(() => {
|
|
|
|
|
|
window.vm.$router.push("/login")
|
|
|
|
|
|
localStorage.removeItem('token')
|
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
|
window.vm.$router.push("/")
|
|
|
|
|
|
localStorage.removeItem('token')
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.reject('登录状态已过期')
|
|
|
|
|
|
} else if (code >= 500 && !superReportError) {
|
|
|
|
|
|
superReportError = 500
|
|
|
|
|
|
localStorage.setItem('superReportError', superReportError)
|
|
|
|
|
|
Message({
|
|
|
|
|
|
dangerouslyUseHTMLString: true,
|
|
|
|
|
|
message: msg,
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
})
|
|
|
|
|
|
// throw msg; // 抛出错误,中断请求链并触发后续的错误处理逻辑
|
|
|
|
|
|
// return Promise.reject(new Error(msg))
|
|
|
|
|
|
} else if (code !== 200) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Notification.error({
|
|
|
|
|
|
title: msg
|
|
|
|
|
|
})
|
|
|
|
|
|
return Promise.reject('error')
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
return res.data
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
error => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let { message } = error;
|
|
|
|
|
|
|
|
|
|
|
|
if (message == "Network Error" || message.includes("timeout")) {
|
|
|
|
|
|
if (!navigator.onLine) {
|
|
|
|
|
|
// 断网状态,添加到重试队列
|
|
|
|
|
|
const requestKey = JSON.stringify({
|
|
|
|
|
|
url: error.config.url,
|
|
|
|
|
|
method: error.config.method,
|
|
|
|
|
|
params: error.config.params,
|
|
|
|
|
|
data: error.config.data
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 根据URL确定请求类型并记录回调
|
|
|
|
|
|
let callback = null;
|
|
|
|
|
|
if (error.config.url.includes('getPoolPower')) {
|
|
|
|
|
|
callback = (data) => {
|
|
|
|
|
|
if (window.vm) {
|
|
|
|
|
|
// 清除loading状态
|
|
|
|
|
|
window.vm.minerChartLoading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
} else if (error.config.url.includes('getBlockInfo')) {
|
|
|
|
|
|
callback = (data) => {
|
|
|
|
|
|
if (window.vm) {
|
|
|
|
|
|
window.vm.reportBlockLoading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!pendingRequests.has(requestKey)) {
|
|
|
|
|
|
pendingRequests.set(requestKey, {
|
|
|
|
|
|
config: error.config,
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
retryCount: 0,
|
|
|
|
|
|
callback: callback
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('请求已加入断网重连队列:', error.config.url);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if ((error.config.retry > 0 && error.config)) {
|
|
|
|
|
|
// 保留现有的重试逻辑
|
|
|
|
|
|
error.config.retry--;
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
resolve(service(error.config));
|
|
|
|
|
|
}, 2000);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!superReportError) {
|
|
|
|
|
|
superReportError = "error"
|
|
|
|
|
|
localStorage.setItem('superReportError', superReportError)
|
2025-04-18 14:45:39 +08:00
|
|
|
|
//使用错误提示管理器errorNotificationManager
|
|
|
|
|
|
if (errorNotificationManager.canShowError(message)) {
|
|
|
|
|
|
if (message == "Network Error") {
|
2025-04-11 10:31:26 +08:00
|
|
|
|
Message({
|
|
|
|
|
|
message: window.vm.$i18n.t(`home.NetworkError`),
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
duration: 4 * 1000,
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-04-18 14:45:39 +08:00
|
|
|
|
else if (message.includes("timeout")) {
|
|
|
|
|
|
Message({
|
|
|
|
|
|
message: window.vm.$i18n.t(`home.requestTimeout`),
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
duration: 5 * 1000,
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (message.includes("Request failed with status code")) {
|
|
|
|
|
|
Message({
|
|
|
|
|
|
message: "系统接口" + message.substr(message.length - 3) + "异常",
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
duration: 5 * 1000,
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Message({
|
|
|
|
|
|
message: message,
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
duration: 5 * 1000,
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 避免完全不提示,可以在控制台记录被抑制的错误
|
|
|
|
|
|
console.log('[错误提示] 已抑制重复错误:', message);
|
2025-04-11 10:31:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-04-18 14:45:39 +08:00
|
|
|
|
// let { message } = error;
|
|
|
|
|
|
// if (message == "Network Error") {
|
|
|
|
|
|
// // message = "后端接口网络连接异常,请刷新重试";
|
|
|
|
|
|
// const now = Date.now();
|
|
|
|
|
|
// if (now - lastNetworkErrorTime > NETWORK_ERROR_THROTTLE_TIME) {
|
|
|
|
|
|
// lastNetworkErrorTime = now; // 更新最后提示时间
|
|
|
|
|
|
// Message({
|
|
|
|
|
|
// message: window.vm.$i18n.t(`home.NetworkError`),
|
|
|
|
|
|
// type: 'error',
|
|
|
|
|
|
// duration: 4 * 1000,
|
|
|
|
|
|
// showClose: true
|
|
|
|
|
|
// });
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else if (message.includes("timeout")) {
|
|
|
|
|
|
// // message = "系统接口请求超时,请刷新重试";
|
|
|
|
|
|
// Message({
|
|
|
|
|
|
// message: window.vm.$i18n.t(`home.requestTimeout`),
|
|
|
|
|
|
// type: 'error',
|
|
|
|
|
|
// duration: 5 * 1000,
|
|
|
|
|
|
// showClose: true
|
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else if (message.includes("Request failed with status code")) {
|
|
|
|
|
|
// // message = "系统接口" + message.substr(message.length - 3) + "异常";
|
|
|
|
|
|
// Message({
|
|
|
|
|
|
// message: "系统接口" + message.substr(message.length - 3) + "异常",
|
|
|
|
|
|
// type: 'error',
|
|
|
|
|
|
// duration: 5 * 1000,
|
|
|
|
|
|
// showClose: true
|
|
|
|
|
|
// })
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
|
|
|
|
// Message({
|
|
|
|
|
|
// message: message,
|
|
|
|
|
|
// type: 'error',
|
|
|
|
|
|
// duration: 5 * 1000,
|
|
|
|
|
|
// showClose: true
|
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-04-11 10:31:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.reject(error)
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default service
|