feat: add pure-admin-thin
This commit is contained in:
21
src/utils/algorithm/index.ts
Normal file
21
src/utils/algorithm/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
interface ProxyAlgorithm {
|
||||
increaseIndexes<T>(val: Array<T>): Array<T>;
|
||||
}
|
||||
|
||||
class algorithmProxy implements ProxyAlgorithm {
|
||||
constructor() {}
|
||||
|
||||
// 数组每一项添加索引字段
|
||||
public increaseIndexes<T>(val: Array<T>): Array<T> {
|
||||
return Object.keys(val)
|
||||
.map(v => {
|
||||
return {
|
||||
...val[v],
|
||||
key: v
|
||||
};
|
||||
})
|
||||
.filter(v => v.meta && v.meta.showLink);
|
||||
}
|
||||
}
|
||||
|
||||
export const algorithm = new algorithmProxy();
|
12
src/utils/debounce/index.ts
Normal file
12
src/utils/debounce/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// 延迟函数
|
||||
export const delay = (timeout: number) =>
|
||||
new Promise(resolve => setTimeout(resolve, timeout));
|
||||
|
||||
// 防抖函数
|
||||
export const debounce = (fn: () => Fn, timeout: number) => {
|
||||
let timmer: TimeoutHandle;
|
||||
return () => {
|
||||
timmer ? clearTimeout(timmer) : null;
|
||||
timmer = setTimeout(fn, timeout);
|
||||
};
|
||||
};
|
37
src/utils/deviceDetection/index.ts
Normal file
37
src/utils/deviceDetection/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
interface deviceInter {
|
||||
match: Fn;
|
||||
}
|
||||
|
||||
interface BrowserInter {
|
||||
browser: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
// 检测设备类型(手机返回true,反之)
|
||||
export const deviceDetection = () => {
|
||||
const sUserAgent: deviceInter = navigator.userAgent.toLowerCase();
|
||||
// const bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
|
||||
const bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";
|
||||
const bIsMidp = sUserAgent.match(/midp/i) == "midp";
|
||||
const bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4";
|
||||
const bIsUc = sUserAgent.match(/ucweb/i) == "ucweb";
|
||||
const bIsAndroid = sUserAgent.match(/android/i) == "android";
|
||||
const bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
|
||||
const bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
|
||||
return (
|
||||
bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM
|
||||
);
|
||||
};
|
||||
|
||||
// 获取浏览器型号以及版本
|
||||
export const getBrowserInfo = () => {
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/;
|
||||
const m = ua.match(re);
|
||||
const Sys: BrowserInter = {
|
||||
browser: m[1].replace(/version/, "'safari"),
|
||||
version: m[2]
|
||||
};
|
||||
|
||||
return Sys;
|
||||
};
|
32
src/utils/http/config.ts
Normal file
32
src/utils/http/config.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
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"];
|
248
src/utils/http/core.ts
Normal file
248
src/utils/http/core.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
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 = "";
|
||||
|
||||
private beforeRequestCallback: EnclosureHttpRequestConfig["beforeRequestCallback"] =
|
||||
undefined;
|
||||
|
||||
private beforeResponseCallback: EnclosureHttpRequestConfig["beforeResponseCallback"] =
|
||||
undefined;
|
||||
|
||||
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 this.beforeRequestCallback === "function") {
|
||||
this.beforeRequestCallback($config);
|
||||
this.beforeRequestCallback = undefined;
|
||||
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) => {
|
||||
// 请求每次成功一次就删除当前canceltoken标记
|
||||
const cancelKey = EnclosureHttp.genUniqueKey(response.config);
|
||||
this.deleteCancelTokenByCancelKey(cancelKey);
|
||||
// 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉
|
||||
if (typeof this.beforeResponseCallback === "function") {
|
||||
this.beforeResponseCallback(response);
|
||||
this.beforeResponseCallback = undefined;
|
||||
return response.data;
|
||||
}
|
||||
if (EnclosureHttp.initConfig.beforeResponseCallback) {
|
||||
EnclosureHttp.initConfig.beforeResponseCallback(response);
|
||||
return response.data;
|
||||
}
|
||||
NProgress.done();
|
||||
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);
|
||||
// 单独处理自定义请求/响应回掉
|
||||
if (axiosConfig?.beforeRequestCallback) {
|
||||
this.beforeRequestCallback = axiosConfig.beforeRequestCallback;
|
||||
}
|
||||
if (axiosConfig?.beforeResponseCallback) {
|
||||
this.beforeResponseCallback = axiosConfig.beforeResponseCallback;
|
||||
}
|
||||
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;
|
2
src/utils/http/index.ts
Normal file
2
src/utils/http/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import EnclosureHttp from "./core";
|
||||
export const http = new EnclosureHttp();
|
50
src/utils/http/types.d.ts
vendored
Normal file
50
src/utils/http/types.d.ts
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import Axios, {
|
||||
AxiosRequestConfig,
|
||||
Canceler,
|
||||
AxiosResponse,
|
||||
Method,
|
||||
AxiosError
|
||||
} from "axios";
|
||||
|
||||
import { METHODS } from "./config";
|
||||
|
||||
export type cancelTokenType = { cancelKey: string; cancelExecutor: Canceler };
|
||||
|
||||
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 {
|
||||
isCancelRequest?: boolean;
|
||||
}
|
||||
|
||||
export default class EnclosureHttp {
|
||||
cancelTokenList: Array<cancelTokenType>;
|
||||
clearCancelTokenList(): void;
|
||||
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
|
||||
): Promise<T>;
|
||||
}
|
29
src/utils/http/utils.ts
Normal file
29
src/utils/http/utils.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
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
|
||||
};
|
||||
}
|
101
src/utils/is.ts
Normal file
101
src/utils/is.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/* eslint-disable */
|
||||
const toString = Object.prototype.toString;
|
||||
|
||||
export function is(val: unknown, type: string) {
|
||||
return toString.call(val) === `[object ${type}]`;
|
||||
}
|
||||
|
||||
export function isDef<T = unknown>(val?: T): val is T {
|
||||
return typeof val !== "undefined";
|
||||
}
|
||||
|
||||
export function isUnDef<T = unknown>(val?: T): val is T {
|
||||
return !isDef(val);
|
||||
}
|
||||
|
||||
export function isObject(val: any): val is Record<any, any> {
|
||||
return val !== null && is(val, "Object");
|
||||
}
|
||||
|
||||
export function isEmpty<T = unknown>(val: T): val is T {
|
||||
if (isArray(val) || isString(val)) {
|
||||
return val.length === 0;
|
||||
}
|
||||
|
||||
if (val instanceof Map || val instanceof Set) {
|
||||
return val.size === 0;
|
||||
}
|
||||
|
||||
if (isObject(val)) {
|
||||
return Object.keys(val).length === 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isDate(val: unknown): val is Date {
|
||||
return is(val, "Date");
|
||||
}
|
||||
|
||||
export function isNull(val: unknown): val is null {
|
||||
return val === null;
|
||||
}
|
||||
|
||||
export function isNullAndUnDef(val: unknown): val is null | undefined {
|
||||
return isUnDef(val) && isNull(val);
|
||||
}
|
||||
|
||||
export function isNullOrUnDef(val: unknown): val is null | undefined {
|
||||
return isUnDef(val) || isNull(val);
|
||||
}
|
||||
|
||||
export function isNumber(val: unknown): val is number {
|
||||
return is(val, "Number");
|
||||
}
|
||||
|
||||
export function isPromise<T = any>(val: unknown): val is Promise<T> {
|
||||
return (
|
||||
is(val, "Promise") &&
|
||||
isObject(val) &&
|
||||
isFunction(val.then) &&
|
||||
isFunction(val.catch)
|
||||
);
|
||||
}
|
||||
|
||||
export function isString(val: unknown): val is string {
|
||||
return is(val, "String");
|
||||
}
|
||||
|
||||
export function isFunction(val: unknown): val is Function {
|
||||
return typeof val === "function";
|
||||
}
|
||||
|
||||
export function isBoolean(val: unknown): val is boolean {
|
||||
return is(val, "Boolean");
|
||||
}
|
||||
|
||||
export function isRegExp(val: unknown): val is RegExp {
|
||||
return is(val, "RegExp");
|
||||
}
|
||||
|
||||
export function isArray(val: any): val is Array<any> {
|
||||
return val && Array.isArray(val);
|
||||
}
|
||||
|
||||
export function isWindow(val: any): val is Window {
|
||||
return typeof window !== "undefined" && is(val, "Window");
|
||||
}
|
||||
|
||||
export function isElement(val: unknown): val is Element {
|
||||
return isObject(val) && !!val.tagName;
|
||||
}
|
||||
|
||||
export const isServer = typeof window === "undefined";
|
||||
|
||||
export const isClient = !isServer;
|
||||
|
||||
export function isUrl(path: string): boolean {
|
||||
const reg =
|
||||
/(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
|
||||
return reg.test(path);
|
||||
}
|
12
src/utils/link.ts
Normal file
12
src/utils/link.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export const openLink = (link: string) => {
|
||||
const $a: HTMLElement = document.createElement("a");
|
||||
$a.setAttribute("href", link);
|
||||
$a.setAttribute("target", "_blank");
|
||||
$a.setAttribute("rel", "noreferrer noopener");
|
||||
$a.setAttribute("id", "external");
|
||||
document.getElementById("external") &&
|
||||
document.body.removeChild(document.getElementById("external"));
|
||||
document.body.appendChild($a);
|
||||
$a.click();
|
||||
$a.remove();
|
||||
};
|
54
src/utils/loaders/index.ts
Normal file
54
src/utils/loaders/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
interface ProxyLoader {
|
||||
loadCss(src: string): any;
|
||||
loadScript(src: string): Promise<any>;
|
||||
loadScriptConcurrent(src: Array<string>): Promise<any>;
|
||||
}
|
||||
|
||||
class loaderProxy implements ProxyLoader {
|
||||
constructor() {}
|
||||
|
||||
protected scriptLoaderCache: Array<string> = [];
|
||||
|
||||
public loadCss = (src: string): any => {
|
||||
const element: HTMLLinkElement = document.createElement("link");
|
||||
element.rel = "stylesheet";
|
||||
element.href = src;
|
||||
document.body.appendChild(element);
|
||||
};
|
||||
|
||||
public loadScript = async (src: string): Promise<any> => {
|
||||
if (this.scriptLoaderCache.includes(src)) {
|
||||
return src;
|
||||
} else {
|
||||
const element: HTMLScriptElement = document.createElement("script");
|
||||
element.src = src;
|
||||
document.body.appendChild(element);
|
||||
element.onload = () => {
|
||||
return this.scriptLoaderCache.push(src);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public loadScriptConcurrent = async (
|
||||
srcList: Array<string>
|
||||
): Promise<any> => {
|
||||
if (Array.isArray(srcList)) {
|
||||
const len: number = srcList.length;
|
||||
if (len > 0) {
|
||||
let count = 0;
|
||||
srcList.map(src => {
|
||||
if (src) {
|
||||
this.loadScript(src).then(() => {
|
||||
count++;
|
||||
if (count === len) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const loader = new loaderProxy();
|
38
src/utils/message/index.ts
Normal file
38
src/utils/message/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
// 消息
|
||||
const Message = (message: string): any => {
|
||||
return ElMessage({
|
||||
showClose: true,
|
||||
message
|
||||
});
|
||||
};
|
||||
|
||||
// 成功
|
||||
const successMessage = (message: string): any => {
|
||||
return ElMessage({
|
||||
showClose: true,
|
||||
message,
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
// 警告
|
||||
const warnMessage = (message: string): any => {
|
||||
return ElMessage({
|
||||
showClose: true,
|
||||
message,
|
||||
type: "warning"
|
||||
});
|
||||
};
|
||||
|
||||
// 失败
|
||||
const errorMessage = (message: string): any => {
|
||||
return ElMessage({
|
||||
showClose: true,
|
||||
message,
|
||||
type: "error"
|
||||
});
|
||||
};
|
||||
|
||||
export { Message, successMessage, warnMessage, errorMessage };
|
21
src/utils/mitt.ts
Normal file
21
src/utils/mitt.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { Emitter } from "mitt";
|
||||
import mitt from "mitt";
|
||||
|
||||
type Events = {
|
||||
resize: {
|
||||
detail: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
};
|
||||
openPanel: string;
|
||||
tagViewsChange: string;
|
||||
tagViewsShowModel: string;
|
||||
logoChange: string;
|
||||
changLayoutRoute: {
|
||||
indexPath: string;
|
||||
parentPath: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const emitter: Emitter<Events> = mitt<Events>();
|
42
src/utils/operate/index.ts
Normal file
42
src/utils/operate/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export const hasClass = (ele: RefType<any>, cls: string): any => {
|
||||
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
||||
};
|
||||
|
||||
export const addClass = (
|
||||
ele: RefType<any>,
|
||||
cls: string,
|
||||
extracls?: string
|
||||
): any => {
|
||||
if (!hasClass(ele, cls)) ele.className += " " + cls;
|
||||
if (extracls) {
|
||||
if (!hasClass(ele, extracls)) ele.className += " " + extracls;
|
||||
}
|
||||
};
|
||||
|
||||
export const removeClass = (
|
||||
ele: RefType<any>,
|
||||
cls: string,
|
||||
extracls?: string
|
||||
): any => {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
|
||||
ele.className = ele.className.replace(reg, " ").trim();
|
||||
}
|
||||
if (extracls) {
|
||||
if (hasClass(ele, extracls)) {
|
||||
const regs = new RegExp("(\\s|^)" + extracls + "(\\s|$)");
|
||||
ele.className = ele.className.replace(regs, " ").trim();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const toggleClass = (
|
||||
flag: boolean,
|
||||
clsName: string,
|
||||
target?: RefType<any>
|
||||
): any => {
|
||||
const targetEl = target || document.body;
|
||||
let { className } = targetEl;
|
||||
className = className.replace(clsName, "");
|
||||
targetEl.className = flag ? `${className} ${clsName} ` : className;
|
||||
};
|
17
src/utils/progress/index.ts
Normal file
17
src/utils/progress/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import NProgress from "nprogress";
|
||||
import "nprogress/nprogress.css";
|
||||
|
||||
NProgress.configure({
|
||||
// 动画方式
|
||||
easing: "ease",
|
||||
// 递增进度条的速度
|
||||
speed: 500,
|
||||
// 是否显示加载ico
|
||||
showSpinner: true,
|
||||
// 自动递增间隔
|
||||
trickleSpeed: 200,
|
||||
// 初始化时的最小百分比
|
||||
minimum: 0.3
|
||||
});
|
||||
|
||||
export default NProgress;
|
34
src/utils/propTypes.ts
Normal file
34
src/utils/propTypes.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { CSSProperties, VNodeChild } from "vue";
|
||||
import { createTypes, VueTypeValidableDef, VueTypesInterface } from "vue-types";
|
||||
|
||||
export type VueNode = VNodeChild | JSX.Element;
|
||||
|
||||
type PropTypes = VueTypesInterface & {
|
||||
readonly style: VueTypeValidableDef<CSSProperties>;
|
||||
readonly VNodeChild: VueTypeValidableDef<VueNode>;
|
||||
};
|
||||
|
||||
const propTypes = createTypes({
|
||||
func: undefined,
|
||||
bool: undefined,
|
||||
string: undefined,
|
||||
number: undefined,
|
||||
object: undefined,
|
||||
integer: undefined
|
||||
}) as PropTypes;
|
||||
|
||||
propTypes.extend([
|
||||
{
|
||||
name: "style",
|
||||
getter: true,
|
||||
type: [String, Object],
|
||||
default: undefined
|
||||
},
|
||||
{
|
||||
name: "VNodeChild",
|
||||
getter: true,
|
||||
type: undefined
|
||||
}
|
||||
]);
|
||||
|
||||
export { propTypes };
|
35
src/utils/resize/index.ts
Normal file
35
src/utils/resize/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
|
||||
const isServer = typeof window === "undefined";
|
||||
|
||||
const resizeHandler = (entries: any[]): void => {
|
||||
for (const entry of entries) {
|
||||
const listeners = entry.target.__resizeListeners__ || [];
|
||||
if (listeners.length) {
|
||||
listeners.forEach((fn: () => any) => {
|
||||
fn();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const addResizeListener = (element: any, fn: () => any): any => {
|
||||
if (isServer) return;
|
||||
if (!element.__resizeListeners__) {
|
||||
element.__resizeListeners__ = [];
|
||||
element.__ro__ = new ResizeObserver(resizeHandler);
|
||||
element.__ro__.observe(element);
|
||||
}
|
||||
element.__resizeListeners__.push(fn);
|
||||
};
|
||||
|
||||
export const removeResizeListener = (element: any, fn: () => any): any => {
|
||||
if (!element || !element.__resizeListeners__) return;
|
||||
element.__resizeListeners__.splice(
|
||||
element.__resizeListeners__.indexOf(fn),
|
||||
1
|
||||
);
|
||||
if (!element.__resizeListeners__.length) {
|
||||
element.__ro__.disconnect();
|
||||
}
|
||||
};
|
53
src/utils/storage/cookie.ts
Normal file
53
src/utils/storage/cookie.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
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();
|
93
src/utils/storage/db.ts
Normal file
93
src/utils/storage/db.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
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();
|
46
src/utils/storage/index.ts
Normal file
46
src/utils/storage/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
interface ProxyStorage {
|
||||
getItem(key: string): any;
|
||||
setItem(Key: string, value: string): void;
|
||||
removeItem(key: string): void;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
//sessionStorage operate
|
||||
class sessionStorageProxy implements ProxyStorage {
|
||||
protected storage: ProxyStorage;
|
||||
|
||||
constructor(storageModel: ProxyStorage) {
|
||||
this.storage = storageModel;
|
||||
}
|
||||
|
||||
// 存
|
||||
public setItem(key: string, value: any): void {
|
||||
this.storage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
// 取
|
||||
public getItem(key: string): any {
|
||||
return JSON.parse(this.storage.getItem(key)) || null;
|
||||
}
|
||||
|
||||
// 删
|
||||
public removeItem(key: string): void {
|
||||
this.storage.removeItem(key);
|
||||
}
|
||||
|
||||
// 清空
|
||||
public clear(): void {
|
||||
this.storage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//localStorage operate
|
||||
class localStorageProxy extends sessionStorageProxy implements ProxyStorage {
|
||||
constructor(localStorage: ProxyStorage) {
|
||||
super(localStorage);
|
||||
}
|
||||
}
|
||||
|
||||
export const storageSession = new sessionStorageProxy(sessionStorage);
|
||||
|
||||
export const storageLocal = new localStorageProxy(localStorage);
|
37
src/utils/storage/responsive.ts
Normal file
37
src/utils/storage/responsive.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// 响应式storage
|
||||
import { App } from "vue";
|
||||
import Storage from "responsive-storage";
|
||||
|
||||
export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
|
||||
app.use(Storage, {
|
||||
// 默认显示首页tag
|
||||
routesInStorage: {
|
||||
type: Array,
|
||||
default: Storage.getData(undefined, "routesInStorage") ?? [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "message.hshome",
|
||||
icon: "el-icon-s-home",
|
||||
showLink: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// 国际化 默认中文zh
|
||||
locale: {
|
||||
type: Object,
|
||||
default: Storage.getData(undefined, "locale") ?? {
|
||||
locale: config.Locale
|
||||
}
|
||||
},
|
||||
// layout模式以及主题
|
||||
layout: {
|
||||
type: Object,
|
||||
default: Storage.getData(undefined, "layout") ?? {
|
||||
layout: config.Layout
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
42
src/utils/useAttrs.ts
Normal file
42
src/utils/useAttrs.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { getCurrentInstance, reactive, shallowRef, watchEffect } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
interface Params {
|
||||
excludeListeners?: boolean;
|
||||
excludeKeys?: string[];
|
||||
}
|
||||
|
||||
const DEFAULT_EXCLUDE_KEYS = ["class", "style"];
|
||||
const LISTENER_PREFIX = /^on[A-Z]/;
|
||||
|
||||
export function entries<T>(obj: Recordable<T>): [string, T][] {
|
||||
return Object.keys(obj).map((key: string) => [key, obj[key]]);
|
||||
}
|
||||
|
||||
export function useAttrs(params: Params = {}): Ref<Recordable> | {} {
|
||||
const instance = getCurrentInstance();
|
||||
if (!instance) return {};
|
||||
|
||||
const { excludeListeners = false, excludeKeys = [] } = params;
|
||||
const attrs = shallowRef({});
|
||||
const allExcludeKeys = excludeKeys.concat(DEFAULT_EXCLUDE_KEYS);
|
||||
|
||||
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
|
||||
instance.attrs = reactive(instance.attrs);
|
||||
|
||||
watchEffect(() => {
|
||||
const res = entries(instance.attrs).reduce((acm, [key, val]) => {
|
||||
if (
|
||||
!allExcludeKeys.includes(key) &&
|
||||
!(excludeListeners && LISTENER_PREFIX.test(key))
|
||||
) {
|
||||
acm[key] = val;
|
||||
}
|
||||
|
||||
return acm;
|
||||
}, {} as Recordable);
|
||||
|
||||
attrs.value = res;
|
||||
});
|
||||
|
||||
return attrs;
|
||||
}
|
72
src/utils/useCopyToClipboard.ts
Normal file
72
src/utils/useCopyToClipboard.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
import { isDef } from "/@/utils/is";
|
||||
interface Options {
|
||||
target?: HTMLElement;
|
||||
}
|
||||
export function useCopyToClipboard(initial?: string) {
|
||||
const clipboardRef = ref(initial || "");
|
||||
const isSuccessRef = ref(false);
|
||||
const copiedRef = ref(false);
|
||||
|
||||
watch(
|
||||
clipboardRef,
|
||||
(str?: string) => {
|
||||
if (isDef(str)) {
|
||||
copiedRef.value = true;
|
||||
isSuccessRef.value = copyTextToClipboard(str);
|
||||
}
|
||||
},
|
||||
{ immediate: !!initial, flush: "sync" }
|
||||
);
|
||||
|
||||
return { clipboardRef, isSuccessRef, copiedRef };
|
||||
}
|
||||
|
||||
export function copyTextToClipboard(
|
||||
input: string,
|
||||
{ target = document.body }: Options = {}
|
||||
) {
|
||||
const element = document.createElement("textarea");
|
||||
const previouslyFocusedElement = document.activeElement;
|
||||
|
||||
element.value = input;
|
||||
|
||||
element.setAttribute("readonly", "");
|
||||
|
||||
(element.style as any).contain = "strict";
|
||||
element.style.position = "absolute";
|
||||
element.style.left = "-9999px";
|
||||
element.style.fontSize = "12pt";
|
||||
|
||||
const selection = document.getSelection();
|
||||
let originalRange;
|
||||
if (selection && selection.rangeCount > 0) {
|
||||
originalRange = selection.getRangeAt(0);
|
||||
}
|
||||
|
||||
target.append(element);
|
||||
element.select();
|
||||
|
||||
element.selectionStart = 0;
|
||||
element.selectionEnd = input.length;
|
||||
|
||||
let isSuccess = false;
|
||||
try {
|
||||
isSuccess = document.execCommand("copy");
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
element.remove();
|
||||
|
||||
if (originalRange && selection) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(originalRange);
|
||||
}
|
||||
|
||||
if (previouslyFocusedElement) {
|
||||
(previouslyFocusedElement as HTMLElement).focus();
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
28
src/utils/uuid.ts
Normal file
28
src/utils/uuid.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
const hexList: string[] = [];
|
||||
for (let i = 0; i <= 15; i++) {
|
||||
hexList[i] = i.toString(16);
|
||||
}
|
||||
|
||||
export function buildUUID(): string {
|
||||
let uuid = "";
|
||||
for (let i = 1; i <= 36; i++) {
|
||||
if (i === 9 || i === 14 || i === 19 || i === 24) {
|
||||
uuid += "-";
|
||||
} else if (i === 15) {
|
||||
uuid += 4;
|
||||
} else if (i === 20) {
|
||||
uuid += hexList[(Math.random() * 4) | 8];
|
||||
} else {
|
||||
uuid += hexList[(Math.random() * 16) | 0];
|
||||
}
|
||||
}
|
||||
return uuid.replace(/-/g, "");
|
||||
}
|
||||
|
||||
let unique = 0;
|
||||
export function buildShortUUID(prefix = ""): string {
|
||||
const time = Date.now();
|
||||
const random = Math.floor(Math.random() * 1000000000);
|
||||
unique++;
|
||||
return prefix + "_" + random + unique + String(time);
|
||||
}
|
Reference in New Issue
Block a user