UNPKG

yanyan-ui

Version:

适用于vue3的前端xy组件库

712 lines (647 loc) 21.9 kB
import type { dateListItemType, dayContentType, formatDateNum, formatDateStr, MessageTypes, dataType, deepLookupData } from "../../types/tools"; import type {Action} from 'element-plus'; import {ElMessage, ElMessageBox } from "element-plus"; import type {MessageBoxState } from "element-plus"; import type {VNode} from "vue" /* *获取某日期所在月份的日历天数 *@param year 年份 *@param month 月份 * @param dayContent 日期内容 *@param startDayOfMonday 是否以星期一开始,true为星期一开始,false为星期日开始 *@returns 返回一个数组,包含当前月份的日历天数 */ export const createCalendar = ( year: number, month: number, dayContent: dayContentType[] = [], startDayOfMonday: boolean = true ): dateListItemType[] => { //当前月有多少天 const daysInMonth:number = new Date(year, month + 1, 0).getDate();//下个月的第0天就是这个月的最后一天 //上个月显示的天数 const startDayOfWeek: 1|0 = startDayOfMonday ? 1 : 0; const monthStartOfWeek:number = new Date(year, month, 1).getDay(); const daysInPrevMonth:number = new Date(year, month, 0).getDate(); const prevMonthDays:number = monthStartOfWeek - startDayOfWeek; //下个月显示的天数 const nextMonthDays:number = 42 - prevMonthDays - daysInMonth; // 创建一个数组,用于存放日历天数 let calendar: dateListItemType[] = []; // 上一个月的最后几天 for (let i = prevMonthDays; i > 0; i--) { calendar.push({ ifCurrentMonth: false, day: daysInPrevMonth - i + 1, date: convertTimeFormat("YYYY-MM-DD", { year: year, month: month, day: daysInPrevMonth - i + 1 }) }); } // 当前月份的天数 for (let i = 1; i <= daysInMonth; i++) { calendar.push({ ifCurrentMonth: true, day: i, date: convertTimeFormat("YYYY-MM-DD", { year: year, month: month + 1, day: i }) }); } // 下一个月的前几天 for (let i = 1; i <= nextMonthDays; i++) { calendar.push({ ifCurrentMonth: false, day: i, date: convertTimeFormat("YYYY-MM-DD", { year: year, month: month + 2, day: i }) }); } dayContent.forEach((item):void => { // @ts-ignore let index:number = calendar.findIndex((value) => value.date === item.date); if (index !== -1) { calendar[index].content = item.content; } }) return calendar; } /** * 转换时间格式 * @param format 时间格式 * @param date 时间 formatDate对象 | Date对象 | 时间戳 * @returns 返回一个字符串,格式为对应的时间格式 * @example Tools.convertTimeFormat('YYYY-MM-DD HH:mm:ss',new Date()) * */ export const convertTimeFormat = (format: string, date: formatDateNum | Date | number): string => { const padZero = (num: number | string): string => String(num).padStart(2, '0'); const formatDate = (date: Date | number): formatDateStr => { const d = typeof date === 'number' ? new Date(date) : date; return { year: String(d.getFullYear()), month: padZero(d.getMonth() + 1), day: padZero(d.getDate()), hour: padZero(d.getHours()), minute: padZero(d.getMinutes()), second: padZero(d.getSeconds()) }; }; const paramUnify = (date: formatDateNum | Date | number): formatDateStr => { if (typeof date === 'object' && ('year' in date || 'month' in date)) { return { year: String(date.year), month: padZero(date.month), day: padZero(date.day), hour: padZero(date.hour), minute: padZero(date.minute), second: padZero(date.second) }; } return formatDate(date as Date | number); }; const afterConvertDate = paramUnify(date); return format .replace('YYYY', afterConvertDate.year) .replace('MM', afterConvertDate.month) .replace('M', String(Number(afterConvertDate.month))) .replace('DD', afterConvertDate.day) .replace('D', String(Number(afterConvertDate.day))) .replace('HH', afterConvertDate.hour) .replace('H', String(Number(afterConvertDate.hour))) .replace('mm', afterConvertDate.minute) .replace('m', String(Number(afterConvertDate.minute))) .replace('ss', afterConvertDate.second) .replace('s', String(Number(afterConvertDate.second))); }; /** * 根据模板直接转换日期格式 * @param date 日期字符串年月日之间必须有任意非数字分隔符 * @param targetFormat 目标的日期格式 * @returns 返回一个字符串,格式为targetFormat的日期格式 * @example Tools.convertDateFormatByTemplate('2021-01-01','YYYY年MM月DD日') * */ export const convertDateFormatByTemplate = (date: string, targetFormat:string): string => { const dateArr:RegExpMatchArray = date.match(/\d+/g) || ['error','error','error']; while(dateArr.length < 3){ dateArr.push('error'); } return targetFormat.replace('YYYY', dateArr[0]).replace('MM', dateArr[1]).replace('DD', dateArr[2]); } /** * 数组分类函数 * @param array 需要分类的数组 * @param rules 分类规则 * @returns 返回一个对象,包含分类后的数组 */ export const groupBy = (array:object[], rules: string | Function):object|{true:[],false:[]} =>{ function paramUnify(rules:Function | string):Function{ if(typeof rules === "string"){ return eval(`(item)=>item.${rules}`); } return rules; } rules = paramUnify(rules); const result:object = {}; for (const item of array) { const key = rules(item); if (!result[key]) { result[key] = []; } result[key].push(item); } return result; } /** * 深拷贝 * @param data 需要拷贝的元素 * @returns 深拷贝后的元素 */ export const deepCopy = (data:any):any=>{ let newData:any = null; if(typeof(data) === 'object' && data != null){ newData = data instanceof Array ? [] : {}; for( const key in data){ newData[key] = deepCopy(data[key]); } }else{ newData = data; } return newData; } /** * 根据key数组去重 * @param array 需要去重的数组 * @param key 去重的key * @returns {*} */ export const accordingToKeyUnique = (array:Array<any>, key?:string):Array<any>=>{ let newArray:Array<any> = [],obj:Object = {}; if(key){ newArray = array.reduce((preVal:any,curVal:any):any=>{ obj[curVal[key]] ? "" : obj[curVal[key]] = preVal.push(curVal); return preVal; },[]) }else{ newArray = array.filter((val:any):Boolean=>{ return obj.hasOwnProperty(typeof val + JSON.stringify(val)) ? false : obj[typeof val + JSON.stringify(val)] = true; }) } return newArray; } /** * 防抖函数 * @param fn 需要防抖的函数 * @param delay 防抖时间 */ export const debounce = (fn:Function, delay:number):Function=>{ let timer = null; return function(...args){ clearTimeout(timer); timer = setTimeout(()=>{ fn.apply(this,args); },delay) } } /** * 节流函数 * @param fn 需要节流的函数 * @param delay 节流时间 */ export const throttle = (fn:Function, delay:number):Function=>{ let oldTime:number = Date.now(); return function(...args){ let newTime:number = Date.now(); if(newTime - oldTime >= delay){ fn.apply(this,args); oldTime = newTime; } } } /** * base64转file * @param urlData base64数据 * @param fileName 文件名 * @returns 返回一个file对象 * */ export const base64ToFile = (urlData:string, fileName:string):File => { let arr:string[] = urlData.split(','); let mime:string = arr[0].match(/:(.*?);/)[1]; let bytes:string = atob(arr[1]); // 解码base64 let n:number = bytes.length let ia:Uint8Array = new Uint8Array(n); while (n--) { ia[n] = bytes.charCodeAt(n); } return new File([ia], fileName, { type: mime }); }; /** * file转base64 * @param file file对象 * @param callBack 回调函数 * */ export const fileToBase64 = (file:File,callBack:(baseStr:any)=>void):void=> { const reader:FileReader = new FileReader(); reader.onload = (e:ProgressEvent<FileReader>):void => { callBack(e.target.result) }; reader.readAsDataURL(file); } /** * 获取数据类型 * @param sourceData 源数据 * @returns 返回数据类型 */ export const getType = <T>(sourceData: T): dataType => { //toString会返回对应不同的标签的构造函数 let toString = Object.prototype.toString; const map: any = { "[object Boolean]": "boolean", "[object Number]": "number", "[object String]": "string", "[object Function]": "function", "[object Array]": "array", "[object Date]": "date", "[object RegExp]": "regExp", "[object Undefined]": "undefined", "[object Null]": "null", "[object Object]": "object", "[object Blob]": "blob", "[object FormData]": "formData", "[object Promise]": "promise" } return map[toString.call(sourceData)]; } /** * 显示消息框 * @param type 消息类型(success / info / warning / error) * @param message 消息内容 * @param offset 消息框距离窗口顶部的偏移量 * @param duration 显示时间, 毫秒。设为 0 则不会自动关闭 * @param grouping 合并内容相同的消息,不支持 VNode 类型的消息 * @param dangerouslyUseHTMLString 是否将 message 属性作为 HTML 片段处理 */ export const showMsg = ( type: MessageTypes, message: string | VNode, offset: number = 60, duration: number = 3000, grouping: boolean = true, dangerouslyUseHTMLString?: boolean ):void => { ElMessage({ type, message, offset, duration, grouping, dangerouslyUseHTMLString }) }; /** * 显示确认框 * @param message 消息内容 * @param callback 若不使用 Promise,可以使用此参数指定 MessageBox 关闭后的回调 * @param title 消息标题 * @param type 消息类型(success / info / warning / error) * @param cancel 在外部自定义捕获异常时的回调 * @param distinguishCancelAndClose 是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 Esc 键)进行区分 * @param showCancelButton 是否显示取消按钮 * @param showConfirmButton 是否显示确定按钮 * @param confirmButtonText 确定按钮的文本内容 * @param cancelButtonText 取消按钮的文本内容 */ export const showConfirm = ( message: string, callback: Function, title?: string, type?: MessageBoxState["type"], cancel?: Function, distinguishCancelAndClose: boolean = false, showCancelButton: boolean = true, showConfirmButton: boolean = true, confirmButtonText: string = "确定", cancelButtonText: string = "取消" ):void => { ElMessageBox.confirm(message, title ? title : "提示", { distinguishCancelAndClose: distinguishCancelAndClose, showCancelButton: showCancelButton, showConfirmButton: showConfirmButton, confirmButtonText: confirmButtonText, cancelButtonText: cancelButtonText, type: type as MessageBoxState["type"] ? type as MessageBoxState["type"] : "warning" }, ).then(() => { callback(); }).catch((action: Action) => { if (cancel) { if (action === "cancel") { cancel(); return; } } showMsg("info", "取消操作"); }); }; /** * 消息提示框 * @param message 消息内容 * @param showClose MessageBox 是否显示右上角关闭按钮 * @param title 消息标题 * @param type 消息类型(success / info / warning / error) * @param distinguishCancelAndClose 是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 Esc 键)进行区分 * @param showCancelButton 是否显示取消按钮 * @param showConfirmButton 是否显示确定按钮 * @param confirmButtonText 确定按钮的文本内容 * @param cancelButtonText 取消按钮的文本内容 * @param callback 若不使用 Promise,可以使用此参数指定 MessageBox 关闭后的回调 * @param cancel 在外部自定义捕获异常时的回调 */ export const showAlert = ( message: string, showClose: boolean = true, title: string = "提示", type: MessageBoxState["type"] = "error", distinguishCancelAndClose: boolean = false, showCancelButton: boolean = false, showConfirmButton: boolean = false, confirmButtonText: string = "确定", cancelButtonText: string = "取消", callback: Function, cancel?: Function, ):void => { ElMessageBox.alert(message, title, { showClose: showClose, distinguishCancelAndClose: distinguishCancelAndClose, showCancelButton: showCancelButton, showConfirmButton: showConfirmButton, confirmButtonText: confirmButtonText, cancelButtonText: cancelButtonText, type: type }, ).then(() => { callback(); }).catch((action: Action) => { if (cancel) { if (action === "cancel") { cancel(); return; } } showMsg("info", "取消操作"); }); }; /** * 列表各项添加深度和位置信息 * @param targetList 目标列表 * @param indentStep 深度步进值 * @param initialIndentValue 初始深度值 * @param currentPos 第一项位置的初始值 * @returns 处理后的列表和下一个位置 */ export const calculateItemDepth = ( targetList: any[], indentStep: number = 1, initialIndentValue: number = 0, currentPos: number = 0 ): { nextPos: number; updatedList: { [p: string]: any; indentValue: number; children?: any[]; listPosition: number }[] } => { let pos:number = currentPos; const updatedList = targetList.map(item => { let newItem = { ...item, indentValue: item.indentValue ? item.indentValue : initialIndentValue, listPosition: pos }; pos += 1; if (newItem.children) { const result = calculateItemDepth(newItem.children, indentStep, initialIndentValue + indentStep, pos); // @ts-ignore newItem.children = result.updatedList; pos = result.nextPos; } return newItem; }); return { updatedList, nextPos: pos }; }; /** * 深度搜索 * @param dataList 目标数组 * @param findRules 查找规则 * @returns 返回一个数组,包含查找到的元素 */ export const deepLookup = (dataList:Array<deepLookupData>,findRules:(item:deepLookupData)=>Boolean):Array<deepLookupData>=>{ let result: Array<deepLookupData> = []; const search = (items: Array<deepLookupData>) => { for (const item of items) { if (findRules(item)) { result.push(item); } if (item.children && item.children.length > 0) { search(item.children); } } }; search(dataList); return result; } /** * 复制文本 * @param text 文本 * @param ifShowMsg 是否显示消息 * @returns 返回一个Promise对象 code=200成功,code=100失败 */ export const copyText = (text:string,ifShowMsg:Boolean=true):Promise<{code:number,message:string}> => { return new Promise((resolve, reject)=>{ if(navigator.clipboard){ navigator.clipboard.writeText(text) .then(() => { if(ifShowMsg){showMsg('success','已复制!')} resolve({code:200,message:"已复制!"}) }) .catch(err => { if(ifShowMsg){showMsg('error','复制失败'+err)} resolve({code:100,message:"复制失败"+err}) }); }else{ //Document.execCommand() 方法实现 const codeElement = document.createElement('pre'); codeElement.style.position = 'absolute'; codeElement.style.left = '-9999px'; codeElement.textContent = text; document.body.appendChild(codeElement); const range = document.createRange(); range.selectNodeContents(codeElement); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); try { const successful = document.execCommand('copy'); if (successful) { if(ifShowMsg){showMsg('success','已复制!')} resolve({code:200,message:"已复制!"}) } else { if(ifShowMsg){showMsg('error','复制失败')} resolve({code:100,message:"复制失败"}) } } catch (err) { if(ifShowMsg){showMsg('error','复制失败'+err)} resolve({code:100,message:"复制失败"+err}) } finally{ document.body.removeChild(codeElement); selection.removeAllRanges(); } } }) } /** * 改变颜色 * @param colorValue 16进制颜色值 或 rgb() 或 rgba() * @param degree 改变的程度,负数加深颜色/透明度变小;正数颜色变浅/透明度变大 * @param originally 返回结果和原本类型一致,false时输出16进制颜色值 * @returns 返回改变后的颜色值 */ export const changeColor = (colorValue: string, degree: number, originally: boolean = true): string => { // 将十六进制颜色值转为 RGB 组件 const hexToRgb = (hex: string) => { let r = 0, g = 0, b = 0, a = 1; if (hex.length === 4) { r = parseInt(hex[1] + hex[1], 16); g = parseInt(hex[2] + hex[2], 16); b = parseInt(hex[3] + hex[3], 16); } else if (hex.length === 7) { r = parseInt(hex[1] + hex[2], 16); g = parseInt(hex[3] + hex[4], 16); b = parseInt(hex[5] + hex[6], 16); } else if (hex.length === 9) { // #RRGGBBAA 格式 r = parseInt(hex[1] + hex[2], 16); g = parseInt(hex[3] + hex[4], 16); b = parseInt(hex[5] + hex[6], 16); a = parseInt(hex[7] + hex[8], 16) / 255; } return { r, g, b, a }; }; // 将 RGB 组件转为十六进制颜色值 const rgbToHex = (r: number, g: number, b: number) => { const toHex = (n: number) => { const hex = n.toString(16); return hex.length === 1 ? '0' + hex : hex; }; return `#${toHex(r)}${toHex(g)}${toHex(b)}`; }; // 将 RGBA 转为带透明度的十六进制颜色值 const rgbaToHex = (r: number, g: number, b: number, a: number) => { const toHex = (n: number) => { const hex = Math.round(n * 255).toString(16); return hex.length === 1 ? '0' + hex : hex; }; return `${rgbToHex(r, g, b)}${toHex(a)}`; }; // 解析颜色值 let r, g, b, a = 1; let isHex = false; let hasAlpha = false; if (colorValue.startsWith('#')) { isHex = true; ({ r, g, b, a } = hexToRgb(colorValue)); hasAlpha = colorValue.length === 9; } else if (colorValue.startsWith('rgb')) { const regex = /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/; const match = regex.exec(colorValue); if (match) { r = parseInt(match[1]); g = parseInt(match[2]); b = parseInt(match[3]); if (match[4]) { a = parseFloat(match[4]); hasAlpha = true; } } } else { throw new Error('Invalid color value'); } // 改变颜色组件的值 const changeColorComponent = (component: number, degree: number) => { return degree > 0 ? Math.min(255, component + degree) : Math.max(0, component + degree); }; // 改变透明度值 const changeAlpha = (a: number, degree: number) => { return degree > 0 ? Math.min(1, a + degree / 100) : Math.max(0, a + degree / 100); }; // 应用颜色和透明度变化 if (hasAlpha) { a = changeAlpha(a, degree); } else { r = changeColorComponent(r, degree); g = changeColorComponent(g, degree); b = changeColorComponent(b, degree); } if (originally) { // 如果 originally 为 true,返回与输入类型一致的颜色值 if (isHex) { return hasAlpha ? rgbaToHex(r, g, b, a) : rgbToHex(r, g, b); } else { return hasAlpha ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`; } } else { // 如果 originally 为 false,返回十六进制颜色值,带有透明度 return hasAlpha ? rgbaToHex(r, g, b, a) : rgbToHex(r, g, b); } }; /** * 区间随机整数 * @param min 最小值 * @param max 最大值 * @returns 返回一个随机整数 */ export const randomInterval = (min,max):number => { return Math.floor(Math.random() * (max - min + 1) + min); } /** * 设置 CSS 变量的值 * @param cssVarName CSS 变量名 * @param value CSS 变量值 * */ export const setCssVar = (cssVarName: string, value: string):void => { document.documentElement.style.setProperty(cssVarName, value); }; /** * 获取CSS 变量的值 * @param cssVarName CSS 变量名 * */ export const getCssVar = (cssVarName:string):string=>{ const rootStyles = getComputedStyle(document.documentElement); return rootStyles.getPropertyValue(cssVarName).trim(); } /** * 获取cookie列表 * */ export const getCookieList = ():{[key:string]:string}=>{ return document.cookie.split(';').reduce((acc, cookie) => { const [key, value] = cookie.split('='); acc[key.trim()] = decodeURIComponent(value); return acc; }, {}); }