Michael Wang 6 éve
szülő
commit
9b573c54c1

+ 4 - 4
.eslintrc.js

@@ -1,14 +1,14 @@
 module.exports = {
   root: true,
   env: {
-    node: true
+    node: true,
   },
   extends: ["plugin:vue/essential", "@vue/prettier"],
   rules: {
     "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
-    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
+    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
   },
   parserOptions: {
-    parser: "babel-eslint"
-  }
+    parser: "babel-eslint",
+  },
 };

+ 4 - 4
babel.config.js

@@ -5,8 +5,8 @@ module.exports = {
       "import",
       {
         libraryName: "iview",
-        libraryDirectory: "src/components"
-      }
-    ]
-  ]
+        libraryDirectory: "src/components",
+      },
+    ],
+  ],
 };

+ 4 - 4
jest.config.js

@@ -4,14 +4,14 @@ module.exports = {
     "^.+\\.vue$": "vue-jest",
     ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$":
       "jest-transform-stub",
-    "^.+\\.jsx?$": "babel-jest"
+    "^.+\\.jsx?$": "babel-jest",
   },
   moduleNameMapper: {
-    "^@/(.*)$": "<rootDir>/src/$1"
+    "^@/(.*)$": "<rootDir>/src/$1",
   },
   snapshotSerializers: ["jest-serializer-vue"],
   testMatch: [
-    "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
+    "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)",
   ],
-  testURL: "http://localhost/"
+  testURL: "http://localhost/",
 };

+ 5 - 3
jsconfig.json

@@ -1,8 +1,10 @@
 {
   "compilerOptions": {
-    "baseUrl": ".",
+    "target": "es6",
+    "baseUrl": "./",
     "paths": {
-      "@/*": ["./src/*"]
+      "@/*": ["src/*"]
     }
-  }
+  },
+  "exclude": ["node_modules", "**/node_modules/*"]
 }

+ 13 - 0
package.json

@@ -21,8 +21,11 @@
     "core-js": "^2.6.5",
     "iview": "^3.4.1",
     "js-cookie": "^2.2.0",
+    "lodash-es": "^4.17.11",
+    "moment": "^2.24.0",
     "register-service-worker": "^1.6.2",
     "vue": "^2.6.10",
+    "vue-navigation": "^1.1.4",
     "vue-router": "^3.0.6",
     "vuex": "^3.1.1",
     "vuex-persistedstate": "^2.5.4"
@@ -42,9 +45,19 @@
     "eslint": "^5.16.0",
     "eslint-plugin-vue": "^5.2.2",
     "iview-loader": "^1.2.2",
+    "lint-staged": "^8.1.5",
     "node-sass": "^4.12.0",
     "sass-loader": "^7.0.1",
     "vue-cli-plugin-axios": "0.0.4",
     "vue-template-compiler": "^2.6.10"
+  },
+  "gitHooks": {
+    "pre-commit": "lint-staged"
+  },
+  "lint-staged": {
+    "*.{js,vue}": [
+      "vue-cli-service lint",
+      "git add"
+    ]
   }
 }

+ 2 - 2
postcss.config.js

@@ -1,5 +1,5 @@
 module.exports = {
   plugins: {
-    autoprefixer: {}
-  }
+    autoprefixer: {},
+  },
 };

+ 3 - 0
prettier.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  trailingComma: "es5",
+};

+ 9 - 0
public/index.html

@@ -15,6 +15,15 @@
       </strong>
     </noscript>
     <div id="app"></div>
+    <!-- <script>
+      var _hmt = _hmt || [];
+      (function() {
+        var hm = document.createElement("script");
+        hm.src = "https://hm.baidu.com/hm.js?073680f0286d68512be67c90775789c9";
+        var s = document.getElementsByTagName("script")[0];
+        s.parentNode.insertBefore(hm, s);
+      })();
+    </script> -->
     <!-- built files will be auto injected -->
   </body>
 </html>

+ 2 - 2
src/App.vue

@@ -17,8 +17,8 @@ import HelloWorld from "./components/HelloWorld.vue";
 export default {
   name: "app",
   components: {
-    HelloWorld
-  }
+    HelloWorld,
+  },
 };
 </script>
 

+ 5 - 3
src/auth/auth.js

@@ -21,10 +21,12 @@ export function getKeyToken() {
   return [Cookies.get(Key), Cookies.get(Token)];
 }
 
-export function setKeyToken({ key, token }) {
-  return Cookies.set(Key, key) && Cookies.set(Token, token);
+export function setKeyToken({ uid, token }) {
+  Cookies.set(Key, uid);
+  Cookies.set(Token, token);
 }
 
 export function removeKeyToken() {
-  return Cookies.remove(Key) && Cookies.remove(Token);
+  Cookies.remove(Key);
+  Cookies.remove(Token);
 }

+ 2 - 2
src/components/HelloWorld.vue

@@ -106,8 +106,8 @@
 export default {
   name: "HelloWorld",
   props: {
-    msg: String
-  }
+    msg: String,
+  },
 };
 </script>
 

+ 97 - 2
src/main.js

@@ -1,4 +1,5 @@
 import Vue from "vue";
+import Navigation from "vue-navigation";
 import App from "./App.vue";
 import router from "./router";
 import store from "./store";
@@ -12,17 +13,111 @@ import "./styles/bootstrap.scss";
 import "./styles/global.css";
 // styles end
 
+Vue.use(Navigation, { router });
+
 Vue.config.productionTip = false;
 
 if (
   process.env.NODE_ENV === "development" &&
   process.env.VUE_APP_ENABLE_VUE_RENDER_LOGS === "true"
 ) {
-  require("./plugins/vueLifecylceLogs");
+  const vueLifecylceLogs = require("./plugins/vueLifecylceLogs").default;
+
+  const options = {
+    ignoreComponents: [
+      undefined,
+      "transition",
+      "router-link",
+      "Affix",
+      "Alert",
+      "Anchor",
+      "AnchorLink",
+      "AutoComplete",
+      "Avatar",
+      "BackTop",
+      "Badge",
+      "Breadcrumb",
+      "BreadcrumbItem",
+      "Button",
+      "ButtonGroup",
+      "Card",
+      "Carousel",
+      "CarouselItem",
+      "Cascader",
+      "Cell",
+      "CellGroup",
+      "Checkbox",
+      "CheckboxGroup",
+      "i-circle",
+      "Col",
+      "Collapse",
+      "ColorPicker",
+      "Content",
+      "Divider",
+      "DatePicker",
+      "Drawer",
+      "Drop",
+      "Dropdown",
+      "DropdownItem",
+      "DropdownMenu",
+      "Footer",
+      "Form",
+      "iForm",
+      "FormItem",
+      "Header",
+      "Icon",
+      "Input",
+      "InputNumber",
+      "Layout",
+      "LoadingBar",
+      "Menu",
+      "MenuGroup",
+      "MenuItem",
+      "Sider",
+      "Submenu",
+      "Modal",
+      "Option",
+      "OptionGroup",
+      "Page",
+      "PageOption",
+      "Panel",
+      "Poptip",
+      "Progress",
+      "Radio",
+      "RadioGroup",
+      "Rate",
+      "Row",
+      "Select",
+      "Slider",
+      "Spin",
+      "Split",
+      "Step",
+      "Steps",
+      "i-switch",
+      "Table",
+      "TableHead",
+      "TableBody",
+      "TableCell",
+      "Tabs",
+      "TabPane",
+      "Tag",
+      "Time",
+      "Timeline",
+      "TimelineItem",
+      "TimePicker",
+      "Tooltip",
+      "Transfer",
+      "Tree",
+      "Upload",
+    ],
+    ignoreComponentsNameRegexArray: [/^i[A-Z].*/, /^fa-.*/],
+    groupCollapsed: true,
+  };
+  vueLifecylceLogs(options);
 }
 
 new Vue({
   router,
   store,
-  render: h => h(App)
+  render: h => h(App),
 }).$mount("#app");

+ 9 - 9
src/plugins/axiosApp.js

@@ -13,7 +13,7 @@ import { getKeyToken, removeKeyToken } from "../auth/auth";
 let config = {
   // baseURL: process.env.baseURL || process.env.apiUrl || ""
   timeout: 60 * 1000, // Timeout
-  withCredentials: true // Check cross-site Access-Control
+  withCredentials: true, // Check cross-site Access-Control
 };
 
 const _axiosApp = axios.create(config);
@@ -39,7 +39,7 @@ _axiosApp.interceptors.request.use(
   function(error) {
     // Do something with request error
     Notice.error({
-      title: error
+      title: error,
     });
     return Promise.reject(error);
   }
@@ -55,7 +55,7 @@ _axiosApp.interceptors.response.use(
       // "Network Error" 网络不通,直接返回
 
       Notice.error({
-        title: "网络连接异常,请检查网络设置。"
+        title: "网络连接异常,请检查网络设置。",
       });
       return Promise.reject(error);
     }
@@ -65,13 +65,13 @@ _axiosApp.interceptors.response.use(
     // 登录失效 跳转登录页面
     if (status == 403 || status == 401) {
       Notice.warning({
-        title: "登录失效,请重新登录!"
+        title: "登录失效,请重新登录!",
       });
       removeKeyToken();
       return Promise.reject(error);
     } else if (status == 405) {
       Notice.warning({
-        title: "没有权限!"
+        title: "没有权限!",
       });
       return Promise.reject(error);
     }
@@ -81,12 +81,12 @@ _axiosApp.interceptors.response.use(
       if (data && data.desc) {
         Notice.error({
           title: "服务器返回",
-          desc: data.desc
+          desc: data.desc,
         });
       } else {
         Notice.error({
           title: "未定义异常: ",
-          desc: JSON.stringify(data, 2)
+          desc: JSON.stringify(data, 2),
         });
       }
       return Promise.reject(error);
@@ -102,8 +102,8 @@ Plugin.install = function(Vue) {
     $http: {
       get() {
         return _axiosApp;
-      }
-    }
+      },
+    },
   });
 };
 

+ 9 - 9
src/plugins/axiosCommonService.js

@@ -14,7 +14,7 @@ axios.defaults.headers.post["Access-Control-Allow-Origin"] = "*";
 let config = {
   // baseURL: process.env.baseURL || process.env.apiUrl || ""
   timeout: 60 * 1000, // Timeout
-  withCredentials: true // Check cross-site Access-Control
+  withCredentials: true, // Check cross-site Access-Control
 };
 
 const _axiosCommonService = axios.create(config);
@@ -42,7 +42,7 @@ _axiosCommonService.interceptors.request.use(
   function(error) {
     // Do something with request error
     Notice.error({
-      title: error
+      title: error,
     });
     return Promise.reject(error);
   }
@@ -58,7 +58,7 @@ _axiosCommonService.interceptors.response.use(
       // "Network Error" 网络不通,直接返回
 
       Notice.error({
-        title: "网络连接异常,请检查网络设置。"
+        title: "网络连接异常,请检查网络设置。",
       });
       return Promise.reject(error);
     }
@@ -68,13 +68,13 @@ _axiosCommonService.interceptors.response.use(
     // 登录失效 跳转登录页面
     if (status == 403 || status == 401) {
       Notice.warning({
-        title: "登录失效,请重新登录!"
+        title: "登录失效,请重新登录!",
       });
       removeKeyToken();
       return Promise.reject(error);
     } else if (status == 405) {
       Notice.warning({
-        title: "没有权限!"
+        title: "没有权限!",
       });
       return Promise.reject(error);
     }
@@ -84,12 +84,12 @@ _axiosCommonService.interceptors.response.use(
       if (data && data.desc) {
         Notice.error({
           title: "服务器返回",
-          desc: data.desc
+          desc: data.desc,
         });
       } else {
         Notice.error({
           title: "未定义异常: ",
-          desc: JSON.stringify(data, 2)
+          desc: JSON.stringify(data, 2),
         });
       }
       return Promise.reject(error);
@@ -105,8 +105,8 @@ Plugin.install = function(Vue) {
     $httpCommonService: {
       get() {
         return _axiosCommonService;
-      }
-    }
+      },
+    },
   });
 };
 

+ 131 - 158
src/plugins/vueLifecylceLogs.js

@@ -1,166 +1,139 @@
 import Vue from "vue";
 
-const ignoreComponents = [
-  undefined,
-  "transition",
-  "RemainTime",
-  "router-link",
-  "Affix",
-  "Alert",
-  "Anchor",
-  "AnchorLink",
-  "AutoComplete",
-  "Avatar",
-  "BackTop",
-  "Badge",
-  "Breadcrumb",
-  "BreadcrumbItem",
-  "Button",
-  "ButtonGroup",
-  "Card",
-  "Carousel",
-  "CarouselItem",
-  "Cascader",
-  "Cell",
-  "CellGroup",
-  "Checkbox",
-  "CheckboxGroup",
-  "i-circle",
-  "Col",
-  "Collapse",
-  "ColorPicker",
-  "Content",
-  "Divider",
-  "DatePicker",
-  "Drawer",
-  "Dropdown",
-  "DropdownItem",
-  "DropdownMenu",
-  "Footer",
-  "Form",
-  "iForm",
-  "FormItem",
-  "Header",
-  "Icon",
-  "Input",
-  "InputNumber",
-  "Layout",
-  "Menu",
-  "MenuGroup",
-  "MenuItem",
-  "Sider",
-  "Submenu",
-  "Modal",
-  "Option",
-  "OptionGroup",
-  "Page",
-  "Panel",
-  "Poptip",
-  "Progress",
-  "Radio",
-  "RadioGroup",
-  "Rate",
-  "Row",
-  "Select",
-  "Slider",
-  "Spin",
-  "Split",
-  "Step",
-  "Steps",
-  "i-switch",
-  "Table",
-  "Tabs",
-  "TabPane",
-  "Tag",
-  "Time",
-  "Timeline",
-  "TimelineItem",
-  "TimePicker",
-  "Tooltip",
-  "Transfer",
-  "Tree",
-  "Upload"
-];
-
-setInterval(
-  () => console.log("----------------------Vue lifecyle logs----------------"),
-  10 * 1000
-);
-
-function getParentNumber(that) {
-  let parentNumber = 0;
-  while (that) {
-    if (that.$parent) {
-      parentNumber++;
-      that = that.$parent;
+// const ignoreComponents = [undefined, "transition", "router-link", "Bar"];
+
+// const ignoreComponentsNameRegexArray = [/^El[A-Z].*/, /^fa-.*/];
+
+// const groupCollapsed = true;
+
+// const RENDER_DURATION = 1 * 1000;
+
+export default function({
+  ignoreComponents = [undefined, "transition", "router-link"],
+  ignoreComponentsNameRegexArray = [/^El[A-Z].*/, /^fa-.*/],
+  groupCollapsed = true,
+  RENDER_DURATION = 3 * 1000,
+}) {
+  function getParentNumber(that) {
+    let parentNumber = 0;
+    while (that) {
+      if (that.$parent) {
+        parentNumber++;
+        that = that.$parent;
+      } else {
+        break;
+      }
+    }
+    return parentNumber;
+  }
+
+  function componentNameMatch(name) {
+    if (ignoreComponents.includes(name)) {
+      return true;
     } else {
-      break;
+      let match = false;
+      for (const regex of ignoreComponentsNameRegexArray) {
+        if (regex.test(name)) {
+          match = true;
+          break;
+        }
+      }
+      return match;
     }
   }
-  return parentNumber;
-}
-Vue.mixin({
-  created() {
-    const parentNumber = getParentNumber(this);
-    if (!ignoreComponents.includes(this.$options.name))
-      console.log(
-        "--".repeat(parentNumber) + `${this.$options.name} %c created`,
-        "color: green"
-      );
-  },
-  mounted() {
-    const parentNumber = getParentNumber(this);
-    if (!ignoreComponents.includes(this.$options.name))
-      console.log(
-        "--".repeat(parentNumber) + `${this.$options.name} %c mounted`,
-        "color: green"
-      );
-  },
-  updated() {
-    const parentNumber = getParentNumber(this);
-    if (!ignoreComponents.includes(this.$options.name))
-      console.log(
-        "--".repeat(parentNumber) + `${this.$options.name} %c updated`,
-        "color: #0000aa"
-      );
-  },
-  beforeRouteEnter(to, from, next) {
-    console.log(
-      "%c " + from.fullPath + " --> " + to.fullPath + " %c beforeRouteEnter",
-      "background: #aaa",
-      "color: #0000aa"
-    );
-
-    next();
-  },
-  beforeRouteUpdate(to, from, next) {
-    const parentNumber = getParentNumber(this);
-    console.log(
-      "%c " +
-        "--".repeat(parentNumber) +
-        `${this.$options.name} %c beforeRouteUpdate`,
-      "background: #aaa",
-      "color: #0000aa"
-    );
-    next();
-  },
-  beforeRouteLeave(to, from, next) {
-    const parentNumber = getParentNumber(this);
-    console.log(
-      "%c " +
-        "--".repeat(parentNumber) +
-        `${this.$options.name} %c beforeRouteLeave`,
-      "background: #aaa",
-      "color: #0000aa"
-    );
-    next();
-  },
-  beforeDestroy() {
-    const parentNumber = getParentNumber(this);
-    if (!ignoreComponents.includes(this.$options.name))
+
+  const injectMethods = [
+    { name: "beforeCreate", style: "color: green" },
+    { name: "created", style: "color: green" },
+    { name: "beforeMount", style: "color: green" },
+    { name: "mounted", style: "color: green" },
+    { name: "beforeUpdate", style: "color: #0000aa" },
+    { name: "updated", style: "color: #0000aa" },
+    { name: "beforeDestroy", style: "color: red; font-weight: bold" },
+    { name: "destroyed", style: "color: red; font-weight: bold" },
+  ];
+
+  const injectRouterMethods = [
+    {
+      name: "beforeRouteEnter",
+      style: ["background: #aaa", "color: #0000aa;"],
+    },
+    {
+      name: "beforeRouteUpdate",
+      style: ["background: #aaa", "color: #0000aa;"],
+    },
+    {
+      name: "beforeRouteLeave",
+      style: ["background: #aaa", "color: #0000aa;"],
+    },
+  ];
+
+  let lifeCycleMixins = {};
+
+  let startLogging = false;
+  let loggingTime = Date.now();
+
+  for (const m of injectMethods) {
+    lifeCycleMixins[m.name] = function() {
+      const parentNumber = getParentNumber(this);
+      if (!componentNameMatch(this.$options.name)) {
+        if (!startLogging) {
+          startLogging = true;
+          if (groupCollapsed) {
+            console.groupCollapsed("Vue lifecyle logs");
+          } else {
+            console.group("Vue lifecyle logs");
+          }
+
+          loggingTime = Date.now();
+        }
+        console.log(
+          "--".repeat(parentNumber) + `${this.$options.name} %c ${m.name}`,
+          m.style
+        );
+      }
+    };
+  }
+
+  for (const m of injectRouterMethods) {
+    lifeCycleMixins[m.name] = function(to, from, next) {
+      if (!startLogging) {
+        startLogging = true;
+        if (groupCollapsed) {
+          console.groupCollapsed("Vue lifecyle logs");
+        } else {
+          console.group("Vue lifecyle logs");
+        }
+
+        loggingTime = Date.now();
+      }
+
       console.log(
-        "--".repeat(parentNumber) + `${this.$options.name} %c beforeDestroy`,
-        "color: red; font-weight: bold"
+        "%c " +
+          from.fullPath +
+          " --> " +
+          to.fullPath +
+          (this ? ` (${this.$options.name}) ` : " ") +
+          `%c ${m.name}`,
+        ...m.style
       );
+
+      next();
+    };
   }
-});
+
+  // setInterval(
+  //   () => console.log("----------------------Vue lifecyle logs----------------"),
+  //   10 * 1000
+  // );
+  setInterval(() => {
+    if (startLogging && Date.now() - loggingTime > RENDER_DURATION) {
+      console.groupEnd();
+      startLogging = false;
+    }
+  }, 1 * 100);
+
+  Vue.mixin({
+    ...lifeCycleMixins,
+  });
+}

+ 1 - 1
src/registerServiceWorker.js

@@ -23,6 +23,6 @@ if (process.env.NODE_ENV === "production") {
     },
     error(error) {
       console.error("Error during service worker registration:", error);
-    }
+    },
   });
 }

+ 21 - 19
src/router/index.js

@@ -1,3 +1,5 @@
+import { getKeyToken } from "@/auth/auth";
+import { isNil } from "lodash-es";
 import Vue from "vue";
 import Router from "vue-router";
 import Home from "../views/Home.vue";
@@ -21,7 +23,7 @@ let router = new Router({
     {
       path: "/",
       name: "home",
-      component: Home
+      component: Home,
     },
     {
       path: "/about",
@@ -30,8 +32,8 @@ let router = new Router({
       // this generates a separate chunk (about.[hash].js) for this route
       // which is lazy-loaded when the route is visited.
       component: () =>
-        import(/* webpackChunkName: "about" */ "../views/About.vue")
-    }
+        import(/* webpackChunkName: "about" */ "../views/About.vue"),
+    },
     // {
     //   path: "/xxx/:id",
     //   component: XXX,
@@ -39,23 +41,23 @@ let router = new Router({
     //     return propsValidator(route, XXX);
     //   }
     // }
-  ]
+  ],
 });
 
-// router.beforeEach((to, from, next) => {
-//   const loginPath = "/login/" + localStorage.getItem("domain");
-//   if (to.path.match(/^\/login\/.+$/)) {
-//     next();
-//   } else {
-//     if (!window.sessionStorage.getItem("token")) {
-//       if (!localStorage.getItem("domain")) {
-//         alert("地址出错,找不到机构!");
-//       }
-//       next({ path: loginPath });
-//     } else {
-//       next();
-//     }
-//   }
-// });
+router.beforeEach((to, from, next) => {
+  const [uid, token] = getKeyToken();
+  if ((isNil(uid) || isNil(token)) && to.path.includes("/login") === false) {
+    if (
+      window.___lastInvalidDate === undefined ||
+      window.___lastInvalidDate < Date.now() - 300
+    ) {
+      window.___lastInvalidDate = Date.now();
+    }
+    router.push("/login");
+    next(false);
+  } else {
+    next();
+  }
+});
 
 export default router;

+ 1 - 1
src/store/index.js

@@ -8,5 +8,5 @@ export default new Vuex.Store({
   plugins: [createPersistedState()],
   state: {},
   mutations: {},
-  actions: {}
+  actions: {},
 });

+ 6 - 0
src/utils/utils.js

@@ -0,0 +1,6 @@
+import { YYYYMMDDHHmmss } from "@/constants/constants";
+import moment from "moment";
+
+export function dateFormatForAPI(date) {
+  return moment(date).format(YYYYMMDDHHmmss);
+}

+ 2 - 2
src/views/Home.vue

@@ -12,7 +12,7 @@ import HelloWorld from "@/components/HelloWorld.vue";
 export default {
   name: "home",
   components: {
-    HelloWorld
-  }
+    HelloWorld,
+  },
 };
 </script>

+ 2 - 2
tests/unit/.eslintrc.js

@@ -1,5 +1,5 @@
 module.exports = {
   env: {
-    jest: true
-  }
+    jest: true,
+  },
 };

+ 1 - 1
tests/unit/example.spec.js

@@ -5,7 +5,7 @@ describe("HelloWorld.vue", () => {
   it("renders props.msg when passed", () => {
     const msg = "new message";
     const wrapper = shallowMount(HelloWorld, {
-      propsData: { msg }
+      propsData: { msg },
     });
     expect(wrapper.text()).toMatch(msg);
   });

+ 8 - 8
vue.config.js

@@ -1,19 +1,19 @@
 let proxy = {
   "/ocean/api": {
     target: "http://192.168.10.30:10060",
-    changeOrigin: true
+    changeOrigin: true,
   },
   "/skynet/api": {
     target: "http://192.168.10.39:8030",
-    changeOrigin: true
-  }
+    changeOrigin: true,
+  },
 };
 
 var webpack = require("webpack");
 module.exports = {
   lintOnSave: process.env.NODE_ENV !== "production" ? true : "error",
   devServer: {
-    proxy
+    proxy,
   },
   chainWebpack: config => {
     // iview Loader
@@ -23,7 +23,7 @@ module.exports = {
       .use("iview-loader")
       .loader("iview-loader")
       .options({
-        prefix: true
+        prefix: true,
       })
       .end();
   },
@@ -32,7 +32,7 @@ module.exports = {
     plugins: [
       // Ignore all locale files of moment.js
       // TODO: use webpack stats to check if iview locale matters
-      new webpack.IgnorePlugin(/^\.\/locale$/, /moment|iview$/)
-    ]
-  }
+      new webpack.IgnorePlugin(/^\.\/locale$/, /moment|iview$/),
+    ],
+  },
 };

+ 337 - 9
yarn.lock

@@ -864,6 +864,13 @@
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
   integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
 
+"@samverschueren/stream-to-observable@^0.3.0":
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
+  integrity sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==
+  dependencies:
+    any-observable "^0.3.0"
+
 "@soda/friendly-errors-webpack-plugin@^1.7.1":
   version "1.7.1"
   resolved "https://registry.yarnpkg.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz#706f64bcb4a8b9642b48ae3ace444c70334d615d"
@@ -1601,6 +1608,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
+any-observable@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
+  integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
+
 any-promise@^1.0.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
@@ -2631,7 +2643,7 @@ caseless@~0.12.0:
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
   integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
-chalk@^1.1.1, chalk@^1.1.3:
+chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
   integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@@ -2642,7 +2654,7 @@ chalk@^1.1.1, chalk@^1.1.3:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
-chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2757,7 +2769,7 @@ clean-css@4.2.x:
   dependencies:
     source-map "~0.6.0"
 
-cli-cursor@^2.1.0:
+cli-cursor@^2.0.0, cli-cursor@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
   integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
@@ -2790,6 +2802,14 @@ cli-table3@^0.5.0:
   optionalDependencies:
     colors "^1.1.2"
 
+cli-truncate@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
+  integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=
+  dependencies:
+    slice-ansi "0.0.4"
+    string-width "^1.0.1"
+
 cli-width@^2.0.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
@@ -2932,6 +2952,11 @@ commander@2.17.x, commander@~2.17.1:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
   integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
 
+commander@^2.14.1, commander@^2.9.0:
+  version "2.20.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
+  integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
+
 commander@^2.18.0, commander@^2.19.0:
   version "2.19.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
@@ -3138,6 +3163,16 @@ cosmiconfig@^5.0.0:
     js-yaml "^3.9.0"
     parse-json "^4.0.0"
 
+cosmiconfig@^5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+  integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+  dependencies:
+    import-fresh "^2.0.0"
+    is-directory "^0.3.1"
+    js-yaml "^3.13.1"
+    parse-json "^4.0.0"
+
 create-ecdh@^4.0.0:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@@ -3496,6 +3531,11 @@ data-urls@^1.0.0:
     whatwg-mimetype "^2.2.0"
     whatwg-url "^7.0.0"
 
+date-fns@^1.27.2:
+  version "1.30.1"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
+  integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
+
 date-now@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
@@ -3544,6 +3584,11 @@ decode-uri-component@^0.2.0:
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
   integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
 
+dedent@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
+  integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
+
 deep-equal@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@@ -3620,6 +3665,18 @@ define-property@^2.0.2:
     is-descriptor "^1.0.2"
     isobject "^3.0.1"
 
+del@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
+  integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=
+  dependencies:
+    globby "^6.1.0"
+    is-path-cwd "^1.0.0"
+    is-path-in-cwd "^1.0.0"
+    p-map "^1.1.1"
+    pify "^3.0.0"
+    rimraf "^2.2.8"
+
 del@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4"
@@ -3887,6 +3944,11 @@ electron-to-chromium@^1.3.133:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.137.tgz#ba7c88024984c038a5c5c434529aabcea7b42944"
   integrity sha512-kGi32g42a8vS/WnYE7ELJyejRT7hbr3UeOOu0WeuYuQ29gCpg9Lrf6RdcTQVXSt/v0bjCfnlb/EWOOsiKpTmkw==
 
+elegant-spinner@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
+  integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
+
 element-resize-detector@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.0.tgz#63344fd6f4e5ecff6f018d027e17b281fd4fa338"
@@ -3990,7 +4052,7 @@ escape-html@~1.0.3:
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
   integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
 
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
@@ -4580,6 +4642,14 @@ figgy-pudding@^3.5.1:
   resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
   integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==
 
+figures@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+  integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=
+  dependencies:
+    escape-string-regexp "^1.0.5"
+    object-assign "^4.1.0"
+
 figures@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
@@ -4710,6 +4780,11 @@ find-cache-dir@^2.0.0:
     make-dir "^1.0.0"
     pkg-dir "^3.0.0"
 
+find-parent-dir@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54"
+  integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=
+
 find-up@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -4764,6 +4839,11 @@ flush-write-stream@^1.0.0:
     inherits "^2.0.1"
     readable-stream "^2.0.4"
 
+fn-name@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7"
+  integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=
+
 follow-redirects@^1.0.0, follow-redirects@^1.3.0:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb"
@@ -4910,6 +4990,15 @@ functional-red-black-tree@^1.0.1:
   resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
   integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
 
+g-status@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97"
+  integrity sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA==
+  dependencies:
+    arrify "^1.0.1"
+    matcher "^1.0.0"
+    simple-git "^1.85.0"
+
 gauge@~2.7.3:
   version "2.7.4"
   resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -5519,6 +5608,11 @@ indent-string@^2.1.0:
   dependencies:
     repeating "^2.0.0"
 
+indent-string@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
+  integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
+
 indexes-of@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
@@ -5865,11 +5959,30 @@ is-obj@^1.0.0, is-obj@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
   integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
 
+is-observable@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e"
+  integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==
+  dependencies:
+    symbol-observable "^1.1.0"
+
+is-path-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+  integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
+
 is-path-cwd@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.1.0.tgz#2e0c7e463ff5b7a0eb60852d851a6809347a124c"
   integrity sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw==
 
+is-path-in-cwd@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
+  integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
+  dependencies:
+    is-path-inside "^1.0.0"
+
 is-path-in-cwd@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb"
@@ -5877,6 +5990,13 @@ is-path-in-cwd@^2.0.0:
   dependencies:
     is-path-inside "^2.1.0"
 
+is-path-inside@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
+  integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
+  dependencies:
+    path-is-inside "^1.0.1"
+
 is-path-inside@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2"
@@ -6621,7 +6741,7 @@ js-yaml@^3.12.0, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1:
     argparse "^1.0.7"
     esprima "^4.0.0"
 
-js-yaml@^3.13.0:
+js-yaml@^3.13.0, js-yaml@^3.13.1:
   version "3.13.1"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
   integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
@@ -6845,6 +6965,81 @@ levn@^0.3.0, levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+lint-staged@^8.1.5:
+  version "8.1.7"
+  resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-8.1.7.tgz#a8988bc83bdffa97d04adb09dbc0b1f3a58fa6fc"
+  integrity sha512-egT0goFhIFoOGk6rasPngTFh2qDqxZddM0PwI58oi66RxCDcn5uDwxmiasWIF0qGnchHSYVJ8HPRD5LrFo7TKA==
+  dependencies:
+    chalk "^2.3.1"
+    commander "^2.14.1"
+    cosmiconfig "^5.2.0"
+    debug "^3.1.0"
+    dedent "^0.7.0"
+    del "^3.0.0"
+    execa "^1.0.0"
+    find-parent-dir "^0.3.0"
+    g-status "^2.0.2"
+    is-glob "^4.0.0"
+    is-windows "^1.0.2"
+    listr "^0.14.2"
+    listr-update-renderer "^0.5.0"
+    lodash "^4.17.11"
+    log-symbols "^2.2.0"
+    micromatch "^3.1.8"
+    npm-which "^3.0.1"
+    p-map "^1.1.1"
+    path-is-inside "^1.0.2"
+    pify "^3.0.0"
+    please-upgrade-node "^3.0.2"
+    staged-git-files "1.1.2"
+    string-argv "^0.0.2"
+    stringify-object "^3.2.2"
+    yup "^0.27.0"
+
+listr-silent-renderer@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
+  integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=
+
+listr-update-renderer@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2"
+  integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==
+  dependencies:
+    chalk "^1.1.3"
+    cli-truncate "^0.2.1"
+    elegant-spinner "^1.0.1"
+    figures "^1.7.0"
+    indent-string "^3.0.0"
+    log-symbols "^1.0.2"
+    log-update "^2.3.0"
+    strip-ansi "^3.0.1"
+
+listr-verbose-renderer@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db"
+  integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==
+  dependencies:
+    chalk "^2.4.1"
+    cli-cursor "^2.1.0"
+    date-fns "^1.27.2"
+    figures "^2.0.0"
+
+listr@^0.14.2:
+  version "0.14.3"
+  resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586"
+  integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==
+  dependencies:
+    "@samverschueren/stream-to-observable" "^0.3.0"
+    is-observable "^1.1.0"
+    is-promise "^2.1.0"
+    is-stream "^1.1.0"
+    listr-silent-renderer "^1.1.1"
+    listr-update-renderer "^0.5.0"
+    listr-verbose-renderer "^0.5.0"
+    p-map "^2.0.0"
+    rxjs "^6.3.3"
+
 load-json-file@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -6914,6 +7109,11 @@ locate-path@^3.0.0:
     p-locate "^3.0.0"
     path-exists "^3.0.0"
 
+lodash-es@^4.17.11:
+  version "4.17.11"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
+  integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==
+
 lodash._reinterpolate@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -6989,6 +7189,13 @@ lodash@4.x, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lod
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
   integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
 
+log-symbols@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
+  integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=
+  dependencies:
+    chalk "^1.0.0"
+
 log-symbols@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@@ -6996,6 +7203,15 @@ log-symbols@^2.2.0:
   dependencies:
     chalk "^2.0.1"
 
+log-update@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708"
+  integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg=
+  dependencies:
+    ansi-escapes "^3.0.0"
+    cli-cursor "^2.0.0"
+    wrap-ansi "^3.0.1"
+
 loglevel@^1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
@@ -7074,6 +7290,13 @@ map-visit@^1.0.0:
   dependencies:
     object-visit "^1.0.0"
 
+matcher@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/matcher/-/matcher-1.1.1.tgz#51d8301e138f840982b338b116bb0c09af62c1c2"
+  integrity sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==
+  dependencies:
+    escape-string-regexp "^1.0.4"
+
 math-random@^1.0.1:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
@@ -7379,6 +7602,11 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
   dependencies:
     minimist "0.0.8"
 
+moment@^2.24.0:
+  version "2.24.0"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
+  integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
+
 move-concurrently@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -7733,6 +7961,13 @@ npm-packlist@^1.1.6:
     ignore-walk "^3.0.1"
     npm-bundled "^1.0.1"
 
+npm-path@^2.0.2:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64"
+  integrity sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==
+  dependencies:
+    which "^1.2.10"
+
 npm-run-path@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@@ -7740,6 +7975,15 @@ npm-run-path@^2.0.0:
   dependencies:
     path-key "^2.0.0"
 
+npm-which@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
+  integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=
+  dependencies:
+    commander "^2.9.0"
+    npm-path "^2.0.2"
+    which "^1.2.10"
+
 "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
@@ -8031,6 +8275,11 @@ p-locate@^3.0.0:
   dependencies:
     p-limit "^2.0.0"
 
+p-map@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
+  integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==
+
 p-map@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
@@ -8158,7 +8407,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
   integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
 
-path-is-inside@^1.0.2:
+path-is-inside@^1.0.1, path-is-inside@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
   integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
@@ -8265,6 +8514,13 @@ pkg-up@^2.0.0:
   dependencies:
     find-up "^2.1.0"
 
+please-upgrade-node@^3.0.2:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac"
+  integrity sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==
+  dependencies:
+    semver-compare "^1.0.0"
+
 pluralize@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
@@ -8898,6 +9154,11 @@ prompts@^0.1.9:
     kleur "^2.0.1"
     sisteransi "^0.1.1"
 
+property-expr@^1.5.0:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f"
+  integrity sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g==
+
 proto-list@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -9512,7 +9773,7 @@ rgba-regex@^1.0.0:
   resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
   integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
 
-rimraf@2, rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2:
+rimraf@2, rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
   integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
@@ -9563,7 +9824,7 @@ rx-lite@*, rx-lite@^4.0.8:
   resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
   integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
 
-rxjs@^6.4.0:
+rxjs@^6.3.3, rxjs@^6.4.0:
   version "6.5.2"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7"
   integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==
@@ -9682,6 +9943,11 @@ selfsigned@^1.10.4:
   dependencies:
     node-forge "0.7.5"
 
+semver-compare@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
+  integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
+
 "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
   version "5.6.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
@@ -9877,6 +10143,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
   integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
 
+simple-git@^1.85.0:
+  version "1.113.0"
+  resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.113.0.tgz#668989728a1e9cf4ec6c72b69ea2eecc93489bea"
+  integrity sha512-i9WVsrK2u0G/cASI9nh7voxOk9mhanWY9eGtWBDSYql6m49Yk5/Fan6uZsDr/xmzv8n+eQ8ahKCoEr8cvU3h+g==
+  dependencies:
+    debug "^4.0.1"
+
 simple-swizzle@^0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
@@ -9899,6 +10172,11 @@ slash@^2.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
   integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
 
+slice-ansi@0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+  integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=
+
 slice-ansi@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
@@ -10143,6 +10421,11 @@ stackframe@^1.0.4:
   resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.4.tgz#357b24a992f9427cba6b545d96a14ed2cbca187b"
   integrity sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==
 
+staged-git-files@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.2.tgz#4326d33886dc9ecfa29a6193bf511ba90a46454b"
+  integrity sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==
+
 static-extend@^0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -10210,6 +10493,11 @@ strict-uri-encode@^1.0.0:
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
   integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
 
+string-argv@^0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736"
+  integrity sha1-2sMECGkMIfPDYwo/86BYd73L1zY=
+
 string-length@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
@@ -10420,11 +10708,21 @@ svgo@^1.0.0:
     unquote "~1.1.1"
     util.promisify "~1.0.0"
 
+symbol-observable@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+  integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
 symbol-tree@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
   integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=
 
+synchronous-promise@^2.0.6:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.8.tgz#01fd026fffdbf2d3d39ec06ee1a7c971578136f7"
+  integrity sha512-xYavZtFC1vKgJu0AOSYdrLeikNCsNwmUeZaV1XF9cMqEhBVVxLq6rEbYzOGrF1MV2MNPkhsJqqiXuQ4a76CEUg==
+
 table@4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
@@ -10686,6 +10984,11 @@ toposort@^1.0.0:
   resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
   integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk=
 
+toposort@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
+  integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
+
 tough-cookie@>=2.3.3:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.0.tgz#d2bceddebde633153ff20a52fa844a0dc71dacef"
@@ -11092,6 +11395,11 @@ vue-loader@^15.7.0:
     vue-hot-reload-api "^2.3.0"
     vue-style-loader "^4.1.0"
 
+vue-navigation@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/vue-navigation/-/vue-navigation-1.1.4.tgz#be990190624647daa34b8cd525fad4fb6d472542"
+  integrity sha512-flOvttNizFmnAj73hxrvGEMyOrbUiS7omKOTs+My+7wYdDjcW6JB8emGijy26/HyzcuPaiXbaUjZmXKRPFGfow==
+
 vue-router@^3.0.6:
   version "3.0.6"
   resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.6.tgz#2e4f0f9cbb0b96d0205ab2690cfe588935136ac3"
@@ -11370,7 +11678,7 @@ which-module@^2.0.0:
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
   integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 
-which@1, which@^1.2.12, which@^1.2.9, which@^1.3.0:
+which@1, which@^1.2.10, which@^1.2.12, which@^1.2.9, which@^1.3.0:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -11543,6 +11851,14 @@ wrap-ansi@^2.0.0:
     string-width "^1.0.1"
     strip-ansi "^3.0.1"
 
+wrap-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba"
+  integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=
+  dependencies:
+    string-width "^2.1.1"
+    strip-ansi "^4.0.0"
+
 wrap-ansi@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
@@ -11744,3 +12060,15 @@ yorkie@^2.0.0:
     is-ci "^1.0.10"
     normalize-path "^1.0.0"
     strip-indent "^2.0.0"
+
+yup@^0.27.0:
+  version "0.27.0"
+  resolved "https://registry.yarnpkg.com/yup/-/yup-0.27.0.tgz#f8cb198c8e7dd2124beddc2457571329096b06e7"
+  integrity sha512-v1yFnE4+u9za42gG/b/081E7uNW9mUj3qtkmelLbW5YPROZzSH/KUUyJu9Wt8vxFJcT9otL/eZopS0YK1L5yPQ==
+  dependencies:
+    "@babel/runtime" "^7.0.0"
+    fn-name "~2.0.1"
+    lodash "^4.17.11"
+    property-expr "^1.5.0"
+    synchronous-promise "^2.0.6"
+    toposort "^2.0.2"