util.js 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. /*
  2. * @Author: WangQiBiao
  3. * @LastEditors: LiZhiWei
  4. * @Description:
  5. * @Date: 2019-04-16 11:44:25
  6. * @LastEditTime: 2025-04-15 15:44:32
  7. */
  8. import html2canvas from 'html2canvas'
  9. import { saveAs } from 'file-saver'
  10. import Cookies from 'js-cookie'
  11. // cookie保存的天数
  12. import config from '@/config'
  13. import { hasOneOf, objEqual } from './tools'
  14. // import beforeClose from '@/router/before-close'
  15. export * from './file'
  16. export const TOKEN_KEY = 'token'
  17. // 缓存表格列宽
  18. export const setTableHeaderDragend = (name, newWidth, column) => {
  19. const tableHeaderData = JSON.parse(window.localStorage.getItem(name)) || []
  20. if (tableHeaderData.length) {
  21. if (tableHeaderData.some(h => h.property === column.property)) {
  22. tableHeaderData[tableHeaderData.findIndex(i => i.property === column.property)].width = newWidth
  23. } else {
  24. tableHeaderData.push({ property: column.property, width: newWidth })
  25. }
  26. } else {
  27. tableHeaderData.push({ property: column.property, width: newWidth })
  28. }
  29. window.localStorage.setItem(name, JSON.stringify(tableHeaderData))
  30. }
  31. // 获取表格列宽
  32. export const getTableHeaderDragend = (name) => {
  33. return JSON.parse(window.localStorage.getItem(name)) || []
  34. }
  35. export const getTableColumnWidth = (name, property) => {
  36. const tableHeaderData = JSON.parse(window.localStorage.getItem(name)) || []
  37. if (tableHeaderData.some(h => h.property === property)) {
  38. return tableHeaderData[tableHeaderData.findIndex(i => i.property === property)].width
  39. } else {
  40. return 150
  41. }
  42. }
  43. export const setToken = token => {
  44. Cookies.set(TOKEN_KEY, token, { expires: config.cookieExpires || 1 })
  45. }
  46. export const removeToken = () => {
  47. Cookies.remove(TOKEN_KEY)
  48. }
  49. export const getToken = () => {
  50. const token = Cookies.get(TOKEN_KEY)
  51. if (token) return token
  52. else return false
  53. }
  54. export const getCookie = (key) => {
  55. return Cookies.get(key)
  56. }
  57. export const removeCookie = (key, options = {}) => {
  58. Cookies.remove(key, options)
  59. }
  60. export const removeAllCookie = () => {
  61. Object.keys(Cookies.get()).forEach(key => Cookies.remove(key))
  62. }
  63. export const hasChild = item => {
  64. return item.children && item.children.length !== 0
  65. }
  66. const showThisMenuEle = (item, access) => {
  67. if (item.meta && item.meta.access && item.meta.access.length) {
  68. if (hasOneOf(item.meta.access, access)) return true
  69. else return false
  70. } else return true
  71. }
  72. /**
  73. * 根据接口返回的路由配置权限路由
  74. * @param {Array} asyncRouter 本地路由
  75. * @param {Array} menus 接口返回的经过格式化的路由
  76. * @returns newMenu 新的权限路由
  77. */
  78. export const asyncRouterMap = (asyncRouter = [], menus = []) => {
  79. const newMenu = []
  80. asyncRouter.forEach(route => {
  81. if (route.name && (route && route.meta && (route.meta.hideInMenu || route.meta.authorized))) {
  82. // 对于一级路由设置隐藏且没有对应菜单的路由
  83. newMenu.push(route)
  84. } else {
  85. menus.forEach(menu => {
  86. if (route.name === menu.name && menu.meta.type !== 'button') {
  87. if (route.children && (menu.children && menu.children.length > 0)) {
  88. // 经过和菜单比较后得到的菜单路由
  89. const compareChild = route.children.filter(item => {
  90. const mChild = menu.children.find(mChild => mChild.name === item.name)
  91. if (mChild) {
  92. return item
  93. } else if (item && item.meta && (item.meta.hideInMenu || item.meta.authorized)) {
  94. return item
  95. }
  96. })
  97. let newRoute = {}
  98. const { children, ...reset } = route
  99. newRoute = reset
  100. newRoute.children = compareChild
  101. newMenu.push(newRoute)
  102. } else {
  103. // 只有一级路由,没有二级路由
  104. const { redirect } = route
  105. newMenu.push({ ...menu, redirect })
  106. }
  107. }
  108. })
  109. }
  110. })
  111. return newMenu
  112. }
  113. /**
  114. * @param {Array} list 通过路由列表得到菜单列表
  115. * @returns {Array}
  116. */
  117. export const getMenuByRouter = (list = [], access) => {
  118. let res = []
  119. const getMenus = (l, a) => {
  120. l.forEach(item => {
  121. if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
  122. let obj = {
  123. icon: (item.meta && item.meta.icon) || '',
  124. name: item.path.replace(/\//, '') || '_', // 过滤 '/'
  125. meta: item.meta,
  126. path: item.path
  127. }
  128. if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, a)) {
  129. obj.children = getMenuByRouter(item.children, a)
  130. }
  131. if (item.meta && item.meta.href) obj.href = item.meta.href
  132. if (showThisMenuEle(item, a)) res.push(obj)
  133. }
  134. })
  135. }
  136. getMenus(list, access)
  137. return res
  138. }
  139. /**
  140. * 根据权限匹配路由
  141. * @param {Array} permission 权限路由
  142. * @param {Array} asyncRouter 异步路由
  143. */
  144. export const routerByMatch = (permission, asyncRouter) => {
  145. permission = JSON.parse(permission)
  146. // 菜单路由表
  147. const router = []
  148. function createRouter (permission) {
  149. // 根据路由名称匹配到router对象添加到router中
  150. permission && permission.forEach(item => {
  151. if (item.children && item.children.length) {
  152. createRouter(item.children)
  153. }
  154. let routerName = item.route
  155. // 过滤路由名称为null的情况
  156. if (routerName) {
  157. // 循环异步路由,将符合权限列表路由加入路由表中
  158. asyncRouter.find(s => {
  159. if (s.name === routerName) {
  160. router.push(s)
  161. }
  162. })
  163. }
  164. })
  165. }
  166. createRouter(permission)
  167. return router
  168. }
  169. /**
  170. * @param {Array} routeMetched 当前路由metched
  171. * @returns {Array}
  172. */
  173. export const getBreadCrumbList = (route) => {
  174. let routeMetched = route.matched
  175. let res = routeMetched
  176. .filter(item => {
  177. return item.meta === undefined || !item.meta.hide
  178. })
  179. .map(item => {
  180. let meta = { ...item.meta }
  181. if (meta.title && typeof meta.title === 'function') meta.title = meta.title(route)
  182. let obj = {
  183. icon: (item.meta && item.meta.icon) || '',
  184. name: item.name,
  185. meta: meta
  186. }
  187. return obj
  188. })
  189. res = res.filter(item => {
  190. return !item.meta.hideInMenu
  191. })
  192. // return [Object.assign(homeRoute, { to: homeRoute.path }), ...res]
  193. return res
  194. }
  195. export const getRouteTitleHandled = route => {
  196. let router = { ...route }
  197. let meta = { ...route.meta }
  198. if (meta.title && typeof meta.title === 'function') meta.title = meta.title(router)
  199. router.meta = meta
  200. return router
  201. }
  202. export const showTitle = (item, vm) => {
  203. return vm.$config.useI18n ? vm.$t(item.name) : (item.meta && item.meta.title) || item.name
  204. }
  205. /**
  206. * @description 本地存储和获取标签导航列表
  207. */
  208. export const setTagNavListInLocalstorage = list => {
  209. localStorage.tagNavList = JSON.stringify(list)
  210. }
  211. /**
  212. * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项
  213. */
  214. export const getTagNavListFromLocalstorage = () => {
  215. const list = localStorage.tagNavList
  216. return list ? JSON.parse(list) : []
  217. }
  218. /**
  219. * @param {Array} routers 路由列表数组
  220. * @description 用于找到路由列表中name为home的对象
  221. */
  222. export const getHomeRoute = routers => {
  223. if (!routers) {
  224. return
  225. }
  226. let i = -1
  227. let len = routers.length
  228. let homeRoute = {}
  229. while (++i < len) {
  230. let item = routers[i]
  231. if (item.children && item.children.length) {
  232. let res = getHomeRoute(item.children)
  233. if (res.name) return res
  234. } else {
  235. // if (item.name === 'home') homeRoute = item
  236. // 目前没有主页、判断为第一个路由、后期加上主页 把这行去掉 取消上行注释
  237. if (item.name === routers[0].name) homeRoute = item
  238. }
  239. }
  240. return homeRoute
  241. }
  242. /**
  243. * @param {*} list 现有标签导航列表
  244. * @param {*} newRoute 新添加的路由原信息对象
  245. * @description 如果该newRoute已经存在则不再添加
  246. */
  247. export const getNewTagList = (list, newRoute) => {
  248. const { name, path, meta } = newRoute
  249. let newList = [...list]
  250. if (newList.findIndex(item => item.name === name) >= 0) return newList
  251. else newList.push({ name, path, meta })
  252. return newList
  253. }
  254. /**
  255. * @param {*} access 用户权限数组,如 ['super_admin', 'admin']
  256. * @param {*} route 路由列表
  257. */
  258. const hasAccess = (access, route) => {
  259. if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access)
  260. else return true
  261. }
  262. /**
  263. * 权鉴
  264. * @param {*} name 即将跳转的路由name
  265. * @param {*} access 用户权限数组
  266. * @param {*} routes 路由列表
  267. * @description 用户是否可跳转到该页
  268. */
  269. export const canTurnTo = (name, access, routes) => {
  270. const routePermissionJudge = list => {
  271. return list.some(item => {
  272. if (item.children && item.children.length) {
  273. return routePermissionJudge(item.children)
  274. } else if (item.name === name) {
  275. return hasAccess(access, item)
  276. }
  277. })
  278. }
  279. return routePermissionJudge(routes)
  280. }
  281. /**
  282. * @param {String} url
  283. * @description 从URL中解析参数
  284. */
  285. export const getParams = url => {
  286. const keyValueArr = url.split('?')[1].split('&')
  287. let paramObj = {}
  288. keyValueArr.forEach(item => {
  289. const keyValue = item.split('=')
  290. paramObj[keyValue[0]] = keyValue[1]
  291. })
  292. return paramObj
  293. }
  294. /**
  295. * @param {Array} list 标签列表
  296. * @param {String} name 当前关闭的标签的name
  297. */
  298. export const getNextRoute = (list, route) => {
  299. let res = {}
  300. if (list.length === 2) {
  301. res = getHomeRoute(list)
  302. } else {
  303. const index = list.findIndex(item => routeEqual(item, route))
  304. if (index === list.length - 1) res = list[list.length - 2]
  305. else res = list[index + 1]
  306. }
  307. return res
  308. }
  309. /**
  310. * @param {Number} times 回调函数需要执行的次数
  311. * @param {Function} callback 回调函数
  312. */
  313. export const doCustomTimes = (times, callback) => {
  314. let i = -1
  315. while (++i < times) {
  316. callback(i)
  317. }
  318. }
  319. /**
  320. * @param {Object} file 从上传组件得到的文件对象
  321. * @returns {Promise} resolve参数是解析后的二维数组
  322. * @description 从Csv文件中解析出表格,解析成二维数组
  323. */
  324. export const getArrayFromFile = file => {
  325. let nameSplit = file.name.split('.')
  326. let format = nameSplit[nameSplit.length - 1]
  327. return new Promise((resolve, reject) => {
  328. let reader = new FileReader()
  329. reader.readAsText(file) // 以文本格式读取
  330. let arr = []
  331. reader.onload = function (evt) {
  332. let data = evt.target.result // 读到的数据
  333. let pasteData = data.trim()
  334. arr = pasteData
  335. .split(/[\n\u0085\u2028\u2029]|\r\n?/g)
  336. .map(row => {
  337. return row.split('\t')
  338. })
  339. .map(item => {
  340. return item[0].split(',')
  341. })
  342. if (format === 'csv') resolve(arr)
  343. else reject(new Error('[Format Error]:你上传的不是Csv文件'))
  344. }
  345. })
  346. }
  347. /**
  348. * @param {Array} array 表格数据二维数组
  349. * @returns {Object} { columns, tableData }
  350. * @description 从二维数组中获取表头和表格数据,将第一行作为表头,用于在iView的表格中展示数据
  351. */
  352. export const getTableDataFromArray = array => {
  353. let columns = []
  354. let tableData = []
  355. if (array.length > 1) {
  356. let titles = array.shift()
  357. columns = titles.map(item => {
  358. return {
  359. title: item,
  360. key: item
  361. }
  362. })
  363. tableData = array.map(item => {
  364. let res = {}
  365. item.forEach((col, i) => {
  366. res[titles[i]] = col
  367. })
  368. return res
  369. })
  370. }
  371. return {
  372. columns,
  373. tableData
  374. }
  375. }
  376. export const findNodeUpper = (ele, tag) => {
  377. if (ele.parentNode) {
  378. if (ele.parentNode.tagName === tag.toUpperCase()) {
  379. return ele.parentNode
  380. } else {
  381. return findNodeUpper(ele.parentNode, tag)
  382. }
  383. }
  384. }
  385. export const findNodeUpperByClasses = (ele, classes) => {
  386. let parentNode = ele.parentNode
  387. if (parentNode) {
  388. let classList = parentNode.classList
  389. if (classList && classes.every(className => classList.contains(className))) {
  390. return parentNode
  391. } else {
  392. return findNodeUpperByClasses(parentNode, classes)
  393. }
  394. }
  395. }
  396. export const findNodeDownward = (ele, tag) => {
  397. const tagName = tag.toUpperCase()
  398. if (ele.childNodes.length) {
  399. let i = -1
  400. let len = ele.childNodes.length
  401. while (++i < len) {
  402. let child = ele.childNodes[i]
  403. if (child.tagName === tagName) return child
  404. else return findNodeDownward(child, tag)
  405. }
  406. }
  407. }
  408. export const showByAccess = (access, canViewAccess) => {
  409. return hasOneOf(canViewAccess, access)
  410. }
  411. /**
  412. * @description 根据name/params/query判断两个路由对象是否相等
  413. * @param {*} route1 路由对象
  414. * @param {*} route2 路由对象
  415. */
  416. export const routeEqual = (route1, route2) => {
  417. const query1 = route1.query || {}
  418. const query2 = route2.query || {}
  419. return route1.name === route2.name
  420. }
  421. /**
  422. * 判断打开的标签列表里是否已存在这个新添加的路由对象
  423. */
  424. export const routeHasExist = (tagNavList, routeItem) => {
  425. let len = tagNavList.length
  426. let res = false
  427. doCustomTimes(len, index => {
  428. if (routeEqual(tagNavList[index], routeItem)) res = true
  429. })
  430. return res
  431. }
  432. export const showLoadingFunc = (obj, flag) => {
  433. let arr = obj.$root.$children
  434. if (arr.length > 0) {
  435. arr[0].isShowLoading = flag
  436. }
  437. }
  438. export const dateFormat = (obj, fmt) => {
  439. if (typeof obj === 'string') {
  440. return obj
  441. }
  442. if (obj && obj.getFullYear) {
  443. var o = {
  444. 'M+': obj.getMonth() + 1, // 月份
  445. 'd+': obj.getDate(), // 日
  446. 'H+': obj.getHours(), // 小时
  447. 'h+': obj.getHours(), // 小时
  448. 'm+': obj.getMinutes(), // 分
  449. 's+': obj.getSeconds(), // 秒
  450. 'q+': Math.floor((obj.getMonth() + 3) / 3), // 季度
  451. S: obj.getMilliseconds() // 毫秒
  452. }
  453. if (/(y+)/.test(fmt)) {
  454. fmt = fmt.replace(/(y+)/, (match, p1) => (obj.getFullYear() + '').substring(4 - p1.length))
  455. }
  456. for (var k in o) {
  457. let reg = new RegExp('(' + k + ')')
  458. if (reg.test(fmt)) {
  459. fmt = fmt.replace(reg, (match, p1) => p1.length === 1 ? o[k] : ('00' + o[k]).substring(('' + o[k]).length))
  460. }
  461. }
  462. return fmt
  463. } else {
  464. return ''
  465. }
  466. }
  467. export const getYearWeek = (date) => {
  468. /*
  469. dateNow是当前日期
  470. dateFirst是当年第一天
  471. dataNumber是当前日期是今年第多少天
  472. 用dataNumber + 当前年的第一天的周差距的和在除以7就是本年第几周
  473. */
  474. let dateNow = date instanceof Date ? date : convertDateFromString(date, 'yyyy-MM-dd')
  475. let dateFirst = new Date(dateNow.getFullYear(), 0, 1)
  476. let dataNumber = Math.round((dateNow.valueOf() - dateFirst.valueOf()) / 86400000)
  477. return Math.ceil((dataNumber + ((dateFirst.getDay() + 1) - 1)) / 7)
  478. }
  479. // 天-小时-分钟
  480. export const dateRule = (value) => {
  481. // 需要转换的时间-秒单位
  482. /**
  483. * var theTime = parseInt(value);
  484. */
  485. var theTime1 = 0 // 分
  486. var theTime2 = 0 // 小时
  487. var theTime3 = 0 // 天
  488. if (value > 60) {
  489. theTime1 = parseInt(value / 60)
  490. /**
  491. * theTime = parseInt(theTime % 60);
  492. */
  493. if (theTime1 > 60) {
  494. theTime2 = parseInt(theTime1 / 60)
  495. theTime1 = parseInt(theTime1 % 60)
  496. if (theTime2 > 24) {
  497. // 大于24小时
  498. theTime3 = parseInt(theTime2 / 24)
  499. theTime2 = parseInt(theTime2 % 24)
  500. }
  501. }
  502. }
  503. var result = ''
  504. /**
  505. * if (theTime > 0) {
  506. result = "" + parseInt(theTime) + "秒";
  507. }
  508. */
  509. if (theTime1 > 0) {
  510. if (theTime2 > 0 || theTime3 > 0) {
  511. result = '' + parseInt(theTime1) + '分' + result
  512. } else {
  513. result = '' + parseInt(theTime1) + '分钟' + result
  514. }
  515. }
  516. if (theTime2 > 0) {
  517. result = '' + parseInt(theTime2) + '小时' + result
  518. }
  519. if (theTime3 > 0) {
  520. result = '' + parseInt(theTime3) + '天' + result
  521. }
  522. return result
  523. }
  524. export const convertDateFromString = (dateString, fmt) => {
  525. if (dateString) {
  526. if (fmt === 'yyyy-MM-dd' || fmt === 'yyyy-MM-dd hh:mm:ss') {
  527. return new Date(dateString.replace(/-/g, '/'))
  528. } else {
  529. return null
  530. }
  531. } else {
  532. return null
  533. }
  534. }
  535. /**
  536. * @description 日期去掉时分秒
  537. * @param string value 日期
  538. */
  539. export const dateRemoveTime = dateString => {
  540. return dateString && dateString.split(' ')[0]
  541. }
  542. export const ObjectNone = object => {
  543. if (JSON.stringify(object) === '{}') return true
  544. else return false
  545. }
  546. /**
  547. * @description 校验邮编号码 长度6位数字
  548. * @param string value 值
  549. */
  550. export const testPostal = value => {
  551. return !/^\d{6}$/.test(value)
  552. }
  553. /**
  554. * @description 校验手机号码
  555. * @param string value 值
  556. */
  557. export const testMobile = value => {
  558. return !/^1[3|4|5|6|7|8|9][0-9]\d{8}$/.test(value)
  559. }
  560. /**
  561. * @description 校验邮箱
  562. * @param string value 值
  563. */
  564. export const checkEmail = value => {
  565. return !/^[a-zA-Z0-9]+([a-zA-Z0-9-_.]*)@([a-zA-Z0-9]+[-|_|.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,4}$/g.test(value)
  566. }
  567. /**
  568. * @description 校验固定电话
  569. * @param string value 值
  570. */
  571. export const testPhone = value => {
  572. return !/^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/.test(value)
  573. }
  574. /**
  575. * @description 校验银行卡
  576. * @param string value 值
  577. */
  578. export const testBankNo = value => {
  579. // 陈曦说8到30 2020年7月10日
  580. return !/^(\d{8,30})$/.test(value)
  581. }
  582. /**
  583. * @description 校验5位数短号 10086、95119
  584. * @param string value 值
  585. */
  586. export const test5ShortPhone = value => {
  587. return !/^(1|9){1}\d{4}$/.test(value)
  588. }
  589. /**
  590. * @description 校验3位数短号, 110、120
  591. * @param string value 值
  592. */
  593. export const test3ShortPhone = value => {
  594. return !/^1\d{2}$/.test(value)
  595. }
  596. /**
  597. * @description 校验400虚拟号, 400-600-7709
  598. * @param string value 值
  599. */
  600. export const test400Phone = value => {
  601. return !/^(400)-(\d{3})-(\d{4}$)/.test(value)
  602. }
  603. /**
  604. * @description 校验800虚拟号, 800-600-7709
  605. * @param string value 值
  606. */
  607. export const test800Phone = value => {
  608. return !/^(800)-(\d{3})-(\d{4}$)/.test(value)
  609. }
  610. /**
  611. * @description 校验身份证18位
  612. * @param string value 值
  613. */
  614. export const testIdCard = value => {
  615. return !/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(value)
  616. }
  617. /**
  618. * @description 纳税人识别号 统一社会信用代码
  619. * @param string value 值
  620. */
  621. export const testUniformSocialCreditCode = value => {
  622. return /^[^_IOZSVa-z\W]{2}\d{6}[^_IOZSVa-z\W]{10}$|^[1-9][0-9A-Z]{14}$|^[A-Z0-9]{20}$/.test(value)
  623. }
  624. /**
  625. * @description 自定义导航标签名
  626. * @param vm 组件实例
  627. * @param title 导航名称
  628. */
  629. export const customTagNavList = (vm, title) => {
  630. vm.$store.state.app.tagNavList.forEach(item => {
  631. if (routeEqual(item, vm.$route)) {
  632. let { meta } = item
  633. vm.$set(
  634. meta,
  635. 'title',
  636. title
  637. )
  638. }
  639. })
  640. if (vm.$store.state.app.tagNavList.length) {
  641. setTagNavListInLocalstorage(vm.$store.state.app.tagNavList)
  642. }
  643. }
  644. /**
  645. * @description 替换同路由导航标签
  646. * @param vm 组件实例
  647. * @param route 当前路由
  648. */
  649. export const replaceTagNavItem = (vm, { name, query, params }) => {
  650. query = query || {}
  651. params = params || {}
  652. vm.$store.state.app.tagNavList.forEach(item => {
  653. if (item.name === name && (!objEqual(item.query, query) || !objEqual(item.params, params))) {
  654. vm.$set(
  655. item,
  656. 'query',
  657. query
  658. )
  659. vm.$set(
  660. item,
  661. 'params',
  662. params
  663. )
  664. }
  665. })
  666. if (vm.$store.state.app.tagNavList.length) {
  667. setTagNavListInLocalstorage(vm.$store.state.app.tagNavList)
  668. }
  669. }
  670. /**
  671. * @description 手动关闭导航标签
  672. * @param vm 组件实例
  673. * @param route 当前路由
  674. * @param callback 回调函数
  675. */
  676. // export const handleTagNavClose = (vm, route, callback) => {
  677. // let list = JSON.parse(JSON.stringify(vm.$store.state.app.tagNavList))
  678. // if (route.meta && route.meta.beforeCloseName && route.meta.beforeCloseName in beforeClose) {
  679. // new Promise(beforeClose[route.meta.beforeCloseName]).then(close => {
  680. // if (close) {
  681. // vm.$store.state.app.tagNavList = list.filter(item => !routeEqual(route, item))
  682. // }
  683. // })
  684. // } else {
  685. // vm.$store.state.app.tagNavList = list.filter(item => !routeEqual(route, item))
  686. // }
  687. // if (vm.$store.state.app.tagNavList.length) {
  688. // setTagNavListInLocalstorage(vm.$store.state.app.tagNavList)
  689. // }
  690. // callback && callback()
  691. // }
  692. /**
  693. * 递归格式化tree
  694. * 返回满足要的tree结构
  695. * @param dataArr {array} tree数据
  696. * @param options.value {string} 值
  697. * @param options.children {array} 子孙节点
  698. * @param options.label {string} 显示内容
  699. * @param expand {boolean} 是否展开
  700. * @param selected {string} 要选中的元素
  701. */
  702. export const recursionFormatTreeJson = (data, opt = { value: 'id', valueKey: 'value', label: 'label', labelKey: 'label', children: 'children', special: true }, selected) => {
  703. const res = []
  704. let obj = {}
  705. data.forEach(row => {
  706. const tmp = { ...row }
  707. if (tmp.children) {
  708. tmp.children = recursionFormatTreeJson(tmp.children, opt, selected)
  709. obj = {
  710. [opt.valueKey]: opt.special ? tmp[opt.value] : `${tmp[opt.value]},${tmp[opt.label]}`,
  711. [opt.labelKey]: tmp[opt.label],
  712. label: tmp[opt.label],
  713. expand: true,
  714. id: tmp[opt.value],
  715. type: tmp['type'] || null,
  716. selected: (tmp[opt.value] === selected),
  717. [opt.children]: tmp[opt.children] && tmp[opt.children].length > 0 ? tmp[opt.children] : null
  718. }
  719. }
  720. res.push(obj)
  721. })
  722. return res
  723. }
  724. /**
  725. * 递归初始化tree
  726. * 返回初始化tree,默认展开选择selected
  727. * @param data {array} tree数据
  728. * @param selected {string} 要选中的元素
  729. */
  730. export const recursionInitTreeJson = (data, selected) => {
  731. let res = false
  732. for (let i = 0, len = data.length; i < len; i++) {
  733. if (data[i].value === selected) {
  734. data[i].selected = true
  735. data[i].expand = true
  736. return true
  737. }
  738. if (data[i].children) {
  739. if ((i === 0) || (data[i - 1] && !data[i - 1].expand)) {
  740. res = recursionInitTreeJson(data[i].children, selected)
  741. data[i].expand = res
  742. } else {
  743. return true
  744. }
  745. }
  746. }
  747. return res
  748. }
  749. /**
  750. * 获取当前选中地址单元描述信息
  751. * @param data {array} 地址单元信息
  752. * @param row {object} 当前地址信息
  753. */
  754. export const getCommunityTitleInfo = (data, row) => {
  755. let resInfo = []
  756. data.filter(item => {
  757. // 小区名称
  758. if (item.communityId === row.communityId) {
  759. resInfo.push(item.title)
  760. // 栋名称
  761. item.children.filter(itemBuild => {
  762. if (itemBuild.buildId === row.buildId) {
  763. resInfo.push(itemBuild.title)
  764. // 单元名称
  765. itemBuild.children.filter(itemUnit => {
  766. if (itemUnit.unitId === row.unitId) {
  767. resInfo.push(itemUnit.title)
  768. // 房屋号
  769. itemUnit.children.filter(itemHouse => {
  770. if (itemHouse.houseId === row.houseId) {
  771. resInfo.push(itemHouse.title)
  772. }
  773. })
  774. }
  775. })
  776. }
  777. })
  778. }
  779. })
  780. return resInfo.join(' / ')
  781. }
  782. /**
  783. * 函数节流
  784. */
  785. export const throttle = (callback, delay) => {
  786. let timer = null
  787. let previous = new Date()
  788. return function () {
  789. let now = new Date()
  790. let remaining = now - previous
  791. let args = arguments
  792. let context = this
  793. if (remaining >= delay) {
  794. if (timer) {
  795. clearTimeout(timer)
  796. }
  797. callback.apply(context, args)
  798. previous = now
  799. } else {
  800. if (!timer) {
  801. timer = setTimeout(function () {
  802. callback.apply(context, args)
  803. previous = new Date()
  804. }, delay - remaining)
  805. }
  806. }
  807. }
  808. }
  809. /**
  810. * 防抖
  811. */
  812. export const debounce = (callback, delay) => {
  813. let timer = null
  814. return function () {
  815. let args = arguments
  816. let context = this
  817. if (timer) {
  818. clearTimeout(timer)
  819. }
  820. timer = setTimeout(function () {
  821. callback.apply(context, args)
  822. }, delay)
  823. }
  824. }
  825. /**
  826. * js导出xlsx文件
  827. * @param data {array} 表头描述;列标题,逗号隔开,每一个逗号(英文逗号)就是隔开一个单元格
  828. * @param columns {array} 表格内容;数组包含对象,每个对象单元为一个表格单元格
  829. * @param fileName {string} 表个名称
  830. */
  831. export const jsExportXlsxFile = (columns = [], data = [], fileName = '数据表') => {
  832. let str = columns.join(',')
  833. str += `\n`
  834. // 增加\t为了不让表格显示科学计数法或者其他格式、正则是为了去掉回车
  835. for (let i = 0; i < data.length; i++) {
  836. for (let item in data[i]) {
  837. str += `${data[i][item].replace(/[\n\r]/g, '') + '\t'},`
  838. }
  839. str += '\n'
  840. }
  841. // encodeURIComponent解决中文乱码
  842. let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str)
  843. // 通过创建a标签实现
  844. let link = document.createElement('a')
  845. link.href = uri
  846. // 对下载的文件命名
  847. link.download = `${fileName}.csv`
  848. document.body.appendChild(link)
  849. link.click()
  850. document.body.removeChild(link)
  851. }
  852. /**
  853. * 文件名称路径拼接
  854. */
  855. export function fileToPath () {
  856. let baseUrl = '/api/'
  857. if (process.env.NODE_ENV === 'production') {
  858. baseUrl = config.baseUrl.files
  859. }
  860. return baseUrl
  861. }
  862. // 资产树选择楼栋,单元,房屋
  863. export const assetTreeFilter = (data, type) => {
  864. let result = []
  865. let item = {}
  866. if (type === 'build') {
  867. data.forEach(build => {
  868. item = Object.assign({}, build)
  869. item.children = []
  870. result.push(item)
  871. })
  872. return result
  873. }
  874. if (type === 'unit') {
  875. let unitItem = {}
  876. data.forEach(build => {
  877. item = Object.assign({}, build)
  878. item.children = []
  879. build.children.forEach(unit => {
  880. unitItem = Object.assign({}, unit)
  881. unitItem.children = []
  882. item.children.push(unitItem)
  883. })
  884. result.push(item)
  885. })
  886. return result
  887. }
  888. if (type === 'house') {
  889. return data
  890. }
  891. }
  892. /**
  893. * 将dom转为图片dataUrl 和 blob
  894. * @param {dom} dom 对象
  895. * @param {string} type default: 获取dataUrl,blob; image: 获取dataUrl; download 自动下载并获取blob
  896. * @param {Number} quality 图片质量 0~1
  897. * @param {string} fileName 保存的图片名称
  898. * @returns Promise
  899. */
  900. export const dom2Image = (dom, type = 'default', quality = 0.9, fileName = '', imagetype = 'image/jpeg', options = {}) => {
  901. return new Promise((resolve, reject) => {
  902. html2canvas(dom, {
  903. backgroundColor: null,
  904. dpi: window.devicePixelRatio * 2,
  905. scale: 10,
  906. useCORS: true,
  907. ...options
  908. })
  909. .then((canvas) => {
  910. let dataUrl = canvas.toDataURL(imagetype, quality)
  911. if (type === 'image') {
  912. resolve(dataUrl)
  913. } else {
  914. canvas.toBlob((blob) => {
  915. if (type === 'download') {
  916. saveAs(blob, fileName)
  917. }
  918. resolve({
  919. dataUrl,
  920. blob
  921. })
  922. }, imagetype, quality)
  923. }
  924. })
  925. .catch(err => {
  926. reject(err)
  927. })
  928. })
  929. }
  930. /* 图片压缩方法-canvas压缩 */
  931. export const compressPhoto = (image, maxSize = 0, quality = 0.7) => {
  932. let canvas = document.createElement('canvas')
  933. let ctx = canvas.getContext('2d')
  934. // let initSize = image.src.length
  935. let { width, height } = image
  936. let per = 0
  937. if (width > height) {
  938. per = maxSize / width
  939. } else {
  940. per = maxSize / height
  941. }
  942. canvas.width = width * per || width
  943. canvas.height = height * per || height
  944. ctx.fillStyle = '#fff'
  945. ctx.fillRect(0, 0, canvas.width, canvas.height)
  946. ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
  947. // 进行压缩0.7
  948. let compressData = canvas.toDataURL('image/jpeg', quality)
  949. // 压缩后调用方法进行base64转Blob
  950. // let blobImg = dataURItoBlob(compressData)
  951. return compressData
  952. }
  953. /* base64转Blob对象 */
  954. export const dataURItoBlob = (data) => {
  955. let byteString
  956. if (data.split(',')[0].indexOf('base64') >= 0) {
  957. byteString = atob(data.split(',')[1])
  958. } else {
  959. byteString = unescape(data.split(',')[1])
  960. }
  961. let mimeString = data
  962. .split(',')[0]
  963. .split(':')[1]
  964. .split(';')[0]
  965. let ia = new Uint8Array(byteString.length)
  966. for (let i = 0; i < byteString.length; i += 1) {
  967. ia[i] = byteString.charCodeAt(i)
  968. }
  969. return new Blob([ia], { type: mimeString })
  970. }
  971. // 枚举转换
  972. export const enumForMat = (data) => {
  973. let enumArr = []
  974. if (data && data.length > 0) {
  975. enumArr = data.map(item => {
  976. return {
  977. text: item.name,
  978. value: item.code
  979. }
  980. })
  981. }
  982. return enumArr
  983. }
  984. export function toHump (str = '') {
  985. return str.replace(/^\w|_\w/g, (match) => match.toUpperCase()).replace(/_/g, '')
  986. }
  987. // 表头统计模板数据转换
  988. export function statisitsChange (data, res) {
  989. for (let item of data) {
  990. item.value = res[item.key]
  991. }
  992. return data
  993. }
  994. // 金额转换大写
  995. export function priceToUpper (n) {
  996. if (Number(n) === 0) {
  997. return '零元整'
  998. }
  999. if (!/^(\+|-)?(0|[1-9]\d*)(\.\d+)?$/.test(n)) { return '数据非法' }
  1000. let unit = '仟佰拾亿仟佰拾万仟佰拾元角分'
  1001. let str = ''
  1002. n += '00'
  1003. let a = parseFloat(n)
  1004. if (a < 0) {
  1005. n = n.substr(1)
  1006. }
  1007. let p = n.indexOf('.')
  1008. if (p >= 0) {
  1009. n = n.substring(0, p) + n.substr(p + 1, 2)
  1010. }
  1011. unit = unit.substr(unit.length - n.length)
  1012. for (let i = 0; i < n.length; i++) { str += '零壹贰叁肆伍陆柒捌玖'.charAt(n.charAt(i)) + unit.charAt(i) }
  1013. if (a > 0) {
  1014. return str.replace(/零(仟|佰|拾|角)/g, '零').replace(/(零)+/g, '零').replace(/零(万|亿|元)/g, '$1').replace(/(亿)万/g, '$1').replace(/^元零?|零分/g, '').replace(/元$/g, '元整')
  1015. } else {
  1016. return '负' + str.replace(/零(仟|佰|拾|角)/g, '零').replace(/(零)+/g, '零').replace(/零(万|亿|元)/g, '$1').replace(/(亿)万|壹(拾)/g, '$1$2').replace(/^元零?|零分/g, '').replace(/元$/g, '元整')
  1017. }
  1018. }