util.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. // +---------------------------------------------------------------------
  2. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  3. // +---------------------------------------------------------------------
  4. // | Copyright (c) 2016~2025 https://www.crmeb.com All rights reserved.
  5. // +---------------------------------------------------------------------
  6. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  7. // +---------------------------------------------------------------------
  8. // | Author: CRMEB Team <admin@crmeb.com>
  9. // +---------------------------------------------------------------------
  10. import Cookies from 'js-cookie';
  11. // cookie保存的天数
  12. import config from '@/config';
  13. import { forEach, hasOneOf, objEqual } from '@/libs/tools';
  14. import { cloneDeep } from 'lodash';
  15. const { title, useI18n } = config;
  16. import { Local } from '@/utils/storage.js';
  17. // 设置setCookies;
  18. // setToken
  19. export const setCookies = (key, val, cookieExpires) => {
  20. Cookies.set(key, val, { expires: cookieExpires || 1 });
  21. };
  22. // 获取getCookies;
  23. // getToken
  24. export const getCookies = (key) => {
  25. return Cookies.get(key);
  26. };
  27. export const removeCookies = (key) => {
  28. return Cookies.remove(key);
  29. };
  30. export const hasChild = (item) => {
  31. return item.children && item.children.length !== 0;
  32. };
  33. const showThisMenuEle = (item, access) => {
  34. if (item.meta && item.meta.access && item.meta.access.length) {
  35. if (hasOneOf(item.meta.access, access)) return true;
  36. else return false;
  37. } else return true;
  38. };
  39. /**
  40. * @param {Array} list 通过路由列表得到菜单列表
  41. * @returns {Array}
  42. */
  43. export const getMenuByRouter = (list, access) => {
  44. let res = [];
  45. forEach(list, (item) => {
  46. if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
  47. let obj = {
  48. icon: (item.meta && item.meta.icon) || '',
  49. name: item.name,
  50. meta: item.meta,
  51. };
  52. if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {
  53. obj.children = getMenuByRouter(item.children, access);
  54. }
  55. if (item.meta && item.meta.href) obj.href = item.meta.href;
  56. if (showThisMenuEle(item, access)) res.push(obj);
  57. }
  58. });
  59. return res;
  60. };
  61. /**
  62. * @param {Array} routeMetched 当前路由metched
  63. * @returns {Array}
  64. */
  65. export const getBreadCrumbList = (route, homeRoute) => {
  66. let homeItem = { ...homeRoute, icon: homeRoute.meta?.icon };
  67. let routeMetched = route.matched;
  68. if (routeMetched.some((item) => item.name === homeRoute.name)) return [homeItem];
  69. let res = routeMetched
  70. .filter((item) => {
  71. return item.meta === undefined || !item.meta.hideInBread;
  72. })
  73. .map((item) => {
  74. let meta = { ...item.meta };
  75. if (meta.title && typeof meta.title === 'function') {
  76. meta.__titleIsFunction__ = true;
  77. meta.title = meta.title(route);
  78. }
  79. let obj = {
  80. icon: (item.meta && item.meta.icon) || '',
  81. name: item.name,
  82. meta: meta,
  83. };
  84. return obj;
  85. });
  86. res = res.filter((item) => {
  87. return !item.meta.hideInMenu;
  88. });
  89. return [{ ...homeItem, to: homeRoute.path }, ...res];
  90. };
  91. export const getRouteTitleHandled = (route) => {
  92. let router = { ...route };
  93. let meta = { ...route.meta };
  94. let title = '';
  95. if (meta.title) {
  96. if (typeof meta.title === 'function') {
  97. meta.__titleIsFunction__ = true;
  98. title = meta.title(router);
  99. } else title = meta.title;
  100. }
  101. meta.title = title;
  102. router.meta = meta;
  103. return router;
  104. };
  105. export const showTitle = (item, vm) => {
  106. let { title, __titleIsFunction__ } = item.meta;
  107. if (!title) return;
  108. if (useI18n) {
  109. if (title.includes('{{') && title.includes('}}') && useI18n)
  110. title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())));
  111. else if (__titleIsFunction__) title = item.meta.title;
  112. else title = vm.$t(item.name);
  113. } else title = (item.meta && item.meta.title) || item.name;
  114. return title;
  115. };
  116. /**
  117. * @description 本地存储和获取标签导航列表
  118. */
  119. export const setTagNavListInLocalstorage = (list) => {
  120. Local.set('tagNaveListJavaPlat', JSON.stringify(list));
  121. };
  122. /**
  123. * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项
  124. */
  125. export const getTagNavListFromLocalstorage = () => {
  126. const list = Local.get('tagNaveListJavaPlat');
  127. return list ? JSON.parse(list) : [];
  128. };
  129. /**
  130. * @param {Array} routers 路由列表数组
  131. * @description 用于找到路由列表中name为home的对象
  132. */
  133. export const getHomeRoute = (routers, homeName = 'home') => {
  134. let i = -1;
  135. let len = routers.length;
  136. let homeRoute = {};
  137. while (++i < len) {
  138. let item = routers[i];
  139. if (item.children && item.children.length) {
  140. let res = getHomeRoute(item.children, homeName);
  141. if (res.name) return res;
  142. } else {
  143. if (item.name === homeName) homeRoute = item;
  144. }
  145. }
  146. return homeRoute;
  147. };
  148. /**
  149. * @param {*} list 现有标签导航列表
  150. * @param {*} newRoute 新添加的路由原信息对象
  151. * @description 如果该newRoute已经存在则不再添加
  152. */
  153. export const getNewTagList = (list, newRoute) => {
  154. const { name, path, meta } = newRoute;
  155. let newList = [...list];
  156. if (newList.findIndex((item) => item.path === path) >= 0) return newList;
  157. else newList.push({ name, path, meta });
  158. return newList;
  159. };
  160. /**
  161. * @param {*} access 用户权限数组,如 ['super_admin', 'admin']
  162. * @param {*} route 路由列表
  163. */
  164. const hasAccess = (access, route) => {
  165. if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access);
  166. else return true;
  167. };
  168. /**
  169. * 权鉴
  170. * @param {*} name 即将跳转的路由name
  171. * @param {*} access 用户权限数组
  172. * @param {*} routes 路由列表
  173. * @description 用户是否可跳转到该页
  174. */
  175. export const canTurnTo = (name, access, routes) => {
  176. const routePermissionJudge = (list) => {
  177. return list.some((item) => {
  178. if (item.children && item.children.length) {
  179. return routePermissionJudge(item.children);
  180. } else if (item.name === name) {
  181. return hasAccess(access, item);
  182. }
  183. });
  184. };
  185. return routePermissionJudge(routes);
  186. };
  187. /**
  188. * @param {String} url
  189. * @description 从URL中解析参数
  190. */
  191. export const getParams = (url) => {
  192. const keyValueArr = url.split('?')[1].split('&');
  193. let paramObj = {};
  194. keyValueArr.forEach((item) => {
  195. const keyValue = item.split('=');
  196. paramObj[keyValue[0]] = keyValue[1];
  197. });
  198. return paramObj;
  199. };
  200. /**
  201. * @param {Array} list 标签列表
  202. * @param {String} name 当前关闭的标签的name
  203. */
  204. export const getNextRoute = (list, route) => {
  205. let res = {};
  206. if (list.length === 2) {
  207. res = getHomeRoute(list);
  208. } else {
  209. const index = list.findIndex((item) => routeEqual(item, route));
  210. if (index === list.length - 1) res = list[list.length - 2];
  211. else res = list[index + 1];
  212. }
  213. return res;
  214. };
  215. /**
  216. * @param {Number} times 回调函数需要执行的次数
  217. * @param {Function} callback 回调函数
  218. */
  219. export const doCustomTimes = (times, callback) => {
  220. let i = -1;
  221. while (++i < times) {
  222. callback(i);
  223. }
  224. };
  225. /**
  226. * @param {Object} file 从上传组件得到的文件对象
  227. * @returns {Promise} resolve参数是解析后的二维数组
  228. * @description 从Csv文件中解析出表格,解析成二维数组
  229. */
  230. export const getArrayFromFile = (file) => {
  231. let nameSplit = file.name.split('.');
  232. let format = nameSplit[nameSplit.length - 1];
  233. return new Promise((resolve, reject) => {
  234. let reader = new FileReader();
  235. reader.readAsText(file); // 以文本格式读取
  236. let arr = [];
  237. reader.onload = function (evt) {
  238. let data = evt.target.result; // 读到的数据
  239. let pasteData = data.trim();
  240. arr = pasteData
  241. .split(/[\n\u0085\u2028\u2029]|\r\n?/g)
  242. .map((row) => {
  243. return row.split('\t');
  244. })
  245. .map((item) => {
  246. return item[0].split(',');
  247. });
  248. if (format === 'csv') resolve(arr);
  249. else reject(new Error('[Format Error]:你上传的不是Csv文件'));
  250. };
  251. });
  252. };
  253. /**
  254. * @param {Array} array 表格数据二维数组
  255. * @returns {Object} { columns, tableData }
  256. * @description 从二维数组中获取表头和表格数据,将第一行作为表头,用于在表格中展示数据
  257. */
  258. export const getTableDataFromArray = (array) => {
  259. let columns = [];
  260. let tableData = [];
  261. if (array.length > 1) {
  262. let titles = array.shift();
  263. columns = titles.map((item) => {
  264. return {
  265. title: item,
  266. key: item,
  267. };
  268. });
  269. tableData = array.map((item) => {
  270. let res = {};
  271. item.forEach((col, i) => {
  272. res[titles[i]] = col;
  273. });
  274. return res;
  275. });
  276. }
  277. return {
  278. columns,
  279. tableData,
  280. };
  281. };
  282. export const findNodeUpper = (ele, tag) => {
  283. if (ele.parentNode) {
  284. if (ele.parentNode.tagName === tag.toUpperCase()) {
  285. return ele.parentNode;
  286. } else {
  287. return findNodeUpper(ele.parentNode, tag);
  288. }
  289. }
  290. };
  291. export const findNodeUpperByClasses = (ele, classes) => {
  292. let parentNode = ele.parentNode;
  293. if (parentNode) {
  294. let classList = parentNode.classList;
  295. if (classList && classes.every((className) => classList.contains(className))) {
  296. return parentNode;
  297. } else {
  298. return findNodeUpperByClasses(parentNode, classes);
  299. }
  300. }
  301. };
  302. export const findNodeDownward = (ele, tag) => {
  303. const tagName = tag.toUpperCase();
  304. if (ele.childNodes.length) {
  305. let i = -1;
  306. let len = ele.childNodes.length;
  307. while (++i < len) {
  308. let child = ele.childNodes[i];
  309. if (child.tagName === tagName) return child;
  310. else return findNodeDownward(child, tag);
  311. }
  312. }
  313. };
  314. export const showByAccess = (access, canViewAccess) => {
  315. return hasOneOf(canViewAccess, access);
  316. };
  317. /**
  318. * @description 根据name/params/query判断两个路由对象是否相等
  319. * @param {*} route1 路由对象
  320. * @param {*} route2 路由对象
  321. */
  322. export const routeEqual = (route1, route2) => {
  323. const params1 = route1.params || {};
  324. const params2 = route2.params || {};
  325. const query1 = route1.query || {};
  326. const query2 = route2.query || {};
  327. return route1.name === route2.name && objEqual(params1, params2) && objEqual(query1, query2);
  328. };
  329. /**
  330. * 判断打开的标签列表里是否已存在这个新添加的路由对象
  331. */
  332. export const routeHasExist = (tagNavList, routeItem) => {
  333. let len = tagNavList.length;
  334. let res = false;
  335. doCustomTimes(len, (index) => {
  336. if (routeEqual(tagNavList[index], routeItem)) res = true;
  337. });
  338. return res;
  339. };
  340. export const localSave = (key, value) => {
  341. localStorage.setItem(key, value);
  342. };
  343. export const localRead = (key) => {
  344. return localStorage.getItem(key) || '';
  345. };
  346. // scrollTop animation
  347. export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => {
  348. if (!window.requestAnimationFrame) {
  349. window.requestAnimationFrame =
  350. window.webkitRequestAnimationFrame ||
  351. window.mozRequestAnimationFrame ||
  352. window.msRequestAnimationFrame ||
  353. function (callback) {
  354. return window.setTimeout(callback, 1000 / 60);
  355. };
  356. }
  357. const difference = Math.abs(from - to);
  358. const step = Math.ceil((difference / duration) * 50);
  359. const scroll = (start, end, step) => {
  360. if (start === end) {
  361. endCallback && endCallback();
  362. return;
  363. }
  364. let d = start + step > end ? end : start + step;
  365. if (start > end) {
  366. d = start - step < end ? end : start - step;
  367. }
  368. if (el === window) {
  369. window.scrollTo(d, d);
  370. } else {
  371. el.scrollTop = d;
  372. }
  373. window.requestAnimationFrame(() => scroll(d, end, step));
  374. };
  375. scroll(from, to, step);
  376. };
  377. /**
  378. * @description 根据当前跳转的路由设置显示在浏览器标签的title
  379. * @param {Object} routeItem 路由对象
  380. * @param {Object} vm Vue实例
  381. */
  382. export const setTitle = (routeItem, vm) => {
  383. let winTitle = localStorage.getItem('ADMIN_TITLE') || title;
  384. const handledRoute = getRouteTitleHandled(routeItem);
  385. const pageTitle = showTitle(handledRoute, vm);
  386. const resTitle = pageTitle ? `${winTitle} - ${pageTitle}` : winTitle;
  387. window.document.title = resTitle;
  388. };
  389. export const R = (menuList, newOpenMenus) => {
  390. menuList.forEach((item) => {
  391. let newMenu = {};
  392. for (let i in item) {
  393. if (i !== 'children') newMenu[i] = cloneDeep(item[i]);
  394. }
  395. newOpenMenus.push(newMenu);
  396. item.children && R(item.children, newOpenMenus);
  397. });
  398. return newOpenMenus;
  399. };
  400. export function getMenuopen(to, menuList) {
  401. const allMenus = [];
  402. menuList.forEach((menu) => {
  403. const menus = transMenu(menu, []);
  404. allMenus.push({
  405. path: menu.path,
  406. openNames: [],
  407. });
  408. menus.forEach((item) => allMenus.push(item));
  409. });
  410. const currentMenu = allMenus.find((item) => item.path === to.path);
  411. return currentMenu ? currentMenu.openNames : [];
  412. }
  413. function transMenu(menu, openNames) {
  414. if (menu.children && menu.children.length) {
  415. const itemOpenNames = openNames.concat([menu.path]);
  416. return menu.children.reduce((all, item) => {
  417. all.push({
  418. path: item.path,
  419. openNames: itemOpenNames,
  420. });
  421. const foundChildren = transMenu(item, itemOpenNames);
  422. return all.concat(foundChildren);
  423. }, []);
  424. } else {
  425. return [menu].map((item) => {
  426. return {
  427. path: item.path,
  428. openNames: openNames,
  429. };
  430. });
  431. }
  432. }
  433. export function wss(wsSocketUrl) {
  434. let ishttps = document.location.protocol == 'https:';
  435. if (ishttps) {
  436. return wsSocketUrl.replace('ws:', 'wss:');
  437. } else {
  438. return wsSocketUrl.replace('wss:', 'ws:');
  439. }
  440. }