jquery加载页面的几种方法(页面加载完成就执行)如何去掉css 渐变时的锯齿效果AJAX请求以及解决跨域的问题浅析Vue3动态组件怎么进行异常处理html+css+js写的AI五子棋游戏(附源码)基于jquery 实现导航条高亮显示的两种方法EasyUI使用DataGrid实现动态列数据绑定Vue怎么通过JSX动态渲染组件JS 获取当前日期、时间、星期聊一聊Node.js中的 GC(垃圾回收)机制Ajax 请求队列解决方案并结合elementUi做全局加载状态前端代码风格规范总结JS Generator函数yield表达式示例详解javascript子窗口父窗口相互操作 取值赋值的问题CSS如何实现波浪效果ECharts实现数据超出Y轴最大值max但不隐藏CSS样式穿透的几种方法JavaScript 类型转换CSS 设置页面缩放如何在Vue3+Vite中使用JSXlinux平台下node cnpm的安装方法vue中v-if和v-for的区别是什么详解Vue PC端如何实现扫码登录功能CSS 网格(Grid)布局JavaScript 请求服务端接口Angular开发问题记录:组件拿不到@Input输入属性聊一聊CSS中的盒模型和box-sizing属性vue3.0对服务端进行渲染Web 页面如何实现动画效果移动端 H5 实现拍照功能的几种方法CSS 样式优先级HTML 块级元素、行内元素和行内块级元素深入聊一聊vue3中的reactive()CSS 媒体查询Angular中的路由(Routing)原理怎么移除css的hover事件让交互更加生动!巧用CSS实现鼠标跟随 3D 旋转效果Web页面的几种布局方式JavaScript 评测代码运行速度的几种方法优化jQuery性能的多种方法(整理总结)说一下Vue组件中的自定义事件和全局事件总线列举一下JavaScript中对数组进行合并的几种方法JavaScript 对象合并带你深入了解一下vue.js中的this.$nextTick!如何在JavaScript中对文件进行处理Vue.js 前端项目在常见 Web 服务器上的部署配置vue中$set的实现方法CSS 实现文本装饰效果JavaScript 高阶函数History 和 Hash 路由模式Vue3学习之深度剖析CSS Modules和ScopeCSS 清除浮动CSS 设置背景图片【整理分享】一些常见Vue面试题(附答案解析)CSS里面的transform 属性CSS中的几种尺寸单位总结JavaScript中的条件判断与比较运算uni-app 滚动通知组件的实现JavaScript 执行上下文与作用域前端兼容性问题总结前端性能优化——图片优化JS中数组去重的几种方法JavaScript里实现继承的几种方式JavaScript使用reduce方法实现简单的 i18n 功能JavaScript 日期和时间的格式化大汇总(收集)JS里的原始值与引用值JavaScript 文件处理检测js代码中可能导致内存泄漏的工具Angular学习之ControlValueAccessor接口详解uni-app 日历组件的实现Vue 组件之间传值JavaScript里的回调函数属于闭包吗?JavaScript 解决冒泡事件导致的性能问题总结一下JavaScript中的一些奇怪问题简单说一下JavaScript中的事件冒泡和事件捕获关于 WebpackJavaScript中的循环类型5个实用的TypeScript操作符,助你提升开发能力!HTML img 元素无法显示 base64 图片的可能原因jQuery 遍历方法总结前端性能优化——内存问题javascript中脚本加载和执行机制Web 页面之间传递参数的几种方法解释一下JavaScript中0.1+0.2不等于0.3的问题总结一下JavaScript 中apply、call、bind的使用方法什么是EventLoop?怎么测试Node或页面的性能JavaScript 中 new Date(time).getTime() 获取时间戳方法在 iOS 中的兼容性问题JS里数组合并的几种方法详解vue各种权限控制与管理的实现思路uni-app 弹出层组件的实现用JavaScript实现文件的上传与下载前端性能优化总结简单说一下JavaScript中的事件委托JavaScript如何删除css总结一下JS中的排序算法关于js中的作用域和闭包让页面元素居中的一些方法【javascript算法】二分查找法如何在前端实现上万行大量数据的秒级响应?CSS中父元素被子元素的margin-top影响的解决方法js使用splice方法删除数组元素可能导致的问题如何运行vue项目(超详细图解)js隐式类型转换的副作用了解js的原型和原型链js中的循环引用js判断变量类型的方法web页面最常用的正则校验规则总结js内存管理和垃圾回收js中this关键字的作用和如何改变其上下文JS中的操作符和运算符大总结Vue3 watch 监听对象数组中对象的特定属性总结一下js的浅拷贝和深拷贝JavaScript中的异步编程JavaScript使用一个数组对另一个对象数组进行过滤一个基于Vue3实现的简单日历组件CSS实现磨砂玻璃(毛玻璃)效果样式解决“Vue3调用本地服务接口失败,老是提示下载并安装本地服务”的办法vue3基于组合式API使异步获取的数据对象具有响应性ASP.NET给前端动态添加修改CSS样式JS 标题 关键字在js中如何获取对象的长度了解一下js中的函数式编程Vue框架中监测数组变化的方法VUE html里的文本框只允许输入数字的两种方法分享几种js格式化金额的方法JavaScript的Promise同步处理js中的简写语法JavaScript 面向切面编程(AOP,装饰者模式)了解一下js的跨域问题谈一谈Vue怎么用extend动态创建组件Vue3实现印章徽章组件js中断 forEach 循环的几种方法vue3组件间怎么通信?简述一下通信方式CSS 设置文字间距CSS 网格(Grid)布局Vue3 实现模态框组件js处理扁平数组和树结构相互转换使用Vue和jsmind如何实现思维导图的历史版本控制和撤销/重做功能?Vue.js与ASP.NET的结合,实现企业级应用的开发和部署检查js中的字符串是否可以成为回文Vue.nextTick函数的用法及在异步更新中的应用关于js中0.1+0.2不等于0.3的问题使用JavaScript开发网页地图导航js中如何使用可选函数参数Vue与js的融合,如何编写现代化的前端应用关于Vue与服务器端的通信:如何实现登录鉴权Vue.js的响应式原理如何使用 FabricJS 禁用椭圆的居中旋转?使用JavaScript实现页面滑动切换效果移动端H5页面的缓存问题js里base64与file之间的转换分享一下利用Vue表单处理实现复杂表单布局如何使用CSS创建渐变阴影?如何在 JavaScript 中查看结构体数组?css的弹性布局(Flex布局)Vue统计图表的数据标签和数值显示技巧js实用工具方法库大总结使用Angular和MongoDB来构建具有登录功能的博客应用程序如何在Vue表单处理中实现表单字段的文件下载使用js开发交互式地图应用Vue.component函数的使用及如何创建局部组件css实现三角形的几种方法移动端H5实现自定义拍照界面如何在js中遍历对象jquery如何修改选中状态如何使用js对图像进行压缩js如何计算base64编码图片的大小关于Vue双向数据绑定原理详解Vue.filter函数及如何自定义过滤器列举一下js数组的reduce方法的一些应用如何在Vue表单处理中实现表单的条件渲染vue中的canvas插件js数组的reduce方法的一些应用VUE里使用虚拟DOM来优化更新流程canvas对象有哪些方法js中URL查询字符串(query string)的序列与反序列化如何使用 JavaScript/jQuery 为网站创建暗/亮模式?总结html5中常见的选择器Vue组件开发:工具提示组件的实现方法总结js中常见的层次选择器利用Vue2实现印章徽章组件Vue组件实战:列表组件开发js中如何将对象转换为数组Vue组件开发:地图组件实现方法如何将数据存储到 DOM 中?Vue实战:图片上传组件开发如何使用Vue实现可视化界面设计?vite+TypeScript+vue3+router4+Pinia+ElmPlus+axios+mock项目基本配置Vue组件库推荐:Ant Design Vue深度解析使用Vue实现弹窗效果React Query 数据库插件:与分布式系统的协作指南如何使用vue ui创建vue项目使用CSS的Positions布局打造响应式网页无感刷新页面(附可运行的前后端源码,前端vue,后端node)Css Flex 弹性布局中的换行与溢出处理方法Vue组件库推荐:Element UI深度解析分享一个用HTML、CSS和jQuery构建的漂亮的登录注册界面如何利用React和Sass实现可定制的前端样式如何在Vue中实现拖拽上传文件vue3+TypeScript全局事件总线mitt分享一个Pinia存储的数据持久化插件HTML、CSS和jQuery:实现图片折叠展开的效果如何使用HTML和CSS创建动画条形图?如何利用React和Flutter构建跨平台移动应用用html、css和jQuery实现图片翻页的特效利用Promise优化Vue异步操作的方法CSS Positions布局与网页导航的优化技巧js多图合成一张图CSS3实现动态旋转加载样式使用Velocity.js将动画添加到网页使用FabricJS创建Image对象的JSON表示vue3递归组件---树形组件vue3+TypeScript全局事件总线mitt在Vue中实现可编辑的表格vue3+TypeScript自定义指令:长按触发绑定的函数Vue中如何处理表单数据的双向绑定和验证Vue 简介Vue 安装Vue 常用指令Vue 实例Vue 计算属性Vue 动态样式绑定Vue 事件处理Vue 侦听器Vue 数据双向绑定Vue过渡 & 动画Vue 实例生命周期Vue 插槽的使用Vue 组件基础Vue动态组件 & keep-aliveVue 组件间通信Vue 过滤器Vue 自定义指令Vue 混入 MixinsVue 插件Vue 渲染函数VueRouter 编程式导航VueRouter 路由嵌套VueRouter 基础应用VueRouter 命名视图VueRouter 命名路由Vuex 简介、安装VueRouter 路由传参VueRouter 路由别名、重定向Vuex GetterVuex StateVuex ActionVuex MutationVue-Cli & VueDevTools安装Vuex ModulesVue 第三方库的使用Vue-Cli 项目文件结构分析Vue 本地 Mock 数据Vue 项目打包部署Element 美化 TODO 项目

js实用工具方法库大总结

/************************* 日期时间 *************************/

/**
 * 日期时间格式化方法一
 * @param {*} fmt 
 * @param {*} date 
 * @returns 
 */
export function dateFormat(fmt,date) {
    let ret;
    const opt = {
        "Y+": date.getFullYear().toString(),// 年
        "M+": (date.getMonth() + 1).toString(),// 月
        "D+": date.getDate().toString(),// 日
        "h+": date.getHours().toString(),// 时
        "m+": date.getMinutes().toString(),// 分
        "s+": date.getSeconds().toString() // 秒
        // 有其他格式化字符需求可以继续添加,必须转化成字符串
    };
    for (let k in opt) {
        ret = new RegExp("(" + k + ")").exec(fmt);
        if (ret) {
            fmt = fmt.replace(ret[1],(ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length,"0")))
        };
    };
    return fmt;
}

/**
 * 将一位数的数字格式化为两位:1->'01'
 * 方法一
 * @param {*} n 
 * @returns 
 */
export function formatNumber(n) {
    n = n.toString();
    return n[1] ? n : '0' + n;
};

/**
 * 将一位数的数字格式化为两位:1->'01'
 * 方法二
 * @param {*} val 
 * @returns 
 */
export function fixedZero(val) {
    return val * 1 < 10 ? `0${val}` : val;
}

/**
 * 日期时间格式化 方法二
 * 将时间日期格式化为:2023/07/05 22:12:00
 * @param {*} date
 * @returns 
 */
export function formatTime(date) {
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const day = date.getDate();
    const hour = date.getHours();
    const minute = date.getMinutes();
    const second = date.getSeconds();
    return [year,month,day].map(formatNumber).join('/') + ' ' + [hour,minute,second].map(formatNumber).join(
        ':');
};

/** 根据出生日期计算年龄(yyyy-MM-dd) */
export function getBrithAge(birthday) {
    const nowDay = new Date();
    // 出生年月日
    const birthYear = birthday.getFullYear();
    const birthMonth = birthday.getMonth() + 1;
    const birthDate = birthday.getDate();
    // 当前年月日
    const nowYear = nowDay.getFullYear();
    const nowMonth = nowDay.getMonth() + 1;
    const nowDate = nowDay.getDate();

    // 计算年差
    let age = nowYear - birthYear;
    // 当前月小于出生月,则岁数不足
    if (nowMonth < birthMonth) {
        age -= 1;
    } else if (nowMonth === birthMonth) {
        // 当前日期小于出日期,则岁数不足
        if (nowDate < birthDate) {
            age -= 1;
        }
    }

    return age;
}

// 获取上周时间段
export function getLastWeek(value) {
    if (value == '' || value == undefined) {
        return value;
    }
    if (value.length == 10) {
        value = value * 1000;
    }

    const myDate = new Date(value - 7 * 24 * 3600 * 1000);
    // 回退7天后是星期几?
    const day = myDate.getDay();

    const time = myDate.getDate() - day + (day === 0 ? -6 : 1);
    const startTime = new Date(myDate.setDate(time));
    const startDateTime =
        startTime.getFullYear() +
        '-' +
        (startTime.getMonth() + 1) +
        '-' +
        startTime.getDate();
    const endTime = new Date(myDate.setDate(time + 6));
    const endDateTime =
        endTime.getFullYear() +
        '-' +
        (endTime.getMonth() + 1) +
        '-' +
        endTime.getDate();
    return {
        startDateTime: startDateTime.replace(/\-/g,"/"),endDateTime: endDateTime.replace(/\-/g,"/")
    };
}

// 获取本周时间段
export function getCurrentWeek(value) {
    if (value == '' || value == undefined) {
        return value;
    }
    if (value.length == 10) {
        value = value * 1000;
    }

    const myDate = new Date(value);

    const nowTime = myDate.getTime();
    const day = myDate.getDay();
    const oneDayTime = 24 * 60 * 60 * 1000;
    // 本周一时间戳
    const MondayTime = nowTime - (day - 1) * oneDayTime;
    // 本周日时间戳
    const SundayTime = nowTime + (7 - day) * oneDayTime;

    // 格式化时间
    const monday = new Date(MondayTime);
    const sunday = new Date(SundayTime);

    const startTime = monday;
    const startDateTime =
        startTime.getFullYear() +
        '-' +
        (startTime.getMonth() + 1) +
        '-' +
        startTime.getDate();
    const endTime = sunday;
    const endDateTime =
        endTime.getFullYear() +
        '-' +
        (endTime.getMonth() + 1) +
        '-' +
        endTime.getDate();
    return {
        startDateTime: startDateTime.replace(/\-/g,"/")
    };
}

// 获取上个月时间段
export function getLastMonth(value) {
    if (value == '' || value == undefined) {
        return value;
    }
    if (value.length == 10) {
        value = value * 1000;
    }

    // 获取上个月时间
    const nowdays = new Date(value);
    let year = nowdays.getFullYear();
    let month = nowdays.getMonth();
    if (month === 0) {
        month = 12;
        year = year - 1;
    }
    if (month < 10) {
        month = '0' + month;
    }

    const yDate = new Date(year,0);
    // 上个月第一天
    const startDateTime = year + '-' + month + '-01 00:00:00';
    //上个月最后一天
    const endDateTime = year + '-' + month + '-' + yDate.getDate() + ' 23:59:59';

    return {
        startDateTime: startDateTime.replace(/\-/g,"/")
    };
}

// 获取本月时间段
export function getCurrentMonth(value) {
    if (value == '' || value == undefined) {
        return value;
    }
    if (value.length == 10) {
        value = value * 1000;
    }
    const nowdays = new Date(value);
    const year = nowdays.getFullYear();
    let month = nowdays.getMonth() + 1;

    if (month < 10) {
        month = '0' + month;
    }

    const yDate = new Date(year,0);

    // 本月第一天
    const startDateTime = year + '-' + month + '-01 00:00:00';
    // 本月最后一天
    const endDateTime = year + '-' + month + '-' + yDate.getDate() + ' 23:59:59';

    return {
        startDateTime: startDateTime.replace(/\-/g,"/")
    };
}

/**
 * 获取当前时间常用不同日期时间形式
 * @returns 
 */
export function commonTime() {
    const time = new Date();

    let obj = {
        year: time.getYear(),//获取当前年份
        fullYear: time.getFullYear(),//获取完整的年份(4位,1970-???)
        month: time.getMonth(),//获取当前月份(0-11,0代表1月)
        nowDate: time.getDate(),//获取当前日(1-31)
        week: time.getDay(),//获取当前星期X(0-6,0代表星期天)
        nowTime: time.getTime(),//获取当前时间(从1970.1.1开始的毫秒数)
        hour: time.getHours(),//获取当前小时数(0-23)
        min: time.getMinutes(),//获取当前分钟数(0-59)
        sec: time.getSeconds(),//获取当前秒数(0-59)
        mSec: time.getMilliseconds(),//获取当前毫秒数(0-999)
        dateFormat: time.toLocaleDateString(),//获取当前日期
        timeFormat: time.toLocaleTimeString(),//获取当前时间
        dateTime: time.toLocaleString() //获取日期与时间
    };

    return obj;
}

/************************* 日期时间End *************************/

/************************* 输入校验Begin *************************/

// 正数验证
export function checkPositive(posInt) {
    return RegExp(/^(([^0][0-9]+|0)\.([0-9]{1,10})$)|^(([^0][0-9]+|0)$)|^(([1-9]+)\.([0-9]{1,10})$)|^(([1-9]+)$)/)
        .test(posInt);
}

// 正整数验证
export function checkPositiveInteger(posInt) {
    return RegExp(/^[1-9]\d*$/).test(posInt);
}

// 手机号验证
export function checkMobile(mobile) {
    return RegExp(/^1[3456789]\d{9}$/).test(mobile);
}

// 身份证号验证
export function checkIdCard(IdNumber) {
    // 身份证号码为15位或18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X
	return RegExp(/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/).test(IdNumber);
}

// 验证输入是否有Emoji表情和特殊字符
export function checkEmojiAndSpecial(input) {
    const emojiRegStr =
        /[^\u0020-\u007E\u00A0-\u00BE\u2E80-\uA4CF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF\u0080-\u009F\u2000-\u201f\u2026\u2022\u20ac\r\n]/g;

    const specialRegStr = "[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]";

    return RegExp(emojiRegStr).test(input) || RegExp(specialRegStr).test(input);
}

/************************* 输入校验End *************************/

export function getPlainNode(nodeList,parentPath = '') {
    const arr = [];
    nodeList.forEach(node => {
        const item = node;
        item.path = `${parentPath}/${item.path || ''}`.replace(/\/+/g,'/');
        item.exact = true;
        if (item.children && !item.component) {
            arr.push(...getPlainNode(item.children,item.path));
        } else {
            if (item.children && item.component) {
                item.exact = false;
            }
            arr.push(item);
        }
    });
    return arr;
}

export function digitUppercase(n) {
    const fraction = ['角','分'];
    const digit = ['零','壹','贰','叁','肆','伍','陆','柒','捌','玖'];
    const unit = [['元','万','亿'],['','拾','佰','仟']];
    let num = Math.abs(n);
    let s = '';
    fraction.forEach((item,index) => {
        s += (digit[Math.floor(num * 10 * 10 ** index) % 10] + item).replace(/零./,'');
    });
    s = s || '整';
    num = Math.floor(num);
    for (let i = 0; i < unit[0].length && num > 0; i += 1) {
        let p = '';
        for (let j = 0; j < unit[1].length && num > 0; j += 1) {
            p = digit[num % 10] + unit[1][j] + p;
            num = Math.floor(num / 10);
        }
        s = p.replace(/(零.)*零$/,'').replace(/^$/,'零') + unit[0][i] + s;
    }

    return s
        .replace(/(零.)*零元/,'元')
        .replace(/(零.)+/g,'零')
        .replace(/^整$/,'零元整');
}

function getRelation(str1,str2) {
    if (str1 === str2) {
        console.warn('Two path are equal!'); // eslint-disable-line
    }
    const arr1 = str1.split('/');
    const arr2 = str2.split('/');
    if (arr2.every((item,index) => item === arr1[index])) {
        return 1;
    } else if (arr1.every((item,index) => item === arr2[index])) {
        return 2;
    }
    return 3;
}

function getRenderArr(routes) {
    let renderArr = [];
    renderArr.push(routes[0]);
    for (let i = 1; i < routes.length; i += 1) {
        let isAdd = false;
        // 是否包含
        isAdd = renderArr.every(item => getRelation(item,routes[i]) === 3);
        // 去重
        renderArr = renderArr.filter(item => getRelation(item,routes[i]) !== 1);
        if (isAdd) {
            renderArr.push(routes[i]);
        }
    }
    return renderArr;
}

/**
 * Get router routing configuration
 * { path:{name,...param}}=>Array<{name,path ...param}>
 * @param {string} path
 * @param {routerData} routerData
 */
export function getRoutes(path,routerData) {
    let routes = Object.keys(routerData).filter(
        routePath => routePath.indexOf(path) === 0 && routePath !== path
    );
    // Replace path to '' eg. path='user' /user/name => name
    routes = routes.map(item => item.replace(path,''));
    // Get the route to be rendered to remove the deep rendering
    const renderArr = getRenderArr(routes);
    // Conversion and stitching parameters
    const renderRoutes = renderArr.map(item => {
        const exact = !routes.some(route => route !== item && getRelation(route,item) === 1);
        return {
            exact,...routerData[`${path}${item}`],key: `${path}${item}`,path: `${path}${item}`,};
    });
    return renderRoutes;
}

// 判断是否为合法 Url
export function isUrl(path) {
    const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/g;
    return reg.test(path);
}

/**
 * 判断当前url是否合法 http(s)
 * @param {string} URL
 * 被检测 URL
 * @returns {boolean}
 * true - 合法有效URL,false - 不合法无效 URL
 */
export function checkURL(URL) {
    var str = URL,Expression = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/,objExp = new RegExp(Expression);
    if (objExp.test(str) == true) {
        return true
    } else {
        return false
    }
}

// 计算base64编码图片大小
export function getBase64ImageSize(base64) {
    const indexBase64 = base64.indexOf('base64,');
    if (indexBase64 < 0) return -1;
    const str = base64.substr(indexBase64 + 6);
    // 大小单位:字节
    return (str.length * 0.75).toFixed(2);
}

/**
 * 	图像压缩,默认同比例压缩
 * @param {Object} imgPath
 *	图像base64编码字符串或图像可访问路径
 * @param {Object} obj
 *	obj 对象 有 width, height, quality(0-1)
 * @param {Object} maxSize
 *	指定压缩后的文件大小,单位:字节
 * @param {Object} callback
 *	回调函数有一个参数,base64的字符串数据
 */
export function compressedImage(path,obj,maxSize,callback) {
    let img = new Image();
    img.src = imgPath;
    img.onload = function () {
        const that = this;
        // 默认按比例压缩
        let w = that.width,h = that.height,scale = w / h;
        w = obj.width || w;
        h = obj.height && obj.height * (w / scale) || h;
        // 生成canvas
        let canvas = document.createElement('canvas');
        let ctx = canvas.getContext('2d');

        canvas.width = w;
        canvas.height = h;

        ctx.drawImage(that,w,h);
        // 图像质量,默认图片质量为0.8
        let quality = 0.8;
        if (obj.quality && obj.quality > 0 && obj.quality <= 1) {
            quality = obj.quality;
        }
        // quality值越小,所绘制出的图像越模糊
        let newBase64Image = canvas.toDataURL('image/jpeg',quality);

        let fileSize = getBase64ImageSize(newBase64Image);
        if (fileSize > maxSize && quality > 0.01) {
            if (quality > 0.05) {
                quality = quality - 0.05;
            } else {
                quality = 0.01;
            }
            compressedImage(imgPath,{
                quality: quality
            },callback);
            return;
        }

        // 回调函数返回压缩后的 base64图像
        callback(newBase64Image);
    }
}

/**
 * 将扁平数组转换为树形对象,
 * 适用于来自同一个表的数据,即idName的数据不重复
 * 
 * @param {*} flatData 同级数组数据
 * @param {*} idName 唯一id
 * @param {*} pidName 父级id
 * @param {*} nameName 自定义(Cascader 级联选择器)键名称
 * @param {*} valueName 自定义(Cascader 级联选择器)值名称
 * @returns 树形结构数组数据,适用于Cascader 级联选择器组件
 */
export function convertToTree(flatData,idName,pidName,nameName,valueName) {
    let treeData = [];
    if (!Array.isArray(flatData)) {
        return treeData;
    }
    flatData.forEach((item) => {
        delete item.children;
    });
    let map = {};
    flatData.forEach((item) => {
        // 深拷贝,该方式将使该转换方法失效
        // map[item[idName]] = JSON.parse(JSON.stringify(item));
        // 浅拷贝,将对 item 的引用传递给 map
        map[item[idName]] = item;
    });
    flatData.forEach((item) => {
        // 深拷贝,该方式将使该转换方法失效
        // let parent = JSON.parse(JSON.stringify(map[item[pidName]]));
        // 浅拷贝,将对 map 的引用传递给 parent
        let parent = map[item[pidName]];
        if (parent) {
            // 利用了浅拷贝的引用传递,最终 flatData 中的 item 将会改变
            (parent.children || (parent.children = [])).push(
                nameName && valueName ? Object.assign(item,{ label: item[nameName],id: item[valueName] }) : item);
        } else {
            treeData.push(
                nameName && valueName ? Object.assign(item,id: item[valueName] }) : item);
        }
    });
    return treeData;
}

// 将树形结构数据转换为扁平数组
export function convertToFlat(treeData) {
    let flatData = [];
    for (let i = 0; i < treeData.length; i++) {
        if (treeData[i].childrens) {
            flatData.push(...convertToFlat(treeData[i].childrens));
            delete treeData[i].childrens;
        }
        flatData.push({ ...treeData[i] });
    }
    return flatData;
}

// 将base64 转换为File
export function base64ToFile(base64,fileName) {
    let arr = base64.split(',');
    let mime = arr[0].match(/:(.*?);/)[1];
    let bstr = atob(arr[1]);
    let n = bstr.length;
    let u8arr = new Uint8Array(n);

    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr],fileName,{ type: mime });
}

// 将base64 转换为Blob
export function base64ToBlob(base64) {
    var arr = base64.split(','),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr],{
        type: mime
    });
}

// 将Blob转换为 File
export function blobToFile(blob,fileName) {
    return new File([blob],fileName);
}

// 将JSON 对象序列化为query string
export function objectToQueryString(obj) {
    return Object.entries(obj).map(([key,value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&');
}

// 将query string 反序列化为JSON 对象
export function queryStringToObject(queryString) {
    let obj = {};
    let arr = queryString.split("&");
    for (let i = 0; i < arr.length; i++) {
        let temp = arr[i].split("=");
        obj[temp[0]] = temp[1];
    }
    return obj;
}

// 防抖
export function debounce(fn,delay) {
    let timer = null;
    return function () {
        const context = this;
        const args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
            fn.apply(context,args);
            timer = null;
        },delay);
    };
}

// 节流
export function throttle(fn,delay) {
    let timer = null;
    return function () {
        const context = this;
        const args = arguments;
        if (!timer) {
            timer = setTimeout(function () {
                fn.apply(context,args);
                timer = null;
            },delay);
        }
    };
}

/**
 * 名字脱敏 保留首位
 * @param fullName
 * @returns {string}
 */
export function desensitizedName(fullName) {
    if (!fullName) {
        return "";
    }

    let str = fullName.substr(0,1);
    for (let i = 0; i < fullName.length - 1; i++) {
        str += '*';
    }

    return str;
}

/**
 * 通过两点间的经纬度计算两点间的距离
 * @param {*} lat1 起点纬度
 * @param {*} lng1 起点经度
 * @param {*} lat2 终点纬度
 * @param {*} lng2 终点经度
 * @returns 
 */
export function getDistance(lat1,lng1,lat2,lng2) {
    var radLat1 = lat1 * Math.PI / 180.0;
    var radLat2 = lat2 * Math.PI / 180.0;
    var a = radLat1 - radLat2;
    var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
    var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2),2) +
        Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2),2)));
    s = s * 6378.137; // EARTH_RADIUS;
    s = Math.round(s * 10000) / 10000;
    return s;
}

/**
 * 判断浏览器类型
 * @returns 
 */
export function getBrowserType() {
    const ua = window.navigator.userAgent;
    if (/MicroMessenger/.test(ua)) {
        // 微信
        return 'wechat';
    } else if (/AlipayClient/.test(ua)) {
        // 支付宝
        return 'alipay';
    } else if (/UnionPay/.test(ua)) {
        // 云闪付
        return 'up';
    } else if (/BankabcAndroid|BankabciPhone/.test(ua)) {
        // 农行掌银
        return 'abc';
    } else {
        return 'other';
    }
}


/**
 * 获取浏览器名称
 * @returns 
 */
export function getBrowserName() {
    const userAgent = navigator.userAgent;
    if (userAgent.indexOf("Opera") > -1 || userAgent.indexOf("OPR") > -1) {
        return 'Opera';
    } else if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1) {
        return 'IE';
    } else if (userAgent.indexOf("Edge") > -1) {
        return 'Edge';
    } else if (userAgent.indexOf("Firefox") > -1) {
        return 'Firefox';
    } else if (userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1) {
        return 'Safari';
    } else if (userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1) {
        return 'Chrome';
    } else if (!!window.ActiveXObject || "ActiveXObject" in window) {
        return 'IE>=11';
    } else {
        return 'Unkonwn';
    }
}

/**
 * 获取浏览器名称及其版本号
 * @returns 
 */
export function getBrowserNameVersion() {
    console.log('navigator.userAgent',navigator.userAgent);
    var Sys = {};
    var ua = navigator.userAgent.toLowerCase();
    var s;
    (s = ua.match(/rv:([\d.]+)\) like gecko/)) ? Sys.ie = s[1] :
        (s = ua.match(/msie ([\d\.]+)/)) ? Sys.ie = s[1] :
            (s = ua.match(/edge\/([\d\.]+)/)) ? Sys.edge = s[1] :
                (s = ua.match(/firefox\/([\d\.]+)/)) ? Sys.firefox = s[1] :
                    (s = ua.match(/(?:opera|opr).([\d\.]+)/)) ? Sys.opera = s[1] :
                        (s = ua.match(/chrome\/([\d\.]+)/)) ? Sys.chrome = s[1] :
                            (s = ua.match(/version\/([\d\.]+).*safari/)) ? Sys.safari = s[1] : 0;
    // 根据关系进行判断
    if (Sys.ie) return ('IE: ' + Sys.ie);
    if (Sys.edge) return ('EDGE: ' + Sys.edge);
    if (Sys.firefox) return ('Firefox: ' + Sys.firefox);
    if (Sys.chrome) return ('Chrome: ' + Sys.chrome);
    if (Sys.opera) return ('Opera: ' + Sys.opera);
    if (Sys.safari) return ('Safari: ' + Sys.safari);
    return 'Unkonwn';
}

/**
 * 比较版本号
 * @param {*} version1 
 * 版本号1
 * @param {*} version2 
 * 版本号2
 * @returns 
 * 如果version1 > version2,返回1;
 * 如果version1 < version2,返回-1,
 * 其他情况返回0
 */
export function compareVersion(version1,version2) {
    const newVersion1 = `${version1}`.split('.').length < 3 ? `${version1}`.concat('.0') : `${version1}`;
    const newVersion2 = `${version2}`.split('.').length < 3 ? `${version2}`.concat('.0') : `${version2}`;
    // 计算版本号大小,转化大小
    function toNum(a) {
        const c = a.toString().split('.');
        const num_place = ["","0","00","000","0000"],r = num_place.reverse();
        for (let i = 0; i < c.length; i++) {
            const len = c[i].length;
            c[i] = r[len] + c[i];
        }
        return c.join('');
    }

    // 检测版本号是否需要更新
    function checkPlugin(a,b) {
        const numA = toNum(a);
        const numB = toNum(b);
        return numA > numB ? 1 : numA < numB ? -1 : 0;
    }
    return checkPlugin(newVersion1,newVersion2);
}

/**
 * 产生随机数组
 * @param {*} num 生成数组长度
 * @param {*} arr 原数组
 * @returns 指定长度和原数组的随机数组
 */
export function randomArr(num,arr) {
    var arrNew = [];
    var copyArr = arr.join('&').split('&')

    for (var i = 0; i < num; i++) {
        var _num = Math.floor(Math.random() * copyArr.length)
        var mm = copyArr[_num];
        copyArr.splice(_num,1)
        arrNew.push(mm)
    }

    return arrNew
}