一、数字
1、数字格式化
数字千分位逗号分隔:new Intl.NumberFormat().format(num)
const formatNumber = (num)=> new Intl.NumberFormat().format(num);
console.log([formatNumber("12345"),formatNumber(12345)])
//["12,345", "12,345"]
货币化:new Intl.NumberFormat(‘zh-CN’,{ style: ‘currency’, currency: ‘CNY’,maximumFractionDigits: maxDig }).format(num)
常见方法
// 格式化货币
const formatMoney = (value, type = 2) => {
let s = value < 0 ? Math.abs(value) : value;
if (/[^0-9.]/.test(s)) return '¥ 0.00';
if (s === null || s === 'null' || s === '') return '¥ 0.00';
s = s.toString().replace(/^(\d*)$/, '$1.');
s = (s + '00').replace(/(\d*\.\d\d)\d*/, '$1');
s = s.replace('.', ',');
var re = /(\d)(\d{3},)/;
while (re.test(s)) s = s.replace(re, '$1,$2');
s = s.replace(/,(\d\d)$/, '.$1');
if (type === 0) {
var a = s.split('.');
if (a[1] === '00') {
s = a[0];
}
}
return value < 0 ? '¥ -' + s : '¥ ' + s;
};
console.log(formatMoney(12345.6789))
//¥ 12,345.67
新方法 ```typescript // 格式化货币 const formatCurrency = (num, maxDig = 6) => { const currency = ‘CNY’; const locale = ‘zh-CN’;
if ( (typeof num === ‘string’ && [‘null’, ‘undefined’, ‘’].includes(num.trim())) || (typeof num !== ‘string’ && isNaN(num)) ) { return ‘¥ 0’; } else { try {
const formatVal = new Intl.NumberFormat(locale, {
style: 'currency',
currency,
...(maxDig ? { maximumFractionDigits: maxDig } : {}),
}).format(parseFloat(String(num)));
return formatVal || '¥ 0';
} catch (err) {
return '¥ 0';
} } };
- 大数据量统计
```javascript
const nums = [1234, 123456.789, 123456789123, 123456789123456,123456789123456789]
nums.map(num => {
return new Intl.NumberFormat('zh-CN', { notation: "compact" }).format(num)
})
//["1234", "12万", "1235亿", "123万亿", "123,457万亿"]
- 百分比展示 ```javascript [0.01, 1.2, 0.0123].map(num => { return new Intl.NumberFormat(undefined, { style: ‘percent’, maximumFractionDigits: 2 }).format(num) }) // [“1%”, “120%”, “1.23%”]
<a name="lWZxg"></a>
### 2、数字校验
```javascript
// 是否是数字
// 1、js方法
const isNumber = value => !isNaN(parseFloat(value)) && isFinite(value);
// 2、正则
const isNumberRegex = value=> /^\d{1,}$/.test(value)
// 是否是正数
// 1、js方法
const isPositive = value => isNumber(value) && Math.sign(value) === 1;
// 2、正则判断正浮点数
const isPositiveFloat = value => Math.sign(Number(value ?? 0) || 0) === 1 && /^(?:[1-9]\d*|0)?(?:\.\d+)?$/.test(value);
// 是否为小数
const isDot = value => /^\d+\.\d+$/.test(value)
// 正浮点数保留两位数字
const formatDot = (value)=> value.toString().match(/^\d+(?:\.\d{0,2})?/).[0]
// 两个小数是否相等
const epsEqu = (x,y)=> {
return Math.abs(x - y) < Number.EPSILON;
}
Number.EPSILON 属性表示 1 与Number可表示的大于 1 的最小的浮点数之间的差值
二、文件与URL
1、通用文件下载
// axios通用文件下载
const downLoadFile = (url, fileName, callback, fileType) => {
const options = {
method: 'GET',
url: `${url}`,
responseType: 'blob',
};
axios(options).then((res) => {
if (callback) {
let files = new window.File([res.data], fileName, {
type:fileType
});
callback(files);
} else {
// 将blob对象转换为域名结合式的url
let blobUrl = window.URL.createObjectURL(res.data);
let link = document.createElement('a');
link.style.display = 'none';
link.href = blobUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
link.parentNode ? link.parentNode.removeChild(link) : document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
}
});
}
// fetch通用文件下载
const fetchDownLoadFile = (url,fileName,token,submitData) => {
const options = {
method: "post",
url: `${url}`,
headers: {
"Content-Type": "application/json;charset=utf-8",
Authorization:token,
},
body: JSON.stringify({...submitData}),
};
fetch(url, options)
.then((response) => response.blob())
.then((res) => {
// 将blob对象转换为域名结合式的url
let blobUrl = window.URL.createObjectURL(res);
let link = document.createElement("a");
link.style.display = "none";
link.href = blobUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
link.parentNode ? link.parentNode.removeChild(link) : document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
});
};
2、文件流转dataUrl
const handleFlieReader = (file: any) => {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = function (e: any) {
let data: any = e.target.result;
resolve(data);
};
reader.readAsDataURL(file);
});
};
//图片文件url 转 base64
function urlToBase64(url, type) {
return new Promise ((resolve,reject) => {
let image = new Image();
image.onload = function() {
let canvas = document.createElement('canvas');
canvas.width = image?.naturalWidth;
canvas.height = image?.naturalHeight;
// 将图片插入画布并开始绘制
canvas.getContext('2d').drawImage(image, 0, 0);
let result = canvas.toDataURL(type || 'image/png')
resolve(result);
};
// CORS 策略,会存在跨域问题https://stackoverflow.com/questions/20424279/canvas-todataurl-securityerror
image.setAttribute("crossOrigin",'Anonymous');
image.src = url;
// 图片加载失败的错误处理
image.onerror = () => {
reject(new Error('urlToBase64 error'));
};
}
3、dataUrl转文件流
//way 1:
function dataURLtoBlob(dataurl: string) {
let arr: any = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
function blobToFile(theBlob: any, fileName: any) {
theBlob.lastModifiedDate = new Date();
theBlob.name = fileName;
const file = new window.File([theBlob], fileName, {
type: theBlob?.type || 'image/png',
});
return file;
}
//way 2:
async function dataUrlToFile(dataUrl: string, fileName: string): Promise<File> {
const res: Response = await fetch(dataUrl);
const blob: Blob = await res.blob();
return new File([blob], fileName, { type: 'image/png' });
}
4、Canvas转Blob兼容性处理
const getCanvasToBlob = (canvas: any, type: any) => {
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function (callback: any, type: any, quality: any) {
let binStr = atob(this.toDataURL(type, quality).split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (let i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(new Blob([arr], { type: type || 'image/png' }));
},
});
}
return new Promise((resolve, reject) => {
return canvas.toBlob((blob: any) => resolve(blob), type);
});
};
5、是否为安全URL
const validateURL = (url) => {
let urlReg = /(http|https):\/\/([\w.]+\/?)\S*/;
if (!url || (url && !urlReg.test(url))) {
return false;
} else {
try {
const parsed = new URL(url);
return ['https:', 'http:'].includes(parsed?.protocol);
} catch (error) {
return false;
}
}
};
6、获取URL中的查询参数
let urlParams = new URLSearchParams('?post=1234&action=edit');
console.log(urlParams.get('action')); // "edit"
三、表单校验
1、生成随机密码
const getRandomPassword = () => {
let lowerCaseStrs = 'abcdefghijklmnopqrstuvwxyz',
upperCaseStrs = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
numberChar = '0123456789';
const passArr = Array.from({ length: 3 }).fill(0);
const password = passArr.reduce((acc) => {
let lower = lowerCaseStrs.charAt(Math.floor(Math.random() * lowerCaseStrs.length));
let upper = upperCaseStrs.charAt(Math.floor(Math.random() * upperCaseStrs.length));
let num = numberChar.charAt(Math.floor(Math.random() * numberChar.length));
return [...acc, `${lower}${upper}${num}`].join('');
}, []);
return password;
};
2、校验手机号、邮箱、密码
// 校验手机号
const validatePhone = (value: any) => {
const phoneReg = /^1[3456789]\d{9}$/;
if (phoneReg.test(value)){
return true
}else{
if(value?.trim()){
console.log("手机号格式不正确!")
}
return false
}
};
// 校验邮箱
const validateMail = (value: any) => {
const mailReg = new RegExp(
'^[a-z0-9A-Z]+[- | a-z0-9A-Z . _]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-z]{2,}$'
);
if (mailReg.test(value)){
return true
}else{
if(value?.trim()){
console.log("邮箱格式不正确!")
}
return false
}
};
// 校验密码
const validatePassword = (val:any)=>{
//至少包含一位大小写字母,只能输入ASCII 码 !-~ 范围,6-20位
const pwdReg = /^(?=.*?[A-Z])(?=.*?[a-z])[!-~]{6,20}$/;
if(pwdReg.test(val)){
return true
}else{
if(value?.trim()){
console.log("密码格式不正确!")
}
return false
}
}
四、日期
1、格式化日期
function formatDate(date) {
let d;
if (date) {
d = new Date(date);
} else {
d = new Date();
}
let month = '' + (d.getMonth() + 1);
let day = `${d.getDate()}`;
let year = d.getFullYear();
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
return [year, month, day].join('/');
}
//formatDate()
//'2022/01/06'
五、字符串与JSON
1、单位数补位
function numExpand(num) {
const str = `${num}`;
return str.length === 1 ? str.padStart(2, '0') : str;
}
2、判断是否为JSON
const isJSON = (str: any) => {
if (typeof str === 'string') {
try {
let obj = JSON.parse(str);
if (typeof obj === 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
console.log('It is not a string!');
return false;
};
六、多媒体
1、浏览器是否支持音视频播放
//音频编解码格式浏览器是否支持
const supportsAudioPlayback = (contentType) => {
let audioElement = document.createElement('audio');
if (audioElement.canPlayType) {
return !!audioElement.canPlayType(contentType).replace(/no/, '');
}
};
//视频编解码格式浏览器是否支持
const supportsVideoPlayback = function (contentType) {
let videoElement = document.createElement('video');
if (videoElement.canPlayType) {
let playable = videoElement.canPlayType(contentType);
if (playable.toLowerCase() === 'maybe' || playable.toLowerCase() === 'probably') {
return true;
}
}
return false;
};
七、交互
1、简易防抖、节流
// 防抖
function debounce(fn, wait = 50, immediate) {// immediate 表示第一次是否立即执行
let timer = null;
return function(...args) {
let context = this;
if (timer) clearTimeout(timer);
if (!timer && immediate) {
fn.apply(context, args);
}
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
// 节流
function throttle(fn, wait) {
let previous = 0;
let timer = null;
return function(...args) {
let context = this;
let now = +new Date();
if (now - previous < wait) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
previous = now;
fn.apply(context, args);
}, wait);
} else {
previous = now;
fn.apply(context, args);
}
};
}
八、水印
1、Canvas生成网页水印
// canvas 实现 watermark
function getCanvasWaterMark({
width = '200px',
height = '150px',
textAlign = 'center',
textBaseline = 'middle',
font = '20px Microsoft Yahei',
fillStyle = '#EFF0F1',
content = '请勿外传',
rotate = '30',
} = {}) {
const canvas = document.createElement('canvas');
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.textAlign = textAlign as CanvasTextAlign;
ctx.textBaseline = textBaseline as CanvasTextBaseline;
ctx.font = font;
ctx.fillStyle = fillStyle;
ctx.rotate((Math.PI / 180) * Number(rotate ?? 0));
ctx.fillText(content, parseFloat(width) / 2, parseFloat(height) / 2);
}
const base64Url = canvas.toDataURL();
return base64Url;
}
// 页面展示 watermark 图片
function showWaterMark({ base64Url = '', container = document.body, zIndex = 1000 } = {}) {
const __wm = document.querySelector('.__wm');
const watermarkDiv = __wm || document.createElement('div');
const styleStr = `
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
z-index:${zIndex};
pointer-events:none;
background-repeat:repeat;
background-image:url('${base64Url}')`;
watermarkDiv.setAttribute('style', styleStr);
watermarkDiv.classList.add('__wm');
if (!__wm) {
container.style.position = 'relative';
container.insertBefore(watermarkDiv, container.firstChild);
}
// 监听 watermark 容器样式更改
const observerWaterMark = () => {
const MutationObserver = window.MutationObserver;
if (MutationObserver) {
let mo: any = new MutationObserver(function () {
const __wm = document.querySelector('.__wm');
// 只在__wm元素变动才重新调用 __canvasWM
if ((__wm && __wm.getAttribute('style') !== styleStr) || !__wm) {
// 避免一直触发
mo.disconnect();
mo = null;
showWaterMark({ base64Url: getCanvasWaterMark({}) });
}
});
mo.observe(container, {
attributes: true,
subtree: true,
childList: true,
});
}
};
observerWaterMark();
}
2、Canvas 生成图片水印
// 绘制图片水印
const drawImgWaterMark = (url = '', text = '请勿外传', callback?: any) => {
if (url) {
const img = new Image();
img.src = url;
img.crossOrigin = 'anonymous';
img.onload = function () {
let canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
let canvasWidth = canvas.width;
let canvasHeight = canvas.height;
const ctx = canvas.getContext('2d');
if (ctx) {
// 文字样式
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = `${canvasWidth / 48}px Microsoft Yahei`;
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
let widthGap = canvasWidth / 12;
let heightGap = canvasHeight / 16;
let textWidth = ctx.measureText(text)?.width;
let textHeight = textWidth * Math.sin((20 * Math.PI) / 180);
let fwidth = textWidth + widthGap,
fheight = textHeight + heightGap;
for (let hh = -canvasHeight; hh <= canvasHeight + fheight; hh += fheight) {
for (let ww = -canvasWidth; ww <= canvasWidth + fwidth; ww += fwidth) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.translate(ww, hh);
// 文字旋转
ctx.rotate((-20 * Math.PI) / 180);
ctx.fillText(text, -fwidth / 2, fheight / 2);
}
}
}
const base64Url = canvas.toDataURL();
callback && callback(base64Url);
};
}
};
九、大文件上传
1、获取文件MD5
const getFileMd5 = (file: any) => {
return new Promise((resolve, reject) => {
const chunkSize = 2 * 1024 * 1024,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
const tempFile: any[] = [];
if (chunks > 2) {
// 大文件分别取前中后三段样本来生成md5
const mid = chunks >> 1;
tempFile.push(file.slice(0, currentChunk * chunkSize));
tempFile.push(file.slice(mid * chunkSize, (mid + 1) * chunkSize));
tempFile.push(file.slice((chunks - 1) * chunkSize, chunks * chunkSize));
} else {
tempFile.push(file);
}
fileReader.readAsArrayBuffer(new Blob(tempFile));
fileReader.onload = async (e) => {
spark.append(e.target?.result as ArrayBuffer);
const md5 = spark.end();
resolve({
md5,
name: file.name,
resourceType: resourceType,
size: file.size,
});
};
fileReader.onerror = function () {
message.error('读取文件出错!');
reject('读取文件出错!');
handleRemove();
};
});
};
2、上传前请求 OSS对象 的权限id、签名、host地址;资源对象的 id等信息
3、上传成功后,根据资源对象 id 获取 文件地址
十、图片预览
const onPreview = async (src: string) => {
const image = new Image();
image.src = src;
const imgWindow = window.open(src);
imgWindow?.document.write(image.outerHTML);
};