diff --git a/package.json b/package.json index 00d48e0..83d500d 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "element-plus": "^2.13.6", "pinia": "^3.0.4", "vue": "^3.5.32", - "vue-router": "^4.6.4" + "vue-router": "^4.6.4", + "await-to-js": "^3.0.0", + "axios": "^1.13.2" } } diff --git a/src/renderer/http/index.js b/src/renderer/http/index.js index 59c53c8..07af19d 100644 --- a/src/renderer/http/index.js +++ b/src/renderer/http/index.js @@ -1,38 +1,140 @@ -import { getBaseUrl } from './url.js' +import axios from 'axios' import { ElMessage } from 'element-plus' +import { getBaseUrl } from './url.js' -async function request(path, options = {}) { - const { headers = {}, silent = false, ...rest } = options - const url = `${getBaseUrl()}${path}` +// baseURL 由主进程动态分配端口,通过 getBaseUrl() 运行时获取 +const axiosInstance = axios.create({ + baseURL: getBaseUrl(), + timeout: 300000, + headers: { + 'Content-Type': 'application/json;charset=utf-8', + }, + responseType: 'json', +}) - const config = { - headers: { - 'Content-Type': 'application/json', - ...headers, - }, - ...rest, +// 请求拦截 +axiosInstance.interceptors.request.use((config) => { + config.headers = config.headers || {} + let Authorization = localStorage.getItem('Authorization') + // 优先使用本地持久化的 Authorization 头(完整值) + config.headers.Authorization = Authorization || '' + + if ('get' === config?.method?.toLowerCase()) { + if (config.params) { + config.params.timestamp = new Date().getTime() + } } + // 移除敏感信息日志 + // console.log(config, 'axios request.use config') + return config +}) +axiosInstance.interceptors.response.use( + (response) => { + // 移除敏感信息日志 + // console.log(response, 'response response') + // 若请求为二进制下载(blob),直接透传响应,交由调用方自行处理 + try { + const isBlob = response?.config?.responseType === 'blob' + if (isBlob) { + // 仍然尝试持久化可能返回的 Authorization + const respHeaders = response?.headers || {} + const newAuthorization = respHeaders['authorization'] || respHeaders['Authorization'] + if (newAuthorization && typeof newAuthorization === 'string') { + localStorage.setItem('Authorization', newAuthorization) + } + return response + } + } catch (e) { + console.log(e) + // 忽略检查失败 + } + // 如果响应头里带有 Authorization,则使用 useStorage 持久化到 localStorage, + // 以便后续请求自动携带该请求头 + try { + const respHeaders = response?.headers || {} + const newAuthorization = respHeaders['authorization'] || respHeaders['Authorization'] + if (newAuthorization && typeof newAuthorization === 'string') { + localStorage.setItem('Authorization', newAuthorization) + } + } catch (e) { + // 忽略持久化失败,避免影响主流程 + console.warn('持久化 Authorization 失败:', e) + } + if (response.status === 200) { + const res = response.data || {} + const code = res.code + const msg = res.message || res.msg - try { - const res = await fetch(url, config) + // 明确的 200 成功,但需要按业务码再判断 + if (code === 0) { + // 业务成功 + return Promise.resolve(res) + } - if (!res.ok) { - const msg = await res.text().catch(() => '') - const errMsg = `请求失败: ${res.status}${msg ? ' - ' + msg : ''}` - if (!silent) ElMessage.error(errMsg) - return Promise.reject(new Error(errMsg)) + // 特殊业务码处理 + if (code === 401) { + // 清除持久化的 Authorization,避免后续使用失效的头部 + localStorage.removeItem('Authorization') + sessionStorage.removeItem('Token') + // 延迟跳转,确保消息显示 + setTimeout(() => { + window.location.href = '/#/login' + }, 500) + return Promise.reject(new Error('认证失败,请重新登录')) + } + + // 其余非 0 的业务码统一拦截提示,但不在这里显示 ElMessage + // 交由业务层使用 await-to-js 处理 + return Promise.reject(new Error(msg || '请求失败')) } - // 空响应 - const text = await res.text() - if (!text) return Promise.resolve(null) + // 非 2xx 按错误分支处理(通常会进入 error 拦截器) + return Promise.reject(new Error('请求失败')) + }, + (error) => { + console.error('请求错误:', error) - const data = JSON.parse(text) - return Promise.resolve(data) - } catch (err) { - if (!silent) ElMessage.error(err.message || '网络连接失败') - return Promise.reject(err) - } -} + if (error.response) { + // 服务器响应错误 + const status = error.response.status + const message = error.response.data?.message || error.response.data?.msg || '请求失败' -export default request + switch (status) { + case 400: + ElMessage.error(`无效的请求参数:${message}`) + break + case 401: + // 清除持久化的 Authorization,避免后续使用失效的头部 + localStorage.removeItem('Authorization') + ElMessage.error('未授权访问或登录已过期,请重新登录') + break + case 403: + ElMessage.error('访问被拒绝') + break + case 404: + ElMessage.error('资源未找到') + break + case 500: + ElMessage.error('服务器内部错误') + break + case 502: + case 503: + case 504: + ElMessage.error('服务暂时不可用,请稍后重试') + break + default: + ElMessage.error(`请求失败: ${message}`) + } + } else if (error.request) { + // 网络错误 + ElMessage.error('网络连接失败,请检查网络连接') + } else { + // 其他错误 + ElMessage.error('请求发送失败') + } + + return Promise.reject(error) + }, +) + +export default axiosInstance diff --git a/src/renderer/http/manage.js b/src/renderer/http/manage.js index 3a3f17a..63cbd3d 100644 --- a/src/renderer/http/manage.js +++ b/src/renderer/http/manage.js @@ -1,26 +1,17 @@ import request from './index.js' export function getAction(url, params) { - const query = params ? '?' + new URLSearchParams(params).toString() : '' - return request(`${url}${query}`, { method: 'GET' }) + return request({ url, method: 'GET', params }) } export function postAction(url, data, headers = {}) { - return request(url, { - method: 'POST', - body: JSON.stringify(data), - headers, - }) + return request({ url, method: 'POST', data, headers }) } export function putAction(url, data) { - return request(url, { - method: 'PUT', - body: JSON.stringify(data), - }) + return request({ url, method: 'PUT', data }) } export function deleteAction(url, params) { - const query = params ? '?' + new URLSearchParams(params).toString() : '' - return request(`${url}${query}`, { method: 'DELETE' }) + return request({ url, method: 'DELETE', params }) }