import axios from 'axios' import { ElMessage } from 'element-plus' /** * 统一请求封装: * - 请求头自动携带 Bearer token * - 响应头 X-Renewed-Token 自动保存(滑动续签) * - 401 清除登录态并跳转登录页 * - 业务 code!==0 弹错并 reject;data 缺省时返回原响应体 */ const TOKEN_KEY = 'ws_token' const USER_KEY = 'ws_user' export function getToken() { return localStorage.getItem(TOKEN_KEY) || '' } export function setToken(token) { localStorage.setItem(TOKEN_KEY, token) } export function getUser() { try { return JSON.parse(localStorage.getItem(USER_KEY)) || {} } catch { return {} } } export function setAuth(token, user) { localStorage.setItem(TOKEN_KEY, token) localStorage.setItem(USER_KEY, JSON.stringify(user || {})) } export function clearAuth() { localStorage.removeItem(TOKEN_KEY) localStorage.removeItem(USER_KEY) } const service = axios.create({ baseURL: '', timeout: 30000 }) service.interceptors.request.use((config) => { const token = getToken() if (token) config.headers.Authorization = 'Bearer ' + token return config }) service.interceptors.response.use( (response) => { // 滑动续签:后端剩余有效期不足一半时下发新 token const renewed = response.headers?.['x-renewed-token'] if (renewed) setToken(renewed) const d = response.data if (d && typeof d === 'object' && 'code' in d) { if (d.code !== 0) { const err = new Error(d.message || '请求失败') showErrThrottled(err.message) return Promise.reject(err) } return d.data !== undefined ? d.data : d } return d }, (error) => { if (error.response && error.response.status === 401) { clearAuth() window.location.hash = '#/login' error.message = '登录已失效,请重新登录' } else { error.message = error.response?.data?.message || (error.code === 'ECONNABORTED' ? '请求超时' : '网络异常:' + (error.message || '未知错误')) } showErrThrottled(error.message) return Promise.reject(error) } ) let lastErrAt = 0 function showErrThrottled(msg) { // 简单节流,避免轮询接口失败刷屏(3 秒内只提示一次) const now = Date.now() if (now - lastErrAt < 3000) return lastErrAt = now ElMessage.error(msg) } export default service