export default class Toolkit { public static isFunction = (val: any) => typeof val === "function"; public static isArray = Array.isArray; public static isString = (val: any) => typeof val === "string"; public static isSymbol = (val: any) => typeof val === "symbol"; public static isObject = (val: any) => val !== null && typeof val === "object"; public static guid() { const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4() + s4() + s4()}`; } public static className(...args: any[]) { const classes = []; for (let i = 0; i < args.length; i++) { const value = args[i]; if (!value) continue; if (this.isString(value)) { classes.push(value); } else if (this.isArray(value)) { for (let i = 0; i < value.length; i++) { const inner: any = this.className(value[i]); if (inner) { classes.push(inner); } } } else if (this.isObject(value)) { for (const name in value) { if (value[name]) { classes.push(name); } } } } return classes.join(" "); } public static isEmpty(obj: any): boolean { if (this.isString(obj)) { return (obj as string).trim().length === 0; } if (this.isArray(obj)) { return (obj as []).length === 0; } if (this.isFunction(obj)) { return this.isEmpty(obj()); } if (this.isObject(obj)) { return obj === undefined || obj === null; } return obj === undefined || obj === null; } public static isNotEmpty(obj: any): boolean { return !this.isEmpty(obj); } /** * ### 延时执行 * * ```js * await sleep(1E3) * ``` * * @param ms 延时毫秒 */ public static async sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * 转为数字 * * @param string 字符串 * @param fallback 如果失败,返回该值 * @returns 转换后或转换失败数据 */ public static toNumber(string: string, fallback?: number): number { if (!string) return fallback as number; const number = Number(string); return isFinite(number) ? number : fallback as number; } /** * 异步执行 * * @param event 函数 */ public static async(event: (...args: any[]) => any) { setTimeout(event, 0); } /** * 生成随机数 * * @param min 最小值 * @param max 最大值 */ public static random(min = 0, max = 100): number { return Math.floor(Math.random() * (max + 1 - min)) + min; } /** * 深克隆对象 * * @param origin 源对象 * @param target 递归对象 * @returns 克隆对象 */ public static deepClone(origin: any, target = {} as any) { const toString = Object.prototype.toString; const arrType = "[object Array]"; for (const key in origin) { if (Object.prototype.hasOwnProperty.call(origin, key)) { if (typeof origin[key] === "object" && origin[key] !== null) { target[key] = toString.call(origin[key]) === arrType ? [] : {}; this.deepClone(origin[key], target[key]); } else { target[key] = origin[key]; } } } return target; } public static keyValueString(obj: object, assign: string, split: string): string { let result = ""; Object.entries(obj).forEach(([k, v]) => result += k + assign + v + split); return result.substring(0, result.length - split.length); } public static toURLArgs(obj?: { [key: string]: any }): string { if (!obj) { return ""; } const args: string[] = []; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = obj[key]; if (Array.isArray(value)) { value.forEach((item) => { args.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`); }); } else { args.push(`${encodeURIComponent(key)}=${encodeURIComponent(value.toString())}`); } } } return args.join("&"); } public static toObject(map: Map): object { return Array.from(map.entries()).reduce((acc, [key, value]) => { acc[key] = value ?? null; return acc; }, {} as { [key: string]: string | undefined }); } public static uuid(): string { const char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const length = [8, 4, 4, 4, 12]; let result = ""; for (let i = 0; i < length.length; i++) { for (let j = 0; j < length[i]; j++) { result += char[this.random(0, char.length - 1)]; } result += "-"; } return result.substring(0, result.length - 1); } public static keyFromValue(e: any, value: any): string { return Object.keys(e)[Object.values(e).indexOf(value)]; } // 防抖 // eslint-disable-next-line public static debounce any>(callback: T, defaultImmediate = true, delay = 600): T & { cancel(): void } { let timerId: ReturnType | null = null; // 存储定时器 let immediate = defaultImmediate; // 定义一个 cancel 办法,用于勾销防抖 const cancel = (): void => { if (timerId) { clearTimeout(timerId); timerId = null; } }; const debounced = function (this: ThisParameterType, ...args: Parameters): void { const context = this; if (timerId) { cancel(); } if (immediate) { callback.apply(context, args); immediate = false; timerId = setTimeout(() => { immediate = defaultImmediate; }, delay); } else { // 设置定时器,在延迟时间后执行指标函数 timerId = setTimeout(() => { callback.apply(context, args); immediate = defaultImmediate; }, delay); } }; // 将 cancel 方法附加到 debounced 函数上 (debounced as any).cancel = cancel; return debounced as T & { cancel(): void }; } public static format(template: string, variables: { [key: string]: any }): string { return template.replace(/\$\{(\w+)}/g, (_, key) => variables[key]); } public static symmetricDiff(arr1: any[], arr2: any[]) { const set1 = new Set(arr1); const set2 = new Set(arr2); return [ ...arr1.filter(item => !set2.has(item)), ...arr2.filter(item => !set1.has(item)) ]; } /** * 将数组元素分配到指定数量的列中 * * - 如果提供 getHeight 函数:使用贪心算法,每次将元素放到当前高度最小的列(适合瀑布流布局) * - 如果不提供 getHeight:使用轮询方式分配(第 1、4、7... 个到列 1,第 2、5、8... 个到列 2) * * @param items 原始数组 * @param columnCount 列数,默认 3 * @param getHeight 可选的高度获取函数,用于计算每个元素的高度 * @returns 分列后的二维数组 * * @example * // 简单轮询分配 * splitItemsIntoColumns([1, 2, 3, 4, 5, 6], 3) * // => [[1, 4], [2, 5], [3, 6]] * * @example * // 基于高度的贪心分配 * const items = [{ h: 100 }, { h: 200 }, { h: 150 }] * splitItemsIntoColumns(items, 2, item => item.h) * // => [[ { h: 100 }, { h: 150 }], [{ h: 200 }]] */ public static splitItemsIntoColumns( items: T[], columnCount = 3, getHeight?: (item: T) => number ): T[][] { const columns: T[][] = Array.from({ length: columnCount }, () => []); if (!getHeight) { // 降级为轮询分配 items.forEach((item, index) => { columns[index % columnCount].push(item); }); } else { // 使用贪心算法:总是放到当前高度最小的列 const columnHeights = Array(columnCount).fill(0); items.forEach((item) => { // 找到高度最小的列 const minHeightIndex = columnHeights.indexOf(Math.min(...columnHeights)); columns[minHeightIndex].push(item); columnHeights[minHeightIndex] += getHeight(item); }); } return columns; } }