import { browse, mapTree } from 'xe-utils' import { camelCase, upperFirst } from 'lodash-es' import { Message } from '@arco-design/web-vue' import CronParser from 'cron-parser' import { isExternal } from '@/utils/validate' export function getProperty(obj: T, key: K): T[K] { return obj[key] } /** * @desc 去除空格 * @param {string} str - 字符串 * @param {string} pos - 去除空格的位置 * pos="both": 去除两边空格 * pos="left": 去除左边空格 * pos="right": 去除右边空格 * pos="all": 去除所有空格 */ type Pos = 'both' | 'left' | 'right' | 'all' export function trim(str: string, pos: Pos = 'both'): string { if (pos === 'both') { return str.replace(/^\s+|\s+$/g, '') } else if (pos === 'left') { return str.replace(/^\s*/, '') } else if (pos === 'right') { return str.replace(/(\s*$)/g, '') } else if (pos === 'all') { return str.replace(/\s+/g, '') } else { return str } } /** * 根据数字获取对应的汉字 * @param {number} num - 数字(0-10) */ export function getHanByNumber(num: number): string { const str = '零一二三四五六七八九十' return str.charAt(num) } /** * 获取指定整数范围内的随机整数 * @param {number} start - 开始范围 * @param {number} end - 结束范围 */ export function getRandomInterger(start = 0, end: number): number { const range = end - start return Math.floor(Math.random() * range + start) } /** @desc 千分位格式化 */ export function formatMoney(money: string) { return money.replace(new RegExp(`(?!^)(?=(\\d{3})+${money.includes('.') ? '\\.' : '$'})`, 'g'), ',') } /** @desc 数据类型检测方法 */ export function getTypeOf(value: any) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase() } /** * @desc 格式化电话号码 @demo 183-7983-6654 */ export function formatPhone(mobile: string, formatStr = '-') { return mobile.replace(/(?=(\d{4})+$)/g, formatStr) } /** * @desc 手机号脱敏 @demo 155****8810 */ export function hidePhone(phone: string) { return phone.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2') } /** @desc 检测数据是否为空数据 */ export function isEmpty(data: unknown) { if (data === '' || data === 'undefined' || data === undefined || data == null || data === 'null') { return true } return JSON.stringify(data) === '{}' || JSON.stringify(data) === '[]' || JSON.stringify(data) === '[{}]' } /** * @desc 大小写转换 * @param {string} str 待转换的字符串 * @param {number} type 1:全大写 2:全小写 3:首字母大写 */ export function toCase(str: string, type: number) { switch (type) { case 1: return str.toUpperCase() case 2: return str.toLowerCase() case 3: return str[0].toUpperCase() + str.substring(1).toLowerCase() default: return str } } /** * @desc 获取随机数 * @param {number} min 最小值 * @param {number} max 最大值 */ export const randomNum = (min: number, max: number) => { return Math.floor(min + Math.random() * (max + 1 - min)) } /** @desc 获取最大值 */ export const max = (arr: number[]) => { return Math.max.apply(null, arr) } /** @desc 获取最小值 */ export const min = (arr: number[]) => { return Math.min.apply(null, arr) } /** @desc 求和 */ export const sum = (arr: number[]) => { return arr.reduce((pre, cur) => pre + cur) } /** @desc 获取平均值 */ export const average = (arr: number[]) => { return sum(arr) / arr.length } /** @desc 深拷贝 */ export const deepClone = (data: any) => { if (typeof data !== 'object' || data == null) return '不是对象' const newData: any = Array.isArray(data) ? [] : {} for (const key in data) { newData[key] = typeof data[key] === 'object' ? deepClone(data[key]) : data[key] } return newData } /** * @desc 判断是否是闰年 * @param {number} year 年份 */ export const isLeapYear = (year: number) => { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 } /** * @desc 判断是否是奇数 * @param {number} num 数字 */ export const isOdd = (num: number) => { return num % 2 !== 0 } /** * @desc 判断是否是偶数 * @param {number} num 数字 */ export const isEven = (num: number) => { return !isOdd(num) } /** @desc 将RGB转化为十六机制 */ export const rgbToHex = (r: number, g: number, b: number) => { return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}` } /** @desc 获取随机十六进制颜色 */ export const randomHex = () => { return `#${Math.floor(Math.random() * 0xFFFFFF) .toString(16) .padEnd(6, '0')}` } /** * @description 动态路由 path 转 name * @demo /system => System * @demo /system/menu => SystemMenu * @demo /data-manage/detail => DataManageDetail */ export const transformPathToName = (path: string) => { if (!path) return '' if (isExternal(path)) return '' return upperFirst(camelCase(path)) } /** * @desc 过滤树 * @param { values } 数组 */ type FilterTree = ( array: T[], iterate: (item: T, index?: number, items?: T[]) => boolean ) => T[] export const filterTree: FilterTree = (values, fn) => { const arr = values.filter(fn) const data = mapTree(arr, (item) => { if (item.children && item.children.length) { item.children = item.children.filter(fn) } return item }) return data } type SortTree = (array: T[]) => T[] /** * @desc 排序树 * @param values / */ export const sortTree: SortTree = (values) => { values?.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) // 排序 return mapTree(values, (item) => { item.children?.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) // 排序 return item }) } /** @desc 是否是h5环境 */ export const isMobile = () => { return browse().isMobile } /** @desc 问候 */ export function goodTimeText() { const time = new Date() const hour = time.getHours() return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour <= 18 ? '下午好' : '晚上好' } /** @desc 格式化文件大小 */ export const formatFileSize = (fileSize: number) => { if (fileSize == null || fileSize === 0) { return '0 Bytes' } const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] let index = 0 const srcSize = Number.parseFloat(fileSize.toString()) index = Math.floor(Math.log(srcSize) / Math.log(1024)) const size = srcSize / 1024 ** index return `${size.toFixed(2)} ${unitArr[index]}` } /** @desc 复制文本 */ export const copyText = (text: any) => { const textarea = document.createElement('textarea') textarea.value = text document.body.appendChild(textarea) textarea.select() document.execCommand('copy') document.body.removeChild(textarea) Message.success('复制成功') } /** @desc 文件的转换base64 */ export const fileToBase64 = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = () => { if (reader.result) { resolve(reader.result.toString()) } else { reject(new Error('文件转base64失败')) } } reader.onerror = (error) => { reject(error) } reader.readAsDataURL(file) }) } export const YMD_HMS = 'yyyy-MM-dd HH:mm:ss' /** * 格式化时间 */ export function dateFormat(date = new Date(), pattern = YMD_HMS) { if (!date) { return '' } const o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'H+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds(), 'q+': Math.floor((date.getMonth() + 3) / 3), 'S+': date.getMilliseconds(), } let formattedDate = pattern // Start with the pattern // Year Handling const yearMatch = formattedDate.match(/(y+)/) if (yearMatch) { formattedDate = formattedDate.replace(yearMatch[0], (`${date.getFullYear()}`).substring(4 - yearMatch[0].length)) } // Other Formatters for (const k in o) { const reg = new RegExp(`(${k})`) const match = formattedDate.match(reg) if (match) { formattedDate = formattedDate.replace(match[0], (match[0].length === 1) ? o[k] : (`00${o[k]}`).substring((`${o[k]}`).length)) } } return formattedDate } /** * 不含年的 cron 表达式 * @param cron */ const expressionNoYear = (cron: string) => { const vs = cron.split(' ') // 长度=== 7 包含年表达式 不解析 if (vs.length === 7) { return vs.slice(0, vs.length - 1).join(' ') } return cron } /** * 解析cron表达式预计未来运行时间 * @param cron cron表达式 */ export function parseCron(cron: string) { try { const parse = expressionNoYear(cron) const iter = CronParser.parseExpression(parse, { currentDate: dateFormat(new Date()), }) const result: string[] = [] for (let i = 1; i <= 5; i++) { const nextDate = iter.next() if (nextDate) { result.push(dateFormat(new Date(nextDate as any))) } } return result.length > 0 ? result.join('\n') : '无执行时间' } catch (e) { return '表达式错误' } }