"use strict";
import {config} from "../config.js";
import {fullMap} from "../fullMap.js";

class util {
    static S4() {
        const array = new Uint16Array(1);
        window.crypto.getRandomValues(array);
        return (array[0] & 0xFFFF).toString(16).padStart(4, '0');
    }
    static getUUID() {
        return ('khc' + util.S4() + util.S4() + util.S4() + util.S4() + util.S4() + util.S4() + util.S4() + util.S4());
    }
    static isNull(str) {
        if(typeof(str)=='string'||str==undefined){
            if (str != undefined && str != null && str.length > 0 && str != 'null' && str != 'NULL') {
                return false;
            } else {
                return true;
            }
        }
        if(str instanceof Array){
            if(str.length>0){
                return false;
            }else{
                return true;
            }
        }else if(str instanceof Object && Object.prototype.toString.call(str) === '[object Object]') {
            if(Object.keys(str).length > 0){
                return false;
            }else{
                return true;
            }
        }else{
            let objStr='';
            try {
                objStr=JSON.stringify(str);//jsonStr
            }catch (e) {
                let keyArray=Object.keys(str);//map key array
                objStr=JSON.stringify(keyArray)
            }
            if (objStr != undefined && objStr != null && objStr.length > 0 && objStr != 'null' && objStr != 'NULL') {
                return false;
            } else {
                return true;
            }
        }
    }
    static isChinese(str){
        let reg = new RegExp("[\\u4E00-\\u9FFF]+","g")
        if(reg.test(str)){
            return true;
        }else{
            return false;
        }
    }
    /**
     * @param jsPath 文件路径
     * @param parentObjID 对象id
     * @param method 方法名
     * @param code 允许为空，此参数用法不一
     */
    static import(jsPath,parentObjID,method,code) {
        if(fullMap.getObj(jsPath)==undefined){//第一次加载js脚本
            import(jsPath).then(module => {
                fullMap.push(jsPath,module)
                if(this.isNull(method)&&this.isNull(code)){
                    module.main(parentObjID);
                }else if(this.isNull(code)){
                    module.main(parentObjID,method);
                }else{
                    module.main(parentObjID,method,code);
                }
            })
        }else{//已加载js脚本，从集合取出对象
            let module=fullMap.getObj(jsPath);
            if(this.isNull(method)&&this.isNull(code)){
                module.main(parentObjID);
            }else if(this.isNull(code)){
                module.main(parentObjID,method);
            }else{
                module.main(parentObjID,method,code);
            }
        }

    }
    /**
     * @param jsPath 文件路径
     * @param jsonConfig json配置便于扩展
     * import2023为传json对象，旧版import传的是固定参数不好扩展
     */
    static import2023(jsPath,jsonConfig) {
        if(fullMap.getObj(jsPath)==undefined){//第一次加载js脚本
            import(jsPath).then(module => {
                fullMap.push(jsPath,module)
                jsonConfig.isFirstLoadJs=true;//标记第一次加载js脚本
                module.main(jsonConfig);
            }).catch(error => {
                console.error(`加载脚本失败：${jsPath}`);
            });
        }else{//已加载js脚本，从集合取出对象
            let module=fullMap.getObj(jsPath);
            jsonConfig.isFirstLoadJs=false;//标记非第一次加载js脚本
            module.main(jsonConfig);
        }
    }
    static toSafe(str) {
        if(util.isNull(str))return '';
        str = str.toString().replace(/\'/g, '&quot;');
        str = str.toString().replace(/\\/g, '\\\\');
        str = str.toString().replace(new RegExp(/( )/g), "&nbsp;");
        str = str.toString().replace(/</g, '&lt;');       // 转换小于号为&lt;
        str = str.toString().replace(/>/g, '&gt;');       // 转换大于号为&gt;
        return str;
    }
    static toOldChar(str) {
        if(util.isNull(str))return '';
        str = str.toString().replace(new RegExp('&quot;','gm'), '\'');
        str = str.toString().replace(new RegExp('&nbsp;','gm'), ' ');
        str = str.toString().replace(new RegExp('&lt;', 'gm'), '<');      // 转换&lt;为<
        str = str.toString().replace(new RegExp('&gt;', 'gm'), '>');      // 转换&gt;为>
        return str;
    }
    // 跳转编辑页面，适用于有子表的编辑在其他页面嵌入式的入口。
    // 比如A页面有两个编辑按钮，第一个按钮是编辑自身的数据，第二个按钮是编辑B页面的数据，但B页面有子表则弹出框不适用子表展示，则可以使用此方法
    // 暂时先放util里调用
    /**
     * @param obj.pageCode 不能为空
     * @param obj.dsCode 不能为空
     * @param obj.queryCode 不能为空
     * @param obj.formCode 不能为空
     * @param obj.pkId 主键不能为空
     * @param obj.pkFieldId 允许为空 默认id，可指定其他字段，如uuid或 parent uuid 等，唯一的即可
     * @param obj.taskId 允许为空 工作流任务id标识是否启用工作流功能，默认不启用 -1
     * @returns {Promise<unknown>|*}
     */
    static toEditPage(obj) {
        if(this.isNull(obj.taskId)){
            obj.taskId='-1';
        }
        let url=config.getIndexUrl()+'#'+obj.pageCode+''
            +'?'
        for(let key in obj) {
            url+=key+'='+obj[key]+'&';
        }
        if(this.isNull(obj['formShowType'])){
            url+='formShowType=page';
        }
        window.location.href=url;
    }
    static toLogin() {
        /*//首窗需要跳转单点登录地址
        let env=config.getEnv();
        if(env=='prd'){//单点登录地址
            window.location.href = 'http://192.141.252.2:8080/cas/oauth2.0/authorize?client_id=gdxt_oauth-cid&redirect_uri=http://192.141.25.169/gd-api/cas/index';
        }else{
            window.location.href = './login.html';
        }*/
        if(config.getConfigJson().loginType=='session'){
            window.location.replace(window.location.origin+config.getConfigJson().contextPath+'/session/login');
        }else{
            window.location.replace(window.location.origin+config.getConfigJson().contextFrontPath+'/khc/login.html');
        }
    }
    /**
     * 下载附件
     * @param fileInfo.source 文件存储源
     * @param fileInfo.diskFileName 文件名
     * @param fileInfo.realFileName 显示名称
     * @param fileInfo.suffix 文件后缀
     */
    static getFileUrl(fileInfo){
        if(fileInfo.source==undefined||fileInfo.source=='service'){
            return config.getConfigJson().downFileUrl+'?realFileName=' + fileInfo.realFileName + '&fileDiskName=' + fileInfo.diskFileName + '&suffix=' + fileInfo.suffix;
        }else{
            return config.getMinioUrl()+'/' + fileInfo.diskFileName;
        }
    }
    /**
     * 滚动至元素位置
     * @param obj.id
     * @param obj.dom
     * @param obj.isFocus 是否获取焦点
     */
    static toTops(obj){
        if(obj.id!=null&&obj.id!=undefined){
            let ele=document.getElementById(obj.id);
            if(ele!=null&&ele!=undefined){
                obj.dom=ele;
            }else{
                return ;
            }
        }else if(obj.dom==null||obj.dom==undefined){
            return ;
        }
        setTimeout(()=>{
            obj.dom.scrollIntoView({behavior:"smooth"});
            if(obj.isFocus){
                obj.dom.focus();
            }
        },100)
    }
    //国密算法sm2加密
    static sm2Encrypt(str) {
        return sm2Encrypt(str,config.getSM2PubKey(),0);
    }
    //对数组json排序
    static jsonSort(array, field, reverse) {
        //数组长度小于2 或 没有指定排序字段 或 不是json格式数据
        if (array.length < 2 || !field || typeof array[0] !== "object") return array;
        //数字类型排序
        if (typeof array[0][field] === "number") {
            array.sort((x, y)=> { return x[field] - y[field] });
        }
        //字符串类型排序
        if (typeof array[0][field] === "string") {
            array.sort((x, y)=> { return x[field].localeCompare(y[field]) });
        }
        //倒序
        if (reverse) {
            array.reverse();
        }
        return array;
    }
    //获取url中的参数
    static getUrlParam(name) {
        let url=window.location.href;
        let index = url.indexOf("?");
        let paramStr = url.substr(index,url.length);
        let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
        let r = paramStr.substr(1).match(reg);  //匹配目标参数
        if (r != null){
            return unescape(r[2]);//返回参数值
        }else{
            return null;
        }
    }
    static loadScript(url,type,callback){
        let script=document.createElement('script');
        script.type=type;
        if(script.readyState){//IE
            script.onreadystatechange=function(){
                if(script.readyState=='loaded'||script.readyState=='complete'){
                    script.onreadystatechange=null; callback();
                }
            };
        }else{//其他浏览器
            script.onload=function(){
                callback();
            };
        }
        script.src=url;
        document.getElementsByTagName('head')[0].appendChild(script);
    }
    static loadCss(url){
        let link=document.createElement('link');
        link.setAttribute('rel', 'stylesheet');
        link.setAttribute('type', 'text/css');
        link.setAttribute('href', url);
        document.getElementsByTagName('head')[0].appendChild(link);
    }

    // 获取cookie值
    static getCookie(cookie_name) {
        cookie_name = util.getPrefixedContext() + cookie_name;
        let allCookies = document.cookie;
        let cookiesArray = allCookies.split('; '); // 按照"; "分割每个cookie

        // 遍历每个cookie
        for (let i = 0; i < cookiesArray.length; i++) {
            let cookie = cookiesArray[i];
            // 找到cookie名称是否与目标名称匹配
            let equalPos = cookie.indexOf('=');
            let name = cookie.substring(0, equalPos); // 获取cookie名称

            if (name === cookie_name) {
                // 获取并返回cookie值
                return unescape(cookie.substring(equalPos + 1));
            }
        }
        // 如果没有找到，返回null
        return null;
    }

    static setCookie(name,val,min){
        let now=new Date();
        //设置时间
        now.setMinutes(now.getMinutes()+min)
        //设置Cookie
        document.cookie=util.getPrefixedContext() + name+'='+val+';expires='+now.toUTCString()+';path=/';
    }

    static removeCookie(name){
        this.setCookie(util.getPrefixedContext() + name,'',-1);
    }

    //es6 filter()方法删除数组值
    static removeArrayValue(array,value){
        let newArray=array.filter(item => item !== value);//删除原始值
        newArray=newArray.filter(item => item !== value+'');//转换字符串继续删
        return newArray;
    }

    static passwordEncrypt(password,timestamp,ip){
        let json={timestamp:timestamp,password:password,ip:ip}
        let jsonStr=JSON.stringify(json);
        return util.sm2Encrypt(jsonStr);
    }

    static textEncrypt(text,timestamp,ip){//和上面方法的区别就是变量名调整了
        let json={timestamp:timestamp,text:text,ip:ip}
        let jsonStr=JSON.stringify(json);
        return util.sm2Encrypt(jsonStr);
    }

    //传入中文获取预设颜色，获取不到则为自定义颜色，直接返回
    static getColorByName(colorName){
        let color=config.getConfigJson().defaultColorJson[colorName];
        if(color){
            return color;
        }else{
            return colorName;
        }
    }
    //传入1~8获取预设字体
    static getFontSizeByLevel(level){
        let fontSize=config.getConfigJson().defaultFontSizeJson[level];
        return fontSize;
    }
    //动态获取颜色，传入背景颜色，动态获取字体颜色（白色\黑色）
    static dynamicColor(bg){
        if(!bg){    //默认白色
            return '#fff';
        }
        if(bg.search('gradient')!=-1){     //渐变色直接返回白色
            return '#fff';
        }
        if(bg.search('#')!=-1){     //16进制转换成rgb
            bg=this.set16ToRgb(bg);
        }
        if(bg.search('rgb')!=-1){     //rgb

        }
        let bgColor = bg.replace("rgb(", "").replace("rgba(", "").replace(")", "");
        let bgColorArray = bgColor.split(",");
        let color = bgColorArray[0] * 0.299 + bgColorArray[1] * 0.587 + bgColorArray[2] * 0.114;
        if (color >= 192) {     //字体深色
            return '#665720';
        } else {        //字体浅色
            return '#fff';
        }
    }
    //【16进制转换为RGB 】
    // set16ToRgb('#ffffff');   // rgb(255,255,0)
    static set16ToRgb(str){
        let reg = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/
        if(!reg.test(str)){return;}
        let newStr = (str.toLowerCase()).replace(/\#/g,'')
        let len = newStr.length;
        if(len == 3){
            let t = ''
            for(let i=0;i<len;i++){
                t += newStr.slice(i,i+1).concat(newStr.slice(i,i+1))
            }
            newStr = t
        }
        let arr = []; //将字符串分隔，两个两个的分隔
        for(let i =0;i<6;i=i+2){
            let s = newStr.slice(i,i+2)
            arr.push(parseInt("0x" + s))
        }
        return 'rgb(' + arr.join(",")  + ')';
    }
    //【RGB转换为16进制 】
    // setRgbTo16('rgb(255,0,255)');   // #FF00FF
    static setRgbTo16(str){
        let reg = /^(rgb|RGB)/;
        if(!reg.test(str)){return;}
        let arr = str.slice(4, str.length-1).split(",")
        let color = '#';
        for(let i=0;i<arr.length;i++){
            let t = Number(arr[i]).toString(16)
            if(t == "0"){   //如果为“0”的话，需要补0操作,否则只有5位数
                t =  t + "0"
            }
            color += t;
        }
        return color;
    }
    //合并对象
    static mergeObject(objA={},objB={}){
        if(typeof objA==typeof objB){//对象类型一样就合并
            return {...objA,...objB}
        }
    }
    // start 比较两个json对象
    static compareJsonObj(objA,objB){
        if(!this.isObj(objA) || !this.isObj(objB)) return false; //判断类型是否正确
        if(this.getLength(objA) != this.getLength(objB)) return false; //判断长度是否一致
        return this.compareObj(objA, objB, true); //默认为true
    }
    static isObj(object) {
        return object && typeof(object) == 'object' && Object.prototype.toString.call(object).toLowerCase() == "[object object]";
    }
    static isArray(object) {
        return object && typeof(object) == 'object' && object.constructor == Array;
    }
    static getLength(object) {
        let count = 0;
        for(let i in object) count++;
        return count;
    }
    static compareObj(objA, objB, flag) {
        if(this.getLength(objA) != this.getLength(objB)) return false;
        for(let key in objA) {
            if(!flag) //flag为false，则跳出整个循环
                break;
            if(!objB.hasOwnProperty(key)) {//是否有自身属性，而不是继承的属性
                flag = false;
                break;
            }
            if(!this.isArray(objA[key])) { //子级不是数组时,比较属性值
                if (this.isObj(objA[key])) {
                    if (this.isObj(objB[key])) {
                        if(!flag) //这里跳出循环是为了不让递归继续
                            break;
                        flag = this.compareObj(objA[key], objB[key], flag);
                    } else {
                        flag = false;
                        break;
                    }
                } else {
                    if(String(objB[key]) != String(objA[key])) { //排除数字比较的类型差异
                        flag = false;
                        break;
                    }
                }
            } else {
                if(!this.isArray(objB[key])) {
                    flag = false;
                    break;
                }
                let oA = objA[key],
                    oB = objB[key];
                if(oA.length != oB.length) {
                    flag = false;
                    break;
                }
                for(let k in oA) {
                    if(!flag) //这里跳出循环是为了不让递归继续
                        break;
                    flag = this.compareObj(oA[k], oB[k], flag);
                }
            }
        }
        return flag;
    }
    // end 比较两个json对象

    /**
     * 计算两个日期之间的天数
     *  date1  开始日期 yyyy-MM-dd
     *  date2  结束日期 yyyy-MM-dd
     *  如果日期相同 返回一天 开始日期大于结束日期，返回0
     */
    static getDaysBetween(date1, date2) {
        let startDate = Date.parse(date1);
        let endDate = Date.parse(date2);
        if (startDate > endDate) {
            return 0;
        }
        if (startDate == endDate) {
            return 1;
        }
        let days = (endDate - startDate) / (1 * 24 * 60 * 60 * 1000);
        return days;
    }
    /**
     * 绑定事件
     * inDomOrID dom或ID
     * type 事件名
     * listener 执行函数
     * 注：listener如果是匿名函数会重复绑定类似 "util.addEventListener(obj,type, ()=>{ myScript });" 该事件会造成重复绑定，避免重复绑定请不要使用匿名函数
     */
    static addEventListener(inDomOrID,type,listener) {
        if(typeof inDomOrID == 'string'){
            inDomOrID=document.getElementById(inDomOrID)
        }
        this.removeEventListener(inDomOrID,type,listener);//先移除，防止重复绑定
        inDomOrID.addEventListener(type, listener);
    }
    /**
     * 移除事件
     * 如果移除函数不同的话 removeEventListener移除事件是不起作用的
     * 示例：
     * 添加事件
     * document.addEventListener('scroll', (e)=> { console.log(e) })
     * 移除事件
     * document.removeEventListener('scroll', (e)=> { console.log(e) })
     * 因为执行函数不是同一个函数（内存地址不一样）所以移除不了这个事件的
     */
    static removeEventListener(inDomOrID,type,listener) {
        if(typeof inDomOrID == 'string'){
            inDomOrID=document.getElementById(inDomOrID)
        }
        inDomOrID.removeEventListener(type, listener);
    }
    static mapToJson(map) {
        let obj= Object.create(null);
        for (let[k,v] of map) {
            obj[k] = v;
        }
        return JSON.stringify(obj);
    }
    static clone(obj){
       
        if(util.isNull(obj)){
            return 'obj is null';
        }else{
           return JSON.parse(JSON.stringify(obj));
        }
        
    }

    //检查当前是pc还是h5
    static checkBrowser(isShowBrowser=false){
        if (/(micromessenger)/i.test(navigator.userAgent)) {
            if(isShowBrowser) alert("微信");
            return 'weixin';
        } else {
            if(isShowBrowser) alert("普通浏览器");
            // 判断h5还是pc true就是h5
            let client =/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
            if (client) {
                if(isShowBrowser) alert("h5");
                return 'h5';
            } else {
                if(isShowBrowser) alert("pc");
                return 'pc';
            }
        }
    }

    /**
     * 1分钟以内显示秒数：刚刚
     * 1分钟以上1小时以内显示分钟数和秒数：分钟前
     * 1小时以上1天以内显示小时数和分钟数：小时前
     * 7天以上30天以内显示周：周前
     * 30天以上1年以内显示周：月前
     * 1年以上显示周：年前
     * @returns {string}
     */
    static simpleDuration(date){//date='2023-06-02 22:20:10'
        let nowDate=new Date().getTime();
        let duration=nowDate-new Date(date).getTime();
        let str = ''
        let
            minute = 60*1000,
            hour = 60*minute,
            day = 24*hour,
            week = 7*day,
            month = 30*day,
            year = 12*month
        ;
        if(duration>=year){
            str = Math.floor(duration/year)+'年前'
        }else if(duration>=month&&duration<year){
            str = Math.floor(duration/month)+'月前'
        }else if(duration>week&&duration<month){
            str = Math.floor(duration/week)+'周前'
        }else if(duration>=day&&duration<week){
            str = Math.floor(duration/day)+'天前'
        }else if(duration>=hour&&duration<day){
            str = Math.floor(duration/hour)+'小时前'
        }else if(duration>minute&&duration<hour){
            str = Math.floor(duration/minute)+'分钟前'
        }else if(duration<=minute){
            str = '刚刚'
        }
        return str;
    }
    static autocomplete(data) {//inp, arr
        /*自动填充函数有两个参数，input 输入框元素和自动填充的数组*/
        let currentFocus;
        /* 监听 input 输入框，当在 input 输入框元素中时执行以下函数*/
        data.inp.addEventListener("input", function(e) {
            let a, b, i, val = this.value;
            /*关闭已打开的自动填充列表*/
            closeAllLists();
            currentFocus = -1;
            /*创建 DIV 元素用于放置自动填充列表的值*/
            a = document.createElement("DIV");
            a.setAttribute("name", this.id + "autocomplete-list");
            a.setAttribute("class", "autocomplete-items");
            /*DIV 作为自动填充容器的子元素*/
            this.parentNode.appendChild(a);
            /*循环数组*/
            for (i = 0; i < data.lookup.length; i++) {
                /*检查填充项是否有与文本字段值相同的内容，不区分大小写*/
                let length=data.lookup[i].value.toUpperCase().indexOf(val.toUpperCase());
                if ((length!=-1)) {
                    /*为每个匹配元素创建一个 DIV 元素 */
                    b = document.createElement("DIV");
                    /*匹配项加粗*/
                    b.innerHTML += "<strong>" + data.lookup[i].value + "</strong>";
                    /*选中的填充项插入到隐藏 input 输入字段，用于保存当前选中值*/
                    b.innerHTML += "<input type='hidden' value='" + data.lookup[i].key + "'>";
                    /*当有人点击填充项（DIV 元素）时执行函数*/
                    b.addEventListener("click", function(e) {
                        /*选中的填充项插入到隐藏 input 搜索字段*/
                        data.onSelect(this.getElementsByTagName("input")[0].value);
                        //data.inp.value = this.getElementsByTagName("input")[0].value;
                        /*关闭自动填充列表*/
                        closeAllLists();
                    });
                    a.appendChild(b);
                }
            }
        });
        data.inp.addEventListener("focus", function(e) {
            let a, b, i, val = this.value;
            /*关闭已打开的自动填充列表*/
            closeAllLists();
            currentFocus = -1;
            /*创建 DIV 元素用于放置自动填充列表的值*/
            a = document.createElement("DIV");
            a.setAttribute("name", this.id + "autocomplete-list");
            a.setAttribute("class", "autocomplete-items");
            /*DIV 作为自动填充容器的子元素*/
            this.parentNode.appendChild(a);
            /*循环数组*/
            for (i = 0; i < data.lookup.length; i++) {
                /*检查填充项是否有与文本字段值相同的内容，不区分大小写*/
                let length=data.lookup[i].value.toUpperCase().indexOf(val.toUpperCase());
                if ((length!=-1)) {
                    /*为每个匹配元素创建一个 DIV 元素 */
                    b = document.createElement("DIV");
                    /*匹配项加粗*/
                    b.innerHTML += "<strong>" + data.lookup[i].value + "</strong>";
                    /*选中的填充项插入到隐藏 input 输入字段，用于保存当前选中值*/
                    b.innerHTML += "<input type='hidden' value='" + data.lookup[i].key + "'>";
                    /*当有人点击填充项（DIV 元素）时执行函数*/
                    b.addEventListener("click", function(e) {
                        /*选中的填充项插入到隐藏 input 搜索字段*/
                        data.onSelect(this.getElementsByTagName("input")[0].value);
                        //data.inp.value = this.getElementsByTagName("input")[0].value;
                        /*关闭自动填充列表*/
                        closeAllLists();
                    });
                    a.appendChild(b);
                }
            }
        });
        /*按下键盘上的一个键时执行函数*/
        data.inp.addEventListener("keydown", function(e) {
            let x = document.getElementsByName(this.id + "autocomplete-list");
            if(x.length>0){
                if (x) x = x[0].getElementsByTagName("div");
                if (e.keyCode == 40) {
                    /*如果按下箭头向下键，currentFocus 变量加 1，即向下移动一位*/
                    currentFocus++;
                    /*使当前选中项更醒目*/
                    addActive(x);
                } else if (e.keyCode == 38) { //up
                    /*按下箭头向上键，选中列表项向上移动一位*/
                    currentFocus--;
                    /*使当前选中项更醒目*/
                    addActive(x);
                } else if (e.keyCode == 13) {
                    /*如果按下 ENTER 键，阻止提交，你也可以设置 submit 提交*/
                    e.preventDefault();
                    if (currentFocus > -1) {
                        /*模拟点击选中项*/
                        if (x) x[currentFocus].click();
                    }
                }
            }
        });
        function addActive(x) {
            /*设置选中的选项函数*/
            if (!x) return false;
            /*移动选项设置不同选中选项的背景颜色*/
            removeActive(x);
            if (currentFocus >= x.length) currentFocus = 0;
            if (currentFocus < 0) currentFocus = (x.length - 1);
            /*添加 "autocomplete-active" 类*/
            x[currentFocus].classList.add("autocomplete-active");
        }
        function removeActive(x) {
            /*移除没有选中选项的 "autocomplete-active" 类*/
            for (let i = 0; i < x.length; i++) {
                x[i].classList.remove("autocomplete-active");
            }
        }
        function closeAllLists(elmnt) {
            /*关闭自动添加列表*/
            let x = document.getElementsByName(data.inp.id+'autocomplete-list')
            for (let i = 0; i < x.length; i++) {
                if (elmnt != x[i] && elmnt != data.inp) {
                    x[i].parentNode.removeChild(x[i]);
                }
            }
        }
        /*点击 HTML 文档任意位置关闭填充列表*/
        document.addEventListener("click", function (e) {
            closeAllLists(e.target);
        });
    }
    // 生成一个位于 [min, max] 区间的随机整数
    static getRandomInt(min, max) {
        const range = max - min + 1;
        const array = new Uint8Array(1);
        window.crypto.getRandomValues(array);

        // 将随机值映射到 [min, max] 区间内
        return min + (array[0] % range);
    }

    /**
     * 检查指定的图片 URL 是否有效。
     * 发送一个 HEAD 请求来检查图片 URL 的有效性，只获取响应头部，不下载图片内容。
     */
    static isImageValid(url) {
        return axios.head(url)
            .then(response => {
                return response.status >= 200 && response.status < 300; // 判断状态码是否为 2xx
            })
            .catch(() => {
                return false; // 请求失败则返回 false
            });
    }
    /**
     * '{data:1}' 修正为 '{"data":1}'
     * 修正字符串，还有单引号的替换和注释删除等使其成为有效的 JSON 字符串
     */
    static fixJsonString(str) {
        // 1. 删除单行注释
        str = str.replace(/\/\/.*?(\r?\n|$)/g, '');
        // 2. 删除多行注释
        str = str.replace(/\/\*[\s\S]*?\*\//g, '');
        // 3. 添加双引号到所有键名
        str = str.replace(/(\w+):/g, '"$1":');
        // 4. 将值中的单引号替换为双引号
        str = str.replace(/'([^']+)'/g, '"$1"');
        return str;
    }
    static replaceTemplate(template, data) {
        // 表达式替换（含 ${...} 条件/表达式）
        const evaluateExpressions = (str, context) => {
            return str.replace(/\${(.*?)}|{{(.*?)}}/g, (match, expr1, expr2) => {
                const expression = expr1 || expr2;
                try {
                    let value = new Function("data", `with (data) { return ${expression}; }`)(context);//todo 这里使用到Function，不推荐使用Function，后续需要替代
                    return (value !== undefined && value !== null) ? String(value).replace(/\n/g, '<br>') : '';
                } catch (e) {
                    console.error('表达式错误:', e);
                    return match;
                }
            });
        };

        // 处理嵌套结构，如 ${#section}...${/section}
        const processSections = (tpl, sectionData = data, currentIndex = 1) => {
            return tpl.replace(/({{#(\w+)}}|\${#(\w+)})([\s\S]*?)({{\/\2}}|\${\/\3})/g, (match, openTag, section1, section2, content, closeTag) => {
                const section = section1 || section2;
                if (sectionData.hasOwnProperty(section)) {
                    const sectionItems = sectionData[section];
                    if (Array.isArray(sectionItems)) {
                        return sectionItems.map((item, index) => {
                            // 递归处理嵌套
                            let itemContent = processNestedSections(content, item, currentIndex + index);
                            itemContent = itemContent.replace(/\${@index}/g, currentIndex + index);
                            itemContent = evaluateExpressions(itemContent, item);
                            return itemContent;
                        }).join('');
                    }
                }
                // 不匹配，返回原内容
                return match;
            });
        };

        // 嵌套数组（如 tags）处理
        const processNestedSections = (tpl, item, classIndex) => {
            return tpl.replace(/({{#(\w+)}}|\${#(\w+)})([\s\S]*?)({{\/\2}}|\${\/\3})/g, (match, openTag, section1, section2, content, closeTag) => {
                const sectionName = section1 || section2;
                if (typeof item[sectionName] === 'string') {
                    try {
                        let val = item[sectionName].trim();
                        if (val.startsWith('[') && val.endsWith(']')) {
                            val = val.replace(/\n/g, '\\n').replace(/\t/g, '\\t');
                            item[sectionName] = JSON.parse(val);
                        } else {
                            item[sectionName] = val.split(',').filter(Boolean).map((v) => ({ index: v.trim() }));
                        }
                    } catch (e) {
                        console.error(`无法解析嵌套数组：${sectionName}`);
                        item[sectionName] = [];
                    }
                }

                if (Array.isArray(item[sectionName])) {
                    return item[sectionName].map((subItem, subIndex) => {
                        let nestedContent = content.replace(/\${@index}/g, subIndex + 1);
                        nestedContent = evaluateExpressions(nestedContent, subItem);
                        return nestedContent;
                    }).join('');
                }
                return '';
            });
        };

        // 先处理 section，再处理表达式（主层）
        let result = processSections(template);
        result = evaluateExpressions(result, data);
        return result;
    }
    /**
     * 动态执行传入的 JavaScript 函数字符串
     * 这只是暂时的解决方案，后续不能再使用 new Function() 和 eval() 因为这可能会有注入风险攻击
     *
     * @param {string} funcStr - 需要执行的函数代码字符串。代码应该是一个有效的 JavaScript 函数体。
     * @param {Object} context - 执行函数时的上下文对象（`this`）。函数会在该上下文中执行。
     */
    static executeDynamicCode(funcStr, context) {
        // 确保传入的 funcStr 是一个有效的字符串
        if (typeof funcStr === 'string') {
            try {
                // 使用 new Function() 创建一个新的函数对象，传入 funcStr 字符串作为函数体
                const func = new Function(funcStr);

                // 使用 apply() 执行这个函数，并将 context 作为其上下文（this）
                return func.apply(context);
            } catch (error) {
                // 如果函数执行过程中出现任何错误，捕获并打印错误信息
                console.error("执行动态代码时出错：", error);
                return false;
            }
        } else {
            // 如果传入的 funcStr 不是字符串，输出错误提示
            console.error("传入的代码字符串无效。");
            return false;
        }
    }

    // 展示水印
    static waterMark(){
        function textBecomeImg(text, fontSize, fontColor){
            const canvas = document.createElement('canvas');
            const width = 280;
            const height = 100;
            canvas.width = width;
            canvas.height = height;
            const context = canvas.getContext('2d');
            context.clearRect(0, 0, width, height);
            context.textAlign = 'left';
            context.fillStyle = fontColor;
            context.font = `${fontSize}px Arial`;
            context.textBaseline = 'middle';
            const lines = String(text).split('\n');
            const lineHeight = Math.round(fontSize * 1.2);
            const startY = height / 2 - ((lines.length - 1) * lineHeight) / 2;
            const textX = 10;
            lines.forEach((line, index) => {
                context.fillText(line, textX, startY + index * lineHeight);
            });
            return canvas.toDataURL('image/png');
        }
        const userName = util.getLocalStorageItem('userName');
        const waterMarkId = 'khc-watermark-layer';

        util.removeWaterMark();

        const now = new Date();
        const formattedTime = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
        const text = `${userName}\n${formattedTime}`;
        const baseDpr = window.devicePixelRatio || 1;
        const watermarkDataUrl = textBecomeImg(text, 20, 'gray');

        const shuiyinDiv = document.createElement('div');
        shuiyinDiv.id = waterMarkId;
        const style = shuiyinDiv.style;
        style.position = 'fixed';
        style.left = '0';
        style.top = '0';
        style.width = '100vw';
        style.height = '100vh';
        style.overflow = 'hidden';
        style.opacity = '0.1';
        style.zIndex = '9999999991';
        style.pointerEvents = 'none';

        const render = () => {
            const currentDpr = window.devicePixelRatio || 1;
            const zoomRatio = currentDpr / baseDpr;
            const coverWidth = Math.ceil((window.innerWidth + window.innerHeight) * zoomRatio);
            const coverHeight = Math.ceil((window.innerWidth + window.innerHeight) * zoomRatio);
            const inverseScale = 1 / zoomRatio;

            shuiyinDiv.innerHTML = `
                <div style="position:absolute;left:50%;top:50%;width:${coverWidth}px;height:${coverHeight}px;transform:translate(-50%,-50%) scale(${inverseScale}) rotate(-30deg);transform-origin:center center;background-image:url(${watermarkDataUrl});background-repeat:repeat;background-size:280px 120px;background-position:0 0;pointer-events:none;"></div>
            `;
        };

        render();
        shuiyinDiv._watermarkResizeHandler = () => {
            render();
        };
        window.addEventListener('resize', shuiyinDiv._watermarkResizeHandler);
        let lastWatermarkMinute = formattedTime;
        shuiyinDiv._watermarkInterval = setInterval(() => {
            const currentWaterMark = document.getElementById(waterMarkId);
            if (!currentWaterMark) {
                clearInterval(shuiyinDiv._watermarkInterval);
                return;
            }
            const currentNow = new Date();
            const currentFormattedTime = `${currentNow.getFullYear()}-${(currentNow.getMonth() + 1).toString().padStart(2, '0')}-${currentNow.getDate().toString().padStart(2, '0')} ${currentNow.getHours().toString().padStart(2, '0')}:${currentNow.getMinutes().toString().padStart(2, '0')}`;
            if (currentFormattedTime === lastWatermarkMinute) {
                return;
            }
            lastWatermarkMinute = currentFormattedTime;
            const currentText = `${userName}\n${currentFormattedTime}`;
            const currentWatermarkDataUrl = textBecomeImg(currentText, 20, 'gray');
            const waterMarkContent = currentWaterMark.querySelector('div');
            if (waterMarkContent) {
                waterMarkContent.style.backgroundImage = `url(${currentWatermarkDataUrl})`;
            }
        }, 30000);
        document.body.appendChild(shuiyinDiv);
    }

    static removeWaterMark(){
        const waterMarkId = 'khc-watermark-layer';
        const oldWaterMark = document.getElementById(waterMarkId);
        if (!oldWaterMark) {
            return;
        }

        if (oldWaterMark._watermarkResizeHandler) {
            window.removeEventListener('resize', oldWaterMark._watermarkResizeHandler);
            oldWaterMark._watermarkResizeHandler = null;
        }

        if (oldWaterMark._watermarkInterval) {
            clearInterval(oldWaterMark._watermarkInterval);
            oldWaterMark._watermarkInterval = null;
        }

        oldWaterMark.remove();
    }
    // 获取前端别名中的前缀部分，并拼接 '-'
    static getPrefixedContext(){
        let contextFrontPath = config.configJson.contextFrontPath || '';
        let prefix = contextFrontPath.replace(/^\/+|\/+$/g, '').replace(/\//g, '-');
        return prefix ? prefix + '-' : '';
    }
    static getLocalStorageItem(key) {
        return localStorage.getItem(util.getPrefixedContext() + key);;
    }
    static setLocalStorageItem(key,value) {
        return localStorage.setItem(util.getPrefixedContext() + key,value);
    }
    static getSessionStorageItem(key) {
        return sessionStorage.getItem(util.getPrefixedContext() + key);;
    }
    static setSessionStorageItem(key,value) {
        return sessionStorage.setItem(util.getPrefixedContext() + key,value);
    }
}
export { util }