前端必备常用基础算法,会不定时持续更新喔~
1、判断车牌号是否正确(包含新能源车)
/**
* @param {Object} str
* 判断车牌号是否正确
*/
export function isCarLicense(str) {
if (!str) {
return false
}
return /^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(str);
}
2、日期格式化
/**
* 时间格式化
* @param {*} date
* @param {*} fmt 'yyyy-MM-dd HH:mm:ss'
*/
export function format(date, fmt) {
const o = {
'M+': date.getMonth() + 1, // 月份
'd+': date.getDate(), // 日
'H+': date.getHours(), // 小时
'm+': date.getMinutes(), // 分
's+': date.getSeconds(), // 秒
'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
S: date.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
}
}
return fmt
}
3、比较两个字符串是否含有共同的子字符串
/**
* 比较两个字符串是否含有共同的子字符串
* @param str1 字符串一
* @param str2 字符串二
* @param includeSelf 子字符串是否包括本身
* @return 比较结果
*/
export function hasSameSubStr(str1, str2, includeSelf = false)
{
if (!str1.length || !str2.length) return false
let shortStr = str1.length > str2.length ? str2: str1;
let longStr = str1.length > str2.length? str1: str2;
let temp = "";
for(let i = 0; i <= shortStr.length - 2; i++)
{
for(let j = i + 2; j <= shortStr.length; j++)
{
temp = shortStr.substring(i, j);
let flag1 = includeSelf && longStr.indexOf(temp) >= 0;
let flag2 = !includeSelf && temp !== shortStr && longStr.indexOf(temp) >= 0;
if(flag1 || flag2)
{
return true;
}
}
}
return false;
}
4、对象数组根据具体参数去重
/**
* 方法一
* 对象数组根据具体参数去重
* @param {*} arr 数组
* @param {*} attr 要去重的属性
*/
export function repeatArr (arr, attr) {
const map = new Map()
for (let i of arr) {
if (!map.has(i[attr ])) {
map.set(i[attr ], i)
}
}
return [...map.values()]
}
/**
* 方法二
* 对象数组根据具体参数去重
* @param {*} arr 数组
* @param {*} attr 要去重的属性
*/
export function repeatArr (arr, attr) {
const map = new Map()
return arr.filter(item => {
const attrVal = item[attr]
return !map.has(attrVal) && map.set(attrVal, 1)
})
}
5、防抖函数
/**
* 防抖函数(常用于input框搜索情况)
* @param {*} func
* @param {*} delay
* @param {*} immediate
* @returns
*/
export function debounce(func, delay, immediate = true) {
let timer = null
return function(args) {
let _this = this
if (timer) {
clearTimeout(timer)
}
if (immediate) {
let now = !timer
timer = setTimeout(() => {
timer = null
}, delay)
now && func.call(_this, args)
} else {
timer = setTimeout(() => {
timer = null
func.call(_this, args)
}, delay)
}
}
}
6、节流函数
/**
* 节流函数(常用于onresize, onmouseover情况)
* @param {*} func
* @param {*} delay
* @param {*} immediate
* @returns
*/
export function throttle(func, delay, immediate = true) {
let timer = null
return function (args) {
let _this = this
if (!timer) {
if (immediate) {
func.call(_this, args)
timer = setTimeout(() => {
timer = null
}, delay)
} else {
timer = setTimeout(() => {
func.call(_this, args)
timer = null
}, delay)
}
}
}
}
7、url中将参数部分转为对象
/**
* 方法一
* url中将参数部分转为对象,处理的数据必须要有 ?分割
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
let search = ''
if (url.indexOf('?') !== -1) {
search = url.split('?')[1]
}
if (!search) return {}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, ' ') +
'"}'
)
}
/**
* 方法二
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
8、base64加解密
/**
* @param {any} 要加密或解密的数据
* @returns any.base64
*/
class Base64 {
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
encode(e) {
let t = "";
let n, r, i, s, o, u, a;
let f = 0;
e = this._utf8_encode(e);
while (f < e.length) {
n = e.charCodeAt(f++);
r = e.charCodeAt(f++);
i = e.charCodeAt(f++);
s = n >> 2;
o = (n & 3) << 4 | r >> 4;
u = (r & 15) << 2 | i >> 6;
a = i & 63;
if (isNaN(r)) {
u = a = 64
} else if (isNaN(i)) {
a = 64
}
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
}
return t
}
decode(e) {
let t = "";
let n, r, i;
let s, o, u, a;
let f = 0;
e=e.replace(/[^A-Za-z0-9+/=]/g,"");
while (f < e.length) {
s = this._keyStr.indexOf(e.charAt(f++));
o = this._keyStr.indexOf(e.charAt(f++));
u = this._keyStr.indexOf(e.charAt(f++));
a = this._keyStr.indexOf(e.charAt(f++));
n = s << 2 | o >> 4;
r = (o & 15) << 4 | u >> 2;
i = (u & 3) << 6 | a;
t = t + String.fromCharCode(n);
if (u != 64) {
t = t + String.fromCharCode(r)
}
if (a != 64) {
t = t + String.fromCharCode(i)
}
}
t = this._utf8_decode(t);
return t
}
_utf8_encode(e) {
e = e.replace(/rn/g, "n");
let t = "";
for (let n = 0; n < e.length; n++) {
let r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r)
} else if (r > 127 && r < 2048) {
t += String.fromCharCode(r >> 6 | 192);
t += String.fromCharCode(r & 63 | 128)
} else {
t += String.fromCharCode(r >> 12 | 224);
t += String.fromCharCode(r >> 6 & 63 | 128);
t += String.fromCharCode(r & 63 | 128)
}
}
return t
}
_utf8_decode(e) {
let t = "";
let n = 0;
let r = 0;
let c1 = 0;
let c2 = 0;
while (n < e.length) {
r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
n++
} else if (r > 191 && r < 224) {
c2 = e.charCodeAt(n + 1);
t += String.fromCharCode((r & 31) << 6 | c2 & 63);
n += 2
} else {
c2 = e.charCodeAt(n + 1);
c3 = e.charCodeAt(n + 2);
t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
n += 3
}
}
return t
}
}
export default Base64
// 调用方式
new Base64().encode('xxx')
9、定时器类
/**
* 定时器类
*/
function Timer() {
this._id = null // 该类唯一id
this.timeId = null // 该类唯一定时id
this.func = null // 执行用户自定义回调函数
}
/**
* 递归定时器
* @param {Function} func 用户自定义回调函数
* @param {Number} interval 延时时间
* @param {Boolean} flag true => setTimeout, false => setInterval, 默认为 true
* @param {Boolean} immediate 是否立即执行,默认为 false
* @returns
*/
Timer.prototype.repeat = function(func, interval, flag = true, immediate = false) {
// 实例无回调函数 则初始化回调函数
if (this.func === null) {
this.func = func
}
// 立即执行函数
if (immediate) {
func()
}
this.timeId = setTimeout(() => {
func()
if (!flag) {
// 确保repeat中只立即执行一次用户自定义回调函数
this.repeat(func, interval, flag)
}
}, interval)
}
// 清除定时器
Timer.prototype.clear = function() {
if (this.timeId) {
console.log('clear timer', this.timeId)
clearTimeout(this.timeId)
this.timeId = null
} else {
console.log('目前定时器为null ')
}
}
export default Timer
// 调用方式
this.timer = new Timer()
this.timer.repeat(
() => {
// 要执行的逻辑
},
1000,
false,
false
)
10、身份证号验证
/**
* 身份证号验证
*/
export const checkIdCard = (value, callback) => {
if (!value) {
return callback(new Error('身份证号不能为空'))
}
else {
let reg = /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/
if(reg.test(value)){
callback();
}
else{
return callback(new Error('身份证号格式不正确'))
}
}
}
11、手机号验证
/**
* 手机号验证
*/
export const checkPhoneNumber = (value, callback) => {
if (!value) {
return callback(new Error('手机号不能为空'))
}
else {
let reg = /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/
if(reg.test(value)){
callback();
}
else{
return callback(new Error('手机号格式不正确'))
}
}
}
12、阿拉伯数字转为汉字
/**
* 阿拉伯数字转为汉字
*/
export function formatNumber(number) {
const chnNumChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
const chnUnitSection = ['', '万', '亿', '万亿', '亿亿']
const chnUnitChar = ['', '十', '百', '千']
// 节内转换算法
function SectionToChinese(section) {
let strIns = ''
let chnStr = ''
let unitPos = 0
let zero = true
while (section > 0) {
const v = section % 10
if (v === 0) {
if (!zero) {
zero = true
chnStr = chnNumChar[v] + chnStr
}
} else {
zero = false
strIns = chnNumChar[v]
strIns += chnUnitChar[unitPos]
chnStr = strIns + chnStr
}
unitPos++
section = Math.floor(section / 10)
}
return chnStr
}
// 转换算法主函数
function NumberToChinese(num) {
let unitPos = 0
let strIns = ''
let chnStr = ''
let needZero = false
if (num === 0) {
return chnNumChar[0]
}
while (num > 0) {
const section = num % 10000
if (needZero) {
chnStr = chnNumChar[0] + chnStr
}
strIns = SectionToChinese(section)
strIns += section !== 0 ? chnUnitSection[unitPos] : chnUnitSection[0]
chnStr = strIns + chnStr
needZero = section < 1000 && section > 0
num = Math.floor(num / 10000)
unitPos++
}
return chnStr
}
return NumberToChinese(number)
}
13、深拷贝
/**
* 深拷贝
*/
export const deepClone = obj => {
const _toString = Object.prototype.toString
// null, undefined, non-object, function
if (!obj || typeof obj !== 'object') {
return obj
}
// DOM Node
if (obj.nodeType && 'cloneNode' in obj) {
return obj.cloneNode(true)
}
// Date
if (_toString.call(obj) === '[object Date]') {
return new Date(obj.getTime())
}
// RegExp
if (_toString.call(obj) === '[object RegExp]') {
const flags = []
if (obj.global) {
flags.push('g')
}
if (obj.multiline) {
flags.push('m')
}
if (obj.ignoreCase) {
flags.push('i')
}
return new RegExp(obj.source, flags.join(''))
}
const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}
for (const key in obj) {
result[key] = deepClone(obj[key])
}
return result
}
14、对象数组差异对比(自用dom-diff)
/**
* before => 差异前数组 type: Array<{id: number, name: string, pid: number, nodeType: number, children: Array[]}>
* after => 差异后数组 type: Array<{id: number, name: string, pid: number, nodeType: number, children: Array[]}>
* type => 用于标识两对象不同的地方, 默认为id
*/
export function compareArray(before, after, type = 'id') {
const res = {
add: [], // 新增
remove: [], // 删除
update: [] // 更新
}
const compareMap = new Map()
for (let i = 0; i < before.length; i++) {
compareMap.set(before[i][type], before[i])
if (before[i].children && before[i].children.length > 0) {
compareMap.get(before[i][type]).children = before[i].children
}
}
// 差异比较 compareMap中保存的是before有的而after不存在的
for (let i = 0; i < after.length; i++) {
if (!compareMap.has(after[i][type])) {
res.add.push(after[i])
} else {
const beforeChild = compareMap.get(after[i][type]).children
if (beforeChild.length) {
const afterChild = after[i].children
const resVal = {
id: after[i].id,
name: after[i].name,
nodeType: after[i].nodeType,
children: []
}
for (let j = 0; j < beforeChild.length; j++) {
if (!afterChild[j].checked) {
resVal.children.push({
checked: afterChild[j].checked,
id: afterChild[j].id,
name: afterChild[j].name,
pid: afterChild[j].pid
})
}
}
res.update.push(resVal)
}
compareMap.delete(after[i][type])
}
}
compareMap.forEach((value, key, map) => {
res.remove.push(value)
})
return res
}
15、全局事件bus
import Vue from 'vue'
/**
* 全局事件bus ,每一次事件监听后需要在beforeDestroy中取消监听 this.$notifiy.off(type),否则会重复注册,造成不可预知的错误
*/
class Notify {
static eventBus = new Vue()
static events = []
static send(type, data) {
Notify.eventBus.$emit(type, data)
}
static on(type, callback) {
if (Notify.events.includes(type)) {
return
}
Notify.events.push(type)
Notify.eventBus.$on(type, res => {
callback(res ? res : null)
})
}
static off(type) {
if (Notify.events.includes(type)) {
Notify.events = Notify.events.filter(i => i !== type)
Notify.eventBus.$off(type)
} else {
console.warn(`EventBus不存在type${type}`)
}
}
}
export default Notify
// 调用方式
this.$notify.on('xxx', () => {})
this.$notify.off('xxx')
16、全屏控制
/**
* 实现打开、退出全屏
*/
export const FullScreen (el) {
/* eslint-disable */
const isFullscreen = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen
if (!isFullscreen) {
// 进入全屏,多重短路表达式
;(el.requestFullscreen && el.requestFullscreen()) ||
(el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
(el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
(el.msRequestFullscreen && el.msRequestFullscreen())
} else {
// 退出全屏,三目运算符
document.exitFullscreen
? document.exitFullscreen()
: document.mozCancelFullScreen
? document.mozCancelFullScreen()
: document.webkitExitFullscreen
? document.webkitExitFullscreen()
: ''
}
}
17、复杂密码校验
/**
* 验证密码 必须包含数字、大小写字母、特殊符号,密码长度在 10 到 20 个字符
*/
export const validatePwd (value, callback) {
if (!value) {
return callback(new Error('密码不能为空'))
} else if (value.length < 10 || value.length > 20) {
return callback(new Error('密码长度应在 10 到 20 个字符'))
} else {
const reg = /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[^a-zA-Z0-9]).{10,20}/
if (reg.test(value)) {
callback()
} else {
return callback(new Error('密码必须包含大小写字母、数字、特殊字符4种'))
}
}
}
18、async/await异常抛出
/**
* async/await 错误异常抛出
*/
export function to(promise: Promise<any>) {
return promise.then((data: any) => [null, data]).catch((err: any) => [err])
}
// 调用方式
let [result_err, result] = await to(getApi())
19、数组转化为树形结构
/**
* 数组转化为树形结构(常用于后台菜单数组转换成树形结构)
*/
export function arrToTree(data) {
let result = []
let map = new Map()
data.forEach(item => {
// 存入字典集里
map.set(item.id, item)
})
data.forEach(item => {
// 判断字典集里是否有该键
let parent = map.get(item.pid)
if (parent) {
// ?. 可选链操作符,常用于判断对象里是否有该属性
// ?? 合并操作符 或等于||,但是不会对左边值进行处理
(parent?.children??(parent.children = [])).push(item)
} else {
result.push(item)
}
})
return result
}
20、数字取整数部分
/**
* 数字取整数部分
* @param {*} v
*/
export function numberTrunc (v) {
if (Math.trunc) {
return Math.trunc(v)
} else {
v = +v
if (!isFinite(v)) return v;
return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0);
}
}
21、网址url验证
/**
* 网址url验证
*/
export function checkUrl (value) {
if (!value) {
return false
}
else {
let reg = /^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?$/
if(reg.test(value)){
return true
}
else{
return false
}
}
}
22、驼峰转下划线写法
/**
* 驼峰转下划线写法
* @param str
*/
public static humpToUnderline(str: String) {
return str.replace(/([A-Z])/g, '_$1')
}
23、下划线转驼峰写法
/**
* 下划线转驼峰写法
* @param str
* @returns
*/
public static undelineToHump(str: String) {
for (let i of str.match(/_(.)/g)) {
str = str.replace(i, i.replace('_', '').toUpperCase())
}
return str
}
24、 宽高为x,y的矩形框分别有m个正方形,n个长方形
注: 本长方形不包括正方形
const rectangleAlgo = (x, y) => {
const square = (x, y) => {
if (x === 1 || y === 1) {
return x * y
} else {
return x * y + square(x - 1, y - 1)
}
}
const rectangle = a => {
if (a === 1) {
return 1
} else {
return a + rectangle(a - 1)
}
}
const squareNum = square(x, y) // 正方形个数
const rectangleNum = rectangle(x) * rectangle(y) - squareNum // 长方形个数
return [squareNum, rectangleNum]
}
运行结果:
25、判断当前变量的类型
const getDataType = (obj) => {
const str = Object.prototype.toString.call(obj); // 检测基本类型值,引用类型值的类型
const map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
};
if(obj instanceof Element){
return 'element';
}
return map[str];
}
举例:
console.log(getDataType('hello lindadayo'))
输出: 'string'
26、Vue3 通过Ref进行输入框防抖
import { customRef } from 'vue'
/**
* Ref防抖
* @param value v-model, 可初始为空字符
* @param delay 延迟时间
* @returns
*/
export function debounceRef(value, delay = 500) {
let timer;
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(val) {
clearTimeout(timer)
timer = setTimeout(() => {
value = val
trigger()
}, delay)
}
}
})
}
举例:
import { debounceRef } from 'xxx'
setup() {
const name= debounceRef('', 500) // 输入值时延时500ms
return () => (
<input v-model={name.value} />
)
}
27、指定长度的随机标识符生成
/**
* 随机标识符生成
* @param {*} length 标识符长度,长度越长越唯一,但是过长循环时间会增加,默认为16位,基本合适
*/
export function randomKey(length: number = 16): string {
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_=-'
let result = ''
for (let i = length; i > 0; --i) {
result += chars[Math.floor(Math.random() * chars.length)]
}
return result
}
28、文件大小单位转换
/**
* 文件大小单位转换
* @param size
* @returns
*/
export function getfilesize(size) {
if (!size) {
return '';
}
const num = 1024.00;
if (size < num) {
return size + 'B';
} else if (size < Math.pow(num, 2)) {
return (size / num).toFixed(2) + 'K'; // kb
} else if (size < Math.pow(num, 3)) {
return (size / Math.pow(num, 2)).toFixed(2) + 'M'; // M
} else if (size < Math.pow(num, 4)) {
return (size / Math.pow(num, 3)).toFixed(2) + 'G'; // G
} else {
return (size / Math.pow(num, 4)).toFixed(2) + 'T'; // T
}
}
29、秒数转换成时分秒
/**
* 秒数转换成时分秒(常用于定时器从0开始计时展示)
* @param {*} s 秒数
*/
const format = (s) => {
const minute = parseInt(s / 60 % 60)
const hour = parseInt(s / 3600) < 10 ? '0' + parseInt(s / 3600) : parseInt(s / 3600)
const second = Math.ceil(s % 60) > 59 ? 59 : Math.ceil(s % 60)
return `${hour > 0 ? `${hour}:` : ''}${minute < 10 ? '0' + minute : minute}:${second < 10 ? '0' + second : second}`
}
举例:
format(100) => '01:40'
30、自定义实现小数tofixed功能
/**
* 自定义实现toFixed功能
* @param value 值
* @param n 保留几位小数
*/
const autoFixed = (value, n) => {
return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
}
31、根据前后数字生成数组
/**
* 根据前后数字快速生成数组
* @param start 开始数字
* @param end 结束数字
*/
const generateArray = (start: number, end: number):null | number[] => {
const arr = []
if (start > end) return null
for (let i = start; i <= end; i++) {
arr.push(i)
}
return arr
}
举例:
32、遍历数组,寻找重复的数字
/**
* 遍历数组,寻找重复的数字
* @param arr 将要遍历的数组
*/
const findRepeatNumber = (arr: number[]):number[] => {
let res: number[] = []
const hash = new Map()
const len = arr.length
for (let i = 0; i < len; i++) {
if (!hash.get(arr[i])) {
hash.set(arr[i], 1)
} else {
const num = hash.get(arr[i]) + 1
hash.set(arr[i], num)
}
}
for (let [k, v] of hash.entries()) {
if (v > 1) {
res.push(k)
}
}
return res
}
举例:
33、批量请求接口并获取当前进度
场景:比如需要批量调用接口,当单个接口完成时,按比例加上进度
const allWithProgress = (requests: any, callback: Function) => {
let index = 0;
requests.forEach((item: any) => {
item.then(()=>{
index ++;
const progress = Math.floor(index * 100 / requests.length);
callback(progress);
})
});
return Promise.allSettled(requests);
}
举例:
const apis = [请求, 请求...]
allWithProgress(apis, function(progress) {
// progress 为执行进度
}).then(res => {
// res 为Promise.allSettled返回的数组
})
34、前端生成压缩包
场景:你知道一批图片的base64和名字,想要前端生成压缩包,就按照以下方式生成
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
const resList = [
{base64: '', keyword: ''}
]
new Promise((resolve, reject) => {
const zip = new JSZip()
const img = zip.folder('images') as any
resList.map((item: any) => {
img.file(item.keyword, item.base64, {
base64: true,
})
})
resolve(zip)
}).then(zip => {
zip.generateAsync({
type: 'blob'
}).then(content => {
saveAs(content, '图片.zip')
})
})
35、原生js实现文件上传功能
场景:不想用input['file'],想要自己点击任意按钮时候实现文件上传
// 此处举例为上传pdf,可根据自己需要进行部分修改
const doInput = () => {
const inputObj = document.createElement('input');
inputObj.addEventListener('change', readFile, false);
inputObj.type = 'file';
inputObj.accept = '*.pdf';
inputObj.click();
}
const readFile = (e: any) => {
const file = e.target.files[0]; // 获取input输入的图片
if(!/.pdf/.test(file.type)){
alert("请确保文件为pdf类型");
return false;
} // 判断是否pdf
}
36、Promise类(Promise/A+ )
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
// 将 onFufilled 的返回值进行判断取值处理,把最后获得的普通值放入最外面那层的 Promise 的 resolve 函数中
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是错误的实现,用一个循环引用的类型错误,结束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
// 只能调用一次,为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)Promise/A+ 2.3.3.3.3
let called;
// 如果 x 不是null,是对象或者方法
if ((typeof x === "object" && x != null) || typeof x === "function") {
try {
// 这个首先存储对 x.then 的引用,然后测试该引用
let then = x.then;
if (typeof then === "function") {
// 那我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象
// 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty Promise/A+ 2.3.3.3
// 第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调
then.call(
x,
(y) => {
// 根据 promise 的状态决定是成功还是失败
if (called) return;
called = true;
// 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
// 只要失败就失败 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
}
);
} else {
// 如果 x.then 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e);
}
} else {
// 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4
resolve(x);
}
};
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
// 如果 value 是一个promise,那我们的库中应该也要实现一个递归解析
if (value instanceof Promise) {
// 递归解析
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
//解决 onFufilled,onRejected 没有传值的问题
//Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后 then 的 resolve 中捕获
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
// 每次调用 then 都返回一个新的 promise Promise/A+ 2.2.7
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
//Promise/A+ 2.2.2
//Promise/A+ 2.2.4 --- setTimeout 宏任务模拟异步
setTimeout(() => {
try {
//Promise/A+ 2.2.7.1
// 因为有的时候需要判断then中的方法是否返回一个promise对象,所以需要判断
// 如果返回值为promise对象,则需要取出结果当作promise2的resolve结果
// 如果不是,直接作为promise2的resolve结果
let x = onFulfilled(this.value);
// x可能是一个proimise
// 抽离出一个公共方法来判断他们是否为promise对象
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
//Promise/A+ 2.2.7.2
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
//Promise/A+ 2.2.3
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
static resolve(data){
return new Promise((resolve,reject)=>{
resolve(data);
})
}
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
static all(values) {
if (!Array.isArray(values)) {
const type = typeof values;
return new TypeError(`TypeError: ${type} ${values} is not iterable`);
}
return new Promise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === values.length) {
resolve(resultArr);
}
};
for (let i = 0; i < values.length; i++) {
let value = values[i];
if (value && typeof value.then === "function") {
value.then((value) => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
});
};
static race(promises) {
return new Promise((resolve, reject) => {
// 一起执行就是for循环
for (let i = 0; i < promises.length; i++) {
let val = promises[i];
if (val && typeof val.then === "function") {
val.then(resolve, reject);
} else {
// 普通值
resolve(val);
}
}
});
};
static any(promises) {
return new Promise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : [];
let len = promises.length;
// 用于收集所有 reject
let errs = [];
// 如果传入的是一个空数组,那么就直接返回 AggregateError
if (len === 0)
return reject(new AggregateError("All promises were rejected"));
promises.forEach((promise) => {
promise.then(
(value) => {
resolve(value);
},
(err) => {
len--;
errs.push(err);
if (len === 0) {
reject(new AggregateError(errs));
}
}
);
});
});
};
static allSettled(promises) {
const rejectHandler = (reason) => ({ status: "rejected", reason });
const resolveHandler = (value) => ({ status: "fulfilled", value });
const convertedPromises = promises.map((p) =>
Promise.resolve(p).then(resolveHandler, rejectHandler)
);
return Promise.all(convertedPromises);
};
}
Promise.prototype.catch = function (errCallback) {
return this.then(undefined, errCallback);
};
Promise.prototype.finally = function (callback) {
return this.then(
(value) => {
return Promise.resolve(callback(value)).then(() => value);
},
(reason) => {
return Promise.resolve(callback(reason)).then(() => {
throw reason;
});
}
);
};
37、浏览器实现拍照功能
index.js:
/**
* 视频外部配置
* @param {*} parentDomName 视频dom父元素名
* @returns 视频初始化配置对象
*/
this.videoConfig = function (parentDomName) {
const constraints = {
video: { // 视频框宽高配置 默认按照4:3比例展示,以宽为基准
width: 0,
height: 0,
facingMode: 'user' // 优先使用前置摄像头
},
audio: false, // 音频开关
video: true // 视频开关
};
constraints.video.width = parentDom.offsetWidth
constraints.video.height = parentDom.offsetHeight
return constraints
}
/**
* 获取浏览器媒体兼容性写法
* @param {*} videoConfig 视频参数配置
* @param {*} success 成功回调事件
* @param {*} error 失败回调事件
*/
this.compatibleMedia = function(videoConfig, success, error) {
if (navigator.mediaDevices.getUserMedia){
// 最新标准API
navigator.mediaDevices.getUserMedia(videoConfig).then(success).catch(error);
} else if (navigator.webkitGetUserMedia){
// webkit内核浏览器
navigator.webkitGetUserMedia(videoConfig).then(success).catch(error);
} else if (navigator.mozGetUserMedia){
// Firefox浏览器
navagator.mozGetUserMedia(videoConfig).then(success).catch(error);
} else if (navigator.getUserMedia){
// 旧版API
navigator.getUserMedia(videoConfig).then(success).catch(error);
}
}
/**
* 生成video canvas底图
*/
this.generateVideoCanvas = function(video) {
this.clearCanvas() // 清除canvas
const ctx = canvas.getContext('2d')
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
}
/**
* 清除canvas绘制区域
*/
this.clearCanvas = () => {
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
/**
* 成功的回调函数
* @param {*} MediaStream 视频流
*/
this.successCallback = function(MediaStream){
// 获得video对象
const video = document.querySelector('#video');
// MediaStream.getTracks() 返回的Tracks数组是按视频参数配置倒序排列
MediaStreamTrack = typeof MediaStream.stop ==='function' ? MediaStream : MediaStream.getTracks()[0];
video.srcObject = MediaStream
// 播放视频
video.play();
const cameraIcon = document.querySelector('#camera-icon')
cameraIcon.style.display = 'block'
// 当点击拍照按钮时,获取当前图片canvas的base64数据
cameraIcon.addEventListener('click', () => {
this.generateVideoCanvas(video)
})
}
//异常的回调函数
this.errorCallback = function(error) {
console.log("访问用户媒体设备失败:", error.name, error.message);
}
/**
* 系统媒体获取
* @param {*} videoConfig 视频外部配置对象
*/
this.getMedia = function(videoConfig) {
if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia){
// 调用用户媒体设备,访问摄像头
this.compatibleMedia(videoConfig, this.successCallback, this.errorCallback);
} else {
console.log("你的浏览器不支持访问用户媒体设备");
}
}
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拍照并将照片绘制成canvas</title>
</head>
<body>
<div class="video-box">
<!-- 拍照区域 -->
<video id="video"></video>
<!-- 拍照后绘制区域 -->
<canvas id="camera-canvas" ></canvas>
<!-- 拍照按钮 -->
<div class="camera-icon" id="camera-icon" style="display: none;"></div>
</div>
<script src="./index.js"></script>
<script>
const canvas = document.querySelector('#camera-canvas') // canvas dom
// 视频初始化配置
const videoConfig = this.videoConfig('video-box')
this.getMedia(videoConfig)
/**
* readme:
* 未点击拍照按钮时,就展示前置摄像头拍摄到的东西,点击拍照时,将拍摄到的照片绘制到canvas上
**/
</script>
</body>
</html>
38、整数验证
export function validateInteger(rule: any, value: any, callback: Function): any {
if (!value) {
return callback(new Error('输入不可以为空'))
}
setTimeout(() => {
if (!Number(value)) {
callback(new Error('请输入正整数'))
} else {
const re = /^[0-9]*[1-9][0-9]*$/
const rsCheck = re.test(value)
if (!rsCheck) {
callback(new Error('请输入正整数'))
} else if (value > 9999999) {
callback(new Error('最大可输入9999999万'))
} else {
callback()
}
}
}, 0)
}
39、图片格式验证
export function validImage(str: string) {
const _t = str.toLowerCase()
return /(.*)\.(jpg|jpeg|png)$/.test(_t)
}
40、原生JS实现浏览器滚动
const easeInOutQuad = function(t: number, b : number, c : number, d : number) {
t /= d / 2
if (t < 1) {
return c / 2 * t * t + b
}
t--
return -c / 2 * (t * (t - 2) - 1) + b
}
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()
/**
* Because it's so fucking difficult to detect the scrolling element, just move them all
* @param {number} amount
*/
function move(amount: number) {
document.documentElement.scrollTop = amount
document.body.parentNode.scrollTop = amount
document.body.scrollTop = amount
}
function position() {
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}
/**
* @param {number} to
* @param {number} duration
* @param {Function} callback
*/
export function scrollTo(to: number, duration: number, callback: Function) {
const start = position()
const change = to - start
const increment = 20
let currentTime = 0
duration = (typeof (duration) === 'undefined') ? 500 : duration
var animateScroll = function() {
// increment the time
currentTime += increment
// find the value with the quadratic in-out easing function
var val = easeInOutQuad(currentTime, start, change, duration)
// move the document.body
move(val)
// do the animation unless its over
if (currentTime < duration) {
requestAnimFrame(animateScroll)
} else {
if (callback && typeof (callback) === 'function') {
// the animation is done so lets callback
callback()
}
}
}
animateScroll()
}
41、【element-plus】ElMessage封装
import { ElMessage } from 'element-plus'
const duration = 2000
export function $success(msg: string) {
ElMessage({
duration: duration,
type: 'success',
message: msg
})
}
export function $error(msg: string, center: boolean = false, showClose: boolean = false) {
ElMessage({
duration: duration,
type: 'error',
message: msg,
center: center,
showClose: showClose
})
}
export function $warning(msg: string) {
ElMessage({
duration: duration,
type: 'warning',
message: msg
})
}
42、【element-plus】ElMessageBox封装
import { ElMessageBox } from 'element-plus'
type MessageType = '' | 'success' | 'warning' | 'info' | 'error'
export function $msgBox(msg: string, title: string, msgType: MessageType = 'warning',
confirmButtonText: string = '确认', cancelButtonText: string = '取消') {
return new Promise((resolve, reject) => {
ElMessageBox.confirm(msg, title,
{
distinguishCancelAndClose: true,
dangerouslyUseHTMLString: true,
confirmButtonText: confirmButtonText,
cancelButtonText: cancelButtonText,
type: msgType
}
).then((success) => {
resolve(success)
}).catch((action) => {
if (action == 'cancel') {
//取消
reject(action)
} else {
reject('close')
}
})
})
}
43、guid算法生成唯一标识符
/**
* 随机标识符生成(和上面第27点都是属于随机标识符算法,但是方式不一样)
*/
export function randomKey(): string {
let d = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
return uuid;
}
44、序列化树形结构
/**
* 平铺序列化树形结构
* @param tree 树形结构
* @param result 转化后一维数组
* @returns Array<TreeNode>
*/
export const flattenTree = (tree, result = []) => {
if (tree.length === 0) {
return result
}
for (const node of tree) {
result.push(node);
if (node.children) {
flattenTree(node.children, result);
}
}
return result;
}
45、一维数组转二维数组
/**
* 一维数组转二位数组(已知二维数组行列)
* @param original 一维数组
* @param m 二维数组行数
* @param n 二维数组列数
* @returns 二维数组
*/
export function construct2Array(original, m, n) {
return original.length === m * n
? Array.from({ length: m }, (_, i) => original.slice(i * n, (i + 1) * n))
: [];
}
/**
* 一维数组转二位数组(只知二维数组列数)
* @param original 一维数组
* @param n 二维数组列数
* @returns 二维数组
*/
export function construct2Array<T>(original: T[], n: number): T[]{
// 行数
const m = Math.ceil(original.length / n)
return Array.from({ length: m }, (_, i) => original.slice(i * n, (i + 1) * n)) as T[];
}
46、获取指定日期是一年中的第几天
const getDayOfDate = (date) => {
return Math.floor((date - new Date(date.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24))
}
示例:
getDayOfDate(new Date()) // 161
47、汉字转阿拉伯数字
function ChineseToNumber(chnStr) {
let rtn = 0;
let section = 0;
let number = 0;
let secUnit = false;
let str = chnStr.split('');
let chnNameValue = {
十: { value: 10, secUnit: false },
百: { value: 100, secUnit: false },
千: { value: 1000, secUnit: false },
万: { value: 10000, secUnit: true },
亿: { value: 100000000, secUnit: true }
}
//增加‘两’的对象,解决“两百零一”等数字的转换问题
let chnNumChar = {
零: 0,
一: 1,
二: 2,
两: 2,
三: 3,
四: 4,
五: 5,
六: 6,
七: 7,
八: 8,
九: 9
};
if (typeof chnNumChar[str[0]] !== 'undefined' || str[0] == '十') {
if (str[0] == '十') {
//因为只需要处理“十”至“十九”十个数,所以问题就很容易解决,对汉字中索引1的位置进行判断,当为空时,即为0+10=10,非空则从chnNumChar对象中取值加上10,即可得出结果。
rtn = (chnNumChar[str[1]] || 0) + 10
} else {
for (let i = 0; i < str.length; i++) {
let num = chnNumChar[str[i]];
if (typeof num !== 'undefined') {
number = num;
if (i === str.length - 1) {
section += number;
}
} else {
let unit = chnNameValue[str[i]].value;
secUnit = chnNameValue[str[i]].secUnit;
if (secUnit) {
section = (section + number) * unit;
rtn += section;
section = 0;
} else {
section += (number * unit);
}
number = 0;
}
}
}
} else {
rtn = 0
section = 0
}
return rtn + section;
}
48、打印分辨率类
/**
* 打印分辨率类
*/
export class Dpi {
getDPI() {
const arrDPI = [];
if ((window.screen as any).deviceXDPI) {
arrDPI[0] = (window.screen as any).deviceXDPI;
arrDPI[1] = (window.screen as any).deviceYDPI;
} else {
const tmpNode = document.createElement('DIV') as any;
tmpNode.style.cssText = 'width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden';
document.body.appendChild(tmpNode);
arrDPI[0] = parseInt(tmpNode.offsetWidth);
arrDPI[1] = parseInt(tmpNode.offsetHeight);
tmpNode.parentNode.removeChild(tmpNode);
}
return arrDPI;
}
/**
* px转换为mm
* @param value
* @returns {number}
*/
pxToMm(value) {
const inch = value / this.getDPI()[0];
const c_value = inch * 25.4;
return c_value;
}
/**
* mm转换为px
* @param value
* @returns {number}
*/
mmToPx(value) {
const inch = value / 25.4;
const c_value = inch * this.getDPI()[0];
return c_value;
}
}
示例:
// 获取当前分辨率下的页面实际像素高度(px)
pageHeight.value = Number(new Dpi().mmToPx(285))
49、清除中文字符串前后的特殊字符
/**
* 清除中文字符串前后的特殊字符
* @param str
* @returns
*/
export const clearNotChineseStr = (str: string): string => {
return str.replace(/[^\u4e00-\u9fa5/]/g, '');
}
50、url图片转base64格式
/**
* @description 图片转base64
*/
export function convertImageToBase64(url, callback): Promise<any> {
return new Promise((res, rej) => {
if (!url) {
res(true)
return;
}
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = url.includes('data:image') ? url : url + `?random=${Math.random()}`;
img.onload = function() {
// 如果图片本身是base64,就不用转,直接返回
if (img.src.includes('data:image')) {
callback(img.src);
img.onload = null
res(true);
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0, img.width, img.height);
const dataURL = canvas.toDataURL();
callback(dataURL);
img.onload = null
res(true);
};
img.onerror = () => {
img.onerror = null
rej('图片转base64失败');
}
})
}
示例:
await convertImageToBase64(url, (dataUrl) => {
// dataUrl是base64格式的字符串
}).catch((err) => {
proxy.$message.warning(err)
})
51、校验成对标签(基础版)
const validateLabel = (html: string):boolean => {
const regex = /<([^>]+)>/g;
let match;
const map = new Map()
try {
while ((match = regex.exec(html)) !== null) {
// 忽略img, br等自闭合标签
if (match[1].match(/img|br/)) {
continue;
}
// 除去属性值之后的原始值(只包含标签值)
const curStr = match[1].startsWith('/') ? match[1] : match[1].replace(/(?=\s+).*/, '')
// 闭标签去掉后的值
const curStrR = curStr.replace(/^\//, '')
if (map.has(curStrR)) {
if (map.get(curStrR).len === curStrR.length && map.get(curStrR).origin !== curStr) {
map.delete(curStrR)
} else {
throw new Error()
}
} else {
map.set(curStrR, {
len: curStrR.length,
origin: curStr
})
}
}
if (map.size !== 0) {
throw new Error()
}
return true
} catch (e) {
return false
}
}
52、获取输入内容的字节数
/**
* @param {string} 输入字符
* @returns {number} 输出字节数
*/
export function byteLength(str: string | number) {
let s = str.length
for (var i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) s++
else if (code > 0x7ff && code <= 0xffff) s += 2
if (code >= 0xDC00 && code <= 0xDFFF) i--
}
return s
}
---持续更新中,喜欢就点赞收藏噢~---