index.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
  2. import type { App, Component } from 'vue'
  3. import { intersectionWith, isEqual, mergeWith, unionWith } from 'lodash-es'
  4. import { unref } from 'vue'
  5. import { isArray, isObject } from '@/utils/is'
  6. export function noop() {}
  7. /**
  8. * @description: Set ui mount node
  9. */
  10. export function getPopupContainer(node?: HTMLElement): HTMLElement {
  11. return (node?.parentNode as HTMLElement) ?? document.body
  12. }
  13. /**
  14. * Add the object as a parameter to the URL
  15. * @param baseUrl url
  16. * @param obj
  17. * @returns {string}
  18. * eg:
  19. * let obj = {a: '3', b: '4'}
  20. * setObjToUrlParams('www.baidu.com', obj)
  21. * ==>www.baidu.com?a=3&b=4
  22. */
  23. export function setObjToUrlParams(baseUrl: string, obj: any): string {
  24. let parameters = ''
  25. for (const key in obj)
  26. parameters += `${key}=${encodeURIComponent(obj[key])}&`
  27. parameters = parameters.replace(/&$/, '')
  28. return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters
  29. }
  30. /**
  31. * Recursively merge two objects.
  32. * 递归合并两个对象。
  33. *
  34. * @param source The source object to merge from. 要合并的源对象。
  35. * @param target The target object to merge into. 目标对象,合并后结果存放于此。
  36. * @param mergeArrays How to merge arrays. Default is "replace".
  37. * 如何合并数组。默认为replace。
  38. * - "union": Union the arrays. 对数组执行并集操作。
  39. * - "intersection": Intersect the arrays. 对数组执行交集操作。
  40. * - "concat": Concatenate the arrays. 连接数组。
  41. * - "replace": Replace the source array with the target array. 用目标数组替换源数组。
  42. * @returns The merged object. 合并后的对象。
  43. */
  44. export function deepMerge<T extends object | null | undefined, U extends object | null | undefined>(
  45. source: T,
  46. target: U,
  47. mergeArrays: 'union' | 'intersection' | 'concat' | 'replace' = 'replace',
  48. ): T & U {
  49. if (!target)
  50. return source as T & U
  51. if (!source)
  52. return target as T & U
  53. return mergeWith({}, source, target, (sourceValue, targetValue) => {
  54. if (isArray(targetValue) && isArray(sourceValue)) {
  55. switch (mergeArrays) {
  56. case 'union':
  57. return unionWith(sourceValue, targetValue, isEqual)
  58. case 'intersection':
  59. return intersectionWith(sourceValue, targetValue, isEqual)
  60. case 'concat':
  61. return sourceValue.concat(targetValue)
  62. case 'replace':
  63. return targetValue
  64. default:
  65. throw new Error(`Unknown merge array strategy: ${mergeArrays as string}`)
  66. }
  67. }
  68. if (isObject(targetValue) && isObject(sourceValue))
  69. return deepMerge(sourceValue, targetValue, mergeArrays)
  70. return undefined
  71. })
  72. }
  73. export function openWindow(
  74. url: string,
  75. opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean },
  76. ) {
  77. const { target = '__blank', noopener = true, noreferrer = true } = opt || {}
  78. const feature: string[] = []
  79. noopener && feature.push('noopener=yes')
  80. noreferrer && feature.push('noreferrer=yes')
  81. window.open(url, target, feature.join(','))
  82. }
  83. // dynamic use hook props
  84. export function getDynamicProps<T extends Record<string, unknown>, U>(props: T): Partial<U> {
  85. const ret: Recordable = {}
  86. // eslint-disable-next-line array-callback-return
  87. Object.keys(props).map((key) => {
  88. ret[key] = unref((props as Recordable)[key])
  89. })
  90. return ret as Partial<U>
  91. }
  92. export function getRawRoute(route: RouteLocationNormalized): RouteLocationNormalized {
  93. if (!route)
  94. return route
  95. const { matched, ...opt } = route
  96. return {
  97. ...opt,
  98. matched: (matched
  99. ? matched.map(item => ({
  100. meta: item.meta,
  101. name: item.name,
  102. path: item.path,
  103. }))
  104. : undefined) as RouteRecordNormalized[],
  105. }
  106. }
  107. // https://github.com/vant-ui/vant/issues/8302
  108. interface EventShim {
  109. new (...args: any[]): {
  110. $props: {
  111. onClick?: (...args: any[]) => void
  112. }
  113. }
  114. }
  115. export type WithInstall<T> = T & {
  116. install(app: App): void
  117. } & EventShim
  118. export type CustomComponent = Component & { displayName?: string }
  119. export function withInstall<T extends CustomComponent>(component: T, alias?: string) {
  120. (component as Record<string, unknown>).install = (app: App) => {
  121. const compName = component.name || component.displayName
  122. if (!compName)
  123. return
  124. app.component(compName, component)
  125. if (alias)
  126. app.config.globalProperties[alias] = component
  127. }
  128. return component as WithInstall<T>
  129. }
  130. /**
  131. * 简单实现防抖方法
  132. *
  133. * 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
  134. * 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
  135. * 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
  136. *
  137. * @param fn 要防抖的函数
  138. * @param delay 防抖的毫秒数
  139. * @returns {Function} simpleDebounce
  140. */
  141. export function simpleDebounce(fn, delay = 100) {
  142. let timer: any | null = null
  143. return () => {
  144. // eslint-disable-next-line prefer-rest-params
  145. const args = arguments
  146. if (timer)
  147. clearTimeout(timer)
  148. timer = setTimeout(() => {
  149. // eslint-disable-next-line ts/no-invalid-this
  150. fn.apply(this, args)
  151. }, delay)
  152. }
  153. }