小说小程序后端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

628 lines
18 KiB

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