util.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. import Vue from 'vue'
  2. import * as api from '@/api/api'
  3. import { isURL } from '@/utils/validate'
  4. import { ACCESS_TOKEN } from '@/store/mutation-types'
  5. import onlineCommons from '@jeecg/antd-online-mini'
  6. import store from '../store'
  7. export function timeFix() {
  8. const time = new Date()
  9. const hour = time.getHours()
  10. return hour < 9 ? '早上好' : (hour <= 11 ? '上午好' : (hour <= 13 ? '中午好' : (hour < 20 ? '下午好' : '晚上好')))
  11. }
  12. export function welcome() {
  13. const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
  14. let index = Math.floor((Math.random()*arr.length))
  15. return arr[index]
  16. }
  17. /**
  18. * 触发 window.resize
  19. */
  20. export function triggerWindowResizeEvent() {
  21. let event = document.createEvent('HTMLEvents')
  22. event.initEvent('resize', true, true)
  23. event.eventType = 'message'
  24. window.dispatchEvent(event)
  25. }
  26. /**
  27. * 过滤对象中为空的属性
  28. * @param obj
  29. * @returns {*}
  30. */
  31. export function filterObj(obj) {
  32. if (!(typeof obj == 'object')) {
  33. return;
  34. }
  35. for ( let key in obj) {
  36. if (obj.hasOwnProperty(key)
  37. && (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
  38. delete obj[key];
  39. }
  40. }
  41. return obj;
  42. }
  43. /**
  44. * 时间格式化
  45. * @param value
  46. * @param fmt
  47. * @returns {*}
  48. */
  49. export function formatDate(value, fmt) {
  50. let regPos = /^\d+(\.\d+)?$/;
  51. if(regPos.test(value)){
  52. //如果是数字
  53. let getDate = new Date(value);
  54. let o = {
  55. 'M+': getDate.getMonth() + 1,
  56. 'd+': getDate.getDate(),
  57. 'h+': getDate.getHours(),
  58. 'm+': getDate.getMinutes(),
  59. 's+': getDate.getSeconds(),
  60. 'q+': Math.floor((getDate.getMonth() + 3) / 3),
  61. 'S': getDate.getMilliseconds()
  62. };
  63. if (/(y+)/.test(fmt)) {
  64. fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
  65. }
  66. for (let k in o) {
  67. if (new RegExp('(' + k + ')').test(fmt)) {
  68. fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
  69. }
  70. }
  71. return fmt;
  72. }else{
  73. //TODO
  74. value = value.trim();
  75. return value.substr(0,fmt.length);
  76. }
  77. }
  78. // 生成首页路由
  79. export function generateIndexRouter(data) {
  80. let indexRouter = [{
  81. path: '/',
  82. name: 'dashboard',
  83. //component: () => import('@/components/layouts/BasicLayout'),
  84. component: resolve => require(['@/components/layouts/TabLayout'], resolve),
  85. meta: { title: '首页' },
  86. redirect: '/tj/sjtjModules',
  87. children: [
  88. ...generateChildRouters(data)
  89. ]
  90. },{
  91. "path": "*", "redirect": "/404", "hidden": true
  92. }]
  93. return indexRouter;
  94. }
  95. // 生成嵌套路由(子路由)
  96. function generateChildRouters (data) {
  97. const routers = [];
  98. for (let item of data) {
  99. let component = "";
  100. if(item.component.indexOf("layouts")>=0){
  101. component = "components/"+item.component;
  102. }else{
  103. component = "views/"+item.component;
  104. }
  105. // eslint-disable-next-line
  106. let URL = (item.meta.url|| '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)) // URL支持{{ window.xxx }}占位符变量
  107. if (isURL(URL)) {
  108. item.meta.url = URL;
  109. }
  110. let componentPath
  111. if(item.component=="modules/online/cgform/OnlCgformHeadList"){
  112. componentPath = onlineCommons.OnlCgformHeadList
  113. }else if(item.component=="modules/online/cgform/OnlCgformCopyList"){
  114. componentPath = onlineCommons.OnlCgformCopyList
  115. }else if(item.component=="modules/online/cgform/auto/OnlCgformAutoList"){
  116. componentPath = onlineCommons.OnlCgformAutoList
  117. }else if(item.component=="modules/online/cgform/auto/OnlCgformTreeList"){
  118. componentPath = onlineCommons.OnlCgformTreeList
  119. }else if(item.component=="modules/online/cgform/auto/erp/OnlCgformErpList"){
  120. componentPath = onlineCommons.OnlCgformErpList
  121. }else if(item.component=="modules/online/cgform/auto/tab/OnlCgformTabList"){
  122. componentPath = onlineCommons.OnlCgformTabList
  123. }else if(item.component=="modules/online/cgform/auto/innerTable/OnlCgformInnerTableList"){
  124. componentPath = onlineCommons.OnlCgformInnerTableList
  125. }else if(item.component=="modules/online/cgreport/OnlCgreportHeadList"){
  126. componentPath = onlineCommons.OnlCgreportHeadList
  127. }else if(item.component=="modules/online/cgreport/auto/OnlCgreportAutoList"){
  128. componentPath = onlineCommons.OnlCgreportAutoList
  129. }else{
  130. componentPath = resolve => require(['@/' + component+'.vue'], resolve)
  131. }
  132. let menu = {
  133. path: item.path,
  134. name: item.name,
  135. redirect:item.redirect,
  136. component: componentPath,
  137. //component: resolve => require(['@/' + component+'.vue'], resolve),
  138. hidden:item.hidden,
  139. //component:()=> import(`@/views/${item.component}.vue`),
  140. meta: {
  141. title:item.meta.title ,
  142. icon: item.meta.icon,
  143. url:item.meta.url ,
  144. permissionList:item.meta.permissionList,
  145. keepAlive:item.meta.keepAlive,
  146. /*update_begin author:wuxianquan date:20190908 for:赋值 */
  147. internalOrExternal:item.meta.internalOrExternal,
  148. /*update_end author:wuxianquan date:20190908 for:赋值 */
  149. componentName:item.meta.componentName
  150. }
  151. }
  152. if(item.alwaysShow){
  153. menu.alwaysShow = true;
  154. menu.redirect = menu.path;
  155. }
  156. /*if (item.children && item.children.length > 0) {
  157. menu.children = [...generateChildRouters( item.children)];
  158. }*/
  159. if (item.children && item.children.length > 0) {
  160. menu.children = [...generateChildRouters( item.children)];
  161. if (item.path.indexOf('hideChildren') > -1) {
  162. store.commit('setStatisticsMenu', item);
  163. }
  164. }
  165. //--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
  166. //判断是否生成路由
  167. if(item.route && item.route === '0'){
  168. //console.log(' 不生成路由 item.route: '+item.route);
  169. //console.log(' 不生成路由 item.path: '+item.path);
  170. }else{
  171. routers.push(menu);
  172. }
  173. //--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
  174. }
  175. return routers
  176. }
  177. /**
  178. * 深度克隆对象、数组
  179. * @param obj 被克隆的对象
  180. * @return 克隆后的对象
  181. */
  182. export function cloneObject(obj) {
  183. return JSON.parse(JSON.stringify(obj))
  184. }
  185. /**
  186. * 随机生成数字
  187. *
  188. * 示例:生成长度为 12 的随机数:randomNumber(12)
  189. * 示例:生成 3~23 之间的随机数:randomNumber(3, 23)
  190. *
  191. * @param1 最小值 | 长度
  192. * @param2 最大值
  193. * @return int 生成后的数字
  194. */
  195. export function randomNumber() {
  196. // 生成 最小值 到 最大值 区间的随机数
  197. const random = (min, max) => {
  198. return Math.floor(Math.random() * (max - min + 1) + min)
  199. }
  200. if (arguments.length === 1) {
  201. let [length] = arguments
  202. // 生成指定长度的随机数字,首位一定不是 0
  203. let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
  204. return parseInt(nums.join(''))
  205. } else if (arguments.length >= 2) {
  206. let [min, max] = arguments
  207. return random(min, max)
  208. } else {
  209. return Number.NaN
  210. }
  211. }
  212. /**
  213. * 随机生成字符串
  214. * @param length 字符串的长度
  215. * @param chats 可选字符串区间(只会生成传入的字符串中的字符)
  216. * @return string 生成的字符串
  217. */
  218. export function randomString(length, chats) {
  219. if (!length) length = 1
  220. if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
  221. let str = ''
  222. for (let i = 0; i < length; i++) {
  223. let num = randomNumber(0, chats.length - 1)
  224. str += chats[num]
  225. }
  226. return str
  227. }
  228. /**
  229. * 随机生成uuid
  230. * @return string 生成的uuid
  231. */
  232. export function randomUUID() {
  233. let chats = '0123456789abcdef'
  234. return randomString(32, chats)
  235. }
  236. /**
  237. * 下划线转驼峰
  238. * @param string
  239. * @returns {*}
  240. */
  241. export function underLine2CamelCase(string){
  242. return string.replace( /_([a-z])/g, function( all, letter ) {
  243. return letter.toUpperCase();
  244. });
  245. }
  246. /**
  247. * 判断是否显示办理按钮
  248. * @param bpmStatus
  249. * @returns {*}
  250. */
  251. export function showDealBtn(bpmStatus){
  252. if(bpmStatus!="1"&&bpmStatus!="3"&&bpmStatus!="4"){
  253. return true;
  254. }
  255. return false;
  256. }
  257. /**
  258. * 增强CSS,可以在页面上输出全局css
  259. * @param css 要增强的css
  260. * @param id style标签的id,可以用来清除旧样式
  261. */
  262. export function cssExpand(css, id) {
  263. let style = document.createElement('style')
  264. style.type = "text/css"
  265. style.innerHTML = `@charset "UTF-8"; ${css}`
  266. // 清除旧样式
  267. if (id) {
  268. let $style = document.getElementById(id)
  269. if ($style != null) $style.outerHTML = ''
  270. style.id = id
  271. }
  272. // 应用新样式
  273. document.head.appendChild(style)
  274. }
  275. /** 用于js增强事件,运行JS代码,可以传参 */
  276. // options 所需参数:
  277. // 参数名 类型 说明
  278. // vm VueComponent vue实例
  279. // event Object event对象
  280. // jsCode String 待执行的js代码
  281. // errorMessage String 执行出错后的提示(控制台)
  282. export function jsExpand(options = {}) {
  283. // 绑定到window上的keyName
  284. let windowKeyName = 'J_CLICK_EVENT_OPTIONS'
  285. if (typeof window[windowKeyName] != 'object') {
  286. window[windowKeyName] = {}
  287. }
  288. // 随机生成JS增强的执行id,防止冲突
  289. let id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
  290. // 封装按钮点击事件
  291. let code = `
  292. (function (o_${id}) {
  293. try {
  294. (function (globalEvent, vm) {
  295. ${options.jsCode}
  296. })(o_${id}.event, o_${id}.vm)
  297. } catch (e) {
  298. o_${id}.error(e)
  299. }
  300. o_${id}.done()
  301. })(window['${windowKeyName}']['EVENT_${id}'])
  302. `
  303. // 创建script标签
  304. const script = document.createElement('script')
  305. // 将需要传递的参数挂载到window对象上
  306. window[windowKeyName]['EVENT_' + id] = {
  307. vm: options.vm,
  308. event: options.event,
  309. // 当执行完成时,无论如何都会调用的回调事件
  310. done() {
  311. // 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
  312. script.outerHTML = ''
  313. delete window[windowKeyName]['EVENT_' + id]
  314. },
  315. // 当js运行出错的时候调用的事件
  316. error(e) {
  317. console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}(${new Date()})`)
  318. console.error(e)
  319. console.groupEnd()
  320. }
  321. }
  322. // 将事件挂载到document中
  323. script.innerHTML = code
  324. document.body.appendChild(script)
  325. }
  326. /**
  327. * 重复值验证工具方法
  328. *
  329. * 使用示例:
  330. * { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
  331. *
  332. * @param tableName 被验证的表名
  333. * @param fieldName 被验证的字段名
  334. * @param fieldVal 被验证的值
  335. * @param dataId 数据ID,可空
  336. * @param callback
  337. */
  338. export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
  339. if (fieldVal) {
  340. let params = { tableName, fieldName, fieldVal, dataId }
  341. api.duplicateCheck(params).then(res => {
  342. res['success'] ? callback() : callback(res['message'])
  343. }).catch(err => {
  344. callback(err.message || err)
  345. })
  346. } else {
  347. callback()
  348. }
  349. }
  350. /**
  351. * 根据编码校验规则code,校验传入的值是否合法
  352. *
  353. * 使用示例:
  354. * { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
  355. *
  356. * @param ruleCode 编码校验规则 code
  357. * @param value 被验证的值
  358. * @param callback
  359. */
  360. export function validateCheckRule(ruleCode, value, callback) {
  361. if (ruleCode && value) {
  362. value = encodeURIComponent(value)
  363. api.checkRuleByCode({ ruleCode, value }).then(res => {
  364. res['success'] ? callback() : callback(res['message'])
  365. }).catch(err => {
  366. callback(err.message || err)
  367. })
  368. } else {
  369. callback()
  370. }
  371. }
  372. /**
  373. * 如果值不存在就 push 进数组,反之不处理
  374. * @param array 要操作的数据
  375. * @param value 要添加的值
  376. * @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
  377. * @returns {boolean} 成功 push 返回 true,不处理返回 false
  378. */
  379. export function pushIfNotExist(array, value, key) {
  380. for (let item of array) {
  381. if (key && (item[key] === value[key])) {
  382. return false
  383. } else if (item === value) {
  384. return false
  385. }
  386. }
  387. array.push(value)
  388. return true
  389. }
  390. /**
  391. * 可用于判断是否成功
  392. * @type {symbol}
  393. */
  394. export const succeedSymbol = Symbol()
  395. /**
  396. * 可用于判断是否失败
  397. * @type {symbol}
  398. */
  399. export const failedSymbol = Symbol()
  400. /**
  401. * 使 promise 无论如何都会 resolve,除非传入的参数不是一个Promise对象或返回Promise对象的方法
  402. * 一般用在 Promise.all 中
  403. *
  404. * @param promise 可传Promise对象或返回Promise对象的方法
  405. * @returns {Promise<any>}
  406. */
  407. export function alwaysResolve(promise) {
  408. return new Promise((resolve, reject) => {
  409. let p = promise
  410. if (typeof promise === 'function') {
  411. p = promise()
  412. }
  413. if (p instanceof Promise) {
  414. p.then(data => {
  415. resolve({ type: succeedSymbol, data })
  416. }).catch(error => {
  417. resolve({ type: failedSymbol, error })
  418. })
  419. } else {
  420. reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
  421. }
  422. })
  423. }
  424. /**
  425. * 简单实现防抖方法
  426. *
  427. * 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
  428. * 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
  429. * 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
  430. *
  431. * @param fn 要防抖的函数
  432. * @param delay 防抖的毫秒数
  433. * @returns {Function}
  434. */
  435. export function simpleDebounce(fn, delay = 100) {
  436. let timer = null
  437. return function () {
  438. let args = arguments
  439. if (timer) {
  440. clearTimeout(timer)
  441. }
  442. timer = setTimeout(() => {
  443. fn.apply(this, args)
  444. }, delay)
  445. }
  446. }
  447. /**
  448. * 不用正则的方式替换所有值
  449. * @param text 被替换的字符串
  450. * @param checker 替换前的内容
  451. * @param replacer 替换后的内容
  452. * @returns {String} 替换后的字符串
  453. */
  454. export function replaceAll(text, checker, replacer) {
  455. let lastText = text
  456. text = text.replace(checker, replacer)
  457. if (lastText !== text) {
  458. return replaceAll(text, checker, replacer)
  459. }
  460. return text
  461. }
  462. /**
  463. * 获取事件冒泡路径,兼容 IE11,Edge,Chrome,Firefox,Safari
  464. * 目前使用的地方:JEditableTable Span模式
  465. */
  466. export function getEventPath(event) {
  467. let target = event.target
  468. let path = (event.composedPath && event.composedPath()) || event.path
  469. if (path != null) {
  470. return (path.indexOf(window) < 0) ? path.concat(window) : path
  471. }
  472. if (target === window) {
  473. return [window]
  474. }
  475. let getParents = (node, memo) => {
  476. memo = memo || []
  477. const parentNode = node.parentNode
  478. if (!parentNode) {
  479. return memo
  480. } else {
  481. return getParents(parentNode, memo.concat(parentNode))
  482. }
  483. }
  484. return [target].concat(getParents(target), window)
  485. }
  486. /**
  487. * 根据组件名获取父级
  488. * @param vm
  489. * @param name
  490. * @returns {Vue | null|null|Vue}
  491. */
  492. export function getVmParentByName(vm, name) {
  493. let parent = vm.$parent
  494. if (parent && parent.$options) {
  495. if (parent.$options.name === name) {
  496. return parent
  497. } else {
  498. let res = getVmParentByName(parent, name)
  499. if (res) {
  500. return res
  501. }
  502. }
  503. }
  504. return null
  505. }
  506. /**
  507. * 使一个值永远不会为(null | undefined)
  508. *
  509. * @param value 要处理的值
  510. * @param def 默认值,如果value为(null | undefined)则返回的默认值,可不传,默认为''
  511. */
  512. export function neverNull(value, def) {
  513. return value == null ? (neverNull(def, '')) : value
  514. }
  515. /**
  516. * 根据元素值移除数组中的一个元素
  517. * @param array 数组
  518. * @param prod 属性名
  519. * @param value 属性值
  520. * @returns {string}
  521. */
  522. export function removeArrayElement(array, prod, value) {
  523. let index = -1
  524. for(let i = 0;i<array.length;i++){
  525. if(array[i][prod] == value){
  526. index = i;
  527. break;
  528. }
  529. }
  530. if(index>=0){
  531. array.splice(index, 1);
  532. }
  533. }
  534. /** 判断是否是OAuth2APP环境 */
  535. export function isOAuth2AppEnv() {
  536. return /wxwork|dingtalk/i.test(navigator.userAgent)
  537. }
  538. /**
  539. * 获取积木报表打印地址
  540. * @param url
  541. * @param id
  542. * @param open 是否自动打开
  543. * @returns {*}
  544. */
  545. export function getReportPrintUrl(url, id, open) {
  546. // URL支持{{ window.xxx }}占位符变量
  547. url = url.replace(/{{([^}]+)?}}/g, (s1, s2) => eval(s2))
  548. if (url.includes('?')) {
  549. url += '&'
  550. } else {
  551. url += '?'
  552. }
  553. url += `id=${id}`
  554. url += `&token=${Vue.ls.get(ACCESS_TOKEN)}`
  555. if (open) {
  556. window.open(url)
  557. }
  558. return url
  559. }
  560. /**
  561. * JS实现AOP切面
  562. *
  563. * @param obj 包含函数的对象
  564. * @param funcName 要切面的函数名
  565. * @param callback 执行方法前的回调,用于切面,callback的返回值就是funcName最终的返回值
  566. */
  567. export function aspectAroundFunction(obj, funcName, callback) {
  568. if (typeof callback !== 'function' || !obj) {
  569. console.warn('【aspectAroundFunction】obj或callback格式不正确')
  570. return
  571. }
  572. // 保存原来的函数
  573. let func = obj[funcName]
  574. if (typeof func !== 'function') {
  575. console.warn('【aspectAroundFunction】' + funcName + '不是一个方法')
  576. return
  577. }
  578. // 赋值新方法
  579. // 实现当外部调用 funcName 时,首先调用我定义的新方法
  580. // 然后调用传入的callback方法,以决定是否执行 funcName,以及更改参数、返回值
  581. obj[funcName] = function (...args) {
  582. return callback({
  583. args,
  584. // 只有执行 proceed 才会真正执行给定的 funcName 方法
  585. proceed() {
  586. try {
  587. return func.apply(obj, args)
  588. } catch (e) {
  589. console.error(e)
  590. }
  591. },
  592. })
  593. }
  594. }