import axios from 'axios'; import { ElMessage } from 'element-plus'; import router from '@/router'; /** * 请求基地址。 * * 开发态(vite dev)强制使用相对路径 '',即请求 /api/**, * 由 Vite dev server 代理转发到后端(见 vite.config.ts 的 server.proxy, * 目标取自 VITE_API_TARGET)。这样浏览器侧始终同源,从根本上避免: * - 跨域被拦截; * - .env 里写死的后端 IP(如 192.168.5.224)在当前网络/主机下不可达, * 或用 localhost 打开页面时该 IP 解析不到,浏览器直接抛 "Failed to fetch"。 * * 生产构建下才使用 VITE_API_BASE;若未配置则同样走相对路径, * 由部署侧(Nginx 等)反向代理 /api。 */ const BASE_URL = import.meta.env.DEV ? '' : import.meta.env.VITE_API_BASE || ''; const request = axios.create({ baseURL: BASE_URL, timeout: 15000, }); request.interceptors.request.use((config) => { const token = localStorage.getItem('admin_token'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; }); /** * 统一响应拦截:除 401 外所有异常都给出错误提示。 * 401 表示令牌失效/未登录,清除本地令牌并跳转登录页。 */ request.interceptors.response.use( (resp) => resp.data, (error) => { const status = error.response?.status; if (status === 401) { localStorage.removeItem('admin_token'); ElMessage.error('登录已失效,请重新登录'); if (router.currentRoute.value.name !== 'login') { router.replace({ name: 'login' }); } } else { ElMessage.error(error.response?.data?.message || error.message || '请求失败'); } return Promise.reject(error); }, ); /** * 不携带任何鉴权头的请求实例,专用于登录等公开接口, * 避免把本地可能过期/失效的令牌误带上去造成 401 / 鉴权混乱。 * 公开接口的 401 表示账号或密码错误,直接提示,不跳转(已在登录页)。 */ export const publicRequest = axios.create({ baseURL: BASE_URL, timeout: 15000, }); publicRequest.interceptors.response.use( (resp) => resp.data, (error) => { const status = error.response?.status; if (status === 401) { ElMessage.error('登录失败,请检查账号或密码'); } else { ElMessage.error(error.response?.data?.message || error.message || '请求失败'); } return Promise.reject(error); }, ); export default request;