init project
This commit is contained in:
31
miniprogram/utils/Cooker.ts
Normal file
31
miniprogram/utils/Cooker.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export default class Cooker {
|
||||
|
||||
static set(name: string, value: string, ttlMS: number) {
|
||||
let expires = "";
|
||||
if (ttlMS) {
|
||||
let date = new Date();
|
||||
date.setTime(date.getTime() + ttlMS);
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||||
}
|
||||
|
||||
static get(name: string) {
|
||||
let nameSplit = name + "=";
|
||||
let ca = document.cookie.split(";");
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == " ") {
|
||||
c = c.substring(1, c.length);
|
||||
}
|
||||
if (c.indexOf(nameSplit) == 0) {
|
||||
return c.substring(nameSplit.length, c.length);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static remove(name: string) {
|
||||
document.cookie = name + "=; Max-Age=-99999999;";
|
||||
}
|
||||
}
|
||||
106
miniprogram/utils/Events.ts
Normal file
106
miniprogram/utils/Events.ts
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* ### 全局事件管理
|
||||
*
|
||||
* ```js
|
||||
* // 注册
|
||||
* Events.register("eventName", () => {
|
||||
* // 触发执行
|
||||
* });
|
||||
*
|
||||
* // 触发
|
||||
* Events.emit("eventName", '支持参数');
|
||||
*
|
||||
* // 移除
|
||||
* Events.remove("eventName");
|
||||
* ```
|
||||
*/
|
||||
export default class Events {
|
||||
|
||||
// 监听数组
|
||||
private static listeners = new Map<any, Observer<any>[]>();
|
||||
|
||||
/**
|
||||
* 注册事件(会叠加)
|
||||
*
|
||||
* @param key 事件名称
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
public static register<T>(key: T, callback: Function) {
|
||||
const observers: Observer<T>[] | undefined = Events.listeners.get(key);
|
||||
if (!observers) {
|
||||
Events.listeners.set(key, []);
|
||||
}
|
||||
Events.listeners.get(key)?.push(new Observer<T>(key, callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置并注册(不会叠加)
|
||||
*
|
||||
* @param key 事件名称
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
public static reset<T>(key: T, callback: Function) {
|
||||
Events.listeners.set(key, []);
|
||||
this.register(key, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件
|
||||
*
|
||||
* @param key 事件名称
|
||||
*/
|
||||
public static remove<T>(key: T) {
|
||||
const observers: Observer<T>[] | undefined = Events.listeners.get(key);
|
||||
if (observers) {
|
||||
for (let i = 0, l = observers.length; i < l; i++) {
|
||||
if (observers[i].equals(key)) {
|
||||
observers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Events.listeners.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
*
|
||||
* @param key 事件名称
|
||||
* @param args 参数
|
||||
*/
|
||||
public static emit<T>(key: T, ...args: any[]) {
|
||||
const observers: Observer<T>[] | undefined = Events.listeners.get(key);
|
||||
if (observers) {
|
||||
for (const observer of observers) {
|
||||
// 通知
|
||||
observer.notify(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 观察者 */
|
||||
class Observer<T> {
|
||||
|
||||
private callback: Function = () => {}; // 回调函数
|
||||
|
||||
private readonly key: T;
|
||||
|
||||
constructor(key: T, callback: Function) {
|
||||
this.key = key;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送通知
|
||||
*
|
||||
* @param args 不定参数
|
||||
*/
|
||||
notify(...args: any[]): void {
|
||||
this.callback.call(this.key, ...args);
|
||||
}
|
||||
|
||||
equals(name: any): boolean {
|
||||
return name === this.key;
|
||||
}
|
||||
}
|
||||
111
miniprogram/utils/IOSize.ts
Normal file
111
miniprogram/utils/IOSize.ts
Normal file
@ -0,0 +1,111 @@
|
||||
export enum Unit {
|
||||
|
||||
/** B */
|
||||
B = "B",
|
||||
|
||||
/** KB */
|
||||
KB = "KB",
|
||||
|
||||
/** MB */
|
||||
MB = "MB",
|
||||
|
||||
/** GB */
|
||||
GB = "GB",
|
||||
|
||||
/** TB */
|
||||
TB = "TB",
|
||||
|
||||
/** PB */
|
||||
PB = "PB",
|
||||
|
||||
/** EB */
|
||||
EB = "EB"
|
||||
}
|
||||
|
||||
/** 储存单位 */
|
||||
export default class IOSize {
|
||||
|
||||
/** 1 字节 */
|
||||
public static BYTE = 1;
|
||||
|
||||
/** 1 KB */
|
||||
public static KB = IOSize.BYTE << 10;
|
||||
|
||||
/** 1 MB */
|
||||
public static MB = IOSize.KB << 10;
|
||||
|
||||
/** 1 GB */
|
||||
public static GB = IOSize.MB << 10;
|
||||
|
||||
/** 1 TB */
|
||||
public static TB = IOSize.GB << 10;
|
||||
|
||||
/** 1 PB */
|
||||
public static PB = IOSize.TB << 10;
|
||||
|
||||
/** 1 EB */
|
||||
public static EB = IOSize.PB << 10;
|
||||
|
||||
public static Unit = Unit;
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量,保留两位小数
|
||||
* <pre>
|
||||
* // 返回 100.01 KB
|
||||
* format(102411, 2);
|
||||
* </pre>
|
||||
*
|
||||
* @param size 字节大小
|
||||
* @param fixed 保留小数
|
||||
* @param stopUnit 停止单位
|
||||
* @return
|
||||
*/
|
||||
public static format(size: number, fixed = 2, stopUnit?: Unit): string {
|
||||
const units = Object.keys(Unit);
|
||||
if (0 < size) {
|
||||
for (let i = 0; i < units.length; i++, size /= 1024) {
|
||||
const unit = units[i];
|
||||
if (size <= 1000 || i === units.length - 1 || unit === stopUnit) {
|
||||
if (i === 0) {
|
||||
// 最小单位不需要小数
|
||||
return size + " B";
|
||||
} else {
|
||||
return `${size.toFixed(fixed)} ${unit}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "0 B";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量,保留两位小数,不带单位
|
||||
* <pre>
|
||||
* // 返回 100.01
|
||||
* format(102411, 2);
|
||||
* </pre>
|
||||
*
|
||||
* @param size 字节大小
|
||||
* @param fixed 保留小数
|
||||
* @param stopUnit 停止单位
|
||||
* @return
|
||||
*/
|
||||
public static formatWithoutUnit(size: number, fixed = 2, stopUnit?: Unit): string {
|
||||
const units = Object.keys(Unit);
|
||||
if (0 < size) {
|
||||
for (let i = 0; i < units.length; i++, size /= 1024) {
|
||||
const unit = units[i];
|
||||
if (size <= 1000 || i === units.length - 1 || unit === stopUnit) {
|
||||
if (i === 0) {
|
||||
// 最小单位不需要小数
|
||||
return size.toFixed(0);
|
||||
} else {
|
||||
return size.toFixed(fixed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
129
miniprogram/utils/Storage.ts
Normal file
129
miniprogram/utils/Storage.ts
Normal file
@ -0,0 +1,129 @@
|
||||
export default class Storage {
|
||||
|
||||
/**
|
||||
* 获取为布尔值
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 布尔值
|
||||
*/
|
||||
public static is(key: string): boolean {
|
||||
return this.getString(key) === "true";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取为布尔值并取反
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 布尔值
|
||||
*/
|
||||
public static not(key: string): boolean {
|
||||
return !this.is(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取为指定对象
|
||||
*
|
||||
* @template T 对象类型
|
||||
* @param key 键
|
||||
* @returns {T | undefined} 返回对象
|
||||
*/
|
||||
public static getObject<T>(key: string): T {
|
||||
if (this.has(key)) {
|
||||
return this.getJSON(key) as T;
|
||||
}
|
||||
throw Error(`not found ${key}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值,如果没有则储存并使用默认值
|
||||
*
|
||||
* @template T 默认值类型
|
||||
* @param key 键
|
||||
* @param def 默认值
|
||||
* @return {T} 对象
|
||||
*/
|
||||
public static getDefault<T>(key: string, def: T): T {
|
||||
if (this.has(key)) {
|
||||
return this.getJSON(key) as T;
|
||||
}
|
||||
this.setObject(key, def);
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取为 JSON
|
||||
*
|
||||
* @param key 键
|
||||
* @returns JSON 对象
|
||||
*/
|
||||
public static getJSON(key: string) {
|
||||
return JSON.parse(this.getString(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取为字符串(其他获取方式一般经过这个方法,找不到配置或配置值无效时会抛错)
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 字符串
|
||||
*/
|
||||
public static getString(key: string): string {
|
||||
const value = localStorage.getItem(key);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
throw new Error(`not found: ${key}, ${value}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在某配置
|
||||
*
|
||||
* @param key 键
|
||||
* @returns true 为存在
|
||||
*/
|
||||
public static has(key: string): boolean {
|
||||
return localStorage.getItem(key) !== undefined && localStorage.getItem(key) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public static setObject(key: string, value: any) {
|
||||
if (value instanceof Object || value instanceof Array) {
|
||||
this.setJSON(key, value);
|
||||
} else {
|
||||
this.setString(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 JSON 值
|
||||
*
|
||||
* @param key 键
|
||||
* @param json JSON 字符串
|
||||
*/
|
||||
public static setJSON(key: string, json: any) {
|
||||
localStorage.setItem(key, JSON.stringify(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public static setString(key: string, value: any) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除属性
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
public static remove(key: string) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
97
miniprogram/utils/Time.ts
Normal file
97
miniprogram/utils/Time.ts
Normal file
@ -0,0 +1,97 @@
|
||||
export default class Time {
|
||||
|
||||
/** 1 秒时间戳 */
|
||||
public static S = 1E3;
|
||||
/** 1 分钟时间戳 */
|
||||
public static M = Time.S * 60;
|
||||
/** 1 小时时间戳 */
|
||||
public static H = Time.M * 60;
|
||||
/** 1 天时间戳 */
|
||||
public static D = Time.H * 24;
|
||||
|
||||
public static now(): number {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unix 时间戳转日期
|
||||
*
|
||||
* @param unix 时间戳
|
||||
*/
|
||||
public static toDate(unix?: number): string {
|
||||
if (!unix) return "";
|
||||
const d = new Date(unix);
|
||||
return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d.getDate().toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unix 时间戳转时间
|
||||
*
|
||||
* @param unix 时间戳
|
||||
*/
|
||||
public static toTime(unix?: number): string {
|
||||
if (!unix) return "";
|
||||
const d = new Date(unix);
|
||||
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unix 时间戳转日期和时间
|
||||
*
|
||||
* @param unix 时间戳
|
||||
*/
|
||||
public static toDateTime(unix?: number): string {
|
||||
if (!unix) return "";
|
||||
return `${this.toDate(unix)} ${this.toTime(unix)}`;
|
||||
}
|
||||
|
||||
public static toPassedDate(unix?: number): string {
|
||||
return this.toPassedDateTime(unix, false);
|
||||
}
|
||||
|
||||
public static toPassedDateTime(unix?: number, withDetailTime = true): string {
|
||||
if (!unix) {
|
||||
return "";
|
||||
}
|
||||
const now = new Date().getTime();
|
||||
const between = now - unix;
|
||||
|
||||
if (Time.D * 4 <= between) {
|
||||
return withDetailTime ? this.toDateTime(unix) : this.toDate(unix);
|
||||
} else if (Time.D < between) {
|
||||
return `${Math.floor(between / Time.D)} 天前`;
|
||||
} else if (Time.H < between) {
|
||||
return `${Math.floor(between / Time.H)} 小时前`;
|
||||
} else if (Time.M < between) {
|
||||
return `${Math.floor(between / Time.M)} 分钟前`;
|
||||
} else {
|
||||
return "刚刚";
|
||||
}
|
||||
}
|
||||
|
||||
public static between(begin: Date, end?: Date) : any {
|
||||
if (!end) {
|
||||
end = new Date();
|
||||
}
|
||||
const cs = 1000, cm = 6E4, ch = 36E5, cd = 864E5, cy = 31536E6;
|
||||
const l = end.getTime() - begin.getTime();
|
||||
const y = Math.floor(l / cy),
|
||||
d = Math.floor((l / cd) - y * 365),
|
||||
h = Math.floor((l - (y * 365 + d) * cd) / ch),
|
||||
m = Math.floor((l - (y * 365 + d) * cd - h * ch) / cm),
|
||||
s = Math.floor((l - (y * 365 + d) * cd - h * ch - m * cm) / cs),
|
||||
ms = Math.floor(((l - (y * 365 + d) * cd - h * ch - m * cm) / cs - s) * cs);
|
||||
return { l, y, d, h, m, s, ms };
|
||||
}
|
||||
|
||||
public static toMediaTime(seconds: number): string {
|
||||
seconds = Math.floor(seconds);
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const second = seconds % 60;
|
||||
if (0 < hours) {
|
||||
return `${hours}:${minutes.toString().padStart(2, "0")}:${second.toString().padStart(2, "0")}`;
|
||||
}
|
||||
return `${minutes.toString().padStart(2, "0")}:${second.toString().padStart(2, "0")}`;
|
||||
}
|
||||
}
|
||||
229
miniprogram/utils/Toolkit.ts
Normal file
229
miniprogram/utils/Toolkit.ts
Normal file
@ -0,0 +1,229 @@
|
||||
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))
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user