PC版本
封装
import axios from 'axios'
import type { InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'interface ResponseData<T = any> {code: numbermsg?: stringmessage?: stringdata?: T[key: string]: any
}interface TokenResponse {code: numberdata: {access_token: stringrefresh_token: stringtoken_type: string}msg?: string
}
let isRefreshing = false
let requests: Array<(token: string) => void> = []const service = axios.create({baseURL: import.meta.env.VITE_APP_BASE_API as string,timeout: 3 * 60 * 1000,headers: {'Content-Type': 'application/json',},
})const refreshToken = async (): Promise<string | null> => {const refreshTokenValue = localStorage.getItem('refresh_token')if (!refreshTokenValue) {return null}try {const response = await post('/api_mani/auth/refresh', {refresh_token: refreshTokenValue,})const { access_token, refresh_token } = response.datalocalStorage.setItem('access_token', access_token)localStorage.setItem('refresh_token', refresh_token)return access_token} catch (error) {console.error('刷新令牌出错:', error)return null}
}
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {const token = localStorage.getItem('access_token')if (token) {config.headers = config.headers || {}config.headers.Authorization = `Bearer ${token}`}return config},(error: AxiosError) => {console.log('request error: ', error)return Promise.reject(error)},
)
service.interceptors.response.use((res: AxiosResponse<ResponseData>) => {if (res.data.code === 401) {ElMessage.error(res.data.msg || res.data.message || '登录已过期,请重新登录')localStorage.removeItem('access_token')localStorage.removeItem('refresh_token')router.replace('/login')return Promise.reject(res.data) as any}if (res.data.code !== 200 && res.data.code !== 201) {ElMessage.error(res.data.msg || res.data.message || '请求失败')return Promise.reject(res.data) as any}return Promise.resolve(res.data)},async (error: AxiosError<ResponseData>) => {const originalRequest = error.configif (error.response?.status === 401 && originalRequest) {if (!originalRequest.url?.includes('/auth/refresh')) {if (!isRefreshing) {isRefreshing = trueconst newToken = await refreshToken()isRefreshing = falseif (newToken) {requests.forEach((cb) => cb(newToken))requests = []if (originalRequest.headers) {originalRequest.headers.Authorization = `Bearer ${newToken}`}return service(originalRequest)} else {ElMessage.error('登录已过期,请重新登录')localStorage.removeItem('access_token')localStorage.removeItem('refresh_token')router.replace('/login')}} else {return new Promise((resolve) => {requests.push((token: string) => {if (originalRequest.headers) {originalRequest.headers.Authorization = `Bearer ${token}`}resolve(service(originalRequest))})})}}}console.log('response error: ', error)if (error.response?.data?.message) {ElMessage.error(error.response.data.message)} else if (error.message && error.message !== 'canceled') {ElMessage.error(error.message)}return Promise.reject(error)},
)
export function get<T = any>(url: string,params: Record<string, any> = {},signal?: AbortSignal,
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'get',params,signal: signal,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}
export function post<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },signal?: AbortSignal,
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'post',data: params,headers,signal: signal,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export function patch<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'patch',data: params,headers,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export function put<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'put',data: params,headers,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export function DELETE<T = any>(url: string,params: Record<string, any> = {},headers: Record<string, string> = { 'Content-Type': 'application/json' },
): Promise<T> {return new Promise((resolve, reject) => {service({url,method: 'DELETE',data: params,headers,}).then((res: any) => {resolve(res as unknown as T)}).catch((err: any) => {reject(err)})})
}export default {get,post,patch,put,DELETE,
}
使用:
import { get, post } from '../service'
import type { API } from 'types/api'const api: API = {login: (params) => post(`/index/login`, params),
}
export default api
interface Params {[key: string]: any
}
type Response<T = any> = Promise<T>type ApiMethod = (params?: Params) => Responseexport interface API {[key: string]: ApiMethod
}
uni-app版本
封装
import Request from 'luch-request';interface ApiResponse<T = any> {data: T;code: number;msg: string;[key: string]: any;
}interface RequestConfig {params?: Record<string, any>; [key: string]: any;
}interface GetParams {[key: string]: string | number | boolean | undefined;
}interface PostData {[key: string]: any;
}const http = new Request({baseURL: import.meta.env.VITE_BASE_URL,timeout: 60000,
});
http.interceptors.request.use((config) => {const token = uni.getStorageSync('access_token');if (token) {config.header = config.header || {};config.header['Authorization'] = 'Bearer ' + token;}return config;},(error) => {return Promise.resolve(error);},
);
let isRefreshing = false;
let requests: Array<(token: string) => void> = [];
http.interceptors.response.use((response: any) => {console.log('response', response.data);return response.data as ApiResponse;},async (error) => {const originalRequest = error.config;console.log('response error', error);if ((error.statusCode === 401 || error.data?.code === 401) &&originalRequest) {console.log('originalRequest', originalRequest);if (!originalRequest.url?.includes('/auth/refresh')) {if (!isRefreshing) {isRefreshing = true;const newToken = await refreshToken();isRefreshing = false;if (newToken) {requests.forEach((cb) => cb(newToken));requests = [];if (originalRequest.header) {originalRequest.header.Authorization = `Bearer ${newToken}`;}return http.request(originalRequest);} else {uni.showToast({title: '登录已过期,请重新登录',icon: 'none',duration: 2000,});uni.removeStorageSync('access_token');uni.removeStorageSync('refresh_token');uni.reLaunch({url: '/pages/login/index',});}} else {return new Promise((resolve) => {requests.push((token: string) => {if (originalRequest.header) {originalRequest.header.Authorization = `Bearer ${token}`;}resolve(http.request(originalRequest));});});}}} else {const err = error?.data?.msg || error.errMsg;console.log('err', err);uni.showToast({title: err,icon: 'none',duration: 2000,});return Promise.resolve(error);}},
);const request = {get<T = any>(url: string,params?: GetParams,config?: RequestConfig,): Promise<ApiResponse<T>> {return http.get(url, {params,...config,});},post<T = any>(url: string,data?: PostData,config?: RequestConfig,): Promise<ApiResponse<T>> {return http.post(url, data, config);},
};const refreshToken = async (): Promise<string | null> => {const refreshTokenValue = uni.getStorageSync('refresh_token');if (!refreshTokenValue) {return null;}try {const response = await request.post('/app/auth/refresh', {refresh_token: refreshTokenValue,});const { access_token, refresh_token } = response.data;uni.setStorageSync('access_token', access_token);uni.setStorageSync('refresh_token', refresh_token);return access_token;} catch (error) {console.error('刷新令牌出错:', error);return null;}
};export default request;
使用
import request from '../utils/request';
export default {login: (data: { phone: string; code: string }) => {return request.post('/app/login', data);},
};