Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e1dacdbc39 | |||
| 03b398af2f | |||
| 451c7547af | |||
| c81221efac | |||
| 4e04a8f8ad | |||
| 9f64321d73 |
@ -1,4 +1,4 @@
|
|||||||
#请求的环境
|
#请求的环境
|
||||||
VITE_HTTP_ENV=DEV
|
VITE_HTTP_ENV=DEV
|
||||||
#请求地址
|
#请求地址
|
||||||
VITE_HTTP_URL=http://192.168.100.57/
|
VITE_HTTP_URL=https://test.aisuit.com.cn
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#请求的环境 正式环境
|
#请求的环境 正式环境
|
||||||
VITE_HTTP_ENV=PROD
|
VITE_HTTP_ENV=PROD
|
||||||
#请求地址
|
#请求地址
|
||||||
VITE_HTTP_URL=http://119.23.220.176:17321
|
VITE_HTTP_URL=http://192.168.100.43:8201
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
VITE_HTTP_ENV=STAGING
|
VITE_HTTP_ENV=STAGING
|
||||||
#请求地址
|
#请求地址
|
||||||
VITE_HTTP_URL=http://119.23.220.176:17321
|
VITE_HTTP_URL=http://192.168.100.43:8201
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.0.3](https://github.com/honghuangdc/soybean-admin/compare/v0.0.2...v0.0.3) (2021-11-23)
|
||||||
|
|
||||||
### 0.0.2 (2021-11-21)
|
### 0.0.2 (2021-11-21)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "soybean-admin",
|
"name": "soybean-admin",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Soybean",
|
"name": "Soybean",
|
||||||
"email": "honghuangdc@gmail.com",
|
"email": "honghuangdc@gmail.com",
|
||||||
@ -32,13 +32,13 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antv/g2plot": "^2.3.40",
|
"@antv/g2plot": "^2.3.40",
|
||||||
"@better-scroll/core": "^2.4.2",
|
"@better-scroll/core": "^2.4.2",
|
||||||
"@vueuse/core": "^6.9.2",
|
"@vueuse/core": "^7.0.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"chroma-js": "^2.1.2",
|
"chroma-js": "^2.1.2",
|
||||||
"clipboard": "^2.0.8",
|
"clipboard": "^2.0.8",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"naive-ui": "^2.20.3",
|
"naive-ui": "^2.21.0",
|
||||||
"pinia": "^2.0.4",
|
"pinia": "^2.0.4",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"qs": "^6.10.1",
|
"qs": "^6.10.1",
|
||||||
@ -68,14 +68,14 @@
|
|||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"cz-customizable": "^6.3.0",
|
"cz-customizable": "^6.3.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"eslint": "^8.2.0",
|
"eslint": "^8.3.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-import": "^2.25.3",
|
"eslint-plugin-import": "^2.25.3",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^8.1.1",
|
"eslint-plugin-vue": "^8.1.1",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "^12.0.3",
|
"lint-staged": "^12.1.0",
|
||||||
"patch-package": "^6.4.7",
|
"patch-package": "^6.4.7",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
@ -87,7 +87,7 @@
|
|||||||
"vite": "~2.5.10",
|
"vite": "~2.5.10",
|
||||||
"vite-plugin-html": "^2.1.1",
|
"vite-plugin-html": "^2.1.1",
|
||||||
"vite-plugin-windicss": "^1.5.1",
|
"vite-plugin-windicss": "^1.5.1",
|
||||||
"vue-tsc": "~0.28.10",
|
"vue-tsc": "^0.29.6",
|
||||||
"vueuc": "^0.4.15",
|
"vueuc": "^0.4.15",
|
||||||
"windicss": "^3.2.1"
|
"windicss": "^3.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
904
pnpm-lock.yaml
generated
904
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -3,20 +3,22 @@ import { ref } from 'vue';
|
|||||||
export default function useBoolean(initValue: boolean = false) {
|
export default function useBoolean(initValue: boolean = false) {
|
||||||
const bool = ref(initValue);
|
const bool = ref(initValue);
|
||||||
|
|
||||||
|
function setBool(value: boolean) {
|
||||||
|
bool.value = value;
|
||||||
|
}
|
||||||
function setTrue() {
|
function setTrue() {
|
||||||
bool.value = true;
|
setBool(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFalse() {
|
function setFalse() {
|
||||||
bool.value = false;
|
setBool(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
bool.value = !bool.value;
|
setBool(!bool.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bool,
|
bool,
|
||||||
|
setBool,
|
||||||
setTrue,
|
setTrue,
|
||||||
setFalse,
|
setFalse,
|
||||||
toggle
|
toggle
|
||||||
|
|||||||
15
src/interface/business/demo.ts
Normal file
15
src/interface/business/demo.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/** 数据字典 */
|
||||||
|
export interface Dictionary {
|
||||||
|
/** 字典名字 */
|
||||||
|
label: string;
|
||||||
|
/** 要素名字(一级指标) */
|
||||||
|
charactorLabel: string;
|
||||||
|
/** 要素下的指标key(二级指标) */
|
||||||
|
indicatorKey: string;
|
||||||
|
/** 要素下的指标名字(二级指标) */
|
||||||
|
indicatorLabel: string;
|
||||||
|
/** 备注 */
|
||||||
|
remark: string;
|
||||||
|
/** 指标公式 */
|
||||||
|
formula: string;
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
export * from './auth';
|
export * from './auth';
|
||||||
|
export * from './demo';
|
||||||
|
|||||||
15
src/interface/common/api.ts
Normal file
15
src/interface/common/api.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/** 数据字典 */
|
||||||
|
export interface ResponseDictionary {
|
||||||
|
/** 字典名字 */
|
||||||
|
modelName: string;
|
||||||
|
/** 要素名字(一级指标) */
|
||||||
|
modelCharactorName: string;
|
||||||
|
/** 要素下的指标key(二级指标) */
|
||||||
|
modelIndicator: string;
|
||||||
|
/** 要素下的指标名字(二级指标) */
|
||||||
|
modelIndicatorName: string;
|
||||||
|
/** 备注 */
|
||||||
|
remarks: string;
|
||||||
|
/** 指标公式 */
|
||||||
|
formula: string;
|
||||||
|
}
|
||||||
@ -1,2 +1,4 @@
|
|||||||
export * from './system';
|
export * from './system';
|
||||||
export * from './route';
|
export * from './route';
|
||||||
|
export * from './service';
|
||||||
|
export * from './api';
|
||||||
|
|||||||
50
src/interface/common/service.ts
Normal file
50
src/interface/common/service.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 请求服务的错误类型:
|
||||||
|
* - axios: axios错误:网络错误, 请求超时, 默认的兜底错误
|
||||||
|
* - http: 请求成功,响应的状态码非200的错误
|
||||||
|
* - backend: 请求成功,响应的状态码为200,由后端定义的业务错误
|
||||||
|
*/
|
||||||
|
export type RequestServiceErrorType = 'axios' | 'http' | 'backend';
|
||||||
|
|
||||||
|
/** 请求服务的错误 */
|
||||||
|
export interface RequestServiceError {
|
||||||
|
/** 请求服务的错误类型 */
|
||||||
|
type: RequestServiceErrorType;
|
||||||
|
/** 错误码 */
|
||||||
|
code: string | number;
|
||||||
|
/** 错误信息 */
|
||||||
|
msg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 后端接口返回的类型结构 */
|
||||||
|
export interface BackendServiceResult {
|
||||||
|
/** 状态码 */
|
||||||
|
code: number;
|
||||||
|
/** 接口数据 */
|
||||||
|
data: any;
|
||||||
|
/** 接口消息 */
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 自定义的请求成功结果 */
|
||||||
|
export interface CustomSuccessRequestResult<ResponseData> {
|
||||||
|
/** 请求错误 */
|
||||||
|
error: null;
|
||||||
|
/** 请求数据 */
|
||||||
|
data: ResponseData;
|
||||||
|
/** 网络状态 */
|
||||||
|
networkStatus: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 自定义的请求失败结果 */
|
||||||
|
export interface CustomFailRequestResult {
|
||||||
|
/** 请求错误 */
|
||||||
|
error: RequestServiceError;
|
||||||
|
/** 请求数据 */
|
||||||
|
data: null;
|
||||||
|
/** 网络状态 */
|
||||||
|
networkStatus: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 自定义的请求结果 */
|
||||||
|
export type CustomRequestResult<ResponseData> = CustomSuccessRequestResult<ResponseData> | CustomFailRequestResult;
|
||||||
27
src/service/api/demo.ts
Normal file
27
src/service/api/demo.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type { ResponseDictionary, Dictionary } from '@/interface';
|
||||||
|
import { request, resultMiddleware } from '../request';
|
||||||
|
import { fecthDictionaryMiddleware } from '../middleware';
|
||||||
|
|
||||||
|
// 接口示例
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据字典(不加middleware处理)
|
||||||
|
* @param keyword - 关键词
|
||||||
|
*/
|
||||||
|
export function fetchDictionary(keyword: string) {
|
||||||
|
return request.post<ResponseDictionary[]>('/emoss-entropy/ehe/model/getByIndicator', {
|
||||||
|
indiCatorName: keyword
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据字典(加middleware处理)
|
||||||
|
* @param keyword - 关键词
|
||||||
|
*/
|
||||||
|
export async function fetchDictionaryWithMiddleware(keyword: string) {
|
||||||
|
const res = await request.post<ResponseDictionary[]>('/emoss-entropy/ehe/model/getByIndicator', {
|
||||||
|
indiCatorName: keyword
|
||||||
|
});
|
||||||
|
|
||||||
|
return resultMiddleware<Dictionary[]>(fecthDictionaryMiddleware, [res]);
|
||||||
|
}
|
||||||
1
src/service/api/index.ts
Normal file
1
src/service/api/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './demo';
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from './api';
|
||||||
|
|||||||
23
src/service/middleware/demo.ts
Normal file
23
src/service/middleware/demo.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { ResponseDictionary, Dictionary } from '@/interface';
|
||||||
|
|
||||||
|
export function fecthDictionaryMiddleware(data: ResponseDictionary[]): Dictionary[] {
|
||||||
|
return data.map(item => {
|
||||||
|
const {
|
||||||
|
modelName: label,
|
||||||
|
modelCharactorName: charactorLabel,
|
||||||
|
modelIndicator: indicatorKey,
|
||||||
|
modelIndicatorName: indicatorLabel,
|
||||||
|
remarks: remark,
|
||||||
|
formula
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
return {
|
||||||
|
label,
|
||||||
|
charactorLabel,
|
||||||
|
indicatorKey,
|
||||||
|
indicatorLabel,
|
||||||
|
remark,
|
||||||
|
formula
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
1
src/service/middleware/index.ts
Normal file
1
src/service/middleware/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './demo';
|
||||||
8
src/service/request/axios/index.ts
Normal file
8
src/service/request/axios/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
|
import CustomAxiosInstance from './instance';
|
||||||
|
import Request from './request';
|
||||||
|
|
||||||
|
export function createRequest(axiosConfig: AxiosRequestConfig) {
|
||||||
|
const customInstance = new CustomAxiosInstance(axiosConfig);
|
||||||
|
return new Request(customInstance.instance);
|
||||||
|
}
|
||||||
63
src/service/request/axios/instance.ts
Normal file
63
src/service/request/axios/instance.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import type { AxiosRequestConfig, AxiosInstance, AxiosError, CancelTokenStatic } from 'axios';
|
||||||
|
import { getToken } from '@/utils';
|
||||||
|
import type { BackendServiceResult } from '@/interface';
|
||||||
|
import { transformRequestData, handleAxiosError, handleResponseError, handleBackendError } from '../helpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装axios请求类
|
||||||
|
* @author Soybean<honghuangdc@gmail.com>
|
||||||
|
*/
|
||||||
|
export default class CustomAxiosInstance {
|
||||||
|
instance: AxiosInstance;
|
||||||
|
|
||||||
|
private backendSuccessCode = 200;
|
||||||
|
|
||||||
|
cancelToken: CancelTokenStatic;
|
||||||
|
|
||||||
|
constructor(axiosConfig: AxiosRequestConfig) {
|
||||||
|
this.instance = axios.create(axiosConfig);
|
||||||
|
this.cancelToken = axios.CancelToken;
|
||||||
|
this.setInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置请求拦截器 */
|
||||||
|
setInterceptor() {
|
||||||
|
this.instance.interceptors.request.use(
|
||||||
|
async config => {
|
||||||
|
const handleConfig = { ...config };
|
||||||
|
if (handleConfig.headers) {
|
||||||
|
// 数据转换
|
||||||
|
const contentType = handleConfig.headers['Content-Type'];
|
||||||
|
handleConfig.data = await transformRequestData(handleConfig.data, contentType);
|
||||||
|
// 设置token
|
||||||
|
handleConfig.headers.Authorization = getToken();
|
||||||
|
}
|
||||||
|
return handleConfig;
|
||||||
|
},
|
||||||
|
(axiosError: AxiosError) => {
|
||||||
|
const error = handleAxiosError(axiosError);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.instance.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
const { status } = response;
|
||||||
|
if (status === 200 || status < 300 || status === 304) {
|
||||||
|
const backendServiceResult = response.data as BackendServiceResult;
|
||||||
|
if (backendServiceResult.code === this.backendSuccessCode) {
|
||||||
|
return Promise.resolve(backendServiceResult.data);
|
||||||
|
}
|
||||||
|
const error = handleBackendError(backendServiceResult);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
const error = handleResponseError(response);
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
(axiosError: AxiosError) => {
|
||||||
|
const error = handleAxiosError(axiosError);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/service/request/axios/request.ts
Normal file
62
src/service/request/axios/request.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
|
||||||
|
import type { RequestServiceError, CustomSuccessRequestResult, CustomFailRequestResult } from '@/interface';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装各个请求方法及结果处理的类
|
||||||
|
* @author Soybean<honghuangdc@gmail.com>
|
||||||
|
*/
|
||||||
|
export default class Request {
|
||||||
|
instance: AxiosInstance;
|
||||||
|
|
||||||
|
constructor(instance: AxiosInstance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static successHandler<ResponseData>(response: AxiosResponse) {
|
||||||
|
const successResult: CustomSuccessRequestResult<ResponseData> = {
|
||||||
|
data: response as unknown as ResponseData,
|
||||||
|
error: null,
|
||||||
|
networkStatus: window.navigator.onLine
|
||||||
|
};
|
||||||
|
|
||||||
|
return successResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static failHandler(error: RequestServiceError) {
|
||||||
|
const failResult: CustomFailRequestResult = {
|
||||||
|
data: null,
|
||||||
|
error,
|
||||||
|
networkStatus: window.navigator.onLine
|
||||||
|
};
|
||||||
|
|
||||||
|
return failResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
get<ResponseData>(url: string, config?: AxiosRequestConfig) {
|
||||||
|
return this.instance
|
||||||
|
.get(url, config)
|
||||||
|
.then(res => Request.successHandler<ResponseData>(res))
|
||||||
|
.catch(Request.failHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
post<ResponseData>(url: string, data?: any, config?: AxiosRequestConfig) {
|
||||||
|
return this.instance
|
||||||
|
.post(url, data, config)
|
||||||
|
.then(res => Request.successHandler<ResponseData>(res))
|
||||||
|
.catch(Request.failHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
put<ResponseData>(url: string, data?: any, config?: AxiosRequestConfig) {
|
||||||
|
return this.instance
|
||||||
|
.put(url, data, config)
|
||||||
|
.then(res => Request.successHandler<ResponseData>(res))
|
||||||
|
.catch(Request.failHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete<ResponseData>(url: string, config?: AxiosRequestConfig) {
|
||||||
|
return this.instance
|
||||||
|
.delete(url, config)
|
||||||
|
.then(res => Request.successHandler<ResponseData>(res))
|
||||||
|
.catch(Request.failHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/service/request/config/index.ts
Normal file
39
src/service/request/config/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/** 请求超时时间 */
|
||||||
|
export const REQUEST_TIMEOUT = 60 * 1000;
|
||||||
|
|
||||||
|
/** 错误信息的显示时间 */
|
||||||
|
export const ERROR_MSG_DURATION = 3 * 1000;
|
||||||
|
|
||||||
|
/** 兜底的请求错误code */
|
||||||
|
export const DEFAULT_REQUEST_ERROR_CODE = 'DEFAULT';
|
||||||
|
/** 兜底的请求错误文本 */
|
||||||
|
export const DEFAULT_REQUEST_ERROR_MSG = '请求错误~';
|
||||||
|
|
||||||
|
/** 请求超时的错误code(为固定值:ECONNABORTED) */
|
||||||
|
export const REQUEST_TIMEOUT_CODE = 'ECONNABORTED';
|
||||||
|
/** 请求超时的错误文本 */
|
||||||
|
export const REQUEST_TIMEOUT_MSG = '请求超时~';
|
||||||
|
|
||||||
|
/** 网络不可用的code */
|
||||||
|
export const NETWORK_ERROR_CODE = 'NETWORK_ERROR';
|
||||||
|
/** 网络不可用的错误文本 */
|
||||||
|
export const NETWORK_ERROR_MSG = '网络不可用~';
|
||||||
|
|
||||||
|
/** 请求不成功各种状态的错误 */
|
||||||
|
export const ERROR_STATUS = {
|
||||||
|
400: '400: 请求出现语法错误',
|
||||||
|
401: '401: 用户未授权~',
|
||||||
|
403: '403: 服务器拒绝访问~',
|
||||||
|
404: '404: 请求的资源不存在~',
|
||||||
|
405: '405: 请求方法未允许~',
|
||||||
|
408: '408: 网络请求超时~',
|
||||||
|
500: '500: 服务器内部错误~',
|
||||||
|
501: '501: 服务器未实现请求功能~',
|
||||||
|
502: '502: 错误网关~',
|
||||||
|
503: '503: 服务不可用~',
|
||||||
|
504: '504: 网关超时~',
|
||||||
|
505: '505: http版本不支持该请求~'
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 不弹出错误信息的code */
|
||||||
|
export const NO_ERROR_MSG_CODE: (string | number)[] = [];
|
||||||
85
src/service/request/helpers/error.ts
Normal file
85
src/service/request/helpers/error.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import type { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import type { RequestServiceError, BackendServiceResult } from '@/interface';
|
||||||
|
import {
|
||||||
|
DEFAULT_REQUEST_ERROR_CODE,
|
||||||
|
DEFAULT_REQUEST_ERROR_MSG,
|
||||||
|
NETWORK_ERROR_CODE,
|
||||||
|
NETWORK_ERROR_MSG,
|
||||||
|
REQUEST_TIMEOUT_CODE,
|
||||||
|
REQUEST_TIMEOUT_MSG,
|
||||||
|
ERROR_STATUS
|
||||||
|
} from '../config';
|
||||||
|
import { showErrorMsg } from './msg';
|
||||||
|
|
||||||
|
type ErrorStatus = keyof typeof ERROR_STATUS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求失败的错误
|
||||||
|
* @param error - 错误
|
||||||
|
*/
|
||||||
|
export function handleAxiosError(axiosError: AxiosError) {
|
||||||
|
const error: RequestServiceError = {
|
||||||
|
type: 'axios',
|
||||||
|
code: DEFAULT_REQUEST_ERROR_CODE,
|
||||||
|
msg: DEFAULT_REQUEST_ERROR_MSG
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.navigator.onLine || axiosError.message === 'Network Error') {
|
||||||
|
// 网路错误
|
||||||
|
Object.assign(error, { code: NETWORK_ERROR_CODE, msg: NETWORK_ERROR_MSG });
|
||||||
|
} else if (axiosError.code === REQUEST_TIMEOUT_CODE && axiosError.message.includes('timeout')) {
|
||||||
|
/** 超时错误 */
|
||||||
|
Object.assign(error, { code: REQUEST_TIMEOUT_CODE, msg: REQUEST_TIMEOUT_MSG });
|
||||||
|
} else if (axiosError.response) {
|
||||||
|
// 请求不成功的错误
|
||||||
|
const errorCode: ErrorStatus = axiosError.response.status as ErrorStatus;
|
||||||
|
const msg = ERROR_STATUS[errorCode] || DEFAULT_REQUEST_ERROR_MSG;
|
||||||
|
Object.assign(error, { code: errorCode, msg });
|
||||||
|
}
|
||||||
|
|
||||||
|
showErrorMsg(error);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求成功后的错误
|
||||||
|
* @param response - 请求的响应
|
||||||
|
*/
|
||||||
|
export function handleResponseError(response: AxiosResponse) {
|
||||||
|
const error: RequestServiceError = {
|
||||||
|
type: 'axios',
|
||||||
|
code: DEFAULT_REQUEST_ERROR_CODE,
|
||||||
|
msg: DEFAULT_REQUEST_ERROR_MSG
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.navigator.onLine) {
|
||||||
|
// 网路错误
|
||||||
|
Object.assign(error, { code: NETWORK_ERROR_CODE, msg: NETWORK_ERROR_MSG });
|
||||||
|
} else {
|
||||||
|
// 请求成功的状态码非200的错误
|
||||||
|
const errorCode: ErrorStatus = response.status as ErrorStatus;
|
||||||
|
const msg = ERROR_STATUS[errorCode] || DEFAULT_REQUEST_ERROR_MSG;
|
||||||
|
Object.assign(error, { type: 'backend', code: errorCode, msg });
|
||||||
|
}
|
||||||
|
|
||||||
|
showErrorMsg(error);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理后端返回的错误
|
||||||
|
* @param backendResult - 后端接口的响应数据
|
||||||
|
*/
|
||||||
|
export function handleBackendError(backendResult: BackendServiceResult) {
|
||||||
|
const error: RequestServiceError = {
|
||||||
|
type: 'backend',
|
||||||
|
code: backendResult.code,
|
||||||
|
msg: backendResult.message
|
||||||
|
};
|
||||||
|
|
||||||
|
showErrorMsg(error);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
26
src/service/request/helpers/handler.ts
Normal file
26
src/service/request/helpers/handler.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { CustomRequestResult, CustomSuccessRequestResult, CustomFailRequestResult } from '@/interface';
|
||||||
|
|
||||||
|
type ResultHandler<T> = (...arg: any) => T;
|
||||||
|
/**
|
||||||
|
* 对请求的结果数据进行格式化的处理
|
||||||
|
* @param resultHandler - 处理函数
|
||||||
|
* @param requests - 请求结果
|
||||||
|
*/
|
||||||
|
export function resultMiddleware<MiddlewareData>(
|
||||||
|
resultHandler: ResultHandler<MiddlewareData>,
|
||||||
|
requests: CustomRequestResult<any>[]
|
||||||
|
) {
|
||||||
|
const errorIndex = requests.findIndex(item => item.error !== null);
|
||||||
|
const hasError = errorIndex > -1;
|
||||||
|
const successResult: CustomSuccessRequestResult<MiddlewareData> = {
|
||||||
|
data: resultHandler(...requests.map(item => item.data)),
|
||||||
|
error: null,
|
||||||
|
networkStatus: window.navigator.onLine
|
||||||
|
};
|
||||||
|
const failResult: CustomFailRequestResult = {
|
||||||
|
data: null,
|
||||||
|
error: requests[errorIndex].error!,
|
||||||
|
networkStatus: window.navigator.onLine
|
||||||
|
};
|
||||||
|
return hasError ? failResult : successResult;
|
||||||
|
}
|
||||||
3
src/service/request/helpers/index.ts
Normal file
3
src/service/request/helpers/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './transform';
|
||||||
|
export * from './error';
|
||||||
|
export * from './handler';
|
||||||
31
src/service/request/helpers/msg.ts
Normal file
31
src/service/request/helpers/msg.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import type { RequestServiceError } from '@/interface';
|
||||||
|
import { NO_ERROR_MSG_CODE, ERROR_MSG_DURATION } from '../config';
|
||||||
|
|
||||||
|
/** 错误消息栈,防止同一错误同时出现 */
|
||||||
|
const errorMsgStack = new Map<string | number, string>([]);
|
||||||
|
|
||||||
|
function addErrorMsg(error: RequestServiceError) {
|
||||||
|
errorMsgStack.set(error.code, error.msg);
|
||||||
|
}
|
||||||
|
function removeErrorMsg(error: RequestServiceError) {
|
||||||
|
errorMsgStack.delete(error.code);
|
||||||
|
}
|
||||||
|
function hasErrorMsg(error: RequestServiceError) {
|
||||||
|
return errorMsgStack.has(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示错误信息
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
export function showErrorMsg(error: RequestServiceError) {
|
||||||
|
if (!NO_ERROR_MSG_CODE.includes(error.code)) {
|
||||||
|
if (!hasErrorMsg(error)) {
|
||||||
|
addErrorMsg(error);
|
||||||
|
window.$message?.error(error.msg, { duration: ERROR_MSG_DURATION });
|
||||||
|
setTimeout(() => {
|
||||||
|
removeErrorMsg(error);
|
||||||
|
}, ERROR_MSG_DURATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/service/request/helpers/transform.ts
Normal file
47
src/service/request/helpers/transform.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import qs from 'qs';
|
||||||
|
import FormData from 'form-data';
|
||||||
|
import { isArray } from '@/utils';
|
||||||
|
import { ContentType } from '@/enum';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求数据的转换
|
||||||
|
* @param requestData - 请求数据
|
||||||
|
* @param contentType - 请求头的Content-Type
|
||||||
|
*/
|
||||||
|
export async function transformRequestData(requestData: any, contentType?: string) {
|
||||||
|
// application/json类型不处理
|
||||||
|
let data = requestData;
|
||||||
|
// form类型转换
|
||||||
|
if (contentType === ContentType.formUrlencoded) {
|
||||||
|
data = qs.stringify(requestData);
|
||||||
|
}
|
||||||
|
// form-data类型转换
|
||||||
|
if (contentType === ContentType.formData) {
|
||||||
|
const key = Object.keys(requestData)[0];
|
||||||
|
const file = requestData.data[key];
|
||||||
|
data = await transformFile(file, key);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口为上传文件的类型时数据转换
|
||||||
|
* @param file - 单文件或多文件
|
||||||
|
* @param key - 文件的属性名
|
||||||
|
*/
|
||||||
|
async function transformFile(file: File[] | File, key: string) {
|
||||||
|
const formData = new FormData();
|
||||||
|
if (isArray(file)) {
|
||||||
|
// 多文件
|
||||||
|
await Promise.all(
|
||||||
|
(file as File[]).map(item => {
|
||||||
|
formData.append(key, item);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 单文件
|
||||||
|
await formData.append(key, file);
|
||||||
|
}
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
@ -1,5 +1,9 @@
|
|||||||
import { createRequest } from './request';
|
import { createRequest } from './axios';
|
||||||
|
import { REQUEST_TIMEOUT } from './config';
|
||||||
|
|
||||||
export const adminRequest = createRequest({
|
export const request = createRequest({
|
||||||
baseURL: import.meta.env.VITE_HTTP_URL_EMOSS_ADMIN as string
|
baseURL: import.meta.env.VITE_HTTP_URL,
|
||||||
|
timeout: REQUEST_TIMEOUT
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export { resultMiddleware } from './helpers';
|
||||||
|
|||||||
@ -1,85 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import qs from 'qs';
|
|
||||||
import type { AxiosRequestConfig, AxiosInstance } from 'axios';
|
|
||||||
import { ContentType } from '@/enum';
|
|
||||||
import { getToken } from '@/utils';
|
|
||||||
import { transformFile, errorHandler } from '../utils';
|
|
||||||
|
|
||||||
export interface StatusConfig {
|
|
||||||
/** 表明请求状态的属性key */
|
|
||||||
statusKey: string;
|
|
||||||
/** 请求信息的属性key */
|
|
||||||
msgKey: string;
|
|
||||||
/** 成功状态的状态码 */
|
|
||||||
successCode: string | number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 封装axios请求类
|
|
||||||
* @author Soybean(曹理斌) 2021-03-13
|
|
||||||
* @class CustomAxiosInstance
|
|
||||||
*/
|
|
||||||
export default class CustomAxiosInstance {
|
|
||||||
instance: AxiosInstance;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
axiosConfig: AxiosRequestConfig,
|
|
||||||
statusConfig: StatusConfig = {
|
|
||||||
statusKey: 'code',
|
|
||||||
msgKey: 'message',
|
|
||||||
successCode: 200
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
this.instance = axios.create(axiosConfig);
|
|
||||||
this.setInterceptor(statusConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 设置请求拦截器 */
|
|
||||||
setInterceptor(statusConfig: StatusConfig): void {
|
|
||||||
this.instance.interceptors.request.use(
|
|
||||||
async config => {
|
|
||||||
const handleConfig = { ...config };
|
|
||||||
if (handleConfig.headers) {
|
|
||||||
// form类型转换
|
|
||||||
if (handleConfig.headers['Content-Type'] === ContentType.formUrlencoded) {
|
|
||||||
handleConfig.data = qs.stringify(handleConfig.data);
|
|
||||||
}
|
|
||||||
// 文件类型转换
|
|
||||||
if (handleConfig?.headers['Content-Type'] === ContentType.formData) {
|
|
||||||
const key = Object.keys(handleConfig.data)[0];
|
|
||||||
const file = handleConfig.data[key];
|
|
||||||
handleConfig.data = await transformFile(file, key);
|
|
||||||
}
|
|
||||||
// 设置token
|
|
||||||
handleConfig.headers.Authorization = getToken();
|
|
||||||
}
|
|
||||||
return handleConfig;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
errorHandler(error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.instance.interceptors.response.use(
|
|
||||||
response => {
|
|
||||||
const { status, data } = response;
|
|
||||||
const { statusKey, msgKey, successCode } = statusConfig;
|
|
||||||
if (status === 200 || status < 300 || status === 304) {
|
|
||||||
const responseData = data as any;
|
|
||||||
if (responseData[statusKey] === successCode) {
|
|
||||||
return Promise.resolve(responseData.data);
|
|
||||||
}
|
|
||||||
window.$message?.error(responseData[msgKey]);
|
|
||||||
return Promise.reject(responseData[msgKey]);
|
|
||||||
}
|
|
||||||
const error = { response };
|
|
||||||
errorHandler(error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
errorHandler(error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
|
|
||||||
import CustomAxiosInstance from './instance';
|
|
||||||
import type { StatusConfig } from './instance';
|
|
||||||
|
|
||||||
type ResponseSuccess = [null, any];
|
|
||||||
type ResponseFail = [any, null];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 封装各个请求方法及结果处理的类
|
|
||||||
* @author Soybean<honghuangdc@gmail.com> 2021-03-15
|
|
||||||
* @class Request
|
|
||||||
*/
|
|
||||||
class Request {
|
|
||||||
instance: AxiosInstance;
|
|
||||||
|
|
||||||
constructor(instance: AxiosInstance) {
|
|
||||||
this.instance = instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static successHandler(response: AxiosResponse) {
|
|
||||||
const result: ResponseSuccess = [null, response];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static failHandler(error: any) {
|
|
||||||
const result: ResponseFail = [error, null];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(url: string, config?: AxiosRequestConfig) {
|
|
||||||
return this.instance.get(url, config).then(Request.successHandler).catch(Request.failHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
post(url: string, data?: any, config?: AxiosRequestConfig) {
|
|
||||||
return this.instance.post(url, data, config).then(Request.successHandler).catch(Request.failHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
put(url: string, data?: any, config?: AxiosRequestConfig) {
|
|
||||||
return this.instance.put(url, data, config).then(Request.successHandler).catch(Request.failHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(url: string, config?: AxiosRequestConfig) {
|
|
||||||
return this.instance.delete(url, config).then(Request.successHandler).catch(Request.failHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRequest(axiosConfig: AxiosRequestConfig, statusConfig?: StatusConfig) {
|
|
||||||
const customInstance = new CustomAxiosInstance(axiosConfig, statusConfig);
|
|
||||||
const request = new Request(customInstance.instance);
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
import FormData from 'form-data';
|
|
||||||
import { isArray } from '@/utils';
|
|
||||||
|
|
||||||
type HandleFunc<T> = (...arg: any) => T;
|
|
||||||
type RequestError = any;
|
|
||||||
type RequestData = any;
|
|
||||||
type RequestResult = [RequestError, RequestData];
|
|
||||||
/**
|
|
||||||
* 对请求的结果数据进行格式化的处理
|
|
||||||
* @param handleFunc - 处理函数
|
|
||||||
* @param requests - 请求结果
|
|
||||||
*/
|
|
||||||
export function handleResponse<T>(handleFunc: HandleFunc<T>, ...requests: RequestResult[]) {
|
|
||||||
let handleData: any = null;
|
|
||||||
let error: any = null;
|
|
||||||
const hasError = requests.some(item => {
|
|
||||||
const isError = Boolean(item[0]);
|
|
||||||
if (isError) {
|
|
||||||
[error] = item;
|
|
||||||
}
|
|
||||||
return isError;
|
|
||||||
});
|
|
||||||
if (!hasError) {
|
|
||||||
handleData = handleFunc(...requests.map(item => item[1]));
|
|
||||||
}
|
|
||||||
return [error, handleData] as [any, T];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 接口为上传文件的类型时数据转换
|
|
||||||
* @param file - 单文件或多文件
|
|
||||||
* @param key - 文件的属性名
|
|
||||||
*/
|
|
||||||
export async function transformFile(file: File[] | File, key: string) {
|
|
||||||
const formData = new FormData();
|
|
||||||
if (isArray(file)) {
|
|
||||||
await Promise.all(
|
|
||||||
(file as File[]).map(item => {
|
|
||||||
formData.append(key, item);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await formData.append(key, file);
|
|
||||||
}
|
|
||||||
return formData;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ERROR_STATUS = {
|
|
||||||
400: '400: 请求出现语法错误',
|
|
||||||
401: '401: 用户未授权~',
|
|
||||||
403: '403: 服务器拒绝访问~',
|
|
||||||
404: '404: 请求的资源不存在~',
|
|
||||||
405: '405: 请求方法未允许~',
|
|
||||||
408: '408: 网络请求超时~',
|
|
||||||
500: '500: 服务器内部错误~',
|
|
||||||
501: '501: 服务器未实现请求功能~',
|
|
||||||
502: '502: 错误网关~',
|
|
||||||
503: '503: 服务不可用~',
|
|
||||||
504: '504: 网关超时~',
|
|
||||||
505: '505: http版本不支持该请求~'
|
|
||||||
};
|
|
||||||
type ErrorStatus = keyof typeof ERROR_STATUS;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 网络请求错误状态处理
|
|
||||||
* @param error - 错误
|
|
||||||
*/
|
|
||||||
export function errorHandler(error: any): void {
|
|
||||||
const { $message: Message } = window;
|
|
||||||
if (error.response) {
|
|
||||||
const status = error.response.status as ErrorStatus;
|
|
||||||
Message?.error(ERROR_STATUS[status]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
|
|
||||||
Message?.error('网络连接超时~');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!window.navigator.onLine || error.message === 'Network Error') {
|
|
||||||
Message?.error('网络不可用~');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Message?.error('请求错误~');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 连续的请求错误依此显示
|
|
||||||
* @param duration - 上一次弹出错误消息到下一次的时间(ms)
|
|
||||||
*/
|
|
||||||
export function continuousErrorHandler(duration: number) {
|
|
||||||
let errorStacks: string[] = [];
|
|
||||||
function pushError(id: string) {
|
|
||||||
errorStacks.push(id);
|
|
||||||
}
|
|
||||||
function removeError(id: string) {
|
|
||||||
errorStacks = errorStacks.filter(item => item !== id);
|
|
||||||
}
|
|
||||||
function handleError(id: string, callback: Function) {
|
|
||||||
callback();
|
|
||||||
setTimeout(() => {
|
|
||||||
removeError(id);
|
|
||||||
}, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleContinuousError(callback: Function) {
|
|
||||||
const id = Date.now().toString(36);
|
|
||||||
const { length } = errorStacks;
|
|
||||||
if (length > 0) {
|
|
||||||
pushError(id);
|
|
||||||
setTimeout(() => {
|
|
||||||
handleError(id, callback);
|
|
||||||
}, duration * length);
|
|
||||||
} else {
|
|
||||||
pushError(id);
|
|
||||||
handleError(id, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return handleContinuousError;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user