常用工具类/函数
防抖/节流
ts
export const debounce = (fun: (...args: any[]) => void, delay: number) => {
let timer: number | null;
return (...args: any[]) => {
if (timer) {
clearTimeout(timer);
}
timer = window.setTimeout(() => {
fun.call(EMPTY, ...args);
timer = null;
}, delay);
};
};
ts
export const throttle = (fun: (...args: any[]) => void, delay: number) => {
let timer: number | null;
return (...args: any[]) => {
if (timer) {
return;
}
timer = window.setTimeout(() => {
fun.call(EMPTY, ...args);
timer = null;
}, delay);
};
};
(毫)秒数->时长HH:mm:ss
ts
export const transformMsToText = (value: number, type: 'ms' | 's' = 'ms') => {
if (!value) {
return '00:00:00';
}
const msSeconds = type === 'ms' ? value / 1000 : value;
// 转换为时分秒
const hours = parseInt(((msSeconds / 60 / 60) % 24).toString());
const hoursText = hours < 10 ? '0' + hours : hours;
let minutes = parseInt(((msSeconds / 60) % 60).toString());
const minutesText = minutes < 10 ? '0' + minutes : minutes;
const seconds = parseInt((msSeconds % 60).toString());
const secondsText = seconds < 10 ? '0' + seconds : seconds;
// 作为返回值返回
return `${hoursText}:${minutesText}:${secondsText}`;
};
常用正则表达式
ts
export const Reg = {
// 自然数(0,1,2,3,4)
natureNo: /^(0|[1-9][0-9]*)$/,
// 非0数量(1,2,3...)
noZeroAmount: /^[1-9]\d*$/,
// 手机号码
mobileTel: /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/,
// 固定电话号码(带区号)
fixedTel: /^(0\d{2,3})-?(\d{7,8})$/,
// url地址(以http或https开头)
url: /^(https?):\/\/[\w-]+(\.[\w-]+)+([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?$/,
// 最多两位小数的数字
fixed2No: /^(([1-9]{1}\d*)|(0{1}))(\.\d{1,2})?$/,
// 邮箱正则
email: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
//检测移动端设备平台(通过navigator.userAgent.match匹配)
isMobile: /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/
};
File/Blob/Base64转换
base64(带文件类型) -> Blob/File
ts
export const base64toBlob = (base64 = '') => {
const arr = base64.split(',');
const mime = arr?.[0]?.match(/:(.*?);/)?.[1];
const suffix = mime?.split('/')[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], { type: mime });
// File对象是基于Blob实现的,可以快速转换成file
// const file = new File([blob], 'fileName',{...options});
return blob
};
base64(无文件类型) -> Blob/File
ts
export function base64toBlobWithoutType(base64Str = '') {
const bstr = atob(base64Str);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], {
type: 'application/octet-stream'
})
// File对象是基于Blob实现的,可以快速转换成file
// const file = new File([blob], 'fileName',{...options});
return blob
}
File/Blob -> base64
ts
export const fileToBase64 = (file:File|Blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
File -> Blob
ts
export const fileToBlob = (file:File) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
//转换成arrayBuffer,blob由arraybuffer的集合组成
reader.readAsArrayBuffer(file);
reader.onload = () => resolve(new Blob([reader.result]));
reader.onerror = (error) => reject(error);
});
};
根据url下载文件
ts
export const downLoadByUrl = (fileLinkUrl, fileName?) => {
const handleFileDownload = (url, filename) => {
// 创建 a 标签
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
};
// handleFileDownload(fileLinkUrl, fileName);
return new Promise((resolve, reject) => {
Toast.loading(true);
fetch(fileLinkUrl, {
method: 'get',
mode: 'cors'
})
.then(function (res) {
if (res.status !== 200) {
throw new Error('文件下载异常');
}
return res.arrayBuffer();
})
.then((blobRes) => {
// 生成 Blob 对象,设置 type 等信息
const e = new Blob([blobRes], {
type: 'application/octet-stream'
});
// 将 Blob 对象转为 url
const link = window.URL.createObjectURL(e);
handleFileDownload(link, fileName);
resolve(true);
})
.catch((err) => {
const strErr = err.toString();
reject(strErr);
Toast.fail(strErr);
})
.finally(() => {
Toast.loading(false);
});
});
};
复制文本到粘贴板
ts
export const copyText = (text) => {
const textArea = document.createElement('textarea') as any;
textArea.value = text;
// 手机端防止页面抖动
textArea.style.width = 0;
textArea.style.position = 'fixed';
textArea.style.left = '-999px';
textArea.style.top = '10px';
textArea.setAttribute('readonly', 'readonly');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
};
指定可滚动的容器元素滚动条到底部
ts
export const scrollToBottom = (
domRef: MutableRefObject<any> | Element | null,
callback?: () => void
) => {
setTimeout(() => {
// 处理滚动到底部的距离计算
const ele = (domRef as MutableRefObject<any>)?.current ?? domRef;
const totalHeight = ele?.scrollHeight || 0;
const clientHeight = ele?.clientHeight || 0;
const scrollTop = totalHeight - clientHeight;
if (ele) {
ele.scrollBy({
top: scrollTop,
behavior: 'smooth'
});
callback?.();
}
}, 200); // 滚动底部有误差,暂时用延时器解决
};
bytes(字节数) -> kb/mb/gb
ts
export const transformBytes = (bytes: number) => {
let size = '';
if (bytes < 0.1 * 1024) {
// 小于0.1KB,则转化成B
size = `${bytes.toFixed(2)} Bytes`;
} else if (bytes < 0.1 * 1024 * 1024) {
// 小于0.1MB,则转化成KB
size = `${(bytes / 1024).toFixed(2)} Kb`;
} else if (bytes < 0.1 * 1024 * 1024 * 1024) {
// 小于0.1GB,则转化成MB
size = `${(bytes / (1024 * 1024)).toFixed(2)} Mb`;
} else {
// 其他转化成GB
size = `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} Gb`;
}
return size;
};
时间戳 -> 天时分秒
ts
export const transformStampToText = (timeStamp: number) => {
if (timeStamp === undefined || timeStamp === null) {
return;
}
const days = Math.floor(timeStamp / 86400);
const hours = Math.floor((timeStamp % 86400) / 3600);
const minutes = Math.floor(((timeStamp % 86400) % 3600) / 60);
const seconds = ((timeStamp % 86400) % 3600) % 60;
return `${days ? `${days}天` : ''}${hours ? `${hours}时` : ''}${minutes ? `${minutes}分` : ''}${seconds}秒`;
};
解析前端路由 query参数
ts
export const getBrowserUrlParams: (url?: string) => Record<string, any> | undefined = (url?: string) => {
const targetUrl = url ?? location.href;
try {
const queryData = new URL(targetUrl).searchParams;
const formatQueryData: Record<string, any> = {};
queryData.forEach((value, key) => {
formatQueryData[key] = value;
});
return formatQueryData;
} catch (e) {
return {};
}
};
ts
export const getHashUrlParams: (url?: string) => Record<string, any> | undefined = (url?: string) => {
const targetUrl = url || location.href;
const searchStartIndex = targetUrl.lastIndexOf('?');
if (searchStartIndex === -1) {
return {};
} else {
return qs.parse(targetUrl.slice(searchStartIndex + 1));
}
};
网路请求axios封装
ts
import Axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { notification } from 'antd';
const CancelToken = Axios.CancelToken;
export const notifyError = async (code: string | number, message: string, onClose?: () => void) => {
let type, textPrefix;
switch (true) {
case String(code) === '401':
// type = 'info';
// textPrefix = '系统提示';
// break;
onClose();
return;
default:
type = 'error';
textPrefix = '请求失败';
}
notification[type]({
key: Date.now(),
message: `${textPrefix} ${code || 'Error'}`,
description: message,
placement: 'topLeft',
duration: 3,
onClose: onClose
});
};
type CustomConfig = {
isControl?: boolean; //为true则在服务端成功返回响应数据时,不进行code报错判断提示,由开发自行控制
};
export type HttpMethod = (
url: string,
params?: any,
config?: AxiosRequestConfig & CustomConfig
) => Promise<any>;
const instance: AxiosInstance = Axios.create({
timeout: 10000
});
/**
* 拦截器
*/
instance.interceptors.request.use(
(config) => {
if (config.method === 'post' && !config.headers['Content-Type']) {
config.headers['Content-Type'] = 'application/json';
}
config.headers.authorization = `Bearer ${localStorage.getItem(tokenKey)}`;
return config;
},
(err: AxiosError) => {
return Promise.reject(err);
}
);
let isNotifying = false; // 控制同时只能显示一个401登录失效的提醒
instance.interceptors.response.use(
(res: AxiosResponse) => {
return res;
},
(err: AxiosError) => {
console.log('err', err);
if (!navigator.onLine || err.code === 'ERR_NETWORK') {
return Promise.reject(new AxiosError('网络请求失败,请检查后重试', '-1'));
}
if (err.code === 'ECONNABORTED') {
return Promise.reject(new AxiosError('网络请求超时,请稍后重试', '-1'));
}
switch (err?.response?.status) {
case 401:
if (isNotifying) {
return;
}
isNotifying = true;
//登录状态失效
return Promise.reject(new AxiosError('登录状态已失效,请重新登录', '401'));
case 403:
//登录失败(例如非管理员)
return Promise.reject(
new AxiosError('您当前无权限访问此资源,请联系管理员', '403')
);
case 404:
return Promise.reject(new AxiosError('抱歉,您访问的接口地址貌似不存在', '404'));
case 500:
return Promise.reject(new AxiosError('抱歉,当前服务器异常,请稍后再试', '500'));
}
return Promise.reject(new AxiosError(err.message, err.response?.status.toString()));
}
);
/**
* 处理response数据
* @param res
* @param resolve
* @param reject
* @param config
*/
const handleRes = async (
res: AxiosResponse,
resolve: (data: any) => void,
reject: (reason: any) => void,
config?: Parameters<HttpMethod>[2]
) => {
if (res?.status === 200) {
let { code, msg, data } = res?.data || {};
switch (code) {
case 0:
return config?.isControl ? resolve(res?.data) : resolve(data);
default:
if (config?.isControl) {
return resolve(res.data);
}
if (code === 401) {
isNotifying = true;
notifyError(code, msg, () => {
isNotifying = false;
});
return;
}
notifyError(code, msg);
reject(msg);
}
} else {
const { statusText = '数据请求失败' } = res || {};
if (!config?.isControl && !isNotifying) {
notifyError(res?.status, statusText);
}
reject(statusText);
}
};
export let cancelReqFun = (msg: string) => {
//取消当前正在执行的请求
//
};
const createHttpMethod = (method: 'get' | 'post' | 'put' | 'delete', ...rest) => {
const [url, data, config] = rest;
return new Promise((resolve, reject) => {
instance({
method,
url: 'http://www.apiTest.com' + url,
params: method === 'get' ? data : {},
data: method === 'get' ? {} : data,
cancelToken: new CancelToken(function (cancel) {
cancelReqFun = cancel;
}),
...config
})
.then((res: AxiosResponse) => {
handleRes(res, resolve, reject, config);
})
.catch((err: { code: string | number; message: string }) => {
const { code, message } = err;
notifyError(code, message, () => {
isNotifying = false;
});
reject(message);
});
});
};
const httpMethods: Record<Parameters<typeof createHttpMethod>[0], HttpMethod> = (
['get', 'post', 'put', 'delete'] as const
).reduce(
(pre, cur, array) => {
pre[cur] = (...params: any[]) => createHttpMethod(cur, ...params);
return pre;
},
{
get: undefined,
post: undefined,
delete: undefined,
put: undefined
}
);
export default httpMethods;