mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-02-14 10:12:26 +08:00
2.0.0
This commit is contained in:
@@ -28,8 +28,9 @@ export function getAppEnvConfig() {
|
||||
VITE_GLOB_APP_SHORT_NAME,
|
||||
VITE_GLOB_API_URL_PREFIX,
|
||||
VITE_GLOB_UPLOAD_URL,
|
||||
VITE_GLOB_PROD_MOCK,
|
||||
VITE_GLOB_IMG_URL,
|
||||
VITE_GLOB_FILE_URL,
|
||||
VITE_USE_MOCK,
|
||||
VITE_LOGGER_MOCK,
|
||||
} = ENV;
|
||||
|
||||
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||
@@ -44,8 +45,9 @@ export function getAppEnvConfig() {
|
||||
VITE_GLOB_APP_SHORT_NAME,
|
||||
VITE_GLOB_API_URL_PREFIX,
|
||||
VITE_GLOB_UPLOAD_URL,
|
||||
VITE_GLOB_PROD_MOCK,
|
||||
VITE_GLOB_IMG_URL,
|
||||
VITE_GLOB_FILE_URL,
|
||||
VITE_USE_MOCK,
|
||||
VITE_LOGGER_MOCK,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
124
src/utils/http/alova/index.ts
Normal file
124
src/utils/http/alova/index.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { createAlova } from 'alova';
|
||||
import VueHook from 'alova/vue';
|
||||
import adapterFetch from 'alova/fetch';
|
||||
import { createAlovaMockAdapter } from '@alova/mock';
|
||||
import { isString } from 'lodash-es';
|
||||
import mocks from './mocks';
|
||||
import { useUser } from '@/store/modules/user';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { ResultEnum } from '@/enums/httpEnum';
|
||||
import { isUrl } from '@/utils';
|
||||
|
||||
const { useMock, apiUrl, urlPrefix, loggerMock } = useGlobSetting();
|
||||
|
||||
const mockAdapter = createAlovaMockAdapter([...mocks], {
|
||||
// 全局控制是否启用mock接口,默认为true
|
||||
enable: useMock,
|
||||
|
||||
// 非模拟请求适配器,用于未匹配mock接口时发送请求
|
||||
httpAdapter: adapterFetch(),
|
||||
|
||||
// mock接口响应延迟,单位毫秒
|
||||
delay: 1000,
|
||||
|
||||
// 自定义打印mock接口请求信息
|
||||
// mockRequestLogger: (res) => {
|
||||
// loggerMock && console.log(`Mock Request ${res.url}`, res);
|
||||
// },
|
||||
mockRequestLogger: loggerMock,
|
||||
onMockError(error, currentMethod) {
|
||||
console.error('🚀 ~ onMockError ~ currentMethod:', currentMethod);
|
||||
console.error('🚀 ~ onMockError ~ error:', error);
|
||||
},
|
||||
});
|
||||
|
||||
export const Alova = createAlova({
|
||||
baseURL: apiUrl,
|
||||
statesHook: VueHook,
|
||||
// 关闭全局请求缓存
|
||||
// cacheFor: null,
|
||||
// 全局缓存配置
|
||||
// cacheFor: {
|
||||
// POST: {
|
||||
// mode: 'memory',
|
||||
// expire: 60 * 10 * 1000
|
||||
// },
|
||||
// GET: {
|
||||
// mode: 'memory',
|
||||
// expire: 60 * 10 * 1000
|
||||
// },
|
||||
// HEAD: 60 * 10 * 1000 // 统一设置HEAD请求的缓存模式
|
||||
// },
|
||||
// 在开发环境开启缓存命中日志
|
||||
cacheLogger: process.env.NODE_ENV === 'development',
|
||||
requestAdapter: mockAdapter,
|
||||
beforeRequest(method) {
|
||||
const userStore = useUser();
|
||||
const token = userStore.getToken;
|
||||
// 添加 token 到请求头
|
||||
if (!method.meta?.ignoreToken && token) {
|
||||
method.config.headers['token'] = token;
|
||||
}
|
||||
// 处理 api 请求前缀
|
||||
const isUrlStr = isUrl(method.url as string);
|
||||
if (!isUrlStr && urlPrefix) {
|
||||
method.url = `${urlPrefix}${method.url}`;
|
||||
}
|
||||
if (!isUrlStr && apiUrl && isString(apiUrl)) {
|
||||
method.url = `${apiUrl}${method.url}`;
|
||||
}
|
||||
},
|
||||
responded: {
|
||||
onSuccess: async (response, method) => {
|
||||
const res = (response.json && (await response.json())) || response.body;
|
||||
|
||||
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||
if (method.meta?.isReturnNativeResponse) {
|
||||
return res;
|
||||
}
|
||||
// 请根据自身情况修改数据结构
|
||||
const { message, code, result } = res;
|
||||
|
||||
// 不进行任何处理,直接返回
|
||||
// 用于需要直接获取 code、result、 message 这些信息时开启
|
||||
if (method.meta?.isTransformResponse === false) {
|
||||
return res.data;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const Message = window.$message;
|
||||
// @ts-ignore
|
||||
const Modal = window.$dialog;
|
||||
|
||||
const LoginPath = PageEnum.BASE_LOGIN;
|
||||
if (ResultEnum.SUCCESS === code) {
|
||||
return result;
|
||||
}
|
||||
// 需要登录
|
||||
if (code === 912) {
|
||||
Modal?.warning({
|
||||
title: '提示',
|
||||
content: '登录身份已失效,请重新登录!',
|
||||
okText: '确定',
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
storage.clear();
|
||||
window.location.href = LoginPath;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 可按需处理错误 一般情况下不是 912 错误,不一定需要弹出 message
|
||||
Message?.error(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 项目,多个不同 api 地址,可导出多个实例
|
||||
// export const AlovaTwo = createAlova({
|
||||
// baseURL: 'http://localhost:9001',
|
||||
// });
|
||||
10
src/utils/http/alova/mocks.ts
Normal file
10
src/utils/http/alova/mocks.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
// 这里按需导入 mock 文件,只有在这里导入并导出,才会执行 mock 拦截
|
||||
// 跟根据实际开发情况配置
|
||||
import UserMock from '../../../../mock/user';
|
||||
import MenusMock from '../../../../mock/user/menus';
|
||||
import ConsoleMock from '../../../../mock/dashboard/console';
|
||||
import TableMock from '../../../../mock/table/list';
|
||||
import SystemMenuMock from '../../../../mock/system/menu';
|
||||
import SystemRoleMock from '../../../../mock/system/role';
|
||||
|
||||
export default [UserMock, MenusMock, TableMock, ConsoleMock, SystemMenuMock, SystemRoleMock];
|
||||
@@ -1,200 +0,0 @@
|
||||
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
|
||||
|
||||
import axios from 'axios';
|
||||
import { AxiosCanceler } from './axiosCancel';
|
||||
import { isFunction } from '@/utils/is';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import type { RequestOptions, CreateAxiosOptions, Result, UploadFileParams } from './types';
|
||||
import { ContentTypeEnum } from '@/enums/httpEnum';
|
||||
|
||||
export * from './axiosTransform';
|
||||
|
||||
/**
|
||||
* @description: axios模块
|
||||
*/
|
||||
export class VAxios {
|
||||
private axiosInstance: AxiosInstance;
|
||||
private options: CreateAxiosOptions;
|
||||
|
||||
constructor(options: CreateAxiosOptions) {
|
||||
this.options = options;
|
||||
this.axiosInstance = axios.create(options);
|
||||
this.setupInterceptors();
|
||||
}
|
||||
|
||||
getAxios(): AxiosInstance {
|
||||
return this.axiosInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 重新配置axios
|
||||
*/
|
||||
configAxios(config: CreateAxiosOptions) {
|
||||
if (!this.axiosInstance) {
|
||||
return;
|
||||
}
|
||||
this.createAxios(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 设置通用header
|
||||
*/
|
||||
setHeader(headers: any): void {
|
||||
if (!this.axiosInstance) {
|
||||
return;
|
||||
}
|
||||
Object.assign(this.axiosInstance.defaults.headers, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 请求方法
|
||||
*/
|
||||
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
||||
let conf: AxiosRequestConfig = cloneDeep(config);
|
||||
const transform = this.getTransform();
|
||||
|
||||
const { requestOptions } = this.options;
|
||||
|
||||
const opt: RequestOptions = Object.assign({}, requestOptions, options);
|
||||
|
||||
const { beforeRequestHook, requestCatch, transformRequestData } = transform || {};
|
||||
if (beforeRequestHook && isFunction(beforeRequestHook)) {
|
||||
conf = beforeRequestHook(conf, opt);
|
||||
}
|
||||
|
||||
//这里重新 赋值成最新的配置
|
||||
// @ts-ignore
|
||||
conf.requestOptions = opt;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.axiosInstance
|
||||
.request<any, AxiosResponse<Result>>(conf)
|
||||
.then((res: AxiosResponse<Result>) => {
|
||||
// 请求是否被取消
|
||||
const isCancel = axios.isCancel(res);
|
||||
if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
|
||||
try {
|
||||
const ret = transformRequestData(res, opt);
|
||||
resolve(ret);
|
||||
} catch (err) {
|
||||
reject(err || new Error('request error!'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
resolve(res as unknown as Promise<T>);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
if (requestCatch && isFunction(requestCatch)) {
|
||||
reject(requestCatch(e));
|
||||
return;
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 创建axios实例
|
||||
*/
|
||||
private createAxios(config: CreateAxiosOptions): void {
|
||||
this.axiosInstance = axios.create(config);
|
||||
}
|
||||
|
||||
private getTransform() {
|
||||
const { transform } = this.options;
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 文件上传
|
||||
*/
|
||||
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
|
||||
const formData = new window.FormData();
|
||||
const customFilename = params.name || 'file';
|
||||
|
||||
if (params.filename) {
|
||||
formData.append(customFilename, params.file, params.filename);
|
||||
} else {
|
||||
formData.append(customFilename, params.file);
|
||||
}
|
||||
|
||||
if (params.data) {
|
||||
Object.keys(params.data).forEach((key) => {
|
||||
const value = params.data![key];
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item) => {
|
||||
formData.append(`${key}[]`, item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
formData.append(key, params.data![key]);
|
||||
});
|
||||
}
|
||||
|
||||
return this.axiosInstance.request<T>({
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-type': ContentTypeEnum.FORM_DATA,
|
||||
ignoreCancelToken: true,
|
||||
},
|
||||
...config,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 拦截器配置
|
||||
*/
|
||||
private setupInterceptors() {
|
||||
const transform = this.getTransform();
|
||||
if (!transform) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
requestInterceptors,
|
||||
requestInterceptorsCatch,
|
||||
responseInterceptors,
|
||||
responseInterceptorsCatch,
|
||||
} = transform;
|
||||
|
||||
const axiosCanceler = new AxiosCanceler();
|
||||
|
||||
// 请求拦截器配置处理
|
||||
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
|
||||
const {
|
||||
headers: { ignoreCancelToken },
|
||||
} = config;
|
||||
const ignoreCancel =
|
||||
ignoreCancelToken !== undefined
|
||||
? ignoreCancelToken
|
||||
: this.options.requestOptions?.ignoreCancelToken;
|
||||
|
||||
!ignoreCancel && axiosCanceler.addPending(config);
|
||||
if (requestInterceptors && isFunction(requestInterceptors)) {
|
||||
config = requestInterceptors(config, this.options);
|
||||
}
|
||||
return config;
|
||||
}, undefined);
|
||||
|
||||
// 请求拦截器错误捕获
|
||||
requestInterceptorsCatch &&
|
||||
isFunction(requestInterceptorsCatch) &&
|
||||
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
|
||||
|
||||
// 响应结果拦截器处理
|
||||
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
|
||||
res && axiosCanceler.removePending(res.config);
|
||||
if (responseInterceptors && isFunction(responseInterceptors)) {
|
||||
res = responseInterceptors(res);
|
||||
}
|
||||
return res;
|
||||
}, undefined);
|
||||
|
||||
// 响应结果拦截器错误捕获
|
||||
responseInterceptorsCatch &&
|
||||
isFunction(responseInterceptorsCatch) &&
|
||||
this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import axios, { AxiosRequestConfig, Canceler } from 'axios';
|
||||
import qs from 'qs';
|
||||
|
||||
import { isFunction } from '@/utils/is/index';
|
||||
|
||||
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
|
||||
let pendingMap = new Map<string, Canceler>();
|
||||
|
||||
export const getPendingUrl = (config: AxiosRequestConfig) =>
|
||||
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&');
|
||||
|
||||
export class AxiosCanceler {
|
||||
/**
|
||||
* 添加请求
|
||||
* @param {Object} config
|
||||
*/
|
||||
addPending(config: AxiosRequestConfig) {
|
||||
this.removePending(config);
|
||||
const url = getPendingUrl(config);
|
||||
config.cancelToken =
|
||||
config.cancelToken ||
|
||||
new axios.CancelToken((cancel) => {
|
||||
if (!pendingMap.has(url)) {
|
||||
// 如果 pending 中不存在当前请求,则添加进去
|
||||
pendingMap.set(url, cancel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 清空所有pending
|
||||
*/
|
||||
removeAllPending() {
|
||||
pendingMap.forEach((cancel) => {
|
||||
cancel && isFunction(cancel) && cancel();
|
||||
});
|
||||
pendingMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除请求
|
||||
* @param {Object} config
|
||||
*/
|
||||
removePending(config: AxiosRequestConfig) {
|
||||
const url = getPendingUrl(config);
|
||||
|
||||
if (pendingMap.has(url)) {
|
||||
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
|
||||
const cancel = pendingMap.get(url);
|
||||
cancel && cancel(url);
|
||||
pendingMap.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 重置
|
||||
*/
|
||||
reset(): void {
|
||||
pendingMap = new Map<string, Canceler>();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* 数据处理类,可以根据项目自行配置
|
||||
*/
|
||||
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import type { RequestOptions, Result } from './types';
|
||||
|
||||
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
||||
authenticationScheme?: string;
|
||||
transform?: AxiosTransform;
|
||||
requestOptions?: RequestOptions;
|
||||
}
|
||||
|
||||
export abstract class AxiosTransform {
|
||||
/**
|
||||
* @description: 请求之前处理配置
|
||||
* @description: Process configuration before request
|
||||
*/
|
||||
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
|
||||
|
||||
/**
|
||||
* @description: 请求成功处理
|
||||
*/
|
||||
transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
|
||||
|
||||
/**
|
||||
* @description: 请求失败处理
|
||||
*/
|
||||
requestCatch?: (e: Error) => Promise<any>;
|
||||
|
||||
/**
|
||||
* @description: 请求之前的拦截器
|
||||
*/
|
||||
requestInterceptors?: (
|
||||
config: AxiosRequestConfig,
|
||||
options: CreateAxiosOptions
|
||||
) => AxiosRequestConfig;
|
||||
|
||||
/**
|
||||
* @description: 请求之后的拦截器
|
||||
*/
|
||||
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
|
||||
|
||||
/**
|
||||
* @description: 请求之前的拦截器错误处理
|
||||
*/
|
||||
requestInterceptorsCatch?: (error: Error) => void;
|
||||
|
||||
/**
|
||||
* @description: 请求之后的拦截器错误处理
|
||||
*/
|
||||
responseInterceptorsCatch?: (error: Error) => void;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
export function checkStatus(status: number, msg: string): void {
|
||||
const $message = window['$message'];
|
||||
switch (status) {
|
||||
case 400:
|
||||
$message.error(msg);
|
||||
break;
|
||||
// 401: 未登录
|
||||
// 未登录则跳转登录页面,并携带当前页面的路径
|
||||
// 在登录成功后返回当前页面,这一步需要在登录页操作。
|
||||
case 401:
|
||||
$message.error('用户没有权限(令牌、用户名、密码错误)!');
|
||||
break;
|
||||
case 403:
|
||||
$message.error('用户得到授权,但是访问是被禁止的。!');
|
||||
break;
|
||||
// 404请求不存在
|
||||
case 404:
|
||||
$message.error('网络请求错误,未找到该资源!');
|
||||
break;
|
||||
case 405:
|
||||
$message.error('网络请求错误,请求方法未允许!');
|
||||
break;
|
||||
case 408:
|
||||
$message.error('网络请求超时');
|
||||
break;
|
||||
case 500:
|
||||
$message.error('服务器错误,请联系管理员!');
|
||||
break;
|
||||
case 501:
|
||||
$message.error('网络未实现');
|
||||
break;
|
||||
case 502:
|
||||
$message.error('网络错误');
|
||||
break;
|
||||
case 503:
|
||||
$message.error('服务不可用,服务器暂时过载或维护!');
|
||||
break;
|
||||
case 504:
|
||||
$message.error('网络超时');
|
||||
break;
|
||||
case 505:
|
||||
$message.error('http版本不支持该请求!');
|
||||
break;
|
||||
default:
|
||||
$message.error(msg);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import { isObject, isString } from '@/utils/is';
|
||||
|
||||
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm';
|
||||
|
||||
export function joinTimestamp<T extends boolean>(
|
||||
join: boolean,
|
||||
restful: T
|
||||
): T extends true ? string : object;
|
||||
|
||||
export function joinTimestamp(join: boolean, restful = false): string | object {
|
||||
if (!join) {
|
||||
return restful ? '' : {};
|
||||
}
|
||||
const now = new Date().getTime();
|
||||
if (restful) {
|
||||
return `?_t=${now}`;
|
||||
}
|
||||
return { _t: now };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Format request parameter time
|
||||
*/
|
||||
export function formatRequestDate(params: Recordable) {
|
||||
if (Object.prototype.toString.call(params) !== '[object Object]') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key in params) {
|
||||
if (params[key] && params[key]._isAMomentObject) {
|
||||
params[key] = params[key].format(DATE_TIME_FORMAT);
|
||||
}
|
||||
if (isString(key)) {
|
||||
const value = params[key];
|
||||
if (value) {
|
||||
try {
|
||||
params[key] = isString(value) ? value.trim() : value;
|
||||
} catch (error) {
|
||||
throw new Error(error as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isObject(params[key])) {
|
||||
formatRequestDate(params[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
|
||||
import { VAxios } from './Axios';
|
||||
import { AxiosTransform } from './axiosTransform';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import { checkStatus } from './checkStatus';
|
||||
import { joinTimestamp, formatRequestDate } from './helper';
|
||||
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
|
||||
import { isString } from '@/utils/is/';
|
||||
import { deepMerge, isUrl } from '@/utils';
|
||||
import { setObjToUrlParams } from '@/utils/urlUtils';
|
||||
|
||||
import { RequestOptions, Result, CreateAxiosOptions } from './types';
|
||||
|
||||
import { useUser } from '@/store/modules/user';
|
||||
|
||||
const globSetting = useGlobSetting();
|
||||
const urlPrefix = globSetting.urlPrefix || '';
|
||||
|
||||
import router from '@/router';
|
||||
import { storage } from '@/utils/Storage';
|
||||
|
||||
/**
|
||||
* @description: 数据处理,方便区分多种处理方式
|
||||
*/
|
||||
const transform: AxiosTransform = {
|
||||
/**
|
||||
* @description: 处理请求数据
|
||||
*/
|
||||
transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
|
||||
const {
|
||||
isShowMessage = true,
|
||||
isShowErrorMessage,
|
||||
isShowSuccessMessage,
|
||||
successMessageText,
|
||||
errorMessageText,
|
||||
isTransformResponse,
|
||||
isReturnNativeResponse,
|
||||
} = options;
|
||||
|
||||
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||
if (isReturnNativeResponse) {
|
||||
return res;
|
||||
}
|
||||
// 不进行任何处理,直接返回
|
||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||
if (!isTransformResponse) {
|
||||
return res.data;
|
||||
}
|
||||
|
||||
const { data } = res;
|
||||
|
||||
const $dialog = window['$dialog'];
|
||||
const $message = window['$message'];
|
||||
|
||||
if (!data) {
|
||||
// return '[HTTP] Request has no return value';
|
||||
throw new Error('请求出错,请稍候重试');
|
||||
}
|
||||
// 这里 code,result,message为 后台统一的字段,需要修改为项目自己的接口返回格式
|
||||
const { code, result, message } = data;
|
||||
// 请求成功
|
||||
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
|
||||
// 是否显示提示信息
|
||||
if (isShowMessage) {
|
||||
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
||||
// 是否显示自定义信息提示
|
||||
$dialog.success({
|
||||
type: 'success',
|
||||
content: successMessageText || message || '操作成功!',
|
||||
});
|
||||
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
||||
// 是否显示自定义信息提示
|
||||
$message.error(message || errorMessageText || '操作失败!');
|
||||
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
||||
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||
$dialog.info({
|
||||
title: '提示',
|
||||
content: message,
|
||||
positiveText: '确定',
|
||||
onPositiveClick: () => {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 接口请求成功,直接返回结果
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
||||
let errorMsg = message;
|
||||
switch (code) {
|
||||
// 请求失败
|
||||
case ResultEnum.ERROR:
|
||||
$message.error(errorMsg);
|
||||
break;
|
||||
// 登录超时
|
||||
case ResultEnum.TIMEOUT:
|
||||
const LoginName = PageEnum.BASE_LOGIN_NAME;
|
||||
const LoginPath = PageEnum.BASE_LOGIN;
|
||||
if (router.currentRoute.value?.name === LoginName) return;
|
||||
// 到登录页
|
||||
errorMsg = '登录超时,请重新登录!';
|
||||
$dialog.warning({
|
||||
title: '提示',
|
||||
content: '登录身份已失效,请重新登录!',
|
||||
positiveText: '确定',
|
||||
//negativeText: '取消',
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
onPositiveClick: () => {
|
||||
storage.clear();
|
||||
window.location.href = LoginPath;
|
||||
},
|
||||
onNegativeClick: () => {},
|
||||
});
|
||||
break;
|
||||
}
|
||||
throw new Error(errorMsg);
|
||||
},
|
||||
|
||||
// 请求之前处理config
|
||||
beforeRequestHook: (config, options) => {
|
||||
const { apiUrl, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options;
|
||||
|
||||
const isUrlStr = isUrl(config.url as string);
|
||||
|
||||
if (!isUrlStr && joinPrefix) {
|
||||
config.url = `${urlPrefix}${config.url}`;
|
||||
}
|
||||
|
||||
if (!isUrlStr && apiUrl && isString(apiUrl)) {
|
||||
config.url = `${apiUrl}${config.url}`;
|
||||
}
|
||||
const params = config.params || {};
|
||||
const data = config.data || false;
|
||||
if (config.method?.toUpperCase() === RequestEnum.GET) {
|
||||
if (!isString(params)) {
|
||||
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
|
||||
config.params = Object.assign(params || {}, joinTimestamp(joinTime, false));
|
||||
} else {
|
||||
// 兼容restful风格
|
||||
config.url = config.url + params + `${joinTimestamp(joinTime, true)}`;
|
||||
config.params = undefined;
|
||||
}
|
||||
} else {
|
||||
if (!isString(params)) {
|
||||
formatDate && formatRequestDate(params);
|
||||
if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
|
||||
config.data = data;
|
||||
config.params = params;
|
||||
} else {
|
||||
config.data = params;
|
||||
config.params = undefined;
|
||||
}
|
||||
if (joinParamsToUrl) {
|
||||
config.url = setObjToUrlParams(
|
||||
config.url as string,
|
||||
Object.assign({}, config.params, config.data)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 兼容restful风格
|
||||
config.url = config.url + params;
|
||||
config.params = undefined;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
||||
/**
|
||||
* @description: 请求拦截器处理
|
||||
*/
|
||||
requestInterceptors: (config, options) => {
|
||||
// 请求之前处理config
|
||||
const userStore = useUser();
|
||||
const token = userStore.getToken;
|
||||
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
||||
// jwt token
|
||||
(config as Recordable).headers.Authorization = options.authenticationScheme
|
||||
? `${options.authenticationScheme} ${token}`
|
||||
: token;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
||||
/**
|
||||
* @description: 响应错误处理
|
||||
*/
|
||||
responseInterceptorsCatch: (error: any) => {
|
||||
const $dialog = window['$dialog'];
|
||||
const $message = window['$message'];
|
||||
const { response, code, message } = error || {};
|
||||
// TODO 此处要根据后端接口返回格式修改
|
||||
const msg: string =
|
||||
response && response.data && response.data.message ? response.data.message : '';
|
||||
const err: string = error.toString();
|
||||
try {
|
||||
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
|
||||
$message.error('接口请求超时,请刷新页面重试!');
|
||||
return;
|
||||
}
|
||||
if (err && err.includes('Network Error')) {
|
||||
$dialog.info({
|
||||
title: '网络异常',
|
||||
content: '请检查您的网络连接是否正常',
|
||||
positiveText: '确定',
|
||||
//negativeText: '取消',
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
onPositiveClick: () => {},
|
||||
onNegativeClick: () => {},
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error as any);
|
||||
}
|
||||
// 请求是否被取消
|
||||
const isCancel = axios.isCancel(error);
|
||||
if (!isCancel) {
|
||||
checkStatus(error.response && error.response.status, msg);
|
||||
} else {
|
||||
console.warn(error, '请求被取消!');
|
||||
}
|
||||
//return Promise.reject(error);
|
||||
return Promise.reject(response?.data);
|
||||
},
|
||||
};
|
||||
|
||||
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||
return new VAxios(
|
||||
deepMerge(
|
||||
{
|
||||
timeout: 10 * 1000,
|
||||
authenticationScheme: '',
|
||||
// 接口前缀
|
||||
prefixUrl: urlPrefix,
|
||||
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||
// 数据处理方式
|
||||
transform,
|
||||
// 配置项,下面的选项都可以在独立的接口请求中覆盖
|
||||
requestOptions: {
|
||||
// 默认将prefix 添加到url
|
||||
joinPrefix: true,
|
||||
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||
isReturnNativeResponse: false,
|
||||
// 需要对返回数据进行处理
|
||||
isTransformResponse: true,
|
||||
// post请求的时候添加参数到url
|
||||
joinParamsToUrl: false,
|
||||
// 格式化提交参数时间
|
||||
formatDate: true,
|
||||
// 消息提示类型
|
||||
errorMessageMode: 'none',
|
||||
// 接口地址
|
||||
apiUrl: globSetting.apiUrl,
|
||||
// 接口拼接地址
|
||||
urlPrefix: urlPrefix,
|
||||
// 是否加入时间戳
|
||||
joinTime: true,
|
||||
// 忽略重复请求
|
||||
ignoreCancelToken: true,
|
||||
// 是否携带token
|
||||
withToken: true,
|
||||
},
|
||||
withCredentials: false,
|
||||
},
|
||||
opt || {}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export const http = createAxios();
|
||||
|
||||
// 项目,多个不同 api 地址,直接在这里导出多个
|
||||
// src/api ts 里面接口,就可以单独使用这个请求,
|
||||
// import { httpTwo } from '@/utils/http/axios'
|
||||
// export const httpTwo = createAxios({
|
||||
// requestOptions: {
|
||||
// apiUrl: 'http://localhost:9001',
|
||||
// urlPrefix: 'api',
|
||||
// },
|
||||
// });
|
||||
@@ -1,65 +0,0 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { AxiosTransform } from './axiosTransform';
|
||||
|
||||
export interface CreateAxiosOptions extends AxiosRequestConfig {
|
||||
transform?: AxiosTransform;
|
||||
requestOptions?: RequestOptions;
|
||||
authenticationScheme?: string;
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
export interface UploadFileParams {
|
||||
// 其他参数
|
||||
data?: Recordable;
|
||||
// 文件参数接口字段名
|
||||
name?: string;
|
||||
// 文件
|
||||
file: File | Blob;
|
||||
// 文件名称
|
||||
filename?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface RequestOptions {
|
||||
// 请求参数拼接到url
|
||||
joinParamsToUrl?: boolean;
|
||||
// 格式化请求参数时间
|
||||
formatDate?: boolean;
|
||||
// 是否显示提示信息
|
||||
isShowMessage?: boolean;
|
||||
// 是否解析成JSON
|
||||
isParseToJson?: boolean;
|
||||
// 成功的文本信息
|
||||
successMessageText?: string;
|
||||
// 是否显示成功信息
|
||||
isShowSuccessMessage?: boolean;
|
||||
// 是否显示失败信息
|
||||
isShowErrorMessage?: boolean;
|
||||
// 错误的文本信息
|
||||
errorMessageText?: string;
|
||||
// 是否加入url
|
||||
joinPrefix?: boolean;
|
||||
// 接口地址, 不填则使用默认apiUrl
|
||||
apiUrl?: string;
|
||||
// 请求拼接路径
|
||||
urlPrefix?: string;
|
||||
// 错误消息提示类型
|
||||
errorMessageMode?: 'none' | 'modal';
|
||||
// 是否添加时间戳
|
||||
joinTime?: boolean;
|
||||
// 不进行任何处理,直接返回
|
||||
isTransformResponse?: boolean;
|
||||
// 是否返回原生响应头
|
||||
isReturnNativeResponse?: boolean;
|
||||
//忽略重复请求
|
||||
ignoreCancelToken?: boolean;
|
||||
// 是否携带token
|
||||
withToken?: boolean;
|
||||
}
|
||||
|
||||
export interface Result<T = any> {
|
||||
code: number;
|
||||
type?: 'success' | 'error' | 'warning';
|
||||
message: string;
|
||||
result?: T;
|
||||
}
|
||||
Reference in New Issue
Block a user