perf: 同步完整版分支代码

This commit is contained in:
xiaoxian521
2021-12-06 17:11:15 +08:00
parent e9dc8274a0
commit f6057458de
39 changed files with 651 additions and 647 deletions

43
src/utils/auth.ts Normal file
View File

@@ -0,0 +1,43 @@
import Cookies from "js-cookie";
import { useUserStoreHook } from "/@/store/modules/user";
const TokenKey = "authorized-token";
type paramsMapType = {
name: string;
expires: number;
accessToken: string;
};
// 获取token
export function getToken() {
// 此处与TokenKey相同此写法解决初始化时Cookies中不存在TokenKey报错
return Cookies.get("authorized-token");
}
// 设置token以及过期时间cookies、sessionStorage各一份
// 后端需要将用户信息和token以及过期时间都返回给前端过期时间主要用于刷新token
export function setToken(data) {
const { accessToken, expires, name } = data;
// 提取关键信息进行存储
const paramsMap: paramsMapType = {
name,
expires: Date.now() + parseInt(expires),
accessToken
};
const dataString = JSON.stringify(paramsMap);
useUserStoreHook().SET_TOKEN(accessToken);
useUserStoreHook().SET_NAME(name);
expires > 0
? Cookies.set(TokenKey, dataString, {
expires: expires / 86400000
})
: Cookies.set(TokenKey, dataString);
sessionStorage.setItem(TokenKey, dataString);
}
// 删除token
export function removeToken() {
Cookies.remove(TokenKey);
sessionStorage.removeItem(TokenKey);
}

25
src/utils/http/README.md Normal file
View File

@@ -0,0 +1,25 @@
## 用法
### Get 请求
```
import { http } from "/@/utils/http";
// params传参
http.request('get', '/xxx', { params: param });
// url拼接传参
http.request('get', '/xxx?message=' + msg);
```
### Post 请求
```
import { http } from "/@/utils/http";
// params传参
http.request('get', '/xxx', { params: param });
// data传参
http.request('get', '/xxx', { data: param });
```

View File

@@ -1,32 +0,0 @@
import { AxiosRequestConfig } from "axios";
import { excludeProps } from "./utils";
/**
* 默认配置
*/
export const defaultConfig: AxiosRequestConfig = {
baseURL: "",
//10秒超时
timeout: 10000,
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
};
export function genConfig(config?: AxiosRequestConfig): AxiosRequestConfig {
if (!config) {
return defaultConfig;
}
const { headers } = config;
if (headers && typeof headers === "object") {
defaultConfig.headers = {
...defaultConfig.headers,
...headers
};
}
return { ...excludeProps(config!, "headers"), ...defaultConfig };
}
export const METHODS = ["post", "get", "put", "delete", "option", "patch"];

View File

@@ -1,236 +0,0 @@
import Axios, {
AxiosRequestConfig,
CancelTokenStatic,
AxiosInstance
} from "axios";
import NProgress from "../progress";
import { genConfig } from "./config";
import { transformConfigByMethod } from "./utils";
import {
cancelTokenType,
RequestMethods,
EnclosureHttpRequestConfig,
EnclosureHttpResoponse,
EnclosureHttpError
} from "./types.d";
class EnclosureHttp {
constructor() {
this.httpInterceptorsRequest();
this.httpInterceptorsResponse();
}
// 初始化配置对象
private static initConfig: EnclosureHttpRequestConfig = {};
// 保存当前Axios实例对象
private static axiosInstance: AxiosInstance = Axios.create(genConfig());
// 保存 EnclosureHttp实例
private static EnclosureHttpInstance: EnclosureHttp;
// axios取消对象
private CancelToken: CancelTokenStatic = Axios.CancelToken;
// 取消的凭证数组
private sourceTokenList: Array<cancelTokenType> = [];
// 记录当前这一次cancelToken的key
private currentCancelTokenKey = "";
public get cancelTokenList(): Array<cancelTokenType> {
return this.sourceTokenList;
}
// eslint-disable-next-line class-methods-use-this
public set cancelTokenList(value) {
throw new Error("cancelTokenList不允许赋值");
}
/**
* @description 私有构造不允许实例化
* @returns void 0
*/
// constructor() {}
/**
* @description 生成唯一取消key
* @param config axios配置
* @returns string
*/
// eslint-disable-next-line class-methods-use-this
private static genUniqueKey(config: EnclosureHttpRequestConfig): string {
return `${config.url}--${JSON.stringify(config.data)}`;
}
/**
* @description 取消重复请求
* @returns void 0
*/
private cancelRepeatRequest(): void {
const temp: { [key: string]: boolean } = {};
this.sourceTokenList = this.sourceTokenList.reduce<Array<cancelTokenType>>(
(res: Array<cancelTokenType>, cancelToken: cancelTokenType) => {
const { cancelKey, cancelExecutor } = cancelToken;
if (!temp[cancelKey]) {
temp[cancelKey] = true;
res.push(cancelToken);
} else {
cancelExecutor();
}
return res;
},
[]
);
}
/**
* @description 删除指定的CancelToken
* @returns void 0
*/
private deleteCancelTokenByCancelKey(cancelKey: string): void {
this.sourceTokenList =
this.sourceTokenList.length < 1
? this.sourceTokenList.filter(
cancelToken => cancelToken.cancelKey !== cancelKey
)
: [];
}
/**
* @description 拦截请求
* @returns void 0
*/
private httpInterceptorsRequest(): void {
EnclosureHttp.axiosInstance.interceptors.request.use(
(config: EnclosureHttpRequestConfig) => {
const $config = config;
NProgress.start(); // 每次切换页面时,调用进度条
const cancelKey = EnclosureHttp.genUniqueKey($config);
$config.cancelToken = new this.CancelToken(
(cancelExecutor: (cancel: any) => void) => {
this.sourceTokenList.push({ cancelKey, cancelExecutor });
}
);
this.cancelRepeatRequest();
this.currentCancelTokenKey = cancelKey;
// 优先判断post/get等方法是否传入回掉否则执行初始化设置等回掉
if (typeof config.beforeRequestCallback === "function") {
config.beforeRequestCallback($config);
return $config;
}
if (EnclosureHttp.initConfig.beforeRequestCallback) {
EnclosureHttp.initConfig.beforeRequestCallback($config);
return $config;
}
return $config;
},
error => {
return Promise.reject(error);
}
);
}
/**
* @description 清空当前cancelTokenList
* @returns void 0
*/
public clearCancelTokenList(): void {
this.sourceTokenList.length = 0;
}
/**
* @description 拦截响应
* @returns void 0
*/
private httpInterceptorsResponse(): void {
const instance = EnclosureHttp.axiosInstance;
instance.interceptors.response.use(
(response: EnclosureHttpResoponse) => {
const $config = response.config;
// 请求每次成功一次就删除当前canceltoken标记
const cancelKey = EnclosureHttp.genUniqueKey($config);
this.deleteCancelTokenByCancelKey(cancelKey);
NProgress.done();
// 优先判断post/get等方法是否传入回掉否则执行初始化设置等回掉
if (typeof $config.beforeResponseCallback === "function") {
$config.beforeResponseCallback(response);
return response.data;
}
if (EnclosureHttp.initConfig.beforeResponseCallback) {
EnclosureHttp.initConfig.beforeResponseCallback(response);
return response.data;
}
return response.data;
},
(error: EnclosureHttpError) => {
const $error = error;
// 判断当前的请求中是否在 取消token数组理存在如果存在则移除单次请求流程
if (this.currentCancelTokenKey) {
const haskey = this.sourceTokenList.filter(
cancelToken => cancelToken.cancelKey === this.currentCancelTokenKey
).length;
if (haskey) {
this.sourceTokenList = this.sourceTokenList.filter(
cancelToken =>
cancelToken.cancelKey !== this.currentCancelTokenKey
);
this.currentCancelTokenKey = "";
}
}
$error.isCancelRequest = Axios.isCancel($error);
NProgress.done();
// 所有的响应异常 区分来源为取消请求/非取消请求
return Promise.reject($error);
}
);
}
public request<T>(
method: RequestMethods,
url: string,
param?: AxiosRequestConfig,
axiosConfig?: EnclosureHttpRequestConfig
): Promise<T> {
const config = transformConfigByMethod(param, {
method,
url,
...axiosConfig
} as EnclosureHttpRequestConfig);
// 单独处理自定义请求/响应回掉
return new Promise((resolve, reject) => {
EnclosureHttp.axiosInstance
.request(config)
.then((response: undefined) => {
resolve(response);
})
.catch((error: any) => {
reject(error);
});
});
}
public post<T>(
url: string,
params?: T,
config?: EnclosureHttpRequestConfig
): Promise<T> {
return this.request<T>("post", url, params, config);
}
public get<T>(
url: string,
params?: T,
config?: EnclosureHttpRequestConfig
): Promise<T> {
return this.request<T>("get", url, params, config);
}
}
export default EnclosureHttp;

View File

@@ -1,2 +1,166 @@
import EnclosureHttp from "./core";
export const http = new EnclosureHttp();
import Axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import {
resultType,
PureHttpError,
RequestMethods,
PureHttpResoponse,
PureHttpRequestConfig
} from "./types.d";
import qs from "qs";
import NProgress from "../progress";
// import { loadEnv } from "@build/index";
import { getToken } from "/@/utils/auth";
import { useUserStoreHook } from "/@/store/modules/user";
// 加载环境变量 VITE_PROXY_DOMAIN开发环境 VITE_PROXY_DOMAIN_REAL打包后的线上环境
// const { VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL } = loadEnv();
// 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
// baseURL:
// process.env.NODE_ENV === "production"
// ? VITE_PROXY_DOMAIN_REAL
// : VITE_PROXY_DOMAIN,
// 当前使用mock模拟请求将baseURL制空如果你的环境用到了http请求请删除下面的baseURL启用上面的baseURL并将11行、16行代码注释取消
baseURL: "",
timeout: 10000,
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"
},
// 数组格式参数序列化
paramsSerializer: params => qs.stringify(params, { indices: false })
};
class PureHttp {
constructor() {
this.httpInterceptorsRequest();
this.httpInterceptorsResponse();
}
// 初始化配置对象
private static initConfig: PureHttpRequestConfig = {};
// 保存当前Axios实例对象
private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
// 请求拦截
private httpInterceptorsRequest(): void {
PureHttp.axiosInstance.interceptors.request.use(
(config: PureHttpRequestConfig) => {
const $config = config;
// 开启进度条动画
NProgress.start();
// 优先判断post/get等方法是否传入回掉否则执行初始化设置等回掉
if (typeof config.beforeRequestCallback === "function") {
config.beforeRequestCallback($config);
return $config;
}
if (PureHttp.initConfig.beforeRequestCallback) {
PureHttp.initConfig.beforeRequestCallback($config);
return $config;
}
const token = getToken();
if (token) {
const data = JSON.parse(token);
const now = new Date().getTime();
const expired = parseInt(data.expires) - now <= 0;
if (expired) {
// token过期刷新
useUserStoreHook()
.refreshToken(data)
.then((res: resultType) => {
config.headers["Authorization"] = "Bearer " + res.accessToken;
return $config;
});
} else {
config.headers["Authorization"] = "Bearer " + data.accessToken;
return $config;
}
} else {
return $config;
}
},
error => {
return Promise.reject(error);
}
);
}
// 响应拦截
private httpInterceptorsResponse(): void {
const instance = PureHttp.axiosInstance;
instance.interceptors.response.use(
(response: PureHttpResoponse) => {
const $config = response.config;
// 关闭进度条动画
NProgress.done();
// 优先判断post/get等方法是否传入回掉否则执行初始化设置等回掉
if (typeof $config.beforeResponseCallback === "function") {
$config.beforeResponseCallback(response);
return response.data;
}
if (PureHttp.initConfig.beforeResponseCallback) {
PureHttp.initConfig.beforeResponseCallback(response);
return response.data;
}
return response.data;
},
(error: PureHttpError) => {
const $error = error;
$error.isCancelRequest = Axios.isCancel($error);
// 关闭进度条动画
NProgress.done();
// 所有的响应异常 区分来源为取消请求/非取消请求
return Promise.reject($error);
}
);
}
// 通用请求工具函数
public request<T>(
method: RequestMethods,
url: string,
param?: AxiosRequestConfig,
axiosConfig?: PureHttpRequestConfig
): Promise<T> {
const config = {
method,
url,
...param,
...axiosConfig
} as PureHttpRequestConfig;
// 单独处理自定义请求/响应回掉
return new Promise((resolve, reject) => {
PureHttp.axiosInstance
.request(config)
.then((response: undefined) => {
resolve(response);
})
.catch(error => {
reject(error);
});
});
}
// 单独抽离的post工具函数
public post<T>(
url: string,
params?: T,
config?: PureHttpRequestConfig
): Promise<T> {
return this.request<T>("post", url, params, config);
}
// 单独抽离的get工具函数
public get<T>(
url: string,
params?: T,
config?: PureHttpRequestConfig
): Promise<T> {
return this.request<T>("get", url, params, config);
}
}
export const http = new PureHttp();

View File

@@ -1,50 +1,39 @@
import Axios, {
AxiosRequestConfig,
Canceler,
AxiosResponse,
Method,
AxiosError
AxiosError,
AxiosResponse,
AxiosRequestConfig
} from "axios";
import { METHODS } from "./config";
export type cancelTokenType = { cancelKey: string; cancelExecutor: Canceler };
export type resultType = {
accessToken?: string;
};
export type RequestMethods = Extract<
Method,
"get" | "post" | "put" | "delete" | "patch" | "option" | "head"
>;
export interface EnclosureHttpRequestConfig extends AxiosRequestConfig {
beforeRequestCallback?: (request: EnclosureHttpRequestConfig) => void; // 请求发送之前
beforeResponseCallback?: (response: EnclosureHttpResoponse) => void; // 相应返回之前
}
export interface EnclosureHttpResoponse extends AxiosResponse {
config: EnclosureHttpRequestConfig;
}
export interface EnclosureHttpError extends AxiosError {
export interface PureHttpError extends AxiosError {
isCancelRequest?: boolean;
}
export default class EnclosureHttp {
cancelTokenList: Array<cancelTokenType>;
clearCancelTokenList(): void;
export interface PureHttpResoponse extends AxiosResponse {
config: PureHttpRequestConfig;
}
export interface PureHttpRequestConfig extends AxiosRequestConfig {
beforeRequestCallback?: (request: PureHttpRequestConfig) => void;
beforeResponseCallback?: (response: PureHttpResoponse) => void;
}
export default class PureHttp {
request<T>(
method: RequestMethods,
url: string,
param?: AxiosRequestConfig,
axiosConfig?: EnclosureHttpRequestConfig
): Promise<T>;
post<T>(
url: string,
params?: T,
config?: EnclosureHttpRequestConfig
): Promise<T>;
get<T>(
url: string,
params?: T,
config?: EnclosureHttpRequestConfig
axiosConfig?: PureHttpRequestConfig
): Promise<T>;
post<T>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<T>;
get<T>(url: string, params?: T, config?: PureHttpRequestConfig): Promise<T>;
}

View File

@@ -1,29 +0,0 @@
import { EnclosureHttpRequestConfig } from "./types.d";
export function excludeProps<T extends { [key: string]: any }>(
origin: T,
prop: string
): { [key: string]: T } {
return Object.keys(origin)
.filter(key => !prop.includes(key))
.reduce((res, key) => {
res[key] = origin[key];
return res;
}, {} as { [key: string]: T });
}
export function transformConfigByMethod(
params: any,
config: EnclosureHttpRequestConfig
): EnclosureHttpRequestConfig {
const { method } = config;
const props = ["delete", "get", "head", "options"].includes(
method!.toLocaleLowerCase()
)
? "params"
: "data";
return {
...config,
[props]: params
};
}

View File

@@ -1,19 +0,0 @@
import { i18n } from "../plugins/i18n";
/**
* 消息转换
* @param message message
* @param isI18n 如果true,获取对应的消息,否则返回this
* @returns message
*/
export function transformI18n(message = "", isI18n = false) {
if (!message) {
return "";
}
if (isI18n) {
//@ts-ignore
return i18n.global.tc.call(i18n.global, message);
} else {
return message;
}
}

View File

@@ -1,53 +0,0 @@
import { loadEnv } from "@build/utils";
import { merge } from "lodash-es";
import tsCookies from "typescript-cookie/dist/src/compat";
class Cookies {
private static env = loadEnv();
constructor() {}
/**
* 存储 cookie 值
* @param name
* @param value
* @param cookieSetting
*/
set(name = "default", value = "", cookieSetting = {}) {
const currentCookieSetting = {
expires: 1
};
merge(currentCookieSetting, cookieSetting);
tsCookies.set(
`${Cookies.env.VITE_TITLE}-${Cookies.env.VITE_VERSION}-${name}`,
value,
currentCookieSetting
);
}
/**
* 拿到 cookie 值
* @param name
* @returns
*/
get(name = "default") {
return tsCookies.get(
`${Cookies.env.VITE_TITLE}-${Cookies.env.VITE_VERSION}-${name}`
);
}
/**
* 拿到 cookie 全部的值
* @returns
*/
getAll() {
return tsCookies.get();
}
/**
* 删除 cookie
* @param name
*/
remove(name = "default") {
tsCookies.remove(
`${Cookies.env.VITE_TITLE}-${Cookies.env.VITE_VERSION}-${name}`
);
}
}
export const cookies = new Cookies();

View File

@@ -1,93 +0,0 @@
import { loadEnv } from "@build/utils";
import { LocalStorage, LowSync } from "lowdb";
import { chain, cloneDeep } from "lodash-es";
import { storageLocal } from ".";
import { cookies } from "./cookie";
type Data = {
database: {};
sys: {};
};
/**
* db 数据存储,采用 LocalStorage存储
*/
class DB {
private db: LowSync<Data>;
private static env = loadEnv();
constructor() {
this.db = new LowSync<Data>(
new LocalStorage<Data>(`${DB.env.VITE_TITLE}-${DB.env.VITE_VERSION}`)
);
this.initialization();
// @ts-ignore
this.db.chain = chain(this.db.data);
}
private initialization() {
this.db.data = storageLocal.getItem(
`${DB.env.VITE_TITLE}-${DB.env.VITE_VERSION}`
) || { database: {}, sys: {} };
this.db.write();
}
/**
* 检查路径是否存在 不存在的话初始化
* @param param0
* @returns path
*/
pathInit({
dbName = "database",
path = "",
user = true,
validator = () => true,
defaultValue = ""
}): string {
const uuid = cookies.get("uuid") || "ghost-uuid";
const currentPath = `${dbName}.${user ? `user.${uuid}` : "public"}${
path ? `.${path}` : ""
}`;
// @ts-ignore
const value = this.db.chain.get(currentPath).value();
// @ts-ignore
if (!(value !== undefined && validator(value))) {
// @ts-ignore
this.db.chain.set(currentPath, defaultValue).value();
this.db.write();
}
return currentPath;
}
/**
*将数据存储到指定位置 | 路径不存在会自动初始化
*
* 效果类似于取值 dbName.path = value
* @param param0
*/
dbSet({ dbName = "database", path = "", value = "", user = false }): void {
const currentPath = this.pathInit({
dbName,
path,
user
});
// @ts-ignore
this.db.chain.set(currentPath, value).value();
this.db.write();
}
/**
* 获取数据
*
* 效果类似于取值 dbName.path || defaultValue
* @param param0
* @returns
*/
dbGet({
dbName = "database",
path = "",
defaultValue = "",
user = false
}): any {
// @ts-ignore
const values = this.db.chain
.get(this.pathInit({ dbName, path, user, defaultValue }))
.value();
return cloneDeep(values);
}
}
export const db = new DB();