other.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /**
  2. * 返回一个自带节流效果的函数,用res表示。
  3. *
  4. * fn:需要被节流的函数
  5. * interval:最短多长时间允许执行一次fn
  6. *
  7. * 功能要点:
  8. * 1.fn代码里如有this,res被执行时,this会指向res的调用者;
  9. * 2.res被执行时的实参会映射到fn的形参;
  10. * 3.第一次调用res时,会立即执行fn。
  11. */
  12. export function throttle(fn, interval) {
  13. let lastRunTime = 0
  14. return function (...args) {
  15. let elapsedTime = Date.now() - lastRunTime
  16. if (elapsedTime < interval) {
  17. return
  18. }
  19. let context = this
  20. lastRunTime = Date.now()
  21. fn.apply(context, args)
  22. }
  23. }
  24. /**
  25. * 返回一个自带消抖效果的函数,下文用fnDebounced表示。
  26. *
  27. * fn: 需要被消抖的函数
  28. * delay: 消抖时长
  29. * isImmediateCall: 是否在一组操作中的第一次调用时立即执行fn
  30. * isRememberLastCall:是否在一组中最后一次调用后等delay时长再执行fn
  31. *
  32. * 如果isRememberLastCall为false,意味着fn不会被延迟执行,所以fnDebounced执行时,要么在内部调用fn,同步返回fn返回值;要么内部决定本次不调用fn,同步返回null。
  33. * 如果isRememberLastCall为true,意味着fn可能被延迟执行,所以fnDebounced会返回一个Promise,在fn被调用时用其返回值resolve该Promise,或者在fn的延时调用计划被取消时用'canceled'resolve该Promise。(不宜reject,否则又没有人去catch,会导致浏览器报错。)
  34. */
  35. export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLastCall = true) {
  36. console.assert(isImmediateCall || isRememberLastCall, 'isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!')
  37. let timer = null
  38. let retPromiseLastTimeResolver = null
  39. // 上次调用的时刻
  40. let lastCallTime = 0
  41. if (isImmediateCall && !isRememberLastCall) {
  42. return function (...args) {
  43. let ret = null
  44. const currentTime = Date.now()
  45. if (currentTime - lastCallTime >= delay) {
  46. ret = fn.apply(this, args)
  47. }
  48. lastCallTime = currentTime
  49. return ret
  50. }
  51. } else if (!isImmediateCall && isRememberLastCall) {
  52. return function (...args) {
  53. if (timer) {
  54. clearTimeout(timer)
  55. timer = null
  56. }
  57. if (retPromiseLastTimeResolver) {
  58. retPromiseLastTimeResolver('canceled')
  59. retPromiseLastTimeResolver = null
  60. }
  61. const ret = new Promise((resolve, reject) => {
  62. retPromiseLastTimeResolver = resolve
  63. timer = setTimeout(() => {
  64. timer = null
  65. retPromiseLastTimeResolver = null
  66. resolve(fn.apply(this, args))
  67. }, delay)
  68. })
  69. return ret
  70. }
  71. } else if (isImmediateCall && isRememberLastCall) {
  72. return function (...args) {
  73. const currentTime = Date.now()
  74. if (currentTime - lastCallTime >= delay) { // 一组操作中的第一次
  75. const res = fn.apply(this, args)
  76. lastCallTime = currentTime
  77. return Promise.resolve(res)
  78. } else { // 一组中的后续调用
  79. if (timer) { // 在此之前存在中间调用
  80. lastCallTime = currentTime
  81. clearTimeout(timer)
  82. timer = null
  83. }
  84. if (retPromiseLastTimeResolver) {
  85. retPromiseLastTimeResolver('canceled')
  86. retPromiseLastTimeResolver = null
  87. }
  88. const ret = new Promise((resolve, reject) => {
  89. retPromiseLastTimeResolver = resolve
  90. timer = setTimeout(() => {
  91. lastCallTime = 0
  92. timer = null
  93. retPromiseLastTimeResolver = null
  94. resolve(fn.apply(this, args))
  95. }, delay)
  96. })
  97. return ret
  98. }
  99. }
  100. } else {
  101. console.error('不应该执行到这里!')
  102. }
  103. }
  104. /**
  105. 同时验证⼿机号码和固定电话号码(带区号或不带区号或带分机号)
  106. 规则说明:
  107. 1、可以是1开头的11位数字(⼿机号)
  108. 2、可以是“区号-电话号-分机号”或者是“(区号)电话号-分机号”格式
  109. 3、区号是0开头的3~4位数字,可以没有区号
  110. 4、电话号是5~8位数字,不能以0开头
  111. 5、分机号是1~8位数字,可以没有分机号
  112. 合法数据⽰例:
  113. ①13812341234
  114. ②010-12345678
  115. ③(0432)1234567-1234
  116. ④12345678
  117. */
  118. export function isValidPhoneNumber(value) {
  119. 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})?$/
  120. // const reg = /^400[0-9]{7}|^1[34578]\d{9}$|^0[0-9]{2,3}-[0-9]{8}/
  121. return reg.test(value)
  122. }
  123. // 深拷贝,为了解决循环引用和共同引用的问题,引入了WeakMap,又因为引入WeakMap可能会导致被拷贝对象被挂上【作为WeakMap的探针的】匿名函数(是pollyfill的行为吧?),所以不会拷贝非根元素的匿名函数。
  124. export function deepClone(target, hash = new WeakMap()) {
  125. // 定义一个变量
  126. let result = null
  127. // 如果当前需要深拷贝的是一个对象的话
  128. if (typeof target === 'object') {
  129. if (hash.has(target)) { // 如果是循环引用
  130. result = hash.get(target)
  131. } else if (Array.isArray(target)) { // 如果是一个数组的话
  132. result = [] // 将result赋值为一个数组,并且执行遍历
  133. hash.set(target, result)
  134. for (let i in target) {
  135. if (!(typeof(target[i]) === 'function' && !target.name)) {
  136. // 递归克隆数组中的每一项
  137. result.push(deepClone(target[i], hash))
  138. }
  139. }
  140. // 判断如果当前的值是null的话;直接赋值为null
  141. } else if (target === null) {
  142. result = null
  143. // 判断如果当前的值是一个RegExp对象的话,直接赋值
  144. } else if (target.constructor === RegExp) {
  145. result = target
  146. } else {
  147. // 否则是普通对象,直接for in循环,递归赋值对象的所有值
  148. result = {}
  149. hash.set(target, result)
  150. for (let i in target) {
  151. if (!(typeof(target[i]) === 'function' && !target.name)) {
  152. result[i] = deepClone(target[i], hash)
  153. }
  154. }
  155. }
  156. } else if (typeof target === 'function') {
  157. result = target
  158. } else { // 如果不是对象也不是函数,直接赋值
  159. result = target
  160. }
  161. // 返回最终结果
  162. return result
  163. }
  164. export function isObjectBroad(p) {
  165. return typeof(p) === 'object' || typeof(p) === 'function'
  166. }
  167. // 判断两个对象内容是否相同。未考虑循环引用、共同引用的情况。
  168. export function isSameObject(object1, object2) {
  169. const keys1 = Object.keys(object1)
  170. const keys2 = Object.keys(object2)
  171. if (keys1.length !== keys2.length) {
  172. return false
  173. }
  174. for (let index = 0; index < keys1.length; index++) {
  175. const val1 = object1[keys1[index]]
  176. const val2 = object2[keys2[index]]
  177. const areObjects = isObjectBroad(val1) && isObjectBroad(val2)
  178. if (
  179. (areObjects && !isSameObject(val1, val2)) ||
  180. (!areObjects && (val1 !== val2))
  181. ) {
  182. return false
  183. }
  184. }
  185. return true
  186. }
  187. export function ossImagePreviewUrlSuffix(downScaleRate = 10) {
  188. return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`
  189. }
  190. export function postOrderTraversal(root, routine) {
  191. if (root.children && Array.isArray(root.children)) {
  192. for (const child of root.children) {
  193. postOrderTraversal(child, routine)
  194. }
  195. }
  196. routine(root)
  197. }
  198. export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
  199. console.assert(nodeIdList && nodeTree && nodeTree.id && nodeTree.name, 'nodeIdList2nodeInfoListByNodeTree: invalid param!')
  200. if (nodeIdList.length === 0) {
  201. return null
  202. }
  203. console.assert(nodeTree.id === nodeIdList[0], 'nodeIdList2nodeInfoListByNodeTree: 不可能的任务!')
  204. let ret = [
  205. {
  206. id: nodeTree.id,
  207. name: nodeTree.name,
  208. }
  209. ]
  210. if (nodeIdList[1] || nodeIdList[1] === 0) {
  211. console.assert(nodeTree.children && nodeTree.children.length > 0, 'nodeIdList2nodeInfoListByNodeTree: 不可能的任务2!')
  212. const nextLevelRoot = nodeTree.children.find((item) => {
  213. return item.id === nodeIdList[1]
  214. })
  215. console.assert(nextLevelRoot, 'nodeIdList2nodeInfoListByNodeTree: invalid param 2!')
  216. ret = ret.concat(nodeIdList2nodeInfoListByNodeTree(nodeIdList.slice(1, nodeIdList.length), nextLevelRoot))
  217. }
  218. return ret
  219. }
  220. export function capitalize(str) {
  221. if (!str) {
  222. return
  223. }
  224. if (str.length === 1) {
  225. return str[0].toUpperCase()
  226. }
  227. return str[0].toUpperCase() + str.slice(1, str.length)
  228. }