Vue3其它工具类:other.ts

javascript 复制代码
import {nextTick, defineAsyncComponent} from 'vue';
import type {App} from 'vue';
import * as svg from '@element-plus/icons-vue';
import router from '/@/router/index';
import {i18n} from '/@/i18n/index';
import {verifyUrl} from '/@/utils/toolsValidate';
import request from '/@/utils/request';
import {useMessage} from '/@/hooks/message';
// @ts-ignore
import * as CryptoJS from 'crypto-js';
import {sm4} from 'sm-crypto'
import {validateNull} from './validate';
import {RouteItem, RouteItems, RouteToFrom} from '/@/types/global';

// 引入组件
const SvgIcon = defineAsyncComponent(() => import('/@/components/SvgIcon/index.vue'));

/**
 * 导出全局注册 element plus svg 图标
 * @param app vue 实例
 * @description 使用:https://element-plus.gitee.io/zh-CN/component/icon.html
 */
export function elSvg(app: App) {
    const icons = svg as any;
    for (const i in icons) {
        app.component(`ele-${icons[i].name}`, icons[i]);
    }
    app.component('SvgIcon', SvgIcon);
}

/**
 * 设置菜单国际化
 * @param {object} item - 菜单项对象
 * @param {string} item.enName - 英文名称
 * @param {string} item.name - 名称
 * @returns {string} - 国际化后的名称
 */
export function setMenuI18n(item: any) {
    let name = i18n.global.t(item.name);
    if (name !== item.name) {
        return name;
    }

    return i18n.global.locale.value === 'en' ? item.meta.enName : item.meta.title;
}

/**
 * 图片懒加载
 * @param el dom 目标元素
 * @param arr 列表数据
 * @description data-xxx 属性用于存储页面或应用程序的私有自定义数据
 */
export const lazyImg = (el: string, arr: EmptyArrayType) => {
    const io = new IntersectionObserver((res) => {
        res.forEach((v: any) => {
            if (v.isIntersecting) {
                const {img, key} = v.target.dataset;
                v.target.src = img;
                v.target.onload = () => {
                    io.unobserve(v.target);
                    arr[key]['loading'] = false;
                };
            }
        });
    });
    nextTick(() => {
        document.querySelectorAll(el).forEach((img) => io.observe(img));
    });
};

/**
 * 对象深克隆
 * @param obj 源对象
 * @returns 克隆后的对象
 */
export function deepClone(obj: EmptyObjectType) {
    let newObj: EmptyObjectType;
    try {
        newObj = obj.push ? [] : {};
    } catch (error) {
        newObj = {};
    }
    for (let attr in obj) {
        if (obj[attr] && typeof obj[attr] === 'object') {
            newObj[attr] = deepClone(obj[attr]);
        } else {
            newObj[attr] = obj[attr];
        }
    }
    return newObj;
}

/**
 * 判断是否是移动端
 */
export function isMobile() {
    if (
        navigator.userAgent.match(
            /('phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone')/i
        )
    ) {
        return true;
    } else {
        return false;
    }
}

/**
 * 判断数组对象中所有属性是否为空,为空则删除当前行对象
 * @description @感谢大黄
 * @param list 数组对象
 * @returns 删除空值后的数组对象
 */
export function handleEmpty(list: EmptyArrayType) {
    const arr = [] as any[];
    for (const i in list) {
        const d = [] as any[];
        for (const j in list[i]) {
            d.push(list[i][j]);
        }
        const leng = d.filter((item) => item === '').length;
        if (leng !== d.length) {
            arr.push(list[i]);
        }
    }
    return arr;
}

/**
 * 打开外部链接
 * @param val 当前点击项菜单
 */
export function handleOpenLink(val: RouteItem) {
    router.push(val.path);
    if (verifyUrl(<string>val.meta?.isLink)) window.open(val.meta?.isLink);
    else window.open(`${val.meta?.isLink}`);
}

/**
 * 打开小窗口
 */
export const openWindow = (url: string, title: string, w: number, h: number) => {
    // @ts-ignore
    const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
    // @ts-ignore
    const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;

    const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
    const height = window.innerHeight
        ? window.innerHeight
        : document.documentElement.clientHeight
            ? document.documentElement.clientHeight
            : screen.height;

    const left = width / 2 - w / 2 + dualScreenLeft;
    const top = height / 2 - h / 2 + dualScreenTop;
    return window.open(
        url,
        title,
        'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' +
        w +
        ', height=' +
        h +
        ', top=' +
        top +
        ', left=' +
        left
    );
};


/**
 *加密处理
 */
export function encryption(src: string, keyWord: string) {
    const key = CryptoJS.enc.Utf8.parse(keyWord);
    // 加密
    var encrypted = CryptoJS.AES.encrypt(src, key, {
        iv: key,
        mode: CryptoJS.mode.CFB,
        padding: CryptoJS.pad.NoPadding,
    });
    return encrypted.toString();
}

/**
 *  解密
 * @param {*} params 参数列表
 * @returns 明文
 */
export function decryption(src: string, keyWord: string) {
    const key = CryptoJS.enc.Utf8.parse(keyWord);
    // 解密逻辑
    var decryptd = CryptoJS.AES.decrypt(src, key, {
        iv: key,
        mode: CryptoJS.mode.CFB,
        padding: CryptoJS.pad.NoPadding,
    });

    return decryptd.toString(CryptoJS.enc.Utf8);
}

/**
 * SM4加密处理
 */
export function sm4Encryption(src: string, keyWord: string) {
    return sm4.encrypt(src, keyWord);
}

/**
 * SM4解密处理
 * @param {*} params 参数列表
 * @returns 明文
 */
export function sm4Decryption(src: string, keyWord: string) {
    return sm4.decrypt(src, keyWord);
}


/**
 * Base64 加密
 * @param {*} src  明文
 * @returns 密文
 */
export function base64Encrypt(src: string) {
    const encodedWord = CryptoJS.enc.Utf8.parse(src);
    return CryptoJS.enc.Base64.stringify(encodedWord);
}

/**
 *
 * @param url 目标下载接口
 * @param query 查询参数
 * @param fileName 文件名称
 * @returns {*}
 */
export function downBlobFile(url: any, query: any, fileName: string) {
    return request({
        url: url,
        method: 'get',
        responseType: 'blob',
        params: query,
    }).then((response) => {
        handleBlobFile(response, fileName);
    });
}

/**
 * blob 文件刘处理
 * @param response 响应结果
 * @returns
 */
export function handleBlobFile(response: any, fileName: string) {
    // 处理返回的文件流
    const blob = response;
    if (blob && blob.size === 0) {
        useMessage().error('内容为空,无法下载');
        return;
    }
    const link = document.createElement('a');

    // 兼容一下 入参不是 File Blob 类型情况
    var binaryData = [] as any;
    binaryData.push(response);
    link.href = window.URL.createObjectURL(new Blob(binaryData));
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    window.setTimeout(function () {
        // @ts-ignore
        URL.revokeObjectURL(blob);
        document.body.removeChild(link);
    }, 0);
}

/**
 * @description 生成唯一 uuid
 * @return string
 */
export function generateUUID() {
    if (typeof crypto === 'object') {
        if (typeof crypto.randomUUID === 'function') {
            return crypto.randomUUID();
        }
        if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') {
            const callback = (c: any) => {
                const num = Number(c);
                return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16);
            };
            return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback);
        }
    }
    let timestamp = new Date().getTime();
    let performanceNow = (typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0;
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
        let random = Math.random() * 16;
        if (timestamp > 0) {
            random = (timestamp + random) % 16 | 0;
            timestamp = Math.floor(timestamp / 16);
        } else {
            random = (performanceNow + random) % 16 | 0;
            performanceNow = Math.floor(performanceNow / 16);
        }
        return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16);
    });
}

/**
 * 统一批量导出
 * @method elSvg 导出全局注册 element plus svg 图标
 * @method useTitle 设置浏览器标题国际化
 * @method setTagsViewNameI18n 设置 自定义 tagsView 名称、 自定义 tagsView 名称国际化
 * @method lazyImg 图片懒加载
 * @method globalComponentSize() element plus 全局组件大小
 * @method deepClone 对象深克隆
 * @method isMobile 判断是否是移动端
 * @method handleEmpty 判断数组对象中所有属性是否为空,为空则删除当前行对象
 * @method handleOpenLink 打开外部链接
 */
const other = {
    elSvg: (app: App) => {
        elSvg(app);
    },
    useTitle: () => {
        useTitle();
    },
    setMenuI18n(item: RouteItems) {
        return setMenuI18n(item);
    },
    lazyImg: (el: string, arr: EmptyArrayType) => {
        lazyImg(el, arr);
    },
    globalComponentSize: () => {
        return globalComponentSize();
    },
    deepClone: (obj: EmptyObjectType) => {
        return deepClone(obj);
    },
    isMobile: () => {
        return isMobile();
    },
    handleEmpty: (list: EmptyArrayType) => {
        return handleEmpty(list);
    },
    handleOpenLink: (val: RouteItem) => {
        handleOpenLink(val);
    },
    encryption: (src: string, keyWord: string) => {
        return encryption(src, keyWord);
    },
    decryption: (src: string, keyWord: string) => {
        return decryption(src, keyWord);
    },
    base64Encrypt: (data: any) => {
        return base64Encrypt(data);
    },
    downBlobFile: (url: any, query: any, fileName: string) => {
        return downBlobFile(url, query, fileName);
    },
    toUnderline: (str: string) => {
        return toUnderline(str);
    },
    openWindow: (url: string, title: string, w: number, h: number) => {
        return openWindow(url, title, w, h);
    },
    getQueryString: (url: string, paraName: string) => {
        return getQueryString(url, paraName);
    },
    adaptationUrl: (url?: string) => {
        return adaptationUrl(url);
    },
    resolveAllEunuchNodeId: (json: any[], idArr: any[], temp: any[] = []) => {
        return resolveAllEunuchNodeId(json, idArr, temp);
    },
    getNonDuplicateID: () => {
        return getNonDuplicateID();
    },

    addUnit: (value: string | number, unit = 'px') => {
        return addUnit(value, unit);
    },
};

export function getQueryString(url: string, paraName: string) {
    const arrObj = url.split('?');
    if (arrObj.length > 1) {
        const arrPara = arrObj[1].split('&');
        let arr;
        for (let i = 0; i < arrPara.length; i++) {
            arr = arrPara[i].split('=');
            // eslint-disable-next-line eqeqeq
            if (arr != null && arr[0] == paraName) {
                return arr[1];
            }
        }
        return '';
    } else {
        return '';
    }
}

/**
 * 列表结构转树结构
 * @param data
 * @param id
 * @param parentId
 * @param children
 * @param rootId
 * @returns {*}
 */
export function handleTree(data: any, id: any, parentId: any, children: any, rootId: any) {
    id = id || 'id';
    parentId = parentId || 'parentId';
    children = children || 'children';
    rootId =
        rootId ||
        Math.min.apply(
            Math,
            data.map((item: any) => {
                return item[parentId];
            })
        ) ||
        0;
    //对源数据深度克隆
    const cloneData = JSON.parse(JSON.stringify(data));
    //循环所有项
    const treeData = cloneData.filter((father: any) => {
        const branchArr = cloneData.filter((child: any) => {
            //返回每一项的子级数组
            return father[id] === child[parentId];
        });
        branchArr.length > 0 ? (father[children] = branchArr) : '';
        //返回第一层
        return father[parentId] === rootId;
    });
    return treeData !== '' ? treeData : data;
}

/**
 * 解析所有太监节点ID
 * @returns
 */
const resolveAllEunuchNodeId = (json: any[], idArr: any[], temp: any[] = []) => {
    for (const item of json) {
        if (item.children && item.children.length !== 0) {
            resolveAllEunuchNodeId(item.children, idArr, temp);
        } else {
            temp.push(...idArr.filter((id) => id === item.id));
        }
    }
    return temp;
};

/**
 *
 * @param str 驼峰转下划线
 * @returns 下划线
 */
export function toUnderline(str: string) {
    return str.replace(/([A-Z])/g, '_$1').toLowerCase();
}

/**
 * 自动适配不同的后端架构
 * 1. 例如 /act/oa/task ,在微服务架构保持不变,在单体架构编程 /admin/oa/task
 * 2. 特殊 /gen/xxx ,在微服务架构、单体架构编程 都需保持不变
 *
 * @param originUrl 原始路径
 */
const adaptationUrl = (originUrl?: string) => {
    // 微服务架构 不做路径转换,为空不做路径转换
    const isMicro = import.meta.env.VITE_IS_MICRO;
    if (validateNull(isMicro) || isMicro === 'true') {
        return originUrl;
    }

    // 转为 /admin 路由前缀的请求
    return `/admin/${originUrl?.split('/').splice(2).join('/')}`;
};

/**
 * @description 获取不重复的id
 * @param length { Number } id的长度
 * @return { String } id
 */
const getNonDuplicateID = (length = 8) => {
    let idStr = Date.now().toString(36);
    idStr += Math.random().toString(36).substring(3, length);
    return idStr;
};

/**
 * @description 添加单位
 * @param {String | Number} value 值 100
 * @param {String} unit 单位 px em rem
 */
const addUnit = (value: string | number, unit = 'px') => {
    return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
};

// 统一批量导出
export default other;
相关推荐
卸任9 小时前
Vibe coding工具使用心得
前端·ai编程
brien910 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance10 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M10 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写10 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李10 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易10 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding10 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马10 小时前
前沿视界
前端
kyriewen11 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员