release: update 3.4.5
This commit is contained in:
5
src/utils/README.md
Normal file
5
src/utils/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
### 注意
|
||||
|
||||
- [文档](https://pure-admin-utils-docs.vercel.app/)
|
||||
- [npm](https://www.npmjs.com/package/@pureadmin/utils)
|
||||
- vue-pure-admin 从 3.3.0 版本之后(不包括 3.3.0 版本),大部分工具和 hooks 都集成到了[@pureadmin/utils](https://pure-admin-utils-docs.vercel.app/)
|
@@ -1,39 +0,0 @@
|
||||
import { unref } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
|
||||
type FunctionArgs<Args extends any[] = any[], Return = void> = (
|
||||
...args: Args
|
||||
) => Return;
|
||||
|
||||
type MaybeRef<T> = T | Ref<T>;
|
||||
|
||||
// 延迟函数
|
||||
export const delay = (timeout: number) =>
|
||||
new Promise(resolve => setTimeout(resolve, timeout));
|
||||
|
||||
/**
|
||||
* 防抖函数
|
||||
* @param fn 函数
|
||||
* @param timeout 延迟时间
|
||||
* @param immediate 是否立即执行
|
||||
* @returns
|
||||
*/
|
||||
export const debounce = <T extends FunctionArgs>(
|
||||
fn: T,
|
||||
timeout: MaybeRef<number> = 200,
|
||||
immediate = false
|
||||
) => {
|
||||
let timmer: TimeoutHandle;
|
||||
const wait = unref(timeout);
|
||||
return () => {
|
||||
timmer && clearTimeout(timmer);
|
||||
if (immediate) {
|
||||
if (!timmer) {
|
||||
fn();
|
||||
}
|
||||
timmer = setTimeout(() => (timmer = null), wait);
|
||||
} else {
|
||||
timmer = setTimeout(fn, wait);
|
||||
}
|
||||
};
|
||||
};
|
@@ -1,37 +0,0 @@
|
||||
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;
|
||||
};
|
118
src/utils/is.ts
118
src/utils/is.ts
@@ -1,118 +0,0 @@
|
||||
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;
|
||||
|
||||
/** url链接正则 */
|
||||
export function isUrl<T>(value: T): boolean {
|
||||
const reg =
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
/(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
|
||||
// @ts-expect-error
|
||||
return reg.test(value);
|
||||
}
|
||||
|
||||
/** 手机号码正则 */
|
||||
export function isPhone<T>(value: T): boolean {
|
||||
const reg =
|
||||
/^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/;
|
||||
// @ts-expect-error
|
||||
return reg.test(value);
|
||||
}
|
||||
|
||||
/** 邮箱正则 */
|
||||
export function isEmail<T>(value: T): boolean {
|
||||
const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
|
||||
// @ts-expect-error
|
||||
return reg.test(value);
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
export const openLink = <T>(link: T): void => {
|
||||
const $a: HTMLElement = document.createElement("a");
|
||||
// @ts-expect-error
|
||||
$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();
|
||||
};
|
@@ -1,54 +0,0 @@
|
||||
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();
|
@@ -1,38 +0,0 @@
|
||||
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 };
|
@@ -11,7 +11,7 @@ type Events = {
|
||||
openPanel: string;
|
||||
tagViewsChange: string;
|
||||
tagViewsShowModel: string;
|
||||
logoChange: string;
|
||||
logoChange: boolean;
|
||||
changLayoutRoute: {
|
||||
indexPath: string;
|
||||
parentPath: string;
|
||||
|
@@ -1,57 +0,0 @@
|
||||
import type { FunctionArgs } from "@vueuse/core";
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
export function useRafThrottle<T extends FunctionArgs>(fn: T): T {
|
||||
let locked = false;
|
||||
// @ts-ignore
|
||||
return function (...args) {
|
||||
if (locked) return;
|
||||
locked = true;
|
||||
window.requestAnimationFrame(() => {
|
||||
fn.apply(this, args);
|
||||
locked = false;
|
||||
});
|
||||
};
|
||||
}
|
@@ -98,8 +98,7 @@ Print.prototype = {
|
||||
const child = selects[k3].children;
|
||||
for (const i in child) {
|
||||
if (child[i].tagName == "OPTION") {
|
||||
// @ts-ignore
|
||||
if (child[i].selected == true) {
|
||||
if ((child[i] as any).selected == true) {
|
||||
child[i].setAttribute("selected", "selected");
|
||||
} else {
|
||||
child[i].removeAttribute("selected");
|
||||
|
@@ -1,35 +0,0 @@
|
||||
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();
|
||||
}
|
||||
};
|
41
src/utils/responsive.ts
Normal file
41
src/utils/responsive.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// 响应式storage
|
||||
import { App } from "vue";
|
||||
import Storage from "responsive-storage";
|
||||
import { routerArrays } from "/@/layout/types";
|
||||
|
||||
const nameSpace = "responsive-";
|
||||
|
||||
export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
|
||||
const configObj = Object.assign(
|
||||
{
|
||||
// 国际化 默认中文zh
|
||||
locale: Storage.getData("locale", nameSpace) ?? {
|
||||
locale: config.Locale ?? "zh"
|
||||
},
|
||||
// layout模式以及主题
|
||||
layout: Storage.getData("layout", nameSpace) ?? {
|
||||
layout: config.Layout ?? "vertical",
|
||||
theme: config.Theme ?? "default",
|
||||
darkMode: config.DarkMode ?? false,
|
||||
sidebarStatus: config.SidebarStatus ?? true,
|
||||
epThemeColor: config.EpThemeColor ?? "#409EFF"
|
||||
},
|
||||
configure: Storage.getData("configure", nameSpace) ?? {
|
||||
grey: config.Grey ?? false,
|
||||
weak: config.Weak ?? false,
|
||||
hideTabs: config.HideTabs ?? false,
|
||||
showLogo: config.ShowLogo ?? true,
|
||||
showModel: config.ShowModel ?? "smart",
|
||||
multiTagsCache: config.MultiTagsCache ?? false
|
||||
}
|
||||
},
|
||||
config.MultiTagsCache
|
||||
? {
|
||||
// 默认显示首页tag
|
||||
tags: Storage.getData("tags", nameSpace) ?? routerArrays
|
||||
}
|
||||
: {}
|
||||
);
|
||||
|
||||
app.use(Storage, { nameSpace, memory: configObj });
|
||||
};
|
@@ -1,46 +0,0 @@
|
||||
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));
|
||||
}
|
||||
|
||||
// 删
|
||||
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);
|
@@ -1,59 +0,0 @@
|
||||
// 响应式storage
|
||||
import { App } from "vue";
|
||||
import Storage from "responsive-storage";
|
||||
|
||||
export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
|
||||
const configObj = Object.assign(
|
||||
{
|
||||
// 国际化 默认中文zh
|
||||
locale: {
|
||||
type: Object,
|
||||
default: Storage.getData(undefined, "locale") ?? {
|
||||
locale: config.Locale ?? "zh"
|
||||
}
|
||||
},
|
||||
// layout模式以及主题
|
||||
layout: {
|
||||
type: Object,
|
||||
default: Storage.getData(undefined, "layout") ?? {
|
||||
layout: config.Layout ?? "vertical",
|
||||
theme: config.Theme ?? "default",
|
||||
darkMode: config.DarkMode ?? false,
|
||||
sidebarStatus: config.SidebarStatus ?? true,
|
||||
epThemeColor: config.EpThemeColor ?? "#409EFF"
|
||||
}
|
||||
},
|
||||
configure: {
|
||||
type: Object,
|
||||
default: Storage.getData(undefined, "configure") ?? {
|
||||
grey: config.Grey ?? false,
|
||||
weak: config.Weak ?? false,
|
||||
hideTabs: config.HideTabs ?? false,
|
||||
showLogo: config.ShowLogo ?? true,
|
||||
showModel: config.ShowModel ?? "smart",
|
||||
multiTagsCache: config.MultiTagsCache ?? false
|
||||
}
|
||||
}
|
||||
},
|
||||
config.MultiTagsCache
|
||||
? {
|
||||
// 默认显示首页tag
|
||||
tags: {
|
||||
type: Array,
|
||||
default: Storage.getData(undefined, "tags") ?? [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "menus.hshome",
|
||||
icon: "home-filled"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
: {}
|
||||
);
|
||||
|
||||
app.use(Storage, configObj);
|
||||
};
|
@@ -1,175 +0,0 @@
|
||||
/**
|
||||
* 提取菜单树中的每一项uniqueId
|
||||
* @param {Array} {menuTree 菜单树}
|
||||
* @param {return}} expandedPaths 每一项uniqueId组成的数组
|
||||
*/
|
||||
const expandedPaths = [];
|
||||
export function extractPathList(menuTree) {
|
||||
if (!Array.isArray(menuTree)) {
|
||||
console.warn("menuTree must be an array");
|
||||
return;
|
||||
}
|
||||
if (!menuTree || menuTree.length === 0) return;
|
||||
for (const node of menuTree) {
|
||||
const hasChildren = node.children && node.children.length > 0;
|
||||
if (hasChildren) {
|
||||
extractPathList(node.children);
|
||||
}
|
||||
expandedPaths.push(node.uniqueId);
|
||||
}
|
||||
return expandedPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果父级下children的length为1,删除children并自动组建唯一uniqueId
|
||||
* @param {Array} {menuTree 菜单树}
|
||||
* @param {Array} {pathList 每一项的id组成的数组}
|
||||
* @param {return}}
|
||||
*/
|
||||
export function deleteChildren(menuTree, pathList = []) {
|
||||
if (!Array.isArray(menuTree)) {
|
||||
console.warn("menuTree must be an array");
|
||||
return;
|
||||
}
|
||||
if (!menuTree || menuTree.length === 0) return;
|
||||
for (const [key, node] of menuTree.entries()) {
|
||||
if (node.children && node.children.length === 1) delete node.children;
|
||||
node.id = key;
|
||||
node.parentId = pathList.length ? pathList[pathList.length - 1] : null;
|
||||
node.pathList = [...pathList, node.id];
|
||||
node.uniqueId =
|
||||
node.pathList.length > 1 ? node.pathList.join("-") : node.pathList[0];
|
||||
const hasChildren = node.children && node.children.length > 0;
|
||||
if (hasChildren) {
|
||||
deleteChildren(node.children, node.pathList);
|
||||
}
|
||||
}
|
||||
return menuTree;
|
||||
}
|
||||
|
||||
// 创建层级关系
|
||||
export function buildHierarchyTree(menuTree, pathList = []) {
|
||||
if (!Array.isArray(menuTree)) {
|
||||
console.warn("menuTree must be an array");
|
||||
return;
|
||||
}
|
||||
if (!menuTree || menuTree.length === 0) return;
|
||||
for (const [key, node] of menuTree.entries()) {
|
||||
node.id = key;
|
||||
node.parentId = pathList.length ? pathList[pathList.length - 1] : null;
|
||||
node.pathList = [...pathList, node.id];
|
||||
const hasChildren = node.children && node.children.length > 0;
|
||||
if (hasChildren) {
|
||||
buildHierarchyTree(node.children, node.pathList);
|
||||
}
|
||||
}
|
||||
return menuTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 广度优先遍历算法,找当前节点
|
||||
* @param {Array} tree 原始树,数组
|
||||
* @param {Number|String} uniqueId 唯一uniqueId
|
||||
* @return {Object} node
|
||||
*/
|
||||
export function getNodeByUniqueId(menuTree, uniqueId) {
|
||||
if (!Array.isArray(menuTree)) {
|
||||
console.warn("menuTree must be an array");
|
||||
return;
|
||||
}
|
||||
if (!menuTree || menuTree.length === 0) return;
|
||||
const item = menuTree.find(node => node.uniqueId === uniqueId);
|
||||
if (item) return item;
|
||||
const childrenList = menuTree
|
||||
.filter(node => node.children)
|
||||
.map(i => i.children)
|
||||
.flat(1);
|
||||
return getNodeByUniqueId(childrenList, uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向当前唯一uniqueId节点追加字段
|
||||
* @param {Array} {menuTree 菜单树}
|
||||
* @param {Number|String} uniqueId 唯一uniqueId
|
||||
* @param {Object} fields 唯一uniqueId
|
||||
* @return {menuTree} 追加字段后的树
|
||||
*/
|
||||
export function appendFieldByUniqueId(
|
||||
menuTree: Array<any>,
|
||||
uniqueId: Number | String,
|
||||
fields: Object
|
||||
) {
|
||||
if (!Array.isArray(menuTree)) {
|
||||
console.warn("menuTree must be an array");
|
||||
return;
|
||||
}
|
||||
if (!menuTree || menuTree.length === 0) return {};
|
||||
for (const node of menuTree) {
|
||||
const hasChildren = node.children && node.children.length > 0;
|
||||
if (
|
||||
node.uniqueId === uniqueId &&
|
||||
Object.prototype.toString.call(fields) === "[object Object]"
|
||||
)
|
||||
Object.assign(node, fields);
|
||||
if (hasChildren) {
|
||||
appendFieldByUniqueId(node.children, uniqueId, fields);
|
||||
}
|
||||
}
|
||||
return menuTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造树型结构数据
|
||||
* @param {*} data 数据源
|
||||
* @param {*} id id字段 默认 'id'
|
||||
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||
* @param {*} children 孩子节点字段 默认 'children'
|
||||
*/
|
||||
export function handleTree(
|
||||
data,
|
||||
id?: string,
|
||||
parentId?: string,
|
||||
children?: string
|
||||
) {
|
||||
const config = {
|
||||
id: id || "id",
|
||||
parentId: parentId || "parentId",
|
||||
childrenList: children || "children"
|
||||
};
|
||||
|
||||
const childrenListMap = {};
|
||||
const nodeIds = {};
|
||||
const tree = [];
|
||||
|
||||
for (const d of data) {
|
||||
const parentId = d[config.parentId];
|
||||
if (childrenListMap[parentId] == null) {
|
||||
childrenListMap[parentId] = [];
|
||||
}
|
||||
nodeIds[d[config.id]] = d;
|
||||
childrenListMap[parentId].push(d);
|
||||
}
|
||||
|
||||
for (const d of data) {
|
||||
const parentId = d[config.parentId];
|
||||
if (nodeIds[parentId] == null) {
|
||||
tree.push(d);
|
||||
}
|
||||
}
|
||||
|
||||
for (const t of tree) {
|
||||
adaptToChildrenList(t);
|
||||
}
|
||||
|
||||
function adaptToChildrenList(o) {
|
||||
if (childrenListMap[o[config.id]] !== null) {
|
||||
o[config.childrenList] = childrenListMap[o[config.id]];
|
||||
}
|
||||
if (o[config.childrenList]) {
|
||||
for (const c of o[config.childrenList]) {
|
||||
adaptToChildrenList(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
import { h, resolveComponent } from "vue";
|
||||
|
||||
export const dynamicComponent = (component: string) =>
|
||||
h(resolveComponent(component));
|
@@ -1,72 +0,0 @@
|
||||
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;
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
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);
|
||||
}
|
@@ -1,116 +0,0 @@
|
||||
import {
|
||||
ref,
|
||||
Ref,
|
||||
unref,
|
||||
shallowRef,
|
||||
onBeforeUnmount,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
import { isDef } from "/@/utils/is";
|
||||
import { useRafThrottle } from "/@/utils/operate";
|
||||
import { addResizeListener, removeResizeListener } from "/@/utils/resize";
|
||||
|
||||
const domSymbol = Symbol("watermark-dom");
|
||||
|
||||
type attr = {
|
||||
font?: string;
|
||||
fillStyle?: string;
|
||||
};
|
||||
|
||||
export function useWatermark(
|
||||
appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>
|
||||
) {
|
||||
const func = useRafThrottle(function () {
|
||||
const el = unref(appendEl);
|
||||
if (!el) return;
|
||||
const { clientHeight: height, clientWidth: width } = el;
|
||||
updateWatermark({ height, width });
|
||||
});
|
||||
const id = domSymbol.toString();
|
||||
const watermarkEl = shallowRef<HTMLElement>();
|
||||
|
||||
const clear = () => {
|
||||
const domId = unref(watermarkEl);
|
||||
watermarkEl.value = undefined;
|
||||
const el = unref(appendEl);
|
||||
if (!el) return;
|
||||
domId && el.removeChild(domId);
|
||||
removeResizeListener(el, func);
|
||||
};
|
||||
|
||||
function createBase64(str: string, attr?: attr) {
|
||||
const can = document.createElement("canvas");
|
||||
const width = 300;
|
||||
const height = 240;
|
||||
Object.assign(can, { width, height });
|
||||
|
||||
const cans = can.getContext("2d");
|
||||
if (cans) {
|
||||
cans.rotate((-20 * Math.PI) / 120);
|
||||
cans.font = attr?.font ?? "15px Reggae One";
|
||||
cans.fillStyle = attr?.fillStyle ?? "rgba(0, 0, 0, 0.15)";
|
||||
cans.textAlign = "left";
|
||||
cans.textBaseline = "middle";
|
||||
cans.fillText(str, width / 20, height);
|
||||
}
|
||||
return can.toDataURL("image/png");
|
||||
}
|
||||
|
||||
function updateWatermark(
|
||||
options: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
str?: string;
|
||||
attr?: attr;
|
||||
} = {}
|
||||
) {
|
||||
const el = unref(watermarkEl);
|
||||
if (!el) return;
|
||||
if (isDef(options.width)) {
|
||||
el.style.width = `${options.width}px`;
|
||||
}
|
||||
if (isDef(options.height)) {
|
||||
el.style.height = `${options.height}px`;
|
||||
}
|
||||
if (isDef(options.str)) {
|
||||
el.style.background = `url(${createBase64(
|
||||
options.str,
|
||||
options.attr
|
||||
)}) left top repeat`;
|
||||
}
|
||||
}
|
||||
|
||||
const createWatermark = (str: string, attr?: attr) => {
|
||||
if (unref(watermarkEl)) {
|
||||
updateWatermark({ str, attr });
|
||||
return id;
|
||||
}
|
||||
const div = document.createElement("div");
|
||||
watermarkEl.value = div;
|
||||
div.id = id;
|
||||
div.style.pointerEvents = "none";
|
||||
div.style.top = "0px";
|
||||
div.style.left = "0px";
|
||||
div.style.position = "absolute";
|
||||
div.style.zIndex = "100000";
|
||||
const el = unref(appendEl);
|
||||
if (!el) return id;
|
||||
const { clientHeight: height, clientWidth: width } = el;
|
||||
updateWatermark({ str, width, height, attr });
|
||||
el.appendChild(div);
|
||||
return id;
|
||||
};
|
||||
|
||||
function setWatermark(str: string, attr?: attr) {
|
||||
createWatermark(str, attr);
|
||||
addResizeListener(document.documentElement, func);
|
||||
const instance = getCurrentInstance();
|
||||
if (instance) {
|
||||
onBeforeUnmount(() => {
|
||||
clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { setWatermark, clear };
|
||||
}
|
Reference in New Issue
Block a user