瀏覽代碼

build: 加载优化

SJ 4 天之前
父節點
當前提交
81009b2f48
共有 7 個文件被更改,包括 1024 次插入1016 次删除
  1. 3 3
      package.json
  2. 1 1
      src/components/page/GlobalHeader.vue
  3. 6 6
      src/main.js
  4. 193 193
      src/utils/request.js
  5. 689 689
      src/utils/util.js
  6. 6 6
      src/views/lg/ProdPlan/ProdPlanList.vue
  7. 126 118
      vue.config.js

+ 3 - 3
package.json

@@ -33,6 +33,7 @@
     "dayjs": "^1.8.0",
     "diagram-js-minimap": "^2.0.4",
     "dom-align": "1.12.0",
+    "echarts": "^5.4.3",
     "element-ui": "2.15.6",
     "enquire.js": "^2.1.6",
     "file-saver": "2.0.4",
@@ -46,6 +47,7 @@
     "mathjs": "^9.5.1",
     "md5": "^2.2.1",
     "min-dash": "^3.5.2",
+    "mini-css-extract-plugin": "^2.9.2",
     "nprogress": "^0.2.0",
     "print-js": "^1.6.0",
     "qiankun": "^2.5.1",
@@ -85,8 +87,7 @@
     "x2js": "^3.4.2",
     "xe-utils": "2.4.8",
     "xml-js": "^1.6.11",
-    "xss": "^1.0.13",
-    "echarts": "^5.4.3"
+    "xss": "^1.0.13"
   },
   "devDependencies": {
     "@babel/polyfill": "^7.2.5",
@@ -96,7 +97,6 @@
     "@vue/compiler-sfc": "^3.0.1",
     "@vue/eslint-config-prettier": "^5.0.0",
     "@vue/eslint-config-standard": "^4.0.0",
-    "@jeecg/antd-online-mini": "3.4.3-beta2",
     "babel-eslint": "^10.1.0",
     "camunda-bpmn-moddle": "^4.4.1",
     "compression-webpack-plugin": "^3.1.0",

+ 1 - 1
src/components/page/GlobalHeader.vue

@@ -18,7 +18,7 @@
         @click="toggle"/>
 
       <!-- <span v-if="device === 'desktop'">欢迎</span> -->
-      <span>LG</span>
+      <!-- <span>LG</span> -->
 
       <user-menu :theme="theme"/>
     </div>

+ 6 - 6
src/main.js

@@ -8,12 +8,12 @@ import router from './router'
 import store from './store/'
 import { VueAxios } from "@/utils/request"
 
-require('@jeecg/antd-online-mini')
-require('@jeecg/antd-online-mini/dist/OnlineForm.css')
-
-import Antd, { version } from 'ant-design-vue'
-console.log('ant-design-vue version:', version)
+// require('@jeecg/antd-online-mini')
+// require('@jeecg/antd-online-mini/dist/OnlineForm.css')
 
+// import Antd, { version } from 'ant-design-vue'
+// console.log('ant-design-vue version:', version)
+import './components/lazy_antd'
 import Viser from 'viser-vue'
 import 'ant-design-vue/dist/antd.less';  // or 'ant-design-vue/dist/antd.less'
 
@@ -54,7 +54,7 @@ Vue.prototype.$echarts = echarts
 Vue.prototype.rules = rules
 Vue.config.productionTip = false
 Vue.use(Storage, config.storageOptions)
-Vue.use(Antd)
+// Vue.use(Antd)
 Vue.use(VueAxios, router)
 Vue.use(Viser)
 Vue.use(hasPermission)

+ 193 - 193
src/utils/request.js

@@ -1,194 +1,194 @@
-import Vue from 'vue'
-import axios from 'axios'
-import store from '@/store'
-import { VueAxios } from './axios'
-import router from '@/router/index'
-import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
-
-/**
- * 【指定 axios的 baseURL】
- * 如果手工指定 baseURL: '/jeecg-boot'
- * 则映射后端域名,通过 vue.config.js
- * @type {*|string}
- */
-let apiBaseUrl = window._CONFIG['domianURL'] || "/jeecg-boot";
-//console.log("apiBaseUrl= ",apiBaseUrl)
-// 创建 axios 实例
-const service = axios.create({
-  //baseURL: '/jeecg-boot',
-  baseURL: apiBaseUrl, // api base_url
-  timeout: 9000 // 请求超时时间
-})
-
-const err = (error) => {
-  if (error.response) {
-    let data = error.response.data
-    const token = Vue.ls.get(ACCESS_TOKEN)
-    console.log("------异常响应------",token)
-    console.log("------异常响应------",error.response.status)
-    switch (error.response.status) {
-      case 403:
-        Vue.prototype.$Jnotification.error({ message: '系统提示', description: '拒绝访问',duration: 4})
-        break
-      case 500:
-        console.log("------error.response------",error.response)
-        // update-begin- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
-        let type=error.response.request.responseType;
-        if(type === 'blob'){
-          blobToJson(data);
-          break;
-        }
-        // update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
-        if(token && data.message.includes("Token失效")){
-          // update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
-          if (/wxwork|dingtalk/i.test(navigator.userAgent)) {
-            Vue.prototype.$Jmessage.loading('登录已过期,正在重新登陆', 0)
-          } else {
-            Vue.prototype.$Jmodal.error({
-              title: '登录已过期',
-              content: '很抱歉,登录已过期,请重新登录',
-              okText: '重新登录',
-              mask: false,
-              onOk: () => {
-                store.dispatch('Logout').then(() => {
-                  Vue.ls.remove(ACCESS_TOKEN)
-                  try {
-                    let path = window.document.location.pathname
-                    console.log('location pathname -> ' + path)
-                    if (path != '/' && path.indexOf('/user/login') == -1) {
-                      window.location.reload()
-                    }
-                  } catch (e) {
-                    window.location.reload()
-                  }
-                })
-              }
-            })
-          }
-          // update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
-        }
-        break
-      case 404:
-          Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉,资源未找到!',duration: 4})
-        break
-      case 504:
-        Vue.prototype.$Jnotification.error({ message: '系统提示', description: '网络超时'})
-        break
-      case 401:
-        Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉,登录已过期,请重新登录',duration: 4})
-        if (token) {
-          store.dispatch('Logout').then(() => {
-            setTimeout(() => {
-              window.location.reload()
-            }, 1500)
-          })
-        }
-        break
-      default:
-        Vue.prototype.$Jnotification.error({
-          message: '系统提示',
-          description: data.message,
-          duration: 4
-        })
-        break
-    }
-  } else if (error.message) {
-    if (error.message.includes('timeout')) {
-      Vue.prototype.$Jnotification.error({message: '系统提示', description: '网络超时'})
-    } else {
-      Vue.prototype.$Jnotification.error({message: '系统提示', description: error.message})
-    }
-  }
-  return Promise.reject(error)
-};
-
-// request interceptor
-service.interceptors.request.use(config => {
-  const token = Vue.ls.get(ACCESS_TOKEN)
-  if (token) {
-    config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
-  }
-
-  // update-begin--author:sunjianlei---date:20200723---for 如果当前在low-app环境,并且携带了appId,就向Header里传递appId
-  const $route = router.currentRoute
-  if ($route && $route.name && $route.name.startsWith('low-app') && $route.params.appId) {
-    config.headers['X-Low-App-ID'] = $route.params.appId
-    // lowApp自定义筛选条件
-    if ($route.params.lowAppFilter) {
-      config.params = {...config.params, ...$route.params.lowAppFilter}
-      delete $route.params.lowAppFilter
-    }
-  }
-  // update-end--author:sunjianlei---date:20200723---for 如果当前在low-app环境,并且携带了appId,就向Header里传递appId
-
-  //update-begin-author:taoyan date:2020707 for:多租户
-  let tenantid = Vue.ls.get(TENANT_ID)
-  if (!tenantid) {
-    tenantid = 0;
-  }
-  config.headers[ 'tenant-id' ] = tenantid
-  //update-end-author:taoyan date:2020707 for:多租户
-  if(config.method=='get'){
-    if(config.url.indexOf("sys/dict/getDictItems")<0){
-      config.params = {
-        _t: Date.parse(new Date())/1000,
-        ...config.params
-      }
-    }
-  }
-  return config
-},(error) => {
-  return Promise.reject(error)
-})
-
-// response interceptor
-service.interceptors.response.use((response) => {
-    return response.data
-  }, err)
-
-const installer = {
-  vm: {},
-  install (Vue, router = {}) {
-    Vue.use(VueAxios, router, service)
-  }
-}
-/**
- * Blob解析
- * @param data
- */
-function blobToJson(data) {
-  let fileReader = new FileReader();
-  let token = Vue.ls.get(ACCESS_TOKEN);
-  fileReader.onload = function() {
-    try {
-      let jsonData = JSON.parse(this.result);  // 说明是普通对象数据,后台转换失败
-      console.log("jsonData",jsonData)
-      if (jsonData.status === 500) {
-        console.log("token----------》",token)
-        if(token && jsonData.message.includes("Token失效")){
-          Modal.error({
-            title: '登录已过期',
-            content: '很抱歉,登录已过期,请重新登录',
-            okText: '重新登录',
-            mask: false,
-            onOk: () => {
-              store.dispatch('Logout').then(() => {
-                Vue.ls.remove(ACCESS_TOKEN)
-                window.location.reload()
-              })
-            }
-          })
-        }
-      }
-    } catch (err) {
-      // 解析成对象失败,说明是正常的文件流
-      console.log("blob解析fileReader返回err",err)
-    }
-  };
-  fileReader.readAsText(data)
-}
-
-export {
-  installer as VueAxios,
-  service as axios
+import Vue from 'vue'
+import axios from 'axios'
+import store from '@/store'
+import { VueAxios } from './axios'
+import router from '@/router/index'
+import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
+
+/**
+ * 【指定 axios的 baseURL】
+ * 如果手工指定 baseURL: '/jeecg-boot'
+ * 则映射后端域名,通过 vue.config.js
+ * @type {*|string}
+ */
+let apiBaseUrl = window._CONFIG['domianURL'] || "/jeecg-boot";
+//console.log("apiBaseUrl= ",apiBaseUrl)
+// 创建 axios 实例
+const service = axios.create({
+  //baseURL: '/jeecg-boot',
+  baseURL: apiBaseUrl, // api base_url
+  timeout: 60000 // 请求超时时间
+})
+
+const err = (error) => {
+  if (error.response) {
+    let data = error.response.data
+    const token = Vue.ls.get(ACCESS_TOKEN)
+    console.log("------异常响应------",token)
+    console.log("------异常响应------",error.response.status)
+    switch (error.response.status) {
+      case 403:
+        Vue.prototype.$Jnotification.error({ message: '系统提示', description: '拒绝访问',duration: 4})
+        break
+      case 500:
+        console.log("------error.response------",error.response)
+        // update-begin- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
+        let type=error.response.request.responseType;
+        if(type === 'blob'){
+          blobToJson(data);
+          break;
+        }
+        // update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
+        if(token && data.message.includes("Token失效")){
+          // update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
+          if (/wxwork|dingtalk/i.test(navigator.userAgent)) {
+            Vue.prototype.$Jmessage.loading('登录已过期,正在重新登陆', 0)
+          } else {
+            Vue.prototype.$Jmodal.error({
+              title: '登录已过期',
+              content: '很抱歉,登录已过期,请重新登录',
+              okText: '重新登录',
+              mask: false,
+              onOk: () => {
+                store.dispatch('Logout').then(() => {
+                  Vue.ls.remove(ACCESS_TOKEN)
+                  try {
+                    let path = window.document.location.pathname
+                    console.log('location pathname -> ' + path)
+                    if (path != '/' && path.indexOf('/user/login') == -1) {
+                      window.location.reload()
+                    }
+                  } catch (e) {
+                    window.location.reload()
+                  }
+                })
+              }
+            })
+          }
+          // update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式,不直接跳转----
+        }
+        break
+      case 404:
+          Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉,资源未找到!',duration: 4})
+        break
+      case 504:
+        Vue.prototype.$Jnotification.error({ message: '系统提示', description: '网络超时'})
+        break
+      case 401:
+        Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉,登录已过期,请重新登录',duration: 4})
+        if (token) {
+          store.dispatch('Logout').then(() => {
+            setTimeout(() => {
+              window.location.reload()
+            }, 1500)
+          })
+        }
+        break
+      default:
+        Vue.prototype.$Jnotification.error({
+          message: '系统提示',
+          description: data.message,
+          duration: 4
+        })
+        break
+    }
+  } else if (error.message) {
+    if (error.message.includes('timeout')) {
+      Vue.prototype.$Jnotification.error({message: '系统提示', description: '网络超时'})
+    } else {
+      Vue.prototype.$Jnotification.error({message: '系统提示', description: error.message})
+    }
+  }
+  return Promise.reject(error)
+};
+
+// request interceptor
+service.interceptors.request.use(config => {
+  const token = Vue.ls.get(ACCESS_TOKEN)
+  if (token) {
+    config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
+  }
+
+  // update-begin--author:sunjianlei---date:20200723---for 如果当前在low-app环境,并且携带了appId,就向Header里传递appId
+  const $route = router.currentRoute
+  if ($route && $route.name && $route.name.startsWith('low-app') && $route.params.appId) {
+    config.headers['X-Low-App-ID'] = $route.params.appId
+    // lowApp自定义筛选条件
+    if ($route.params.lowAppFilter) {
+      config.params = {...config.params, ...$route.params.lowAppFilter}
+      delete $route.params.lowAppFilter
+    }
+  }
+  // update-end--author:sunjianlei---date:20200723---for 如果当前在low-app环境,并且携带了appId,就向Header里传递appId
+
+  //update-begin-author:taoyan date:2020707 for:多租户
+  let tenantid = Vue.ls.get(TENANT_ID)
+  if (!tenantid) {
+    tenantid = 0;
+  }
+  config.headers[ 'tenant-id' ] = tenantid
+  //update-end-author:taoyan date:2020707 for:多租户
+  if(config.method=='get'){
+    if(config.url.indexOf("sys/dict/getDictItems")<0){
+      config.params = {
+        _t: Date.parse(new Date())/1000,
+        ...config.params
+      }
+    }
+  }
+  return config
+},(error) => {
+  return Promise.reject(error)
+})
+
+// response interceptor
+service.interceptors.response.use((response) => {
+    return response.data
+  }, err)
+
+const installer = {
+  vm: {},
+  install (Vue, router = {}) {
+    Vue.use(VueAxios, router, service)
+  }
+}
+/**
+ * Blob解析
+ * @param data
+ */
+function blobToJson(data) {
+  let fileReader = new FileReader();
+  let token = Vue.ls.get(ACCESS_TOKEN);
+  fileReader.onload = function() {
+    try {
+      let jsonData = JSON.parse(this.result);  // 说明是普通对象数据,后台转换失败
+      console.log("jsonData",jsonData)
+      if (jsonData.status === 500) {
+        console.log("token----------》",token)
+        if(token && jsonData.message.includes("Token失效")){
+          Modal.error({
+            title: '登录已过期',
+            content: '很抱歉,登录已过期,请重新登录',
+            okText: '重新登录',
+            mask: false,
+            onOk: () => {
+              store.dispatch('Logout').then(() => {
+                Vue.ls.remove(ACCESS_TOKEN)
+                window.location.reload()
+              })
+            }
+          })
+        }
+      }
+    } catch (err) {
+      // 解析成对象失败,说明是正常的文件流
+      console.log("blob解析fileReader返回err",err)
+    }
+  };
+  fileReader.readAsText(data)
+}
+
+export {
+  installer as VueAxios,
+  service as axios
 }

+ 689 - 689
src/utils/util.js

@@ -1,689 +1,689 @@
-import Vue from 'vue'
-import * as api from '@/api/api'
-import { isURL } from '@/utils/validate'
-import { ACCESS_TOKEN } from '@/store/mutation-types'
-import onlineCommons from '@jeecg/antd-online-mini'
-
-export function timeFix() {
-  const time = new Date()
-  const hour = time.getHours()
-  return hour < 9 ? '早上好' : (hour <= 11 ? '上午好' : (hour <= 13 ? '中午好' : (hour < 20 ? '下午好' : '晚上好')))
-}
-
-export function welcome() {
-  const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
-  let index = Math.floor((Math.random()*arr.length))
-  return arr[index]
-}
-
-/**
- * 触发 window.resize
- */
-export function triggerWindowResizeEvent() {
-  let event = document.createEvent('HTMLEvents')
-  event.initEvent('resize', true, true)
-  event.eventType = 'message'
-  window.dispatchEvent(event)
-}
-
-/**
- * 过滤对象中为空的属性
- * @param obj
- * @returns {*}
- */
-export function filterObj(obj) {
-  if (!(typeof obj == 'object')) {
-    return;
-  }
-
-  for ( let key in obj) {
-    if (obj.hasOwnProperty(key)
-      && (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
-      delete obj[key];
-    }
-  }
-  return obj;
-}
-
-/**
- * 时间格式化
- * @param value
- * @param fmt
- * @returns {*}
- */
-export function formatDate(value, fmt) {
-  let regPos = /^\d+(\.\d+)?$/;
-  if(regPos.test(value)){
-    //如果是数字
-    let getDate = new Date(value);
-    let o = {
-      'M+': getDate.getMonth() + 1,
-      'd+': getDate.getDate(),
-      'h+': getDate.getHours(),
-      'm+': getDate.getMinutes(),
-      's+': getDate.getSeconds(),
-      'q+': Math.floor((getDate.getMonth() + 3) / 3),
-      'S': getDate.getMilliseconds()
-    };
-    if (/(y+)/.test(fmt)) {
-      fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
-    }
-    for (let k in o) {
-      if (new RegExp('(' + k + ')').test(fmt)) {
-        fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
-      }
-    }
-    return fmt;
-  }else{
-    //TODO
-    value = value.trim();
-    return value.substr(0,fmt.length);
-  }
-}
-
-// 生成首页路由
-export function generateIndexRouter(data) {
-  let indexRouter = [{
-    path: '/',
-    name: 'dashboard',
-    //component: () => import('@/components/layouts/BasicLayout'),
-    component: resolve => require(['@/components/layouts/TabLayout'], resolve),
-    meta: { title: '首页' },
-    redirect: '/dashboard/analysis',
-    children: [
-      ...generateChildRouters(data)
-    ]
-  },{
-    "path": "*", "redirect": "/404", "hidden": true
-  }]
-  return indexRouter;
-}
-
-// 生成嵌套路由(子路由)
-
-function  generateChildRouters (data) {
-  const routers = [];
-  for (let item of data) {
-    let component = "";
-    if(item.component.indexOf("layouts")>=0){
-      component = "components/"+item.component;
-    }else{
-      component = "views/"+item.component;
-    }
-
-    // eslint-disable-next-line
-    let URL = (item.meta.url|| '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)) // URL支持{{ window.xxx }}占位符变量
-    if (isURL(URL)) {
-      item.meta.url = URL;
-    }
-
-    let componentPath
-    if(item.component=="modules/online/cgform/OnlCgformHeadList"){
-      componentPath = onlineCommons.OnlCgformHeadList
-    }else if(item.component=="modules/online/cgform/OnlCgformCopyList"){
-      componentPath = onlineCommons.OnlCgformCopyList
-    }else if(item.component=="modules/online/cgform/auto/OnlCgformAutoList"){
-      componentPath = onlineCommons.OnlCgformAutoList
-    }else if(item.component=="modules/online/cgform/auto/OnlCgformTreeList"){
-      componentPath = onlineCommons.OnlCgformTreeList
-    }else if(item.component=="modules/online/cgform/auto/erp/OnlCgformErpList"){
-      componentPath = onlineCommons.OnlCgformErpList
-    }else if(item.component=="modules/online/cgform/auto/tab/OnlCgformTabList"){
-      componentPath = onlineCommons.OnlCgformTabList
-    }else if(item.component=="modules/online/cgform/auto/innerTable/OnlCgformInnerTableList"){
-      componentPath = onlineCommons.OnlCgformInnerTableList
-    }else if(item.component=="modules/online/cgreport/OnlCgreportHeadList"){
-      componentPath = onlineCommons.OnlCgreportHeadList
-    }else if(item.component=="modules/online/cgreport/auto/OnlCgreportAutoList"){
-      componentPath = onlineCommons.OnlCgreportAutoList
-    }else{
-      componentPath = resolve => require(['@/' + component+'.vue'], resolve)
-    }
-
-    let menu =  {
-      path: item.path,
-      name: item.name,
-      redirect:item.redirect,
-      component: componentPath,
-      //component: resolve => require(['@/' + component+'.vue'], resolve),
-      hidden:item.hidden,
-      //component:()=> import(`@/views/${item.component}.vue`),
-      meta: {
-        title:item.meta.title ,
-        icon: item.meta.icon,
-        url:item.meta.url ,
-        permissionList:item.meta.permissionList,
-        keepAlive:item.meta.keepAlive,
-        /*update_begin author:wuxianquan date:20190908 for:赋值 */
-        internalOrExternal:item.meta.internalOrExternal,
-        /*update_end author:wuxianquan date:20190908 for:赋值 */
-        componentName:item.meta.componentName
-      }
-    }
-    if(item.alwaysShow){
-      menu.alwaysShow = true;
-      menu.redirect = menu.path;
-    }
-    if (item.children && item.children.length > 0) {
-      menu.children = [...generateChildRouters( item.children)];
-    }
-    //--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
-    //判断是否生成路由
-    if(item.route && item.route === '0'){
-      //console.log(' 不生成路由 item.route:  '+item.route);
-      //console.log(' 不生成路由 item.path:  '+item.path);
-    }else{
-      routers.push(menu);
-    }
-    //--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
-  }
-  return routers
-}
-
-/**
- * 深度克隆对象、数组
- * @param obj 被克隆的对象
- * @return 克隆后的对象
- */
-export function cloneObject(obj) {
-  return JSON.parse(JSON.stringify(obj))
-}
-
-/**
- * 随机生成数字
- *
- * 示例:生成长度为 12 的随机数:randomNumber(12)
- * 示例:生成 3~23 之间的随机数:randomNumber(3, 23)
- *
- * @param1 最小值 | 长度
- * @param2 最大值
- * @return int 生成后的数字
- */
-export function randomNumber() {
-  // 生成 最小值 到 最大值 区间的随机数
-  const random = (min, max) => {
-    return Math.floor(Math.random() * (max - min + 1) + min)
-  }
-  if (arguments.length === 1) {
-    let [length] = arguments
-  // 生成指定长度的随机数字,首位一定不是 0
-    let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
-    return parseInt(nums.join(''))
-  } else if (arguments.length >= 2) {
-    let [min, max] = arguments
-    return random(min, max)
-  } else {
-    return Number.NaN
-  }
-}
-
-/**
- * 随机生成字符串
- * @param length 字符串的长度
- * @param chats 可选字符串区间(只会生成传入的字符串中的字符)
- * @return string 生成的字符串
- */
-export function randomString(length, chats) {
-  if (!length) length = 1
-  if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
-  let str = ''
-  for (let i = 0; i < length; i++) {
-    let num = randomNumber(0, chats.length - 1)
-    str += chats[num]
-  }
-  return str
-}
-
-/**
- * 随机生成uuid
- * @return string 生成的uuid
- */
-export function randomUUID() {
-  let chats = '0123456789abcdef'
-  return randomString(32, chats)
-}
-
-/**
- * 下划线转驼峰
- * @param string
- * @returns {*}
- */
-export function underLine2CamelCase(string){
-  return string.replace( /_([a-z])/g, function( all, letter ) {
-    return letter.toUpperCase();
-  });
-}
-
-/**
- * 判断是否显示办理按钮
- * @param bpmStatus
- * @returns {*}
- */
-export function showDealBtn(bpmStatus){
-  if(bpmStatus!="1"&&bpmStatus!="3"&&bpmStatus!="4"){
-    return true;
-  }
-  return false;
-}
-
-/**
- * 增强CSS,可以在页面上输出全局css
- * @param css 要增强的css
- * @param id style标签的id,可以用来清除旧样式
- */
-export function cssExpand(css, id) {
-  let style = document.createElement('style')
-  style.type = "text/css"
-  style.innerHTML = `@charset "UTF-8"; ${css}`
-  // 清除旧样式
-  if (id) {
-    let $style = document.getElementById(id)
-    if ($style != null) $style.outerHTML = ''
-    style.id = id
-  }
-  // 应用新样式
-  document.head.appendChild(style)
-}
-
-
-/** 用于js增强事件,运行JS代码,可以传参 */
-// options 所需参数:
-//    参数名         类型            说明
-//    vm             VueComponent    vue实例
-//    event          Object          event对象
-//    jsCode         String          待执行的js代码
-//    errorMessage   String          执行出错后的提示(控制台)
-export function jsExpand(options = {}) {
-
-  // 绑定到window上的keyName
-  let windowKeyName = 'J_CLICK_EVENT_OPTIONS'
-  if (typeof window[windowKeyName] != 'object') {
-    window[windowKeyName] = {}
-  }
-
-  // 随机生成JS增强的执行id,防止冲突
-  let id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
-  // 封装按钮点击事件
-  let code = `
-    (function (o_${id}) {
-      try {
-        (function (globalEvent, vm) {
-          ${options.jsCode}
-        })(o_${id}.event, o_${id}.vm)
-      } catch (e) {
-        o_${id}.error(e)
-      }
-      o_${id}.done()
-    })(window['${windowKeyName}']['EVENT_${id}'])
-  `
-  // 创建script标签
-  const script = document.createElement('script')
-  // 将需要传递的参数挂载到window对象上
-  window[windowKeyName]['EVENT_' + id] = {
-    vm: options.vm,
-    event: options.event,
-    // 当执行完成时,无论如何都会调用的回调事件
-    done() {
-      // 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
-      script.outerHTML = ''
-      delete window[windowKeyName]['EVENT_' + id]
-    },
-    // 当js运行出错的时候调用的事件
-    error(e) {
-      console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}(${new Date()})`)
-      console.error(e)
-      console.groupEnd()
-    }
-  }
-  // 将事件挂载到document中
-  script.innerHTML = code
-  document.body.appendChild(script)
-}
-
-
-/**
- * 重复值验证工具方法
- *
- * 使用示例:
- * { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
- *
- * @param tableName 被验证的表名
- * @param fieldName 被验证的字段名
- * @param fieldVal 被验证的值
- * @param dataId 数据ID,可空
- * @param callback
- */
-export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
-  if (fieldVal) {
-    let params = { tableName, fieldName, fieldVal, dataId }
-    api.duplicateCheck(params).then(res => {
-      res['success'] ? callback() : callback(res['message'])
-    }).catch(err => {
-      callback(err.message || err)
-    })
-  } else {
-    callback()
-  }
-}
-
-/**
- * 根据编码校验规则code,校验传入的值是否合法
- *
- * 使用示例:
- * { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
- *
- * @param ruleCode 编码校验规则 code
- * @param value 被验证的值
- * @param callback
- */
-export function validateCheckRule(ruleCode, value, callback) {
-  if (ruleCode && value) {
-    value = encodeURIComponent(value)
-    api.checkRuleByCode({ ruleCode, value }).then(res => {
-      res['success'] ? callback() : callback(res['message'])
-    }).catch(err => {
-      callback(err.message || err)
-    })
-  } else {
-    callback()
-  }
-}
-
-/**
- * 如果值不存在就 push 进数组,反之不处理
- * @param array 要操作的数据
- * @param value 要添加的值
- * @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
- * @returns {boolean} 成功 push 返回 true,不处理返回 false
- */
-export function pushIfNotExist(array, value, key) {
-  for (let item of array) {
-    if (key && (item[key] === value[key])) {
-      return false
-    } else if (item === value) {
-      return false
-    }
-  }
-  array.push(value)
-  return true
-}
-
-/**
- * 可用于判断是否成功
- * @type {symbol}
- */
-export const succeedSymbol = Symbol()
-/**
- * 可用于判断是否失败
- * @type {symbol}
- */
-export const failedSymbol = Symbol()
-
-/**
- * 使 promise 无论如何都会 resolve,除非传入的参数不是一个Promise对象或返回Promise对象的方法
- * 一般用在 Promise.all 中
- *
- * @param promise 可传Promise对象或返回Promise对象的方法
- * @returns {Promise<any>}
- */
-export function alwaysResolve(promise) {
-  return new Promise((resolve, reject) => {
-    let p = promise
-    if (typeof promise === 'function') {
-      p = promise()
-    }
-    if (p instanceof Promise) {
-      p.then(data => {
-        resolve({ type: succeedSymbol, data })
-      }).catch(error => {
-        resolve({ type: failedSymbol, error })
-      })
-    } else {
-      reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
-    }
-  })
-}
-
-/**
- * 简单实现防抖方法
- *
- * 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
- * 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
- * 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
- *
- * @param fn 要防抖的函数
- * @param delay 防抖的毫秒数
- * @returns {Function}
- */
-export function simpleDebounce(fn, delay = 100) {
-  let timer = null
-  return function () {
-    let args = arguments
-    if (timer) {
-      clearTimeout(timer)
-    }
-    timer = setTimeout(() => {
-      fn.apply(this, args)
-    }, delay)
-  }
-}
-
-/**
- * 不用正则的方式替换所有值
- * @param text 被替换的字符串
- * @param checker  替换前的内容
- * @param replacer 替换后的内容
- * @returns {String} 替换后的字符串
- */
-export function replaceAll(text, checker, replacer) {
-  let lastText = text
-  text = text.replace(checker, replacer)
-  if (lastText !== text) {
-    return replaceAll(text, checker, replacer)
-  }
-  return text
-}
-
-/**
- * 获取事件冒泡路径,兼容 IE11,Edge,Chrome,Firefox,Safari
- * 目前使用的地方:JEditableTable Span模式
- */
-export function getEventPath(event) {
-  let target = event.target
-  let path = (event.composedPath && event.composedPath()) || event.path
-
-  if (path != null) {
-    return (path.indexOf(window) < 0) ? path.concat(window) : path
-  }
-
-  if (target === window) {
-    return [window]
-  }
-
-  let getParents = (node, memo) => {
-    memo = memo || []
-    const parentNode = node.parentNode
-
-    if (!parentNode) {
-      return memo
-    } else {
-      return getParents(parentNode, memo.concat(parentNode))
-    }
-  }
-  return [target].concat(getParents(target), window)
-}
-
-/**
- * 根据组件名获取父级
- * @param vm
- * @param name
- * @returns {Vue | null|null|Vue}
- */
-export function getVmParentByName(vm, name) {
-  let parent = vm.$parent
-  if (parent && parent.$options) {
-    if (parent.$options.name === name) {
-      return parent
-    } else {
-      let res = getVmParentByName(parent, name)
-      if (res) {
-        return res
-      }
-    }
-  }
-  return null
-}
-
-/**
- * 使一个值永远不会为(null | undefined)
- *
- * @param value 要处理的值
- * @param def 默认值,如果value为(null | undefined)则返回的默认值,可不传,默认为''
- */
-export function neverNull(value, def) {
-  return value == null ? (neverNull(def, '')) : value
-}
-
-/**
- * 根据元素值移除数组中的一个元素
- * @param array 数组
- * @param prod 属性名
- * @param value 属性值
- * @returns {string}
- */
-export function removeArrayElement(array, prod, value) {
-  let index = -1
-  for(let i = 0;i<array.length;i++){
-    if(array[i][prod] == value){
-      index = i;
-      break;
-    }
-  }
-  if(index>=0){
-    array.splice(index, 1);
-  }
-}
-
-/** 判断是否是OAuth2APP环境 */
-export function isOAuth2AppEnv() {
-  return /wxwork|dingtalk/i.test(navigator.userAgent)
-}
-
-/**
- * 获取积木报表打印地址
- * @param url
- * @param id
- * @param open 是否自动打开
- * @returns {*}
- */
-export function getReportPrintUrl(url, id, open) {
-  // URL支持{{ window.xxx }}占位符变量
-  url = url.replace(/{{([^}]+)?}}/g, (s1, s2) => eval(s2))
-  if (url.includes('?')) {
-    url += '&'
-  } else {
-    url += '?'
-  }
-  url += `id=${id}`
-  url += `&token=${Vue.ls.get(ACCESS_TOKEN)}`
-  if (open) {
-    window.open(url)
-  }
-  return url
-}
-
-/**
- * JS实现AOP切面
- *
- * @param obj 包含函数的对象
- * @param funcName 要切面的函数名
- * @param callback 执行方法前的回调,用于切面,callback的返回值就是funcName最终的返回值
- */
-export function aspectAroundFunction(obj, funcName, callback) {
-  if (typeof callback !== 'function' || !obj) {
-    console.warn('【aspectAroundFunction】obj或callback格式不正确')
-    return
-  }
-  // 保存原来的函数
-  let func = obj[funcName]
-  if (typeof func !== 'function') {
-    console.warn('【aspectAroundFunction】' + funcName + '不是一个方法')
-    return
-  }
-  // 赋值新方法
-  // 实现当外部调用 funcName 时,首先调用我定义的新方法
-  // 然后调用传入的callback方法,以决定是否执行 funcName,以及更改参数、返回值
-  obj[funcName] = function (...args) {
-    return callback({
-      args,
-      // 只有执行 proceed 才会真正执行给定的 funcName 方法
-      proceed() {
-        try {
-          return func.apply(obj, args)
-        } catch (e) {
-          console.error(e)
-        }
-      },
-    })
-  }
-}
-
-/**
- * 休眠
- * @param ms 毫秒
- * @return {Promise<unknown>}
- */
-export function sleep(ms) {
-  return new Promise(function (resolve) {
-    return setTimeout(resolve, ms);
-  });
-}
-
-/**
- * 获取指定的 $refs 对象
- * 有时候可能会遇到组件未挂载到页面中的情况,导致无法获取 $refs 中的某个对象
- * 这个方法可以等待挂载完成之后再返回 $refs 的对象,避免报错
- *
- * 用法示例:let modalRef = getRefPromise(this, 'modal')
- * @param vm vue实例
- * @param name 要获取的ref名称
- * @param noComment $el 标签不能是注释
- **/
-export function getRefPromise(vm, name, noComment = true) {
-  return new Promise((resolve) => {
-    (function next() {
-      let ref = vm.$refs[name]
-      if (ref && (noComment && ref.$el.tagName)) {
-        resolve(ref)
-      } else {
-        setTimeout(() => {
-          if (noComment) {
-            vm.$forceUpdate()
-          }
-          next()
-        }, 10)
-      }
-    })()
-  })
-}
-
-/**
- * 导出文件xlsx的mime-type
- * xls:	application/vnd.ms-excel
- * @type {string}
- */
-export const EXPORT_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
-/**
- * 导出excel文件后缀
- * @type {string}
- */
-export const EXPORT_FILE_SUFFIX = ".xlsx";
-
-/**
- * 字符串是否为null或null字符串
- * @param str
- * @return {boolean}
- */
-export function stringIsNull(str) {
-  return str == null || str === 'null' || str === 'undefined';
-}
+import Vue from 'vue'
+import * as api from '@/api/api'
+import { isURL } from '@/utils/validate'
+import { ACCESS_TOKEN } from '@/store/mutation-types'
+// import onlineCommons from '@jeecg/antd-online-mini'
+
+export function timeFix() {
+  const time = new Date()
+  const hour = time.getHours()
+  return hour < 9 ? '早上好' : (hour <= 11 ? '上午好' : (hour <= 13 ? '中午好' : (hour < 20 ? '下午好' : '晚上好')))
+}
+
+export function welcome() {
+  const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了']
+  let index = Math.floor((Math.random()*arr.length))
+  return arr[index]
+}
+
+/**
+ * 触发 window.resize
+ */
+export function triggerWindowResizeEvent() {
+  let event = document.createEvent('HTMLEvents')
+  event.initEvent('resize', true, true)
+  event.eventType = 'message'
+  window.dispatchEvent(event)
+}
+
+/**
+ * 过滤对象中为空的属性
+ * @param obj
+ * @returns {*}
+ */
+export function filterObj(obj) {
+  if (!(typeof obj == 'object')) {
+    return;
+  }
+
+  for ( let key in obj) {
+    if (obj.hasOwnProperty(key)
+      && (obj[key] == null || obj[key] == undefined || obj[key] === '')) {
+      delete obj[key];
+    }
+  }
+  return obj;
+}
+
+/**
+ * 时间格式化
+ * @param value
+ * @param fmt
+ * @returns {*}
+ */
+export function formatDate(value, fmt) {
+  let regPos = /^\d+(\.\d+)?$/;
+  if(regPos.test(value)){
+    //如果是数字
+    let getDate = new Date(value);
+    let o = {
+      'M+': getDate.getMonth() + 1,
+      'd+': getDate.getDate(),
+      'h+': getDate.getHours(),
+      'm+': getDate.getMinutes(),
+      's+': getDate.getSeconds(),
+      'q+': Math.floor((getDate.getMonth() + 3) / 3),
+      'S': getDate.getMilliseconds()
+    };
+    if (/(y+)/.test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
+    }
+    for (let k in o) {
+      if (new RegExp('(' + k + ')').test(fmt)) {
+        fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+      }
+    }
+    return fmt;
+  }else{
+    //TODO
+    value = value.trim();
+    return value.substr(0,fmt.length);
+  }
+}
+
+// 生成首页路由
+export function generateIndexRouter(data) {
+  let indexRouter = [{
+    path: '/',
+    name: 'dashboard',
+    //component: () => import('@/components/layouts/BasicLayout'),
+    component: resolve => require(['@/components/layouts/TabLayout'], resolve),
+    meta: { title: '首页' },
+    redirect: '/dashboard/analysis',
+    children: [
+      ...generateChildRouters(data)
+    ]
+  },{
+    "path": "*", "redirect": "/404", "hidden": true
+  }]
+  return indexRouter;
+}
+
+// 生成嵌套路由(子路由)
+
+function  generateChildRouters (data) {
+  const routers = [];
+  for (let item of data) {
+    let component = "";
+    if(item.component.indexOf("layouts")>=0){
+      component = "components/"+item.component;
+    }else{
+      component = "views/"+item.component;
+    }
+
+    // eslint-disable-next-line
+    let URL = (item.meta.url|| '').replace(/{{([^}}]+)?}}/g, (s1, s2) => eval(s2)) // URL支持{{ window.xxx }}占位符变量
+    if (isURL(URL)) {
+      item.meta.url = URL;
+    }
+
+    let componentPath
+    // if(item.component=="modules/online/cgform/OnlCgformHeadList"){
+    //   componentPath = onlineCommons.OnlCgformHeadList
+    // }else if(item.component=="modules/online/cgform/OnlCgformCopyList"){
+    //   componentPath = onlineCommons.OnlCgformCopyList
+    // }else if(item.component=="modules/online/cgform/auto/OnlCgformAutoList"){
+    //   componentPath = onlineCommons.OnlCgformAutoList
+    // }else if(item.component=="modules/online/cgform/auto/OnlCgformTreeList"){
+    //   componentPath = onlineCommons.OnlCgformTreeList
+    // }else if(item.component=="modules/online/cgform/auto/erp/OnlCgformErpList"){
+    //   componentPath = onlineCommons.OnlCgformErpList
+    // }else if(item.component=="modules/online/cgform/auto/tab/OnlCgformTabList"){
+    //   componentPath = onlineCommons.OnlCgformTabList
+    // }else if(item.component=="modules/online/cgform/auto/innerTable/OnlCgformInnerTableList"){
+    //   componentPath = onlineCommons.OnlCgformInnerTableList
+    // }else if(item.component=="modules/online/cgreport/OnlCgreportHeadList"){
+    //   componentPath = onlineCommons.OnlCgreportHeadList
+    // }else if(item.component=="modules/online/cgreport/auto/OnlCgreportAutoList"){
+    //   componentPath = onlineCommons.OnlCgreportAutoList
+    // }else{
+      componentPath = resolve => require(['@/' + component+'.vue'], resolve)
+    // }
+
+    let menu =  {
+      path: item.path,
+      name: item.name,
+      redirect:item.redirect,
+      component: componentPath,
+      //component: resolve => require(['@/' + component+'.vue'], resolve),
+      hidden:item.hidden,
+      //component:()=> import(`@/views/${item.component}.vue`),
+      meta: {
+        title:item.meta.title ,
+        icon: item.meta.icon,
+        url:item.meta.url ,
+        permissionList:item.meta.permissionList,
+        keepAlive:item.meta.keepAlive,
+        /*update_begin author:wuxianquan date:20190908 for:赋值 */
+        internalOrExternal:item.meta.internalOrExternal,
+        /*update_end author:wuxianquan date:20190908 for:赋值 */
+        componentName:item.meta.componentName
+      }
+    }
+    if(item.alwaysShow){
+      menu.alwaysShow = true;
+      menu.redirect = menu.path;
+    }
+    if (item.children && item.children.length > 0) {
+      menu.children = [...generateChildRouters( item.children)];
+    }
+    //--update-begin----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
+    //判断是否生成路由
+    if(item.route && item.route === '0'){
+      //console.log(' 不生成路由 item.route:  '+item.route);
+      //console.log(' 不生成路由 item.path:  '+item.path);
+    }else{
+      routers.push(menu);
+    }
+    //--update-end----author:scott---date:20190320------for:根据后台菜单配置,判断是否路由菜单字段,动态选择是否生成路由(为了支持参数URL菜单)------
+  }
+  return routers
+}
+
+/**
+ * 深度克隆对象、数组
+ * @param obj 被克隆的对象
+ * @return 克隆后的对象
+ */
+export function cloneObject(obj) {
+  return JSON.parse(JSON.stringify(obj))
+}
+
+/**
+ * 随机生成数字
+ *
+ * 示例:生成长度为 12 的随机数:randomNumber(12)
+ * 示例:生成 3~23 之间的随机数:randomNumber(3, 23)
+ *
+ * @param1 最小值 | 长度
+ * @param2 最大值
+ * @return int 生成后的数字
+ */
+export function randomNumber() {
+  // 生成 最小值 到 最大值 区间的随机数
+  const random = (min, max) => {
+    return Math.floor(Math.random() * (max - min + 1) + min)
+  }
+  if (arguments.length === 1) {
+    let [length] = arguments
+  // 生成指定长度的随机数字,首位一定不是 0
+    let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
+    return parseInt(nums.join(''))
+  } else if (arguments.length >= 2) {
+    let [min, max] = arguments
+    return random(min, max)
+  } else {
+    return Number.NaN
+  }
+}
+
+/**
+ * 随机生成字符串
+ * @param length 字符串的长度
+ * @param chats 可选字符串区间(只会生成传入的字符串中的字符)
+ * @return string 生成的字符串
+ */
+export function randomString(length, chats) {
+  if (!length) length = 1
+  if (!chats) chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
+  let str = ''
+  for (let i = 0; i < length; i++) {
+    let num = randomNumber(0, chats.length - 1)
+    str += chats[num]
+  }
+  return str
+}
+
+/**
+ * 随机生成uuid
+ * @return string 生成的uuid
+ */
+export function randomUUID() {
+  let chats = '0123456789abcdef'
+  return randomString(32, chats)
+}
+
+/**
+ * 下划线转驼峰
+ * @param string
+ * @returns {*}
+ */
+export function underLine2CamelCase(string){
+  return string.replace( /_([a-z])/g, function( all, letter ) {
+    return letter.toUpperCase();
+  });
+}
+
+/**
+ * 判断是否显示办理按钮
+ * @param bpmStatus
+ * @returns {*}
+ */
+export function showDealBtn(bpmStatus){
+  if(bpmStatus!="1"&&bpmStatus!="3"&&bpmStatus!="4"){
+    return true;
+  }
+  return false;
+}
+
+/**
+ * 增强CSS,可以在页面上输出全局css
+ * @param css 要增强的css
+ * @param id style标签的id,可以用来清除旧样式
+ */
+export function cssExpand(css, id) {
+  let style = document.createElement('style')
+  style.type = "text/css"
+  style.innerHTML = `@charset "UTF-8"; ${css}`
+  // 清除旧样式
+  if (id) {
+    let $style = document.getElementById(id)
+    if ($style != null) $style.outerHTML = ''
+    style.id = id
+  }
+  // 应用新样式
+  document.head.appendChild(style)
+}
+
+
+/** 用于js增强事件,运行JS代码,可以传参 */
+// options 所需参数:
+//    参数名         类型            说明
+//    vm             VueComponent    vue实例
+//    event          Object          event对象
+//    jsCode         String          待执行的js代码
+//    errorMessage   String          执行出错后的提示(控制台)
+export function jsExpand(options = {}) {
+
+  // 绑定到window上的keyName
+  let windowKeyName = 'J_CLICK_EVENT_OPTIONS'
+  if (typeof window[windowKeyName] != 'object') {
+    window[windowKeyName] = {}
+  }
+
+  // 随机生成JS增强的执行id,防止冲突
+  let id = randomString(16, 'qwertyuioplkjhgfdsazxcvbnm'.toUpperCase())
+  // 封装按钮点击事件
+  let code = `
+    (function (o_${id}) {
+      try {
+        (function (globalEvent, vm) {
+          ${options.jsCode}
+        })(o_${id}.event, o_${id}.vm)
+      } catch (e) {
+        o_${id}.error(e)
+      }
+      o_${id}.done()
+    })(window['${windowKeyName}']['EVENT_${id}'])
+  `
+  // 创建script标签
+  const script = document.createElement('script')
+  // 将需要传递的参数挂载到window对象上
+  window[windowKeyName]['EVENT_' + id] = {
+    vm: options.vm,
+    event: options.event,
+    // 当执行完成时,无论如何都会调用的回调事件
+    done() {
+      // 执行完后删除新增的 script 标签不会撤销执行结果(已产生的结果不会被撤销)
+      script.outerHTML = ''
+      delete window[windowKeyName]['EVENT_' + id]
+    },
+    // 当js运行出错的时候调用的事件
+    error(e) {
+      console.group(`${options.errorMessage || '用户自定义JS增强代码运行出错'}(${new Date()})`)
+      console.error(e)
+      console.groupEnd()
+    }
+  }
+  // 将事件挂载到document中
+  script.innerHTML = code
+  document.body.appendChild(script)
+}
+
+
+/**
+ * 重复值验证工具方法
+ *
+ * 使用示例:
+ * { validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
+ *
+ * @param tableName 被验证的表名
+ * @param fieldName 被验证的字段名
+ * @param fieldVal 被验证的值
+ * @param dataId 数据ID,可空
+ * @param callback
+ */
+export function validateDuplicateValue(tableName, fieldName, fieldVal, dataId, callback) {
+  if (fieldVal) {
+    let params = { tableName, fieldName, fieldVal, dataId }
+    api.duplicateCheck(params).then(res => {
+      res['success'] ? callback() : callback(res['message'])
+    }).catch(err => {
+      callback(err.message || err)
+    })
+  } else {
+    callback()
+  }
+}
+
+/**
+ * 根据编码校验规则code,校验传入的值是否合法
+ *
+ * 使用示例:
+ * { validator: (rule, value, callback) => validateCheckRule('common', value, callback) }
+ *
+ * @param ruleCode 编码校验规则 code
+ * @param value 被验证的值
+ * @param callback
+ */
+export function validateCheckRule(ruleCode, value, callback) {
+  if (ruleCode && value) {
+    value = encodeURIComponent(value)
+    api.checkRuleByCode({ ruleCode, value }).then(res => {
+      res['success'] ? callback() : callback(res['message'])
+    }).catch(err => {
+      callback(err.message || err)
+    })
+  } else {
+    callback()
+  }
+}
+
+/**
+ * 如果值不存在就 push 进数组,反之不处理
+ * @param array 要操作的数据
+ * @param value 要添加的值
+ * @param key 可空,如果比较的是对象,可能存在地址不一样但值实际上是一样的情况,可以传此字段判断对象中唯一的字段,例如 id。不传则直接比较实际值
+ * @returns {boolean} 成功 push 返回 true,不处理返回 false
+ */
+export function pushIfNotExist(array, value, key) {
+  for (let item of array) {
+    if (key && (item[key] === value[key])) {
+      return false
+    } else if (item === value) {
+      return false
+    }
+  }
+  array.push(value)
+  return true
+}
+
+/**
+ * 可用于判断是否成功
+ * @type {symbol}
+ */
+export const succeedSymbol = Symbol()
+/**
+ * 可用于判断是否失败
+ * @type {symbol}
+ */
+export const failedSymbol = Symbol()
+
+/**
+ * 使 promise 无论如何都会 resolve,除非传入的参数不是一个Promise对象或返回Promise对象的方法
+ * 一般用在 Promise.all 中
+ *
+ * @param promise 可传Promise对象或返回Promise对象的方法
+ * @returns {Promise<any>}
+ */
+export function alwaysResolve(promise) {
+  return new Promise((resolve, reject) => {
+    let p = promise
+    if (typeof promise === 'function') {
+      p = promise()
+    }
+    if (p instanceof Promise) {
+      p.then(data => {
+        resolve({ type: succeedSymbol, data })
+      }).catch(error => {
+        resolve({ type: failedSymbol, error })
+      })
+    } else {
+      reject('alwaysResolve: 传入的参数不是一个Promise对象或返回Promise对象的方法')
+    }
+  })
+}
+
+/**
+ * 简单实现防抖方法
+ *
+ * 防抖(debounce)函数在第一次触发给定的函数时,不立即执行函数,而是给出一个期限值(delay),比如100ms。
+ * 如果100ms内再次执行函数,就重新开始计时,直到计时结束后再真正执行函数。
+ * 这样做的好处是如果短时间内大量触发同一事件,只会执行一次函数。
+ *
+ * @param fn 要防抖的函数
+ * @param delay 防抖的毫秒数
+ * @returns {Function}
+ */
+export function simpleDebounce(fn, delay = 100) {
+  let timer = null
+  return function () {
+    let args = arguments
+    if (timer) {
+      clearTimeout(timer)
+    }
+    timer = setTimeout(() => {
+      fn.apply(this, args)
+    }, delay)
+  }
+}
+
+/**
+ * 不用正则的方式替换所有值
+ * @param text 被替换的字符串
+ * @param checker  替换前的内容
+ * @param replacer 替换后的内容
+ * @returns {String} 替换后的字符串
+ */
+export function replaceAll(text, checker, replacer) {
+  let lastText = text
+  text = text.replace(checker, replacer)
+  if (lastText !== text) {
+    return replaceAll(text, checker, replacer)
+  }
+  return text
+}
+
+/**
+ * 获取事件冒泡路径,兼容 IE11,Edge,Chrome,Firefox,Safari
+ * 目前使用的地方:JEditableTable Span模式
+ */
+export function getEventPath(event) {
+  let target = event.target
+  let path = (event.composedPath && event.composedPath()) || event.path
+
+  if (path != null) {
+    return (path.indexOf(window) < 0) ? path.concat(window) : path
+  }
+
+  if (target === window) {
+    return [window]
+  }
+
+  let getParents = (node, memo) => {
+    memo = memo || []
+    const parentNode = node.parentNode
+
+    if (!parentNode) {
+      return memo
+    } else {
+      return getParents(parentNode, memo.concat(parentNode))
+    }
+  }
+  return [target].concat(getParents(target), window)
+}
+
+/**
+ * 根据组件名获取父级
+ * @param vm
+ * @param name
+ * @returns {Vue | null|null|Vue}
+ */
+export function getVmParentByName(vm, name) {
+  let parent = vm.$parent
+  if (parent && parent.$options) {
+    if (parent.$options.name === name) {
+      return parent
+    } else {
+      let res = getVmParentByName(parent, name)
+      if (res) {
+        return res
+      }
+    }
+  }
+  return null
+}
+
+/**
+ * 使一个值永远不会为(null | undefined)
+ *
+ * @param value 要处理的值
+ * @param def 默认值,如果value为(null | undefined)则返回的默认值,可不传,默认为''
+ */
+export function neverNull(value, def) {
+  return value == null ? (neverNull(def, '')) : value
+}
+
+/**
+ * 根据元素值移除数组中的一个元素
+ * @param array 数组
+ * @param prod 属性名
+ * @param value 属性值
+ * @returns {string}
+ */
+export function removeArrayElement(array, prod, value) {
+  let index = -1
+  for(let i = 0;i<array.length;i++){
+    if(array[i][prod] == value){
+      index = i;
+      break;
+    }
+  }
+  if(index>=0){
+    array.splice(index, 1);
+  }
+}
+
+/** 判断是否是OAuth2APP环境 */
+export function isOAuth2AppEnv() {
+  return /wxwork|dingtalk/i.test(navigator.userAgent)
+}
+
+/**
+ * 获取积木报表打印地址
+ * @param url
+ * @param id
+ * @param open 是否自动打开
+ * @returns {*}
+ */
+export function getReportPrintUrl(url, id, open) {
+  // URL支持{{ window.xxx }}占位符变量
+  url = url.replace(/{{([^}]+)?}}/g, (s1, s2) => eval(s2))
+  if (url.includes('?')) {
+    url += '&'
+  } else {
+    url += '?'
+  }
+  url += `id=${id}`
+  url += `&token=${Vue.ls.get(ACCESS_TOKEN)}`
+  if (open) {
+    window.open(url)
+  }
+  return url
+}
+
+/**
+ * JS实现AOP切面
+ *
+ * @param obj 包含函数的对象
+ * @param funcName 要切面的函数名
+ * @param callback 执行方法前的回调,用于切面,callback的返回值就是funcName最终的返回值
+ */
+export function aspectAroundFunction(obj, funcName, callback) {
+  if (typeof callback !== 'function' || !obj) {
+    console.warn('【aspectAroundFunction】obj或callback格式不正确')
+    return
+  }
+  // 保存原来的函数
+  let func = obj[funcName]
+  if (typeof func !== 'function') {
+    console.warn('【aspectAroundFunction】' + funcName + '不是一个方法')
+    return
+  }
+  // 赋值新方法
+  // 实现当外部调用 funcName 时,首先调用我定义的新方法
+  // 然后调用传入的callback方法,以决定是否执行 funcName,以及更改参数、返回值
+  obj[funcName] = function (...args) {
+    return callback({
+      args,
+      // 只有执行 proceed 才会真正执行给定的 funcName 方法
+      proceed() {
+        try {
+          return func.apply(obj, args)
+        } catch (e) {
+          console.error(e)
+        }
+      },
+    })
+  }
+}
+
+/**
+ * 休眠
+ * @param ms 毫秒
+ * @return {Promise<unknown>}
+ */
+export function sleep(ms) {
+  return new Promise(function (resolve) {
+    return setTimeout(resolve, ms);
+  });
+}
+
+/**
+ * 获取指定的 $refs 对象
+ * 有时候可能会遇到组件未挂载到页面中的情况,导致无法获取 $refs 中的某个对象
+ * 这个方法可以等待挂载完成之后再返回 $refs 的对象,避免报错
+ *
+ * 用法示例:let modalRef = getRefPromise(this, 'modal')
+ * @param vm vue实例
+ * @param name 要获取的ref名称
+ * @param noComment $el 标签不能是注释
+ **/
+export function getRefPromise(vm, name, noComment = true) {
+  return new Promise((resolve) => {
+    (function next() {
+      let ref = vm.$refs[name]
+      if (ref && (noComment && ref.$el.tagName)) {
+        resolve(ref)
+      } else {
+        setTimeout(() => {
+          if (noComment) {
+            vm.$forceUpdate()
+          }
+          next()
+        }, 10)
+      }
+    })()
+  })
+}
+
+/**
+ * 导出文件xlsx的mime-type
+ * xls:	application/vnd.ms-excel
+ * @type {string}
+ */
+export const EXPORT_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+/**
+ * 导出excel文件后缀
+ * @type {string}
+ */
+export const EXPORT_FILE_SUFFIX = ".xlsx";
+
+/**
+ * 字符串是否为null或null字符串
+ * @param str
+ * @return {boolean}
+ */
+export function stringIsNull(str) {
+  return str == null || str === 'null' || str === 'undefined';
+}

+ 6 - 6
src/views/lg/ProdPlan/ProdPlanList.vue

@@ -4,17 +4,17 @@
         width: '600px',
         height: '400px',
         position: 'absolute',
-        // left: '-9999px',
-        // top: '-9999px',
-        // visibility: 'hidden'
+        left: '-9999px',
+        top: '-9999px',
+        visibility: 'hidden'
       }" />
     <div ref="barLine" :style="{
         width: '600px',
         height: '400px',
-        // position: 'absolute',
          left: '650px',
-        // top: '-9999px',
-        // visibility: 'hidden'
+        position: 'absolute',
+        top: '-9999px',
+        visibility: 'hidden'
       }" />
     <!-- 查询区域 -->
     <div class="table-page-search-wrapper">

+ 126 - 118
vue.config.js

@@ -1,119 +1,127 @@
-const path = require('path')
-const CompressionPlugin = require("compression-webpack-plugin")
-
-function resolve(dir) {
-  return path.join(__dirname, dir)
-}
-
-// vue.config.js
-module.exports = {
-  /*
-    Vue-cli3:
-    Crashed when using Webpack `import()` #2463
-    https://github.com/vuejs/vue-cli/issues/2463
-   */
-  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
-  productionSourceMap: false,
-  //qiankuan打包时放开
-  //outputDir: "../dist/main",
-  // 多入口配置
-  // pages: {
-  //   index: {
-  //     entry: 'src/main.js',
-  //     template: 'public/index.html',
-  //     filename: 'index.html',
-  //   }
-  // },
-  //打包app时放开该配置
-  //publicPath:'/',
-  configureWebpack: config => {
-    //生产环境取消 console.log
-    if (process.env.NODE_ENV === 'production') {
-      config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
-    }
-  },
-  chainWebpack: (config) => {
-    config.resolve.alias
-      .set('@$', resolve('src'))
-      .set('@api', resolve('src/api'))
-      .set('@assets', resolve('src/assets'))
-      .set('@comp', resolve('src/components'))
-      .set('@views', resolve('src/views'))
-
-    //生产环境,开启js\css压缩
-    if (process.env.NODE_ENV === 'production') {
-        config.plugin('compressionPlugin').use(new CompressionPlugin({
-          test: /\.(js|css|less)$/, // 匹配文件名
-          threshold: 10240, // 对超过10k的数据压缩
-          deleteOriginalAssets: false // 不删除源文件
-        }))
-    }
-
-    // 配置 webpack 识别 markdown 为普通的文件
-    config.module
-      .rule('markdown')
-      .test(/\.md$/)
-      .use()
-      .loader('file-loader')
-      .end()
-
-    // 编译vxe-table包里的es6代码,解决IE11兼容问题
-    config.module
-      .rule('vxe')
-      .test(/\.js$/)
-      .include
-        .add(resolve('node_modules/vxe-table'))
-        .add(resolve('node_modules/vxe-table-plugin-antd'))
-        .end()
-      .use()
-      .loader('babel-loader')
-      .end()
-
-  },
-
-  css: {
-    loaderOptions: {
-      less: {
-        modifyVars: {
-          /* less 变量覆盖,用于自定义 ant design 主题 */
-          'primary-color': '#1890FF',
-          'link-color': '#1890FF',
-          'border-radius-base': '4px',
-        },
-        javascriptEnabled: true,
-      }
-    }
-  },
-
-  devServer: {
-    port: 3000,
-    // hot: true,
-    // disableHostCheck: true,
-    // overlay: {
-    //     warnings: false,
-    //     errors: true,
-    // },
-    // headers: {
-    //     'Access-Control-Allow-Origin': '*',
-    // },
-    proxy: {
-     /* '/api': {
-        target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro', //mock API接口系统
-        ws: false,
-        changeOrigin: true,
-        pathRewrite: {
-          '/jeecg-boot': ''  //默认所有请求都加了jeecg-boot前缀,需要去掉
-        }
-      },*/
-      /* 注意:jeecgboot前端做了改造,此处不需要配置跨域和后台接口(只需要改.env相关配置文件即可)
-          issues/3462 很多人此处做了配置,导致刷新前端404问题,请一定注意*/
-      '/jeecg-boot': {
-        target: 'http://localhost:8080',
-        ws: false,
-        changeOrigin: true
-      },
-    }
-  },
-
-  lintOnSave: undefined
+const path = require('path')
+const webpack = require('webpack')
+const CompressionPlugin = require("compression-webpack-plugin")
+
+function resolve(dir) {
+  return path.join(__dirname, dir)
+}
+
+// vue.config.js
+module.exports = {
+  /*
+    Vue-cli3:
+    Crashed when using Webpack `import()` #2463
+    https://github.com/vuejs/vue-cli/issues/2463
+   */
+  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
+  productionSourceMap: false,
+  //qiankuan打包时放开
+  //outputDir: "../dist/main",
+  // 多入口配置
+  // pages: {
+  //   index: {
+  //     entry: 'src/main.js',
+  //     template: 'public/index.html',
+  //     filename: 'index.html',
+  //   }
+  // },
+  //打包app时放开该配置
+  //publicPath:'/',
+  configureWebpack: config => {
+    //生产环境取消 console.log
+    if (process.env.NODE_ENV === 'production') {
+      config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
+    }
+  },
+  chainWebpack: (config) => {
+    config.plugins.delete('prefetch')
+    //只保留中文语言包
+    config.plugin('ContextReplacementPlugin').use(webpack.ContextReplacementPlugin, [/moment[/\\]locale$/, /zh-cn/])
+    config.resolve.alias
+      .set('@$', resolve('src'))
+      .set('@api', resolve('src/api'))
+      .set('@assets', resolve('src/assets'))
+      .set('@comp', resolve('src/components'))
+      .set('@views', resolve('src/views'))
+
+    //生产环境,开启js\css压缩
+    if (process.env.NODE_ENV === 'production') {
+        config.plugin('compressionPlugin').use(new CompressionPlugin({
+          test: /\.(js|css|less)$/, // 匹配文件名
+          threshold: 10240, // 对超过10k的数据压缩
+          deleteOriginalAssets: false // 不删除源文件
+        }))
+        config.plugin('chunkPlugin').use(webpack.optimize.LimitChunkCountPlugin, [{
+          maxChunks: 5,
+          minChunkSize: 10000
+        }])
+    }
+
+    // 配置 webpack 识别 markdown 为普通的文件
+    config.module
+      .rule('markdown')
+      .test(/\.md$/)
+      .use()
+      .loader('file-loader')
+      .end()
+
+    // 编译vxe-table包里的es6代码,解决IE11兼容问题
+    config.module
+      .rule('vxe')
+      .test(/\.js$/)
+      .include
+        .add(resolve('node_modules/vxe-table'))
+        .add(resolve('node_modules/vxe-table-plugin-antd'))
+        .end()
+      .use()
+      .loader('babel-loader')
+      .end()
+
+  },
+
+  css: {
+    loaderOptions: {
+      less: {
+        modifyVars: {
+          /* less 变量覆盖,用于自定义 ant design 主题 */
+          'primary-color': '#1890FF',
+          'link-color': '#1890FF',
+          'border-radius-base': '4px',
+        },
+        javascriptEnabled: true,
+      }
+    }
+  },
+
+  devServer: {
+    port: 3000,
+    // hot: true,
+    // disableHostCheck: true,
+    // overlay: {
+    //     warnings: false,
+    //     errors: true,
+    // },
+    // headers: {
+    //     'Access-Control-Allow-Origin': '*',
+    // },
+    proxy: {
+     /* '/api': {
+        target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro', //mock API接口系统
+        ws: false,
+        changeOrigin: true,
+        pathRewrite: {
+          '/jeecg-boot': ''  //默认所有请求都加了jeecg-boot前缀,需要去掉
+        }
+      },*/
+      /* 注意:jeecgboot前端做了改造,此处不需要配置跨域和后台接口(只需要改.env相关配置文件即可)
+          issues/3462 很多人此处做了配置,导致刷新前端404问题,请一定注意*/
+      '/jeecg-boot': {
+        target: 'http://localhost:8080',
+        ws: false,
+        changeOrigin: true
+      },
+    }
+  },
+
+  lintOnSave: undefined
 }