util.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /**
  2. * 常用函数类Util
  3. */
  4. import { ref, computed } from 'vue'
  5. export function formatCls(props, prefix, prefixCls) {
  6. const mergeClass = computed(() => {
  7. let mergeCls = ref([])
  8. let preCls
  9. for(let key in props) {
  10. if(isArray(prefixCls) && prefixCls.includes(key) && props[key]) {
  11. if(isBool(props[key])) {
  12. if(JSON.parse(props[key])) {
  13. preCls = `${prefix}${key}`
  14. }
  15. }else {
  16. preCls = `${prefix}${props[key]}`
  17. }
  18. // 去重
  19. const index = mergeCls.value.findIndex(v => v == preCls)
  20. if(index == -1) {
  21. mergeCls.value.push(preCls)
  22. }else {
  23. mergeCls.value.splice(index, 1)
  24. }
  25. }
  26. }
  27. return [...mergeCls.value].join(' ')
  28. })
  29. return mergeClass
  30. }
  31. export function Int(v) {
  32. return parseInt(v)
  33. }
  34. export function isBool(str) {
  35. return /^true|false$/i.test(str)
  36. }
  37. export function isTrue(str) {
  38. return /^true$/i.test(str)
  39. }
  40. export function isString(val) {
  41. const type = typeof val
  42. return type === 'string' || (type === 'object' && val != null && !Array.isArray(val) && Object.prototype.toString.call(val) === '[object String]')
  43. }
  44. export function isNumber(val) {
  45. return !isNaN(parseFloat(val)) && isFinite(val)
  46. }
  47. export function isFunction(str) {
  48. return typeof str == 'function'
  49. }
  50. export function isArray(str) {
  51. return isObject(str) || Array.isArray(str)
  52. }
  53. export function isObject(str) {
  54. return str != null && typeof str == 'object'
  55. }
  56. export function isPromise(str) {
  57. return isObject(str) && isFunction(str.then) && isFunction(str.catch)
  58. }
  59. export function isEmpty(val) {
  60. if(val == null) return true
  61. if(typeof val == 'boolean') return false
  62. if(typeof val == 'number') return !isNumber(val)
  63. if(val instanceof Error) return val.message == ''
  64. switch(Object.prototype.toString.call(val)) {
  65. // String or Array
  66. case '[object String]':
  67. case '[object Array]':
  68. return !val.length
  69. // Map or Set or File
  70. case '[object File]':
  71. case '[object Map]':
  72. case '[object Set]':
  73. return !val.size
  74. // Plain Object
  75. case '[object Object]':
  76. return !Object.keys(val).length;
  77. }
  78. return false
  79. }
  80. export function convert(str) {
  81. const c = str.charAt(0)
  82. return c.toUpperCase() + str.replace(c, '')
  83. }
  84. export function getScroll(type) {
  85. const _s = convert(type)
  86. return document.documentElement['scroll' + _s] || document.body['scroll' + _s]
  87. }
  88. export function getClient(type) {
  89. const _s = convert(type)
  90. return document.documentElement['client' + _s] || document.body['client' + _s]
  91. }
  92. export function getOffset(el, type) {
  93. if(!el || !type) return
  94. const _s = convert(type)
  95. return el['offset' + _s]
  96. }
  97. export function getStyle(el, style) {
  98. if(!el || !style) return
  99. return el.currentStyle ? el.currentStyle[style] : document.defaultView.getComputedStyle(el, null)[style]
  100. }
  101. // 获取弹窗最大层级
  102. export function getZIndex(zIndex) {
  103. for(var zIdx = parseInt(zIndex), el = document.getElementsByTagName('*'), i = 0, len = el.length; i < len; i++)
  104. zIdx = Math.max(zIdx, el[i].style.zIndex)
  105. return zIdx
  106. }
  107. export function hasLenUnit(value) {
  108. if(!value) return false
  109. if(typeof value == 'boolean') return false
  110. if(!isNaN(value)) return true
  111. if(/\%|px|rem/.test(value)) return true
  112. return false
  113. }
  114. export function addUnit(value, defaultUnit = 'px') {
  115. if(!value && value != 0) return ''
  116. if(/\%|px|rem/.test(value)) {
  117. return value
  118. }else {
  119. return `${value}${defaultUnit}`
  120. }
  121. }
  122. // 对象上是否存在某个属性
  123. export function hasOwn(options, prop) {
  124. const hasOwnProperty = Object.prototype.hasOwnProperty
  125. return hasOwnProperty.call(options, prop)
  126. }
  127. /**
  128. * 防抖原理(一定时间只有最后一次操作被执行)
  129. * 应用于:搜索框、商品下单支付
  130. */
  131. export function debounce(fn, wait = 500) {
  132. let timer = null
  133. return function() {
  134. clearTimeout(timer)
  135. timer = setTimeout(() => {
  136. fn.apply(this, arguments)
  137. }, wait)
  138. }
  139. }
  140. /**
  141. * 节流原理(一定时间内只触发一次)
  142. * 应用于:鼠标频繁点击、监听resize|scroll及底部加载更多
  143. */
  144. export function throttle(fn, wait = 500, immediate = true) {
  145. /*let lastTime = 0
  146. return function() {
  147. let nowTime = new Date().getTime()
  148. if(nowTime - lastTime > wait) {
  149. fn.apply(this, arguments)
  150. lastTime = nowTime
  151. }
  152. }*/
  153. let flag = false
  154. return function() {
  155. if(flag) return
  156. flag = true
  157. setTimeout(() => {
  158. fn.apply(this, arguments)
  159. flag = false
  160. }, wait)
  161. }
  162. }
  163. /**
  164. * 事件处理
  165. */
  166. export const listener = {
  167. on: function(el, event, handle) {
  168. el.attachEvent ? el.attachEvent('on' + event, handle) : el.addEventListener(event, handle, false)
  169. },
  170. off: function(el, event, handle) {
  171. el.detachEvent ? el.detachEvent('on' + event, handle) : el.removeEventListener(event, handle, false)
  172. }
  173. }
  174. /**
  175. * 深拷贝
  176. * @param target 原对象
  177. * @param map 克隆容器map
  178. */
  179. export function deepClone(target, map = new Map()) {
  180. if(typeof target == 'object' && target != null) {
  181. let cache = map.get(target)
  182. if(cache) return cache
  183. let isArray = Array.isArray(target)
  184. const result = isArray ? [] : {}
  185. map.set(target, result)
  186. if(isArray) {
  187. // 数组
  188. target.forEach((item, index) => {
  189. result[index] = deepClone(item, map)
  190. })
  191. }else {
  192. // 对象
  193. Object.keys(target).forEach(key => {
  194. result[key] = deepClone(target[key], map)
  195. })
  196. }
  197. return result
  198. }else {
  199. return target
  200. }
  201. }
  202. export function objectAssign(target) {
  203. for(let i = 1, j = arguments.length; i < j; i++) {
  204. let source = arguments[i] || {}
  205. for(let prop in source) {
  206. if(source.hasOwnProperty(prop)) {
  207. let val = source[prop]
  208. if(val != undefined) {
  209. target[prop] = val
  210. }
  211. }
  212. }
  213. }
  214. return target
  215. }
  216. export function isIE() {
  217. return !!window.ActiveXObject || 'ActiveXObject' in window
  218. }
  219. /**
  220. * 获取系统滚动条宽度
  221. */
  222. export function getScrollBarSize() {
  223. let scrollBarWidth
  224. if(scrollBarWidth !== undefined) return scrollBarWidth
  225. const outer = document.createElement('div')
  226. outer.style.visibility = 'hidden'
  227. outer.style.width = '100px'
  228. outer.style.position = 'absolute'
  229. outer.style.top = '-9999px'
  230. document.body.appendChild(outer)
  231. const withNoScroll = outer.offsetWidth
  232. outer.style.overflow = 'scroll'
  233. const inner = document.createElement('div')
  234. inner.style.width = '100%'
  235. outer.appendChild(inner)
  236. const withScroll = inner.offsetWidth
  237. outer.parentNode.removeChild(outer)
  238. scrollBarWidth = withNoScroll - withScroll
  239. return scrollBarWidth
  240. }
  241. /**
  242. * 获取元素坐标点
  243. */
  244. export function getBoundingClientRect(el) {
  245. let rect = el.getBoundingClientRect()
  246. return {
  247. left: rect.left,
  248. top: rect.top,
  249. right: rect.right,
  250. bottom: rect.bottom,
  251. width: rect.right - rect.left,
  252. height: rect.bottom - rect.top
  253. }
  254. }
  255. /**
  256. * 获取跟随定位坐标
  257. */
  258. export function getFollowPos(follow, placement, ow, oh, gap) {
  259. let followOffset = {}
  260. let ftype = typeof follow
  261. let wW = getClient('width')
  262. let wH = getClient('height')
  263. let reference, referenceRect
  264. let basePlacement = placement.split('-')[0]
  265. let prefixPlacement = placement.split('-')[1]
  266. // 定位元素(#xxx | .xxx)
  267. if(ftype == 'string') {
  268. reference = document.querySelector(follow)
  269. referenceRect = getBoundingClientRect(reference)
  270. followOffset.direction = basePlacement
  271. // top/right/bottom/left
  272. if(['right', 'left'].indexOf(basePlacement) != -1) {
  273. followOffset.top = referenceRect.top + referenceRect.height / 2 - oh / 2
  274. if(basePlacement == 'left') {
  275. followOffset.left = referenceRect.left - ow - gap
  276. if(followOffset.left < 0) {
  277. followOffset.left = referenceRect.right + gap
  278. followOffset.direction = 'right'
  279. }
  280. }else {
  281. followOffset.left = referenceRect.right + gap
  282. if(referenceRect.right + ow > wW) {
  283. followOffset.left = referenceRect.left - ow - gap
  284. followOffset.direction = 'left'
  285. }
  286. }
  287. }else {
  288. followOffset.left = referenceRect.left + referenceRect.width / 2 - ow / 2
  289. if(basePlacement == 'top') {
  290. followOffset.top = referenceRect.top - oh - gap
  291. if(followOffset.top < 0) {
  292. followOffset.top = referenceRect.bottom + gap
  293. followOffset.direction = 'bottom'
  294. }
  295. }else {
  296. followOffset.top = referenceRect.bottom + gap
  297. if(referenceRect.bottom + oh > wH) {
  298. followOffset.top = referenceRect.top - oh - gap
  299. followOffset.direction = 'top'
  300. }
  301. }
  302. }
  303. // top-start|top-end|right-start|right-end|bottom-start|bottom-end|left-start|left-end
  304. if(prefixPlacement) {
  305. let prefixOffset = {
  306. y: {
  307. start: {
  308. top: referenceRect.top
  309. },
  310. end: {
  311. top: referenceRect.top + referenceRect.height - oh
  312. }
  313. },
  314. x: {
  315. start: {
  316. left: referenceRect.left
  317. },
  318. end: {
  319. left: referenceRect.left + referenceRect.width - ow
  320. }
  321. }
  322. }
  323. let axis = ['bottom', 'top'].indexOf(basePlacement) != -1 ? 'x' : 'y'
  324. followOffset = Object.assign(followOffset, prefixOffset[axis][prefixPlacement])
  325. }
  326. followOffset.left += getScroll('left')
  327. followOffset.top += getScroll('top')
  328. }
  329. // 定位元素(坐标点clientX|clientY)
  330. if(ftype == 'object') {
  331. let l = follow[0]
  332. let t = follow[1]
  333. let sL = l + getScroll('left')
  334. let sT = t + getScroll('top')
  335. let left = (l + ow) > wW ? (sL - ow) : sL
  336. let top = (t + oh) > wH ? (sT - oh) : sT
  337. followOffset.left = left
  338. followOffset.top = top
  339. }
  340. return followOffset
  341. }
  342. /**
  343. * 下载图片流
  344. */
  345. export function downloadBlob(url, name) {
  346. let img = new Image()
  347. img.setAttribute('crossOrigin', 'anonymous')
  348. img.src = url
  349. img.onload = () => {
  350. let canvas = document.createElement('canvas')
  351. canvas.width = img.width
  352. canvas.height = img.height
  353. let ctx = canvas.getContext('2d')
  354. ctx.drawImage(img, 0, 0, img.width, img.height)
  355. canvas.toBlob(blob => {
  356. let url = URL.createObjectURL(blob)
  357. download(url, name)
  358. // 释放URL对象
  359. URL.revokeObjectURL(url)
  360. })
  361. }
  362. }
  363. // 下载
  364. export function download(url, name) {
  365. let a = document.createElement('a')
  366. a.setAttribute('target', '_blank')
  367. a.href = url
  368. a.download = name
  369. a.click()
  370. a.remove()
  371. }