Files
gaoYuJournal/miniprogram/utils/Toolkit.ts
2025-12-11 19:21:41 +08:00

278 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<unknown> {
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<any, any>): 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<T extends (...args: any[]) => any>(callback: T, defaultImmediate = true, delay = 600): T & {
cancel(): void
} {
let timerId: ReturnType<typeof setTimeout> | null = null; // 存储定时器
let immediate = defaultImmediate;
// 定义一个 cancel 办法,用于勾销防抖
const cancel = (): void => {
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
};
const debounced = function (this: ThisParameterType<T>, ...args: Parameters<T>): 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<T>(
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;
}
}