import { ApiStatus } from './status' import { $t } from '@/locales' class HttpError extends Error { constructor(message, code, options) { super(message) this.name = 'HttpError' this.code = code this.data = options?.data this.timestamp = /* @__PURE__ */ new Date().toISOString() this.url = options?.url this.method = options?.method this.status = options?.status this.contentType = options?.contentType } toLogData() { return { code: this.code, message: this.message, data: this.data, timestamp: this.timestamp, url: this.url, method: this.method, status: this.status, contentType: this.contentType, stack: this.stack } } } function resolveRequestUrl(config) { const baseURL = String(config?.baseURL || '').trim() const requestUrl = String(config?.url || '').trim() if (!baseURL && !requestUrl) { return void 0 } if (!baseURL) { return requestUrl || void 0 } if (!requestUrl) { return baseURL } const isAbsoluteBase = /^https?:\/\//i.test(baseURL) if (!isAbsoluteBase) { const normalizedBase = baseURL.replace(/\/+$/, '') const normalizedUrl = requestUrl.replace(/^\/+/, '') if (!normalizedBase) { return requestUrl } if (!normalizedUrl) { return normalizedBase } return `${normalizedBase}/${normalizedUrl}` } try { const origin = globalThis?.location?.origin || 'http://localhost' const normalizedBase = new URL(baseURL, origin) return new URL(requestUrl, normalizedBase).toString() } catch { const normalizedBase = baseURL.replace(/\/+$/, '') const normalizedUrl = requestUrl.replace(/^\/+/, '') if (!normalizedBase) { return requestUrl } if (!normalizedUrl) { return normalizedBase } return `${normalizedBase}/${normalizedUrl}` } } const getErrorMessage = (status) => { const errorMap = { [ApiStatus.unauthorized]: 'httpMsg.unauthorized', [ApiStatus.forbidden]: 'httpMsg.forbidden', [ApiStatus.notFound]: 'httpMsg.notFound', [ApiStatus.methodNotAllowed]: 'httpMsg.methodNotAllowed', [ApiStatus.requestTimeout]: 'httpMsg.requestTimeout', [ApiStatus.internalServerError]: 'httpMsg.internalServerError', [ApiStatus.badGateway]: 'httpMsg.badGateway', [ApiStatus.serviceUnavailable]: 'httpMsg.serviceUnavailable', [ApiStatus.gatewayTimeout]: 'httpMsg.gatewayTimeout' } return $t(errorMap[status] || 'httpMsg.internalServerError') } function isRequestCancelled(error) { const message = String(error?.message || '').toLowerCase() return ( error?.code === 'ERR_CANCELED' || error?.name === 'CanceledError' || error?.name === 'AbortError' || message.includes('canceled') || message.includes('cancelled') || message.includes('aborted') || message.includes('请求已取消') ) } function handleError(error) { const requestConfig = error.config if (isRequestCancelled(error)) { throw new HttpError($t('httpMsg.requestCancelled'), 'REQUEST_CANCELLED', { url: resolveRequestUrl(requestConfig), method: requestConfig?.method?.toUpperCase() }) } const statusCode = error.response?.status const errorMessage = error.response?.data?.msg || error.message if (!error.response) { throw new HttpError($t('httpMsg.networkError'), ApiStatus.error, { url: resolveRequestUrl(requestConfig), method: requestConfig?.method?.toUpperCase() }) } const message = statusCode ? getErrorMessage(statusCode) : errorMessage || $t('httpMsg.requestFailed') throw new HttpError(message, statusCode || ApiStatus.error, { data: error.response.data, url: resolveRequestUrl(requestConfig), method: requestConfig?.method?.toUpperCase(), status: statusCode, contentType: error.response?.headers?.['content-type'] }) } function showError(error, showMessage = true) { if (error.code === 'REQUEST_CANCELLED') { return } if (showMessage) { ElMessage.error(error.message) } console.error('[HTTP Error]', error.toLogData()) } function showSuccess(message, showMessage = true) { if (showMessage) { ElMessage.success(message) } } const isHttpError = (error) => { return error instanceof HttpError } export { HttpError, handleError, isHttpError, resolveRequestUrl, showError, showSuccess }