feat: add pure-admin-thin

This commit is contained in:
xiaoxian521
2021-10-16 16:16:58 +08:00
parent 7067396ade
commit f4b5150b03
127 changed files with 11709 additions and 2 deletions

View 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();

View 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);
};
};

View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
import EnclosureHttp from "./core";
export const http = new EnclosureHttp();

50
src/utils/http/types.d.ts vendored Normal file
View 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
View 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
View 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
View 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();
};

View 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();

View 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
View 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>();

View 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;
};

View 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
View 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
View 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();
}
};

View 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
View 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();

View 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);

View 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
View 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;
}

View 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
View 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);
}