You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

727 lines
20 KiB

class Tools {
/**
* 判断一个对象是否为 undefined
* @param obj 对象
* @returns 对象是否为 undefined
*/
public static isUndefined(obj: any): boolean {
return typeof obj === 'undefined';
}
/**
* 判断一个对象是否为 null 或者 undefined
* @param obj 对象
* @returns 对象是否为 null 或者 undefined
*/
public static isUndefinedOrNull(obj: any): boolean {
return obj === null || typeof obj === 'undefined';
}
/**
* 判断一个对象是否为 null 或者 undefined 或者空字符串
* @param obj 对象
* @returns 对象是否为 null 或者 undefined 或者 空字符串
*/
public static isEmpty(obj: any): boolean {
return obj == null || typeof obj == 'undefined' || obj == '';
}
/**
* 判断一个对象是否是对象类型
* @param obj 对象
* @returns 对象是否是对象类型
*/
public static isObject(obj: any): boolean {
return !Tools.isEmpty(obj) && typeof obj === 'object';
}
/**
* 判断一个对象是否是数组
* @param obj 对象
* @returns 对象是否是数组
*/
public static isArray(obj: any): boolean {
return !Tools.isEmpty(obj) && Array.isArray(obj);
}
/**
* 判断给定的日期是否是当前日期
* @param date 日期
* @returns 判断日期是否是当前日期
*/
public static isCurrentDay(date: Date): boolean {
return new Date().toISOString().slice(0, 10) === date.toISOString().slice(0, 10);
}
/**
* 判断一个日期是否在两个日期之间
* @param min 下边界日期
* @param max 上边界日期
* @param date 日期
* @returns 判断日期是否在两个日期之间
*/
public static isBetweenTwoDates(min: Date, max: Date, date: Date): boolean {
return date.getTime() >= min.getTime() && date.getTime() <= max.getTime();
}
/**
* 判断一个日期是否在周末
* @param date 日期
* @returns 日期是否在周末
*/
public static isWeekend(date: Date): boolean {
return date.getDay() === 6 || date.getDay() === 0;
}
/**
* 判断一个日期是否在某年内
* @param date 日期
* @param year 年
* @returns 日期是否在某年内
*/
public static isInAYear(date: Date, year: number): boolean {
return date.getUTCFullYear() === new Date(`${year}`).getUTCFullYear();
}
/**
* 将 24 小时转换为 am. 或 pm. 格式
* @param h 小时
* @returns 转换后的字符串
*/
public static toAMPMFormat(h: number): string {
return `${h % 12 === 0 ? 12 : h % 12}${h < 12 ? ' am.' : ' pm.'}`;
}
/**
* 将句子的第一个字母大写
* @param param0 字符串
* @returns 第一个字母大写后的字符串
*/
public static capitalize([first, ...rest]: any): string {
return `${first.toUpperCase()}${rest.join('')}`;
}
/**
* 将句子的第一个字母小写
* @param param0 字符串
* @returns 第一个字母小写后的字符串
*/
public static lowercaseFirst([first, ...rest]: any): string {
return `${first.toLowerCase()}${rest.join('')}`;
}
/**
* 将英文字母转成对应的 emoji 形式
* @param c 字母
* @returns emoji 字母
*/
public static letterToEmoji(c: string): string {
return String.fromCodePoint(c.toLowerCase().charCodeAt(0) + 127365);
}
/**
* 判断一个字符串是不是回文
* @param str 字符串
* @returns 字符串是不是回文
*/
public static isPalindrome(str: string): boolean {
return str.toLowerCase() === str.toLowerCase().split('').reverse().join('');
}
/**
* 计算一个数的阶乘
* @param n 数
* @returns 一个数的阶乘
*/
public static getFactorial(n: number): number {
return n <= 1 ? 1 : n * Tools.getFactorial(n - 1);
}
/**
* 计算一个斐波那契数列第 N 项
* @param n N 项
* @param memo
* @returns
*/
public static getFibonacci(n: number, memo: number[]): number {
return memo[n] || (n <= 2 ? 1 : (memo[n] = Tools.getFibonacci(n - 1, memo) + Tools.getFibonacci(n - 2, memo)));
}
/**
* 复制数组
* @param arr 数组
* @returns 复制后的数组
*/
public static copyToArray(arr: any[]): any[] {
return [...arr];
}
/**
* 数组去重
* @param arr 数组
* @returns 去重后的数组
*/
public static getUnique(arr: any[]): any[] {
return [...new Set(arr)];
}
/**
* 生成随机数字数组
* @param arr 数组
* @returns 生成随机数字数组
*/
public static shuffle(arr: number[]): number[] {
return arr.sort(() => Math.random() - 0.5);
}
/**
* 反转字符串
* @param str 字符串
* @returns 反转后的字符串
*/
public static reverseString(str: string): string {
return str.split('').reverse().join('');
}
/**
* 检查两个数组是否包含相同的值
* @param arr1 第一个数组
* @param arr2 第二个数组
* @returns 两个数组是否包含相同的值
*/
public static containSameValues(arr1: any[], arr2: any[]): boolean {
return arr1.sort().join(',') === arr2.sort().join(',');
}
/**
* 温度转换(摄氏度->华氏度)
* 华氏度 = 32 + 摄氏度× 1.8
* @param celsius 摄氏度
* @returns 华氏度
*/
public static toFahrenheit(celsius: number): number {
return (celsius * 9) / 5 + 32;
}
/**
* 温度转换(华氏度->摄氏度)
* 华氏度 = 32 + 摄氏度× 1.8
* @param fahrenheit 华氏度
* @returns 摄氏度
*/
public static toCelsius(fahrenheit: number): number {
return ((fahrenheit - 32) * 5) / 9;
}
/**
* 清除浏览器中的所有 cookie
* @returns void
*/
public static clearAllCookies(): void {
document.cookie.split(';').forEach((c) => (document.cookie = c.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`)));
}
/**
* 检查函数是否为异步函数
* @param f 函数
* @returns 函数是否为异步函数
*/
public static isAsyncFunction(f: any): boolean {
return Object.prototype.toString.call(f) === '[object AsyncFunction]';
}
/**
* 判断代码是否在浏览器中运行
* @returns 代码是否在浏览器中运行
*/
public static runningInBrowser(): boolean {
return typeof window === 'object' && typeof document === 'object';
}
/**
* 判断代码是否在 Node 中运行
* @returns 代码是否在 Node 中运行
*/
public static runningInNode(): boolean {
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
}
/**
* 判断系统是否是暗模式
* @returns 系统是否是暗模式
*/
public static isDarkMode(): boolean {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
/**
* 将 dom 元素滚动到顶部
* @param element dom 元素
* @returns void
*/
public static toTop(element: HTMLElement): void {
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
/**
* 将 dom 元素滚动到底部
* @param element dom 元素
* @returns void
*/
public static toBottom(element: HTMLElement): void {
element.scrollIntoView({ behavior: 'smooth', block: 'end' });
}
/**
* 将 JSON 转换为 MAP
* @param json json 字符串
* @returns MAP
*/
public static jsonToMap(json: string): Map<any, any> {
return new Map(Object.entries(JSON.parse(json)));
}
/**
* 对象转 json 字符串
* @param obj 对象
* @returns json 字符串
*/
public static object2Json(obj: any): string | null {
return Tools.isEmpty(obj) ? null : JSON.stringify(obj);
}
/**
* json 字符串转对象
* @param json json 字符串
* @returns 对象
*/
public static json2Object(json: string): any {
return Tools.isEmpty(json) ? null : JSON.parse(json);
}
/**
* 通过连接字符串连接数组
* 使用说明:
* const array =['001','002','003'];
* const joined =join(array); // joined='001-002-003'
*
* const objArray =[{name:'001',age:10},{name:'002',age:20}];
* const joined =join(array,'-','name'); // joined='001-002'
* @param array 数组
* @param joiner 连接字符串
* @param propertyName 属性名, 可选参数, 如果数组元素为 js 对象,可通过该参数指定需要连接的 js 对象的属性,如果传入 null ,则直接连接数组元素
* @returns 通过连接字符串连接数组
*/
public static join(array: any, joiner: string, propertyName?: string): string {
if (Tools.isEmpty(array)) {
return '';
}
propertyName = propertyName || '';
if (Array.isArray(array)) {
let result = '';
if (array.length > 0) {
for (let i = 0; i < array.length; i++) {
if (propertyName == '') {
result += array[i] + joiner;
} else {
result += array[i][propertyName] + joiner;
}
}
}
if (result != '') {
result = result.substring(0, result.length - 1);
}
return result;
} else {
return array;
}
}
/**
* 深度克隆对象
* @param target 需要克隆对象
* @returns 克隆后的对象
*/
public static deepClone(target: any): any {
const map = new WeakMap();
function isObject(target: any) {
return (typeof target === 'object' && target) || typeof target === 'function';
}
function clone(data: any) {
if (!isObject(data)) {
return data;
}
if ([Date, RegExp].includes(data.constructor)) {
return new data.constructor(data);
}
if (typeof data === 'function') {
return new Function('return ' + data.toString())();
}
const exist = map.get(data);
if (exist) {
return exist;
}
if (data instanceof Map) {
const result = new Map();
map.set(data, result);
data.forEach((val, key) => {
if (isObject(val)) {
result.set(key, clone(val));
} else {
result.set(key, val);
}
});
return result;
}
if (data instanceof Set) {
const result = new Set();
map.set(data, result);
data.forEach((val) => {
if (isObject(val)) {
result.add(clone(val));
} else {
result.add(val);
}
});
return result;
}
const keys = Reflect.ownKeys(data);
const allDesc = Object.getOwnPropertyDescriptors(data);
const result = Object.create(Object.getPrototypeOf(data), allDesc);
map.set(data, result);
keys.forEach((key) => {
const val = data[key];
if (isObject(val)) {
result[key] = clone(val);
} else {
result[key] = val;
}
});
return result;
}
return clone(target);
}
/**
* 构建 Http Get 请求查询参数 url
* 使用说明:
* 1. 键值对数组
* const params =[
* {key:'name',value:'姓名'},
* {key:'年龄',value:20}
* ];
* const url =buildHttpQueryString(params); //url ='name=%E5%A7%93%E5%90%8D&%E5%B9%B4%E9%BE%84=20'
* 2. 简单对象
* const params ={
* name:'姓名',
* 年龄:20
* };
* const url =buildHttpQueryString(params); //url ='name=%E5%A7%93%E5%90%8D&%E5%B9%B4%E9%BE%84=20'
* 3. 对象属性也支持数组
* const params ={
* name:'姓名',
* phone:[
* '13012345678',
* '13812345678'
* ]
* };
* const url =buildHttpQueryString(params); //url ='name=%E5%A7%93%E5%90%8D&phone=13012345678,13812345678'
* @param parameters 查询参数
* @param encode 编码字符集
* @returns 编码后的 url
*/
public static buildHttpQueryString(parameters: any, encode?: true): string | null {
if (Tools.isEmpty(parameters)) return null;
const _parameters: any[] = [];
if (Tools.isArray(parameters)) {
//支持以 [{key:'name',value:'姓名'},{key:'age',value:20}] 形式的参数
for (let i = 0; i < parameters.length; i++) {
if (encode) {
_parameters[i] = encodeURIComponent(parameters[i].key) + '=' + encodeURIComponent(parameters[i].value);
} else {
_parameters[i] = parameters[i].key + '=' + parameters[i].value;
}
}
return Tools.join(_parameters, '&');
} else if (Tools.isObject(parameters)) {
//支持以 {name:'姓名',age:20} 形式的参数
let i = 0;
for (const key in parameters) {
const value = parameters[key];
if (Tools.isArray(value)) {
//支持以 {address:['上海','beijing']} 形式的参数
for (let j = 0; j < value.length; j++) {
if (encode) {
value[j] = encodeURIComponent(value[j]);
}
}
if (encode) {
_parameters[i++] = encodeURIComponent(key) + '=' + Tools.join(value, ',');
} else {
_parameters[i++] = key + '=' + Tools.join(value, ',');
}
} else {
if (encode) {
_parameters[i++] = encodeURIComponent(key) + '=' + encodeURIComponent(parameters[key]);
} else {
_parameters[i++] = key + '=' + parameters[key];
}
}
}
return Tools.join(_parameters, '&');
} else {
return null;
}
}
/**
* 连接两个 URL 组成一个 URL
* @param url1 第一个 URL
* @param url2 第二个 URL
*/
public static concatUrl(url1: string | null, url2: string | null): string {
return (Tools.removeUrlSuffixSlash(url1) || '') + '/' + (Tools.removeUrlPrefixSlash(url2) || '');
}
/**
* 移除 URL 中所有的前导 /
* @param url url
* @returns 移除后的 URL
*/
public static removeUrlPrefixSlash(url: string | null): string | null {
if (url) {
let _url = url;
while (_url.startsWith('/')) {
_url = _url.substring(1);
}
return _url;
}
return null;
}
/**
* 移除 URL 中所有的后导 /
* @param url url
* @returns 移除后的 URL
*/
public static removeUrlSuffixSlash(url: string | null): string | null {
if (url) {
let _url = url;
while (_url.endsWith('/')) {
_url = _url.substring(0, _url.length - 1);
}
return _url;
}
return null;
}
/**
* 给 dom 元素增加 class
* @param target dom 元素
* @param className class 名
*/
public static addClassName(target: HTMLElement | null, className: string | null): void {
if (target && className) {
const _class = target.getAttribute('class');
if (_class) {
const classes = _class.split(' ');
classes.push(className);
const clazzSet = [...new Set(classes)];
target.setAttribute('class', clazzSet.join(' '));
} else {
target.setAttribute('class', className);
}
}
}
/**
* 移除 dom 元素的 class
* @param target dom 元素
* @param className class 名
*/
public static removeClassName(target: HTMLElement | null, className: string | null): void {
if (target && className) {
const _class = target.getAttribute('class');
if (_class) {
let classes = _class.split(' ');
classes = [...new Set(classes)];
classes = classes.filter((item) => item !== className);
target.setAttribute('class', classes.join(' '));
}
}
}
/**
* 获取页面所有外部引用的 javascript 的 url
* @returns 页面所有外部引用的 javascript 的 url
*/
public static getJavascriptElementUrls(): string[] {
const result: string[] = [];
const scripts = document.getElementsByTagName('script');
for (const script of scripts) {
const url = script.getAttribute('src');
if (url) {
result.push(url);
}
}
return result;
}
/**
* 在 dom 中插入 <script src="..."></script> 标签元素
* @param src javascript url
* @param target 插入到 dom 中的元素, 如果未指定则插入到 dom 的 head 元素中
* @param callback 加载成功后回调函数
*/
public static appendJavascriptTag(src: string | null, target?: HTMLElement, callback?: any): void {
if (src) {
const script = document.createElement('script') as HTMLScriptElement;
script.type = 'text/javascript';
script.src = src;
if (callback) {
script.onload = callback;
}
if (target) {
target.appendChild(script);
} else {
document.head.appendChild(script);
}
}
}
/**
* 下载 URL, 创建 iframe, 并在 iframe 中下载资源, 避免页面跳转
* @param url URL
*/
public static download(url: string | null): void {
if (url) {
const iframeId = '_download_iframe';
let iframe = document.getElementById(iframeId) as HTMLIFrameElement;
if (iframe) {
iframe.src = url;
} else {
iframe = document.createElement('iframe');
iframe.id = iframeId;
iframe.src = url;
iframe.style.display = 'none';
document.getElementsByTagName('body')[0].appendChild(iframe);
}
}
}
/**
* 设置页面标题
* @param title 页面标题
*/
public static setTitile(title: string | null): void {
if (title) {
document.title = title;
}
}
/**
* 设置页面 icon
* @param iconUrl 页面 icon url 地址
*/
public static setFavicon(favicon: string | null): void {
if (favicon) {
let faviconElement: HTMLLinkElement = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
if (faviconElement) {
console.log(faviconElement.href);
faviconElement.href = favicon;
} else {
faviconElement = document.createElement('link');
faviconElement.rel = 'shortcut icon';
faviconElement.href = favicon;
document.getElementsByTagName('head')[0].appendChild(faviconElement);
}
}
}
/**
* 移除 dom 元素
* @param element dom 元素对象或者 dom 元素 ID
*/
public static removeDomElement(element: HTMLElement | string): void {
if (element) {
if (Tools.isObject(element)) {
(element as HTMLElement).parentNode?.removeChild(element as HTMLElement);
} else {
const e = document.getElementById(element as string);
if (e) {
e.parentNode?.removeChild(e);
}
}
}
}
/**
* 返回在一个数值范围内的数值
* @param value 数值
* @param min 数值范围下界
* @param max 数值范围上界
* @returns 在数值范围内的数值
*/
public static range(value: number, min: number, max: number): number {
if (value < min) {
return min;
} else if (value > max) {
return max;
} else {
return value;
}
}
/**
* 合并对象,将源对象的属性合并到目标对象上
* @param target 目标对象
* @param source 源对象
*/
public static mergeObject(target: object, source: object): object {
if (source && target) {
for (const property in source) {
const value = source[property];
if (Tools.isObject(value) && !Tools.isArray(value)) {
//正常对象,排除 undefined 和 null
if (Tools.isUndefinedOrNull(target[property])) {
target[property] = {};
}
Tools.mergeObject(target[property], value);
} else {
if (!Tools.isUndefinedOrNull(value)) {
target[property] = value;
}
}
}
}
return target;
}
public static objectValueEquals(o1: object | null | undefined, o2: object | null | undefined): boolean {
if (Tools.isUndefinedOrNull(o1) && Tools.isUndefinedOrNull(o2)) {
return true;
}
if ((!Tools.isUndefinedOrNull(o1) && Tools.isUndefinedOrNull(o2)) || (Tools.isUndefinedOrNull(o1) && !Tools.isUndefinedOrNull(o2))) {
return false;
}
if (o1 === o2) {
return true;
}
for (const p1 in o1) {
if (o1[p1] != o2[p1]) {
return false;
}
}
for (const p2 in o2) {
if (o1[p2] != o2[p2]) {
return false;
}
}
return true;
}
}
export { Tools };