|
@@ -1,27 +1,27 @@
|
|
|
/**
|
|
|
* 返回一个自带节流效果的函数,用res表示。
|
|
|
- *
|
|
|
+ *
|
|
|
* fn:需要被节流的函数
|
|
|
* interval:最短多长时间允许执行一次fn
|
|
|
- *
|
|
|
+ *
|
|
|
* 功能要点:
|
|
|
* 1.fn代码里如有this,res被执行时,this会指向res的调用者;
|
|
|
* 2.res被执行时的实参会映射到fn的形参;
|
|
|
* 3.第一次调用res时,会立即执行fn。
|
|
|
*/
|
|
|
export function throttle(fn, interval) {
|
|
|
- let lastRunTime = 0
|
|
|
+ let lastRunTime = 0;
|
|
|
|
|
|
return function (...args) {
|
|
|
- let elapsedTime = Date.now() - lastRunTime
|
|
|
+ let elapsedTime = Date.now() - lastRunTime;
|
|
|
if (elapsedTime < interval) {
|
|
|
- return
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- let context = this
|
|
|
- lastRunTime = Date.now()
|
|
|
- fn.apply(context, args)
|
|
|
- }
|
|
|
+ let context = this;
|
|
|
+ lastRunTime = Date.now();
|
|
|
+ fn.apply(context, args);
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -31,78 +31,81 @@ export function throttle(fn, interval) {
|
|
|
* delay: 消抖时长
|
|
|
* isImmediateCall: 是否在一组操作中的第一次调用时立即执行fn
|
|
|
* isRememberLastCall:是否在一组中最后一次调用后等delay时长再执行fn
|
|
|
- *
|
|
|
+ *
|
|
|
* 如果isRememberLastCall为false,意味着fn不会被延迟执行,所以fnDebounced执行时,要么在内部调用fn,同步返回fn返回值;要么内部决定本次不调用fn,同步返回null。
|
|
|
* 如果isRememberLastCall为true,意味着fn可能被延迟执行,所以fnDebounced会返回一个Promise,在fn被调用时用其返回值resolve该Promise,或者在fn的延时调用计划被取消时用'canceled'resolve该Promise。(不宜reject,否则又没有人去catch,会导致浏览器报错。)
|
|
|
*/
|
|
|
export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLastCall = true) {
|
|
|
- console.assert(isImmediateCall || isRememberLastCall, 'isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!')
|
|
|
- let timer = null
|
|
|
- let retPromiseLastTimeResolver = null
|
|
|
+ console.assert(isImmediateCall || isRememberLastCall, "isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!");
|
|
|
+ let timer = null;
|
|
|
+ let retPromiseLastTimeResolver = null;
|
|
|
// 上次调用的时刻
|
|
|
- let lastCallTime = 0
|
|
|
+ let lastCallTime = 0;
|
|
|
|
|
|
if (isImmediateCall && !isRememberLastCall) {
|
|
|
return function (...args) {
|
|
|
- let ret = null
|
|
|
- const currentTime = Date.now()
|
|
|
+ let ret = null;
|
|
|
+ const currentTime = Date.now();
|
|
|
if (currentTime - lastCallTime >= delay) {
|
|
|
- ret = fn.apply(this, args)
|
|
|
+ ret = fn.apply(this, args);
|
|
|
}
|
|
|
- lastCallTime = currentTime
|
|
|
- return ret
|
|
|
- }
|
|
|
+ lastCallTime = currentTime;
|
|
|
+ return ret;
|
|
|
+ };
|
|
|
} else if (!isImmediateCall && isRememberLastCall) {
|
|
|
return function (...args) {
|
|
|
if (timer) {
|
|
|
- clearTimeout(timer)
|
|
|
- timer = null
|
|
|
+ clearTimeout(timer);
|
|
|
+ timer = null;
|
|
|
}
|
|
|
if (retPromiseLastTimeResolver) {
|
|
|
- retPromiseLastTimeResolver('canceled')
|
|
|
- retPromiseLastTimeResolver = null
|
|
|
+ retPromiseLastTimeResolver("canceled");
|
|
|
+ retPromiseLastTimeResolver = null;
|
|
|
}
|
|
|
const ret = new Promise((resolve, reject) => {
|
|
|
- retPromiseLastTimeResolver = resolve
|
|
|
+ retPromiseLastTimeResolver = resolve;
|
|
|
timer = setTimeout(() => {
|
|
|
- timer = null
|
|
|
- retPromiseLastTimeResolver = null
|
|
|
- resolve(fn.apply(this, args))
|
|
|
- }, delay)
|
|
|
- })
|
|
|
- return ret
|
|
|
- }
|
|
|
+ timer = null;
|
|
|
+ retPromiseLastTimeResolver = null;
|
|
|
+ resolve(fn.apply(this, args));
|
|
|
+ }, delay);
|
|
|
+ });
|
|
|
+ return ret;
|
|
|
+ };
|
|
|
} else if (isImmediateCall && isRememberLastCall) {
|
|
|
return function (...args) {
|
|
|
- const currentTime = Date.now()
|
|
|
- if (currentTime - lastCallTime >= delay) { // 一组操作中的第一次
|
|
|
- const res = fn.apply(this, args)
|
|
|
- lastCallTime = currentTime
|
|
|
- return Promise.resolve(res)
|
|
|
- } else { // 一组中的后续调用
|
|
|
- if (timer) { // 在此之前存在中间调用
|
|
|
- lastCallTime = currentTime
|
|
|
- clearTimeout(timer)
|
|
|
- timer = null
|
|
|
+ const currentTime = Date.now();
|
|
|
+ if (currentTime - lastCallTime >= delay) {
|
|
|
+ // 一组操作中的第一次
|
|
|
+ const res = fn.apply(this, args);
|
|
|
+ lastCallTime = currentTime;
|
|
|
+ return Promise.resolve(res);
|
|
|
+ } else {
|
|
|
+ // 一组中的后续调用
|
|
|
+ if (timer) {
|
|
|
+ // 在此之前存在中间调用
|
|
|
+ lastCallTime = currentTime;
|
|
|
+ clearTimeout(timer);
|
|
|
+ timer = null;
|
|
|
}
|
|
|
if (retPromiseLastTimeResolver) {
|
|
|
- retPromiseLastTimeResolver('canceled')
|
|
|
- retPromiseLastTimeResolver = null
|
|
|
+ retPromiseLastTimeResolver("canceled");
|
|
|
+ retPromiseLastTimeResolver = null;
|
|
|
}
|
|
|
const ret = new Promise((resolve, reject) => {
|
|
|
- retPromiseLastTimeResolver = resolve
|
|
|
+ retPromiseLastTimeResolver = resolve;
|
|
|
timer = setTimeout(() => {
|
|
|
- lastCallTime = 0
|
|
|
- timer = null
|
|
|
- retPromiseLastTimeResolver = null
|
|
|
- resolve(fn.apply(this, args))
|
|
|
- }, delay)
|
|
|
- })
|
|
|
- return ret
|
|
|
+ lastCallTime = 0;
|
|
|
+ timer = null;
|
|
|
+ retPromiseLastTimeResolver = null;
|
|
|
+ resolve(fn.apply(this, args));
|
|
|
+ }, delay);
|
|
|
+ });
|
|
|
+ return ret;
|
|
|
}
|
|
|
- }
|
|
|
+ };
|
|
|
} else {
|
|
|
- console.error('不应该执行到这里!')
|
|
|
+ console.error("不应该执行到这里!");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -123,116 +126,116 @@ export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLas
|
|
|
④12345678
|
|
|
*/
|
|
|
export function isValidPhoneNumber(value) {
|
|
|
- const reg = /^1\d{10}$|^400[0-9]{7}|^(0\d{2,3}-?|\(0\d{2,3}\))?[1-9]\d{4,7}(-\d{1,8})?$/
|
|
|
+ const reg = /^1\d{10}$|^400[0-9]{7}|^(0\d{2,3}-?|\(0\d{2,3}\))?[1-9]\d{4,7}(-\d{1,8})?$/;
|
|
|
// const reg = /^400[0-9]{7}|^1[34578]\d{9}$|^0[0-9]{2,3}-[0-9]{8}/
|
|
|
|
|
|
- return reg.test(value)
|
|
|
+ return reg.test(value);
|
|
|
}
|
|
|
|
|
|
// 深拷贝,为了解决循环引用和共同引用的问题,引入了WeakMap,又因为引入WeakMap可能会导致被拷贝对象被挂上【作为WeakMap的探针的】匿名函数(是pollyfill的行为吧?),所以不会拷贝非根元素的匿名函数。
|
|
|
export function deepClone(target, hash = new WeakMap()) {
|
|
|
// 定义一个变量
|
|
|
- let result = null
|
|
|
+ let result = null;
|
|
|
// 如果当前需要深拷贝的是一个对象的话
|
|
|
- if (typeof target === 'object') {
|
|
|
- if (hash.has(target)) { // 如果是循环引用
|
|
|
- result = hash.get(target)
|
|
|
- } else if (Array.isArray(target)) { // 如果是一个数组的话
|
|
|
- result = [] // 将result赋值为一个数组,并且执行遍历
|
|
|
- hash.set(target, result)
|
|
|
+ if (typeof target === "object") {
|
|
|
+ if (hash.has(target)) {
|
|
|
+ // 如果是循环引用
|
|
|
+ result = hash.get(target);
|
|
|
+ } else if (Array.isArray(target)) {
|
|
|
+ // 如果是一个数组的话
|
|
|
+ result = []; // 将result赋值为一个数组,并且执行遍历
|
|
|
+ hash.set(target, result);
|
|
|
for (let i in target) {
|
|
|
- if (!(typeof (target[i]) === 'function' && !target.name)) {
|
|
|
+ if (!(typeof target[i] === "function" && !target.name)) {
|
|
|
// 递归克隆数组中的每一项
|
|
|
- result.push(deepClone(target[i], hash))
|
|
|
+ result.push(deepClone(target[i], hash));
|
|
|
}
|
|
|
}
|
|
|
// 判断如果当前的值是null的话;直接赋值为null
|
|
|
} else if (target === null) {
|
|
|
- result = null
|
|
|
+ result = null;
|
|
|
// 判断如果当前的值是一个RegExp对象的话,直接赋值
|
|
|
} else if (target.constructor === RegExp) {
|
|
|
- result = target
|
|
|
+ result = target;
|
|
|
} else {
|
|
|
// 否则是普通对象,直接for in循环,递归赋值对象的所有值
|
|
|
- result = {}
|
|
|
- hash.set(target, result)
|
|
|
+ result = {};
|
|
|
+ hash.set(target, result);
|
|
|
for (let i in target) {
|
|
|
- if (!(typeof (target[i]) === 'function' && !target.name)) {
|
|
|
- result[i] = deepClone(target[i], hash)
|
|
|
+ if (!(typeof target[i] === "function" && !target.name)) {
|
|
|
+ result[i] = deepClone(target[i], hash);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } else if (typeof target === 'function') {
|
|
|
- result = target
|
|
|
- } else { // 如果不是对象也不是函数,直接赋值
|
|
|
- result = target
|
|
|
+ } else if (typeof target === "function") {
|
|
|
+ result = target;
|
|
|
+ } else {
|
|
|
+ // 如果不是对象也不是函数,直接赋值
|
|
|
+ result = target;
|
|
|
}
|
|
|
// 返回最终结果
|
|
|
- return result
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
export function isObjectBroad(p) {
|
|
|
- return typeof (p) === 'object' || typeof (p) === 'function'
|
|
|
+ return typeof p === "object" || typeof p === "function";
|
|
|
}
|
|
|
|
|
|
// 判断两个对象内容是否相同。未考虑循环引用、共同引用的情况。
|
|
|
export function isSameObject(object1, object2) {
|
|
|
- const keys1 = Object.keys(object1)
|
|
|
- const keys2 = Object.keys(object2)
|
|
|
+ const keys1 = Object.keys(object1);
|
|
|
+ const keys2 = Object.keys(object2);
|
|
|
|
|
|
if (keys1.length !== keys2.length) {
|
|
|
- return false
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
for (let index = 0; index < keys1.length; index++) {
|
|
|
- const val1 = object1[keys1[index]]
|
|
|
- const val2 = object2[keys2[index]]
|
|
|
- const areObjects = isObjectBroad(val1) && isObjectBroad(val2)
|
|
|
- if (
|
|
|
- (areObjects && !isSameObject(val1, val2)) ||
|
|
|
- (!areObjects && (val1 !== val2))
|
|
|
- ) {
|
|
|
- return false
|
|
|
+ const val1 = object1[keys1[index]];
|
|
|
+ const val2 = object2[keys2[index]];
|
|
|
+ const areObjects = isObjectBroad(val1) && isObjectBroad(val2);
|
|
|
+ if ((areObjects && !isSameObject(val1, val2)) || (!areObjects && val1 !== val2)) {
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
- return true
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
export function ossImagePreviewUrlSuffix(downScaleRate = 10) {
|
|
|
- return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`
|
|
|
+ return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`;
|
|
|
}
|
|
|
|
|
|
export function postOrderTraversal(root, routine) {
|
|
|
if (root.children && Array.isArray(root.children)) {
|
|
|
for (const child of root.children) {
|
|
|
- postOrderTraversal(child, routine)
|
|
|
+ postOrderTraversal(child, routine);
|
|
|
}
|
|
|
}
|
|
|
- routine(root)
|
|
|
+ routine(root);
|
|
|
}
|
|
|
|
|
|
export function preOrderTraversalSearch(root, checkNode, targetNodePath) {
|
|
|
- console.assert(root && checkNode && targetNodePath, `param invalid: ${root} ${checkNode} ${targetNodePath}`)
|
|
|
- targetNodePath.push(root)
|
|
|
- let rootCheckRes = checkNode(root)
|
|
|
+ console.assert(root && checkNode && targetNodePath, `param invalid: ${root} ${checkNode} ${targetNodePath}`);
|
|
|
+ targetNodePath.push(root);
|
|
|
+ let rootCheckRes = checkNode(root);
|
|
|
if (rootCheckRes) {
|
|
|
- return true
|
|
|
+ return true;
|
|
|
}
|
|
|
if (root.children && Array.isArray(root.children)) {
|
|
|
for (const child of root.children) {
|
|
|
- let childCheckRes = preOrderTraversalSearch(child, checkNode, targetNodePath)
|
|
|
+ let childCheckRes = preOrderTraversalSearch(child, checkNode, targetNodePath);
|
|
|
if (childCheckRes) {
|
|
|
- return true
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- targetNodePath.pop()
|
|
|
- return false
|
|
|
+ targetNodePath.pop();
|
|
|
+ return false;
|
|
|
}
|
|
|
export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
|
|
|
- nodeIdList = nodeIdList.filter(i => i);
|
|
|
+ nodeIdList = nodeIdList.filter((i) => i);
|
|
|
if (nodeIdList.length === 0) {
|
|
|
- return null
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
// console.log('nodeIdList', nodeIdList)
|
|
@@ -242,27 +245,115 @@ export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
|
|
|
{
|
|
|
id: nodeTree.id,
|
|
|
name: nodeTree.name,
|
|
|
- }
|
|
|
- ]
|
|
|
+ },
|
|
|
+ ];
|
|
|
if (nodeIdList[1] || nodeIdList[1] === 0) {
|
|
|
// console.assert(nodeTree.children && nodeTree.children.length > 0, 'nodeIdList2nodeInfoListByNodeTree: 不可能的任务2!')
|
|
|
const nextLevelRoot = nodeTree.children.find((item) => {
|
|
|
- return item.id === nodeIdList[1]
|
|
|
- })
|
|
|
+ return item.id === nodeIdList[1];
|
|
|
+ });
|
|
|
// console.log('nextLevelRoot', nextLevelRoot)
|
|
|
// console.assert(nextLevelRoot, 'nodeIdList2nodeInfoListByNodeTree: invalid param 2!')
|
|
|
- ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot))
|
|
|
+ ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot));
|
|
|
}
|
|
|
|
|
|
- return ret
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
export function capitalize(str) {
|
|
|
if (!str) {
|
|
|
- return
|
|
|
+ return;
|
|
|
}
|
|
|
if (str.length === 1) {
|
|
|
- return str[0].toUpperCase()
|
|
|
+ return str[0].toUpperCase();
|
|
|
+ }
|
|
|
+ return str[0].toUpperCase() + str.slice(1, str.length);
|
|
|
+}
|
|
|
+
|
|
|
+export function getRandomSid() {
|
|
|
+ //5-7位随机字符串 + 6位时间 为热点准备
|
|
|
+ var pre = randomWord(true, 5, 7);
|
|
|
+ var post = new Date().getTime() + "";
|
|
|
+ var len = post.length;
|
|
|
+ post = post.substring(len - 8, len - 5) + post.substring(len - 3, len); //其实还是有可能重复的....
|
|
|
+ return pre + post;
|
|
|
+}
|
|
|
+function randomWord(randomFlag, min, max) {
|
|
|
+ //随机字符串
|
|
|
+ var str = "",
|
|
|
+ range = min,
|
|
|
+ arr = [
|
|
|
+ "0",
|
|
|
+ "1",
|
|
|
+ "2",
|
|
|
+ "3",
|
|
|
+ "4",
|
|
|
+ "5",
|
|
|
+ "6",
|
|
|
+ "7",
|
|
|
+ "8",
|
|
|
+ "9",
|
|
|
+ "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",
|
|
|
+ "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",
|
|
|
+ ];
|
|
|
+
|
|
|
+ if (randomFlag) {
|
|
|
+ // 随机长度
|
|
|
+ range = Math.round(Math.random() * (max - min)) + min;
|
|
|
}
|
|
|
- return str[0].toUpperCase() + str.slice(1, str.length)
|
|
|
-}
|
|
|
+ for (var i = 0; i < range; i++) {
|
|
|
+ var pos = Math.round(Math.random() * (arr.length - 1));
|
|
|
+ str += arr[pos];
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+}
|