1 Star 1 Fork 0

Humor/utils

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
utils.js 50.00 KB
一键复制 编辑 原始数据 按行查看 历史
2803307553@qq.com 提交于 2024-03-26 23:04 . feat 新增3个库工具方法
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614
/*
前端使用class封装公共的js方法:
1、定义或者声明一个类
方式1:使用关键字class可以在 JS 中定义了一个类
class 类名 { 类的主体 }
方式2:使用类表达式,将类分配给变量
const 变量名 = class { 类的主体 }
方式3:将类导出为 ES6 模块的一部分,默认导出语法
export default class User { 主体 }
方式4:将类导出为 ES6 模块的一部分,命名导出
export class User { 主体 }
2、如何使用:使用new运算符实例化该类 const myUser = new 类名(); 使用之前必须new一下
3、初始化:constructor()
constructor(param1, param2, ...)是用于初始化实例的类主体中的一种特殊方法。
在这里可以设置字段的初始值或进行任何类型的对象设置。
class User {
constructor(name) {
this.name = name;
}
}
User的构造函数有一个参数 name,用于设置字段this.name的初始值
在构造函数中,this 值等于新创建的实例。用于实例化类的参数成为构造函数的参数:
class User {
constructor(name) {
name; // => '前端小智'
this.name = name;
}
}
const user = new User('前端小智');
构造函数中的name参数的值为'前端小智'。如果没有定义该类的构造函数,则会创建一个默认的构造函数。默认的构造函数是一个空函数,它不修改实例。
同时,一个JavaScript 类最多可以有一个构造函数。
总结:
-第一步:封装js类方法
-第二步:main.js中全局引入(如:utils),挂载工具js文件,挂载时候注意使用 = new utils();
例如696d6b:Vue.prototype.$utils = new utils()
-第三步:this.$utils.类方法名()
*/
/**
* JavaScript常用js工具类
* @class Utils
*/
class Utils {
/**
* 随机数范围:生成随机数
* @param min
* @param max
*/
random(min, max) {
if (arguments.length === 2) {
return Math.floor(min + Math.random() * ((max + 1) - min))
} else {
return null;
}
}
/**
* 判断数组中是否存在某元素
* @param arr
* @param val
*/
containsElement(arr, val) {
return arr.indexOf(val) != -1 ? true : false;
}
/**
* 数组排序,{type} 1:从小到大 2:从大到小 3:随机
* @param arr
* @param type
*/
sort(arr, type) {
return arr.sort((a, b) => {
switch (type) {
case 1:
return a - b;
case 2:
return b - a;
case 3:
return Math.random() - 0.5;
default:
return arr;
}
})
}
/**
* 将类数组转换为数组
* @param ary
*/
formArray(ary) {
let arr = [];
if (Array.isArray(ary)) {
arr = ary;
} else {
arr = Array.prototype.slice.call(ary);
};
return arr;
}
/**
* 数组中的最大值
* @param arr
* @returns
*/
max(arr) {
return Math.max.apply(null, arr);
}
/**
* 数组中的最小值
* @param arr
*/
min(arr) {
return Math.min.apply(null, arr);
}
/**
* 数组求和
* @param arr
*/
sum(arr) {
return arr.reduce((pre, cur) => {
return pre + cur
})
}
/**
* 数组求平均值
* @param arr
*/
average(arr) {
return this.sum(arr) / arr.length
}
/**
* 去除空格,type: 1-所有空格 2-前后空格 3-前空格 4-后空格
* @param str
* @param type
*/
trim(str, type) {
type = type || 1
switch (type) {
case 1:
return str.replace(/\s+/g, "");
case 2:
return str.replace(/(^\s*)|(\s*$)/g, "");
case 3:
return str.replace(/(^\s*)/g, "");
case 4:
return str.replace(/(\s*$)/g, "");
default:
return str;
}
}
/**
* 字符转换,type: 1:首字母大写 2:首字母小写 3:大小写转换 4:全部大写 5:全部小写
* @param str
* @param type
*/
changeCase(str, type) {
type = type || 4
switch (type) {
case 1:
return str.replace(/\b\w+\b/g, function(word) {
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
});
case 2:
return str.replace(/\b\w+\b/g, function(word) {
return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
});
case 3:
return str.split('').map(function(word) {
if (/[a-z]/.test(word)) {
return word.toUpperCase();
} else {
return word.toLowerCase()
}
}).join('')
case 4:
return str.toUpperCase();
case 5:
return str.toLowerCase();
default:
return str;
}
}
/**
* 获取日期 具体到日或者月type或者时分秒: 天day 月month hour时分秒
* @param type
*/
getDate(type) {
let mid;
const date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
let hh = date.getHours();
let mm = date.getMinutes();
let ss = date.getSeconds();
month = month > 9 ? month : '0' + month;
day = day > 9 ? day : '0' + day;
hh = hh > 9 ? hh : '0' + hh;
mm = mm > 9 ? mm : '0' + mm;
ss = ss > 9 ? ss : '0' + ss;
if (type == 'month') {
mid = `${year}-${month}`;
} else if (type == 'day') {
mid = `${year}-${month}-${day}`
} else if (type == 'hour') {
mid = `${year}-${month}-${day} ${hh}:${mm}:${ss}`
}
return mid;
}
/**
* 标准时间格式化
* @param date
*/
formatterDate(date) {
date = new Date(date)
let y = date.getFullYear()
let m = date.getMonth() + 1
m = m < 10 ? '0' + m : m
let d = date.getDate()
d = d < 10 ? '0' + d : d
let h = date.getHours()
h = h < 10 ? '0' + h : h
let minute = date.getMinutes()
minute = minute < 10 ? '0' + minute : minute
let second = date.getSeconds()
second = second < 10 ? '0' + second : second
return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second
}
/**
* 标准时间截取 年 月 日
* @param date
* */
filterymd(date) {
if (date) {
// 将日期以空格隔开,即['2020-06-13', '17:10:09']
date = (date + '').split(/[ ]+/);
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
// 用截取出来的年月日进行正则表达式匹配
date = reg.exec(date[0])[0];
}
return date;
}
/**
* 格式化秒为 时分秒
* @param value
* @returns
*/
formateSecond(value) {
let result = parseInt(value)
let h = Math.floor(result / 3600) < 10 ? '0' + Math.floor(result / 3600) : Math.floor(result / 3600)
let m = Math.floor((result / 60 % 60)) < 10 ? '0' + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60))
let s = Math.floor((result % 60)) < 10 ? '0' + Math.floor((result % 60)) : Math.floor((result % 60))
result = `${h}:${m}:${s}`
return result
}
/**
* 标准日期格式化为:yy-mm-dd
* @param {*} date
* @returns
*/
formatDate(date) {
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
month = month > 9 ? month : '0' + month;;
day = day > 9 ? day : '0' + day;
return year + "-" + month + "-" + day;
}
/**
* 判断某年某月最后一天:平年28 闰年29 月小30 月大31
* @param val
*/
judgeEndDays(val) {
let first_Day, last_Day, mid_var_y, mid_var_m, obj;
mid_var_y = Number(val.split('-')[0]); //获取年并转为Number类型
mid_var_m = Number(val.split('-')[1]) - 1; //获取月并转为Number类型
if (mid_var_y && mid_var_m >= 0) {
first_Day = this.formatDate(new Date(mid_var_y, mid_var_m, 1))
last_Day = this.formatDate(new Date(mid_var_y, mid_var_m + 1, 0))
}
obj = {
begin: first_Day,
end: last_Day
}
return obj;
}
/**
* 将非整10、100、……转为整10、100…
* 提现:只能提整百、整千、整万……
* @param date: 所有date
* @param param: 整百提取 param=100 、整千提取param = 1000……
*/
besExtract(date, param) {
date = Math.floor(date / param) * param;
return date;
}
/**
* 将带有负号的负数改为无符号整数
* @param val(String): 待处理数据
*/
handleMinus(val) {
val = val.replace(/-/g, '')
return val;
}
/**
* 隐藏电话号码中间4位
* @param phone(String):11位电话号码
*/
hiddenMidPhone(phone) {
let reg = /(\d{3})(\d{4})(\d{4})/;
phone = phone.toString()
return phone.replace(reg, '$1****$2');
}
/**
* 正则验证手机号码
* @param phone:验证的手机号码
*/
rulesPhone(phone) {
let reg = /^(13[0-9]|14[5|7]|15[0-9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/;
let meetRules = reg.test(phone);
return meetRules;
}
/**
* 处理baseUrl+url连接处'/'问题
* @param baseUrl
* @param url
*/
combine(baseUrl, url) {
let u = '';
if (baseUrl && baseUrl.length > 0) {
u += baseUrl + (baseUrl.charAt(baseUrl.length - 1) === '/' ? '' : '/');
u += (url.charAt(0) === '/' ? url.substring(1) : url);
return u;
}
return url;
}
/**
* 时间戳转为 YY-MM:DD HH:MM:SS 格式
* @param {*} time 时间戳
* - 13位是毫秒时间戳 10位是秒时间戳。
* - 10位需*1000,时间戳为13位的话不需乘1000
* @returns
*/
timestampToTime(time) {
if (time.toString().length == 10) {
time = time * 1000
} else if (time.toString().length == 13) {
time = time
}
let date = new Date(time);
let year = date.getFullYear(),
month = ("0" + (date.getMonth() + 1)).slice(-2),
sdate = ("0" + date.getDate()).slice(-2),
hour = ("0" + date.getHours()).slice(-2),
minute = ("0" + date.getMinutes()).slice(-2),
second = ("0" + date.getSeconds()).slice(-2);
let result = year + "-" + month + "-" + sdate + ' ' + hour + ':' + minute + ':' + second;
return result;
}
/**
* JavaScript 中常见false的6种情况,其余都为 true
* @param {*} val false null undefined ""空的字符串 数字 0 (包括:—0、-0、+0) 数字 NaN
* @returns
*/
judgTrueAndFalse(val) {
if (!val) {
return false;
} else {
return true;
}
}
/**
* 查找字符串【charAt()可以根据参数(非负整数的下标值)返回指定位置的字符:如果参数不在 0 和字符串的 length-1 之间,则返回空字符串;】
* @param {*} str 源字符串
* @param {*} index 0~length-1
* @returns
*/
charAtFindStr(str, index) {
return str.charAt(index);
}
/**
* 获取url参数
* @param {*} param 键名
* @returns
*/
getParam(param) {
let query = window.location.search.substring(1);
let vars = query.split("&");
for (let i = 0; i < vars.length; i++) {
let pair = vars[i].split("=");
if (pair[0] == param) {
return pair[1];
}
}
return false;
}
/**
* cookie中存储内容 document.cookie ='name='+ value || 'name=value'
* @param {*} key 键名
* @param {*} value 键值
*/
setCookie(key, value) {
document.cookie = `${key}=` + value
}
/**
* 获取cookie中的内容
* @param {*} key 键名
* @returns
*/
getCookie(key) {
let arr,
reg = new RegExp("(^| )" + key + "=([^;]*)(;|$)");
if ((arr = document.cookie.match(reg))) return unescape(arr[2]);
else return null;
}
/**
* local Storage 本地缓存中存储内容
* @param {*} key 键名
* @param {*} value 键值
*/
setStorage(key, value) {
localStorage.setItem(key, value);
}
/**
* 获取localStorage中的内容
* @param {*} key
* @returns
*/
getStorage(key) {
return localStorage.getItem(key)
}
/**
* 删除树结构中key不存在的节点
* @param {*} list treeList
* @param {*} key 指定键名
* @returns
*/
delNullLeafNode(list, key) {
list.forEach(item => {
item[key] === '' || item[key] === undefined || item[key] === null || item[key].length < 1 ? delete item[key] : delNullLeafNode(item[key], key)
})
return list;
}
/**
* json数组对象去重:利用ES6 Map 数据结构特性
* @param {*} arr json数组对象
* @param {*} key 自定义键名
* @returns
*/
jsonUnique(arr, key) {
let map = new Map()
for (let i of arr) {
// has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中
if (!map.has(i[key])) {
// set方法设置键名key对应的键值为value
map.set(i[key], i)
}
}
arr = [...map.values()]
return arr;
}
/**
* 普通数组去重:利用ES6的Set数据结构中成员唯一的特性
* @param {*} arr 源数据
* @returns
*/
arrUnique(arr) {
return [...new Set(arr)]
}
/**
* ES6 扁平化函数 flat()
* 如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。
* @param {*} arr
* @returns
*/
flat(arr) {
return arr.flat(Infinity)
}
/**
* 树形结构数据扁平化即转为一维数组
* 数组顺序:root顺序(索引升序)每个节点 root->leaf
* @param {*} tree 源数据
* @param {*} childs 自定义key
* @param {*} arrAttr 自定义需要返回的字段
* @returns
*/
treeToArray(tree, childs, arrAttr) {
let attrList = [];
let list = [];
// tree为非数组类型并且长度为 0 ,则返回 [] 数组
if (!Array.isArray(tree) && !tree.length) return [];
// 如果传递的 key 不是字符串,则返回 [] 数组
if (typeof childs !== 'string') return [];
if (!Array.isArray(arrAttr) || Array.isArray(arrAttr) && !arrAttr.length) {
attrList = Object.keys(tree[0])
attrList.splice(attrList.indexOf(childs), 1)
} else {
attrList = arrAttr
}
const getObj = (arr) => {
arr.forEach(row => {
let obj = {};
attrList.forEach(item => {
obj[item] = row[item]
})
list.push(obj)
if (row[childs]) {
getObj(row[childs])
}
})
return list;
}
return getObj(tree);
}
/**
* 线性数组树结构化:通过pid
* @param {*} data
* @returns
*/
arrToTree(data) {
let result = [];
let obj = {};
data.forEach(item => {
obj[item.id] = item;
})
data.forEach(item => {
let parent = obj[item.pid]
if (parent) {
(parent.children || (parent.children = [])).push(item)
} else {
result.push(item)
}
})
return result;
}
/**
* 深拷贝:递归实现深拷贝
* @param {source} source
* @returns
*/
deepClone(source) {
let target;
//对象包含:数组、对象、方法
if (typeof source === 'object') {
target = Array.isArray(source) ? [] : {}
for (let key in source) {
if (source.hasOwnProperty(key)) {
// 判断属性是引用数据类型还是基本数据类型
if (typeof source[key] !== 'object') {
// 对象属性:基本数据类型
target[key] = source[key];
} else {
// 对象属性:引用数据类型
target[key] = deepClone(source[key]);
}
}
}
} else {
target = source;
}
return target;
}
/**
* 返回数组并集:都是一维数组
* @param {*} arr1
* @param {*} arr2
* @returns
*/
arrayUnion(arr1, arr2) {
return [...new Set([...arr1, ...arr2])]
}
/**
* 返回数组交集:都是一维数组
* @param {*} arr1
* @param {*} arr2
* @returns
*/
arrayIntersect(arr1, arr2) {
return [...new Set([...arr1].filter(value => arr2.includes(value)))]
}
/**
* 数组差集,出现在第一个数组中,但是没有在其他数组中
* @param {*} arr1
* @param {*} arr2
* @returns
*/
arrayDiff(arr1, arr2) {
return [...new Set([...arr1].filter(value => !arr2.includes(value)))]
}
/**
* money千分位(,)分隔
* @param {*} n 具体数字
* @returns 返回字符串
*/
formatMoney(n) {
let num = n.toString();
let len = num.length;
if (len <= 3) {
return num;
} else {
let temp = '';
let remainder = len % 3;
if (remainder > 0) { // 不是3的整数倍
return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp;
} else { // 3的整数倍
return num.slice(0, len).match(/\d{3}/g).join(',') + temp;
}
}
}
/**
* 驼峰命名转换成短横线命名
* @param {*} str 字符串
* @returns
*/
camelCaseTo(str) {
return str.replace(/[A-Z]/g, (item) => '-' + item.toLowerCase())
}
/**
* 短横线命名转换成驼峰命名
* @param {*} str 字符串
* @returns
*/
toCamelCase(str) {
return str.replace(/-([a-z])/g, (i, item) => item.toUpperCase())
}
/**
* 获取系统当前时间戳
* @param {*} n 位数:只能是 10位(秒)或者 13位(毫秒)
* @returns 返回自定义位数的时间戳
*/
getNowTimestamp(n) {
let timeTamp;
if (n === 10) {
timeTamp = Math.round(new Date().getTime() / 1000)
} else if (n === 13) {
timeTamp = Date.now();
}
return timeTamp;
}
/**
* YY-MM-DD HH:MM:SS 转为毫秒(13位)时间戳
* @param {*} d
* d: YY-MM-DD HH:MM:SS格式
* @returns 13位毫秒时间戳
*/
getTimeToStamp(d) {
let date = new Date(d)
return date.getTime()
}
/**
* 计算结束日期距离系统当前时间的:年月日时分秒
* @param {*} endDate 结束时间:YY-MM:DD HH:MM:SS 格式
* 返回 year+'年,'+month+'月,'+day+'天,'+hour+'小时,'+minute+'分,'+second+"秒";
* @returns
*/
dateComputed(endDate) {
let curDate = new Date(); //转换为中国标准时间
endDate = new Date(endDate);
curDate = curDate.getTime(); //转换为时间戳
endDate = endDate.getTime();
let runTime = (endDate - curDate) / 1000; //开始得出时间差,然后计算
let year = Math.floor(runTime / 86400 / 365);
runTime = runTime % (86400 * 365);
let month = Math.floor(runTime / 86400 / 30);
runTime = runTime % (86400 * 30);
let day = Math.floor(runTime / 86400);
runTime = runTime % 86400;
let hour = Math.floor(runTime / 3600);
runTime = runTime % 3600;
let minute = Math.floor(runTime / 60);
runTime = runTime % 60;
let second = runTime.toFixed(0);
return year + '' + month + '' + day + '' + ' ' + hour + '小时' + minute + '' + second + "";
}
/**
* 截止日期到当前系统日期距离多少天
* @param {*} endDate 截止日期
* - endDate格式:YY-MM-DD HH:mm:ss
* @param {*} type 精确到:天、小时、分钟、秒
* @returns 根据type返回自己所需
*/
diffTime(endDate, type) {
let startDate = new Date()
endDate = new Date(endDate)
let diff = endDate.getTime() - startDate.getTime(); //时间差的毫秒数
//计算出相差天数
let days = Math.floor(diff / (24 * 3600 * 1000))
//计算出小时数
let leave1 = diff % (24 * 3600 * 1000); //计算天数后剩余的毫秒数
let hours = Math.floor(leave1 / (3600 * 1000));
//计算相差分钟数
let leave2 = leave1 % (3600 * 1000); //计算小时数后剩余的毫秒数
let minutes = Math.floor(leave2 / (60 * 1000));
//计算相差秒数
let leave3 = leave2 % (60 * 1000); //计算分钟数后剩余的毫秒数
let seconds = Math.round(leave3 / 1000);
let res;
switch (type) {
case 'day':
res = days
break;
case 'hours':
res = days + '' + hours + '小时'
break;
case 'minutes':
res = days + '' + hours + '小时' + minutes + ''
break;
case 'seconds':
res = days + '' + hours + '小时' + minutes + '' + seconds + ''
break;
}
return res
}
/**
* 数据类型的检测
* @param {any} data 要检测数据类型的数据
* @returns {string} type 返回具体的小写类型名称
*/
isType(data) {
return Object.prototype.toString.call(data).replace(/\[object (\w+)\]/, '$1').toLowerCase()
}
/**
* 通过子节点的id查找他的所有父节点
* @param {*} tree 树结构数据
* @param {*} func 方法
* @param {*} path []
* @param {*} dataStructure 自定义返回的数据对象
* @returns 返回所有父节点及本身节点组成的一维数组
*/
findNode(tree, func, path, dataStructure) {
if (!tree) return [];
for (const data of tree) {
let newObj = {};
for (const key in dataStructure) {
newObj[key] = data[key];
}
path.push(newObj);
if (func(data)) return path;
if (data.children) {
const findChildren = this.findNode(
data.children,
func,
path,
dataStructure
);
if (findChildren.length) return findChildren;
}
path.pop();
}
return [];
}
/**
* 验证是否邮箱
* @param: email 输入的邮箱
* @returns boolean
*/
isEmail(email) {
let reg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/
return reg.test(email)
}
/**
* 验证是否手机号
* @param: phone 输入的手机号
* @returns boolean
*/
isPhone(phone) {
let reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
return reg.test(phone)
}
/**
* 是否字符串
* @param: val 输入的内容
* @returns boolean
*/
isString(val) {
return Object.prototype.toString.call(val).slice(8, -1) === 'String'
}
/**
* 是否数字
* @param: val 输入的内容
* @returns boolean
*/
isNumber(val) {
return Object.prototype.toString.call(val).slice(8, -1) === 'Number'
}
/**
* 是否null
* @param: val 输入的内容
* @returns boolean
*/
isNull(val) {
return Object.prototype.toString.call(val).slice(8, -1) === 'Null'
}
/**
* 是否undefined
* @param: val 输入的内容
* @returns boolean
*/
isUndefined(val) {
return Object.prototype.toString.call(val).slice(8, -1) === 'Undefined'
}
/**
* 是否对象
* @param: obj 需要判断的对象
* @returns boolean
*/
isObj(obj) {
return Object.prototype.toString.call(obj).slice(8, -1) === 'Object'
}
/**
* 是否数组
* @param: arr 需要判断的数组
* @returns boolean
*/
isArray(arr) {
return Object.prototype.toString.call(arr).slice(8, -1) === 'Array'
}
/**
* 数组最大值
* @param: arr 目标数组
* @returns 最大值
*/
arrMax(arr) {
return Math.max.apply(null, arr);
}
/**
* 数组最小值
* @param: arr 目标数组
* @returns 最小值
*/
arrMin(arr) {
return Math.min.apply(null, arr);
}
/**
* 检测密码强度
* @param: pwd 目标字符串
* @returns 密码强度等级
*/
checkPwd(pwd) {
// 正则表达式模式
let patterns = {
weak: /^.{1,7}$/, // 弱密码:少于8个字符
medium: /^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/, // 较强密码:至少8个字符,包含大小写字母和数字
strong: /^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).+$/ // 强密码:至少8个字符,包含大小写字母、数字和特殊字符
};
if (patterns.strong.test(pwd)) {
return ''; // 密码强度为强
} else if (patterns.medium.test(pwd)) {
return '较强'; // 密码强度为较强
} else if (patterns.weak.test(pwd)) {
return ''; // 密码强度为弱
} else {
return '无效'; // 无效密码
}
}
/**
* 序号/索引 转为Excel字母序号
* @param {num} 序号/索引
* @returns {string} 字母
*/
indexToLetter(num) {
let letter = ''
const letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
let loopNum = parseInt(num / 26)
if (loopNum > 0) {
letter += this.indexToLetter(loopNum - 1)
}
letter += letters[num % 26]
return letter
}
/**
* Excel字母序号转为索引
* @param {string} 字母
* @returns {num} 序号/索引
*/
letterToIndex(letter) {
let index = 0;
for (let i = 0; i < letter.length; i++) {
let charCode = letter.charCodeAt(i);
index = index * 26 + charCode - 64; // 减去 'A' 的 ASCII 码值(65-1)
}
return index - 1; // 索引从 0 开始,需要减去 1
}
/**
* 日期格式互转(仅限只是年月日格式,不能有时分秒)
* @param {type:String} 转为目标日期格式 '1'=>YYYY年MM月DD日 '/'=>YYYY/MM/DD '-'=>YYYY-MM-DD ':'=> YYYY:MM:DD '.'=> YYYY.MM.DD
* @param {date:Strinng} 目标转换日期
* @returns {String} 返回字符串日期
*/
format(type, date) {
if (!date) return
let reg = /[0-9]+/g // 取出数字
let result = ''
let dateArr = date.match(reg)
if (type === '1') {
result = `${dateArr[0]}${dateArr[1]}${dateArr[2]}日`
} else {
result = dateArr.join(type)
}
return result
}
/**
* 复制内容到剪贴板的两种方式
* @param {text:string} 内容
*/
copy(text) {
const clipboardData = window.clipboardData;
if (clipboardData) {
clipboardData.clearData();
clipboardData.setData("Text", text);
return true;
} else if (document.execCommand) {
const el = document.createElement("textarea");
el.value = text;
el.setAttribute("readonly", "");
el.style.position = "absolute";
el.style.left = "-9999px";
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
return true;
}
return false;
}
/**
* 光标所在位置插入字符
* @param {dom} 元素
* @param {val} 内容
*/
insertAtCursor(dom, val) {
if (document.selection) {
dom.focus()
let sel = document.selection.createRange()
sel.text = val
sel.select()
} else if (dom.selectionStart || dom.selectionStart == '0') {
let startPos = dom.selectionStart
let endPos = dom.selectionEnd
let restoreTop = dom.scrollTop
dom.value = dom.value.substring(0, startPos) + val + dom.value.substring(endPos, dom.value.length)
if (restoreTop > 0) {
dom.scrollTop = restoreTop
}
dom.focus()
dom.selectionStart = startPos + val.length
dom.selectionEnd = startPos + val.length
} else {
dom.value += val
dom.focus()
}
}
/**
* 图片地址转base64
* @param {imgUrl: string,callback} 传入图片路径及回调函数
* @returns 返回callback
*/
base64(imgUrl, callback) {
const image = new Image();
image.crossOrigin = 'anonymous';
image.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.height = image.naturalHeight;
canvas.width = image.naturalWidth;
ctx.drawImage(image, 0, 0);
const dataUrl = canvas.toDataURL();
callback && callback(dataUrl)
}
image.src = imgUrl;
}
/**
* base64图片下载
* @param {base64: string} 图片地址
* @param {fileName: string} 下载文件名称
*/
downBase64(base64, fileName) {
let base64ToBlob = code => {
let parts = code.split(';base64,');
let contentType = parts[0].split(':')[1];
let raw = window.atob(parts[1]);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {
type: contentType
});
};
let aLink = document.createElement('a');
let blob = base64ToBlob(base64); //new Blob([content]);
let evt = document.createEvent("HTMLEvents");
evt.initEvent("click", true, true); //initEvent不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
aLink.download = fileName;
aLink.href = URL.createObjectURL(blob);
aLink.click();
}
/**
* 判断是android还是ios还是web
* @returns 返回Web || iOS || Android
*/
isDevice() {
var ua = navigator.userAgent.toLowerCase()
if (ua.match(/iPhone\sOS/i) === 'iphone os' || ua.match(/iPad/i) === 'ipad') { // ios
return 'iOS'
}
if (ua.match(/Android/i) === 'android') { // android
return 'Android'
}
return 'Web'
}
/**
* H5软键盘缩回、弹起回调
* @returns downCb 当软键盘弹起后,缩回的回调,upCb 当软键盘弹起的回调
*/
h5Resize(downCb, upCb) { //当软件键盘弹起会改变当前 window.innerHeight,监听这个值变化 [downCb 当软键盘弹起后,缩回的回调,upCb 当软键盘弹起的回调]
let clientHeight = window.innerHeight;
downCb = typeof downCb === 'function' ? downCb : function() {}
upCb = typeof upCb === 'function' ? upCb : function() {}
window.addEventListener('resize', () => {
let height = window.innerHeight;
if (height === clientHeight) {
downCb();
}
if (height < clientHeight) {
upCb();
}
});
}
/**
* 添加水印
* @params {str1, str2} 水印内容
*/
setWatermark(str1, str2) {
let id = '1.23452384164.123412415'
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id))
}
let can = document.createElement('canvas')
// 设置canvas画布大小
can.width = 250
can.height = 80
let cans = can.getContext('2d')
cans.rotate(-20 * Math.PI / 180) // 水印旋转角度
cans.font = '15px Vedana'
cans.fillStyle = '#000000'
cans.textAlign = 'center'
cans.textBaseline = 'Middle'
cans.fillText(str1, can.width / 2, can.height) // 水印在画布的位置x,y轴
cans.fillText(str2, can.width / 2, can.height + 22)
let div = document.createElement('div')
div.id = id
div.style.pointerEvents = 'none'
div.style.top = '40px'
div.style.left = '0px'
div.style.opacity = '0.9'
div.style.position = 'fixed'
div.style.zIndex = '100000'
div.style.width = document.documentElement.clientWidth + 'px'
div.style.height = document.documentElement.clientHeight + 'px'
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
document.body.appendChild(div)
return id
}
/**
* 添加水印
* @params {str1, str2} 水印内容
*/
setWaterMark(str1, str2) {
let id = this.setWatermark(str1, str2)
if (document.getElementById(id) === null) {
id = this.setWatermark(str1, str2)
}
}
/**
* 移除水印
*/
rmWaterMark() {
let id = '1.23452384164.123412415'
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id))
}
}
/**
* 函数防抖
* @param {func } 函数
* @param {wait} 延迟执行毫秒数
* @param {immediate} true 表立即执行,false 表非立即执行,[立即执行是触发事件后函数会立即执行,然后n秒内不触发事件才能继续执行函数的效果]
*/
debounce(func, wait, immediate) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
} else {
timeout = setTimeout(function() {
func.apply(context, args)
}, wait);
}
}
}
/**
* 函数节流
* @param {func } 函数
* @param {wait} 延迟执行毫秒数
* @param {typpe} 1 表时间戳版,2 表定时器版
*/
throttle(func, wait, type) {
if (type === 1) {
let previous = 0;
} else if (type === 2) {
let timeout;
}
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
/**
* 金额格式化
* @param {number:number}要格式化的数字
* @param {decimals:number}保留几位小数
* @param {sign:string}小数点符号
* @param {thousands_sep:string}千分位符号
* @returns string
*/
moneyFormat(number, decimals, sign, thousands_sep) {
number = (number + '').replace(/[^0-9+-Ee.]/g, '')
const n = !isFinite(+number) ? 0 : +number
const prec = !isFinite(+decimals) ? 2 : Math.abs(decimals)
const sep = typeof thousands_sep === 'undefined' ? ',' : thousands_sep
const dec = typeof sign === 'undefined' ? '.' : sign
let s = ''
const toFixedFix = function(n, prec) {
const k = Math.pow(10, prec)
return '' + Math.ceil(n * k) / k
}
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.')
const re = /(-?\d+)(\d{3})/
while (re.test(s[0])) {
s[0] = s[0].replace(re, '$1' + sep + '$2')
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || ''
s[1] += new Array(prec - s[1].length + 1).join('0')
}
return s.join(dec)
}
/**
* 全屏/退出全屏
*/
fullScreen() {
let el = document.documentElement;
el.webkitRequestFullScreen ?
el.webkitRequestFullScreen() :
el.mozRequestFullScreen ?
el.mozRequestFullScreen() :
el.msRequestFullscreen ?
el.msRequestFullscreen() :
el.requestFullScreen ?
el.requestFullScreen() :
alert("当前浏览器不支持该功能");
}
/**
*退出全屏
*/
exitFullScreen() {
let el = document;
el.webkitCancelFullScreen ?
el.webkitCancelFullScreen() :
el.mozCancelFullScreen ?
el.mozCancelFullScreen() :
el.cancelFullScreen ?
el.cancelFullScreen() :
el.msExitFullscreen ?
el.msExitFullscreen() :
el.exitFullscreen ?
el.exitFullscreen() :
alert("当前浏览器不支持该功能");
}
/**
* 金额转大写
* @param {money:number} 转换金额
* @returns 返回金额大写
*/
moneyCapitalize(money) {
//汉字的数字
const cnNums = new Array(
"",
"",
"",
"",
"",
"",
"",
"",
"",
""
);
//基本单位
const cnIntRadice = new Array("", "", "", "");
//对应整数部分扩展单位
const cnIntUnits = new Array("", "", "亿", "");
//对应小数部分单位
const cnDecUnits = new Array("", "", "", "");
//整数金额时后面跟的字符
const cnInteger = "";
//整型完以后的单位
const cnIntLast = "";
//最大处理的数字
const maxNum = 999999999999999.9999;
//金额整数部分
let integerNum;
//金额小数部分
let decimalNum;
//输出的中文金额字符串
let chineseStr = "";
//分离金额后用的数组,预定义
let parts;
// 传入的参数为空情况
if (money == "") {
return "";
}
money = parseFloat(money);
if (money >= maxNum) {
return "";
}
// 传入的参数为0情况
if (money == 0) {
chineseStr = cnNums[0] + cnIntLast + cnInteger;
return chineseStr;
}
// 转为字符串
money = money.toString();
// indexOf 检测某字符在字符串中首次出现的位置 返回索引值(从0 开始) -1 代表无
if (money.indexOf(".") == -1) {
integerNum = money;
decimalNum = "";
} else {
parts = money.split(".");
integerNum = parts[0];
decimalNum = parts[1].substr(0, 4);
}
//转换整数部分
if (parseInt(integerNum, 10) > 0) {
let zeroCount = 0;
let IntLen = integerNum.length;
for (let i = 0; i < IntLen; i++) {
let n = integerNum.substr(i, 1);
let p = IntLen - i - 1;
let q = p / 4;
let m = p % 4;
if (n == "0") {
zeroCount++;
} else {
if (zeroCount > 0) {
chineseStr += cnNums[0];
}
zeroCount = 0;
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
}
if (m == 0 && zeroCount < 4) {
chineseStr += cnIntUnits[q];
}
}
// 最后+ 元
chineseStr += cnIntLast;
}
// 转换小数部分
if (decimalNum != "") {
let decLen = decimalNum.length;
for (let i = 0; i < decLen; i++) {
let n = decimalNum.substr(i, 1);
if (n != "0") {
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
}
}
}
if (chineseStr == "") {
chineseStr += cnNums[0] + cnIntLast + cnInteger;
} else if (decimalNum == "") {
chineseStr += cnInteger;
}
return chineseStr;
}
/**
* 解决运算精度丢失的问题加法
* @param {arg1:number, arg2:number} 加数1 与 加数2
* @returns sum 和
*/
plus(arg1, arg2) {
let r1, r2, m;
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return (arg1 * m + arg2 * m) / m;
}
/**
* 解决运算精度丢失的问题减法
* @param {arg1:number, arg2:number} 被减数 与 减数
* @returns arg1 与 arg2 的差
*/
subtract(arg1, arg2) {
let r1, r2, m, n;
try {
r1 = arg1.toString().split(".")[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
n = r1 >= r2 ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
/**
* 解决运算精度丢失的问题乘法
* @param {arg1:number, arg2:number} 乘数1 与 乘数2
* @returns 乘积
*/
multiply(arg1, arg2) {
let m = 0,
s1 = arg1.toString(),
s2 = arg2.toString();
try {
m += s1.split(".")[1].length;
} catch (e) {}
try {
m += s2.split(".")[1].length;
} catch (e) {}
return (
(Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) /
Math.pow(10, m)
);
}
/**
* 解决运算精度丢失的问题除法
* @param {arg1:number, arg2:number} 被除数 与 除数
* @returns 商
*/
divide(arg1, arg2) {
let t1 = 0,
t2 = 0,
r1,
r2;
try {
t1 = arg1.toString().split(".")[1].length;
} catch (e) {}
try {
t2 = arg2.toString().split(".")[1].length;
} catch (e) {}
r1 = Number(arg1.toString().replace(".", ""));
r2 = Number(arg2.toString().replace(".", ""));
return (r1 / r2) * Math.pow(10, t2 - t1);
}
/**
* 获取文件扩展名
* @param {filename:string} 文件名称
* @returns 扩展名字符
*/
getFileExtension(filename) {
return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined;
}
/**
* 当前浏览器是否支持webp格式图片
* @returns {boolean} true | false
*/
hasSupportWebp() {
return !![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0
}
/**
* 生成唯一标识符(UUID)
* @returns {string} string
*/
uuid(){
let formatStr = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
let reg = /[xy]/g
let uuidStr = formatStr.replace(reg, (c) => {
let r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return uuidStr
}
/**
* 获取传入日期是第几周
* @param d 传入的日期值
* @returns {number} 返回第几周数字值
*/
getWeek(d) {
d = new Date(d);
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
let startOfYear = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
let weekNumber = Math.ceil((((d - startOfYear) / 86400000) + 1) / 7);
return weekNumber;
}
/**
* 时间日期转换
* @param date {Date} 当前时间,new Date() 格式
* @param format {string} 需要转换的时间格式字符串
* @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd`
* @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ"
* @description format 星期:"YYYY-mm-dd HH:MM:SS WWW"
* @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ"
* @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
* @returns {string} 返回拼接后的时间字符串
*/
formatDate(date, format ) {
let we = date.getDay(); // 星期
let z = this.getWeek(date); // 周
let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
const opt = {
'Y+': date.getFullYear().toString(), // 年
'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
'd+': date.getDate().toString(), // 日
'H+': date.getHours().toString(), // 时
'M+': date.getMinutes().toString(), // 分
'S+': date.getSeconds().toString(), // 秒
'q+': qut, // 季度
};
// 中文数字 (星期)
const week = {
'0': '',
'1': '',
'2': '',
'3': '',
'4': '',
'5': '',
'6': '',
};
// 中文数字(季度)
const quarter = {
'1': '',
'2': '',
'3': '',
'4': '',
};
if (/(W+)/.test(format))
format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '' + week[we]) : week[we]);
if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '' + quarter[qut] + '季度' : quarter[qut]);
if (/(Z+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '' + z + '' : z + '');
for (let k in opt) {
let r = new RegExp('(' + k + ')').exec(format);
// 若输入的长度不为1,则前面补零
if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
}
return format;
}
/**
* 将时间转换为 `几秒前`、`几分钟前`、`几小时前`、`几天前`
* @param {string | Date} 当前时间,new Date() 格式或者字符串时间格式
* @param format {string} 需要转换的时间格式字符串
* @description param 10秒: 10 * 1000
* @description param 1分: 60 * 1000
* @description param 1小时: 60 * 60 * 1000
* @description param 24小时:60 * 60 * 24 * 1000
* @description param 3天: 60 * 60* 24 * 1000 * 3
* @returns {string} 返回拼接后的时间字符串
*/
pastTime(param, format = 'YYYY-mm-dd') {
// 传入格式处理、存储转换值
let t, s;
// 获取js 时间戳
let time = new Date().getTime();
// 是否是对象
typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param);
// 当前时间戳 - 传入时间戳
time = Number.parseInt(`${time - t}`);
if (time < 10000) {
// 10秒内
return '刚刚';
} else if (time < 60000 && time >= 10000) {
// 超过10秒少于1分钟内
s = Math.floor(time / 1000);
return `${s}秒前`;
} else if (time < 3600000 && time >= 60000) {
// 超过1分钟少于1小时
s = Math.floor(time / 60000);
return `${s}分钟前`;
} else if (time < 86400000 && time >= 3600000) {
// 超过1小时少于24小时
s = Math.floor(time / 3600000);
return `${s}小时前`;
} else if (time < 259200000 && time >= 86400000) {
// 超过1天少于3天内
s = Math.floor(time / 86400000);
return `${s}天前`;
} else {
// 超过3天
let date = typeof param === 'string' || 'object' ? new Date(param) : param;
return this.formatDate(date, format);
}
}
/**
* 不同时间段问好
* @description param 调用 `timeGreeting(new Date())` 输出 `上午好`
* @param param {Date} 当前时间,new Date() 格式
* @returns {string} 返回拼接后的时间字符串
*/
timeGreeting(param) {
let hour = new Date(param).getHours();
if (hour < 6) return '凌晨好';
else if (hour < 9) return '早上好';
else if (hour < 12) return '上午好';
else if (hour < 14) return '中午好';
else if (hour < 17) return '下午好';
else if (hour < 19) return '傍晚好';
else if (hour < 22) return '晚上好';
else return '夜里好';
}
}
export default new Utils()
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/giteeHumor/utils.git
git@gitee.com:giteeHumor/utils.git
giteeHumor
utils
utils
master

搜索帮助