diff --git a/README.ja.md b/README.ja.md
index 2f43128c..0c49685b 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -30,7 +30,7 @@
 
 ## 概要
 
-[vue-element-admin](https://panjiachen.github.io/vue-element-admin) は管理画面のフロントエンドのインタフェース,[vue](https://github.com/vuejs/vue) と [element-ui](https://github.com/ElemeFE/element)を使っています。i18nの多言語対応、可変ルート、権限、典型的なビジネスアプリテンプレートであり、豊富なコンポーネントを提供しています、素早くビジネス用の管理画面の現型を構築に役立ちます。
+[vue-element-admin](https://panjiachen.github.io/vue-element-admin) は管理画面のフロントエンドのインタフェースで、[vue](https://github.com/vuejs/vue) と [element-ui](https://github.com/ElemeFE/element)を使っています。i18nの多言語対応、可変ルート、権限、典型的なビジネスアプリテンプレートであり、豊富なコンポーネントを提供しています。素早くビジネス用の管理画面の現型を構築に役立ちます。
 
 - [デモページ](https://panjiachen.github.io/vue-element-admin)
 
@@ -44,18 +44,18 @@
 
 - おすすめシンプルテンプレート: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
 - デスクトップバージョン: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
-- Typescriptバージョン: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
+- Typescriptバージョン: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (感謝: [@Armour](https://github.com/Armour))
 - [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
 
-**After the `v4.1.0+` version, the default master branch will not support i18n. Please use [i18n Branch](https://github.com/PanJiaChen/vue-element-admin/tree/i18n), it will keep up with the master update**
+**バージョン`v4.1.0+`以降について、デフォルトのmasterブランチではi18nをサポートしていません。masterブランチと共にアップデートされる[i18n Branch](https://github.com/PanJiaChen/vue-element-admin/tree/i18n)を使用してください。 **
 
-**現在のバージョン `v4.0+` は `vue-cli` で構築,バグ報告は[issue](https://github.com/PanJiaChen/vue-element-admin/issues/new)のissueでお願いします。旧バージョン[tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0)もあります。`vue-cli`に依存しないです。**
+**現在のバージョン `v4.0+` は `vue-cli` で構築していて、バグ報告は[issue](https://github.com/PanJiaChen/vue-element-admin/issues/new)のissueでお願いします。旧バージョン[tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0)もあります。こちらは`vue-cli`に依存しないです。**
 
 **低いバージョンのブラウザはサーポートしないです(例えば ie),必要があれば polyfill を追加してください。 [詳細はこちら](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
 
 ## 前準備
 
-ローカル環境に [node](http://nodejs.org/) と [git](https://git-scm.com/)をインストールが必要です。[ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[vue-cli](https://github.com/vuejs/vue-cli) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element)で開発しています。Requestは[Mock.js](https://github.com/nuysoft/Mock)のモックデータを使っています。
+ローカル環境に [node](http://nodejs.org/) と [git](https://git-scm.com/)のインストールが必要です。[ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[vue-cli](https://github.com/vuejs/vue-cli) 、[axios](https://github.com/axios/axios) と [element-ui](https://github.com/ElemeFE/element)で開発しています。Requestは[Mock.js](https://github.com/nuysoft/Mock)のモックデータを使っています。
 
 **バグ修正や新規機能追加のissue と pull requestは大歓迎です。**
 
@@ -87,9 +87,9 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
   - 多言語切替
   - テーマ切替
   - サイトメニュー(ルートから生成)
-  - Breadcrumb Navigation
-  - Tag Navigation
-  - Svg Sprite Icon
+  - パンくずリストナビゲーション
+  - タブナビゲーション
+  - Svg Sprite アイコン
   - ローカル/バックエンド モック データ
   - Screenfull
 
@@ -104,33 +104,33 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
   - リード
   - Zip
 
-- Table
-  - Dynamic Table
-  - Drag And Drop Table
-  - Inline Edit Table
+- テーブル
+  - ダイナミックテーブル
+  - ドラッグアンドドロップテーブル
+  - インラインエディットテーブル
 
-- Error Page
+- エラーページ
   - 401
   - 404
 
 - コンポーネント
-  - Avatar Upload
-  - Back To Top
-  - Drag Dialog
-  - Drag Select
-  - Drag Kanban
-  - Drag List
-  - SplitPane
+  - アバターアップロード
+  - トップに戻る
+  - ドラッグダイアログ
+  - ドラッグ選択
+  - ドラッグKanban
+  - ドラッグリスト
+  - ペインの分割
   - Dropzone
-  - Sticky
+  - スティッキー
   - CountTo
 
-- Advanced Example
-- Error Log
-- Dashboard
-- Guide Page
+- 高度なサンプル
+- エラーログ
+- ダッシュボード
+- ガイドページ
 - ECharts
-- Clipboard
+- クリップボード
 - Markdown to html
 ```
 
@@ -150,7 +150,7 @@ npm install
 npm run dev
 ```
 
-This will automatically open http://localhost:9527
+http://localhost:9527 が自動的に開きます。
 
 ## Build
 
@@ -178,11 +178,11 @@ npm run lint
 npm run lint -- --fix
 ```
 
-Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
+詳細は [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) を参照してください。
 
 ## Changelog
 
-Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
+各リリースの詳細は [release notes](https://github.com/PanJiaChen/vue-element-admin/releases) にあります。
 
 ## Online Demo
 
diff --git a/README.md b/README.md
index 9cd95cc1..a6a10447 100644
--- a/README.md
+++ b/README.md
@@ -30,9 +30,9 @@ English | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Spa
 
 ## Introduction
 
-[vue-element-admin](https://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
+[vue-element-admin](https://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It is based on [vue](https://github.com/vuejs/vue) and uses the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
 
-It is a magical vue admin based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
+[vue-element-admin](https://panjiachen.github.io/vue-element-admin) is based on the newest development stack of vue and it has a built-in i18n solution, typical templates for enterprise applications, and lots of awesome features. It helps you build large and complex Single-Page Applications. I believe whatever your needs are, this project will help you.
 
 - [Preview](https://panjiachen.github.io/vue-element-admin)
 
diff --git a/README.zh-CN.md b/README.zh-CN.md
index bbb94a48..3df8b8ea 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -221,6 +221,10 @@ Detailed changes for each release are documented in the [release notes](https://
 
 [Buy me a coffee](https://www.buymeacoffee.com/Pan)
 
+## 购买贴纸
+
+你也可以通过 购买[官方授权的贴纸](https://smallsticker.com/product/vue-element-admin) 的方式来支持 vue-element-admin - 每售出一张贴纸,本项目将获得 2 元的捐赠。
+
 ## Browsers support
 
 Modern browsers and Internet Explorer 10+.
diff --git a/mock/article.js b/mock/article.js
index bc236eb9..50218ae4 100644
--- a/mock/article.js
+++ b/mock/article.js
@@ -18,7 +18,7 @@ for (let i = 0; i < count; i++) {
     forecast: '@float(0, 100, 2, 2)',
     importance: '@integer(1, 3)',
     'type|1': ['CN', 'US', 'JP', 'EU'],
-    'status|1': ['published', 'draft', 'deleted'],
+    'status|1': ['published', 'draft'],
     display_time: '@datetime',
     comment_disabled: true,
     pageviews: '@integer(300, 5000)',
@@ -29,7 +29,7 @@ for (let i = 0; i < count; i++) {
 
 export default [
   {
-    url: '/article/list',
+    url: '/vue-element-admin/article/list',
     type: 'get',
     response: config => {
       const { importance, type, title, page = 1, limit = 20, sort } = config.query
@@ -58,7 +58,7 @@ export default [
   },
 
   {
-    url: '/article/detail',
+    url: '/vue-element-admin/article/detail',
     type: 'get',
     response: config => {
       const { id } = config.query
@@ -74,7 +74,7 @@ export default [
   },
 
   {
-    url: '/article/pv',
+    url: '/vue-element-admin/article/pv',
     type: 'get',
     response: _ => {
       return {
@@ -92,7 +92,7 @@ export default [
   },
 
   {
-    url: '/article/create',
+    url: '/vue-element-admin/article/create',
     type: 'post',
     response: _ => {
       return {
@@ -103,7 +103,7 @@ export default [
   },
 
   {
-    url: '/article/update',
+    url: '/vue-element-admin/article/update',
     type: 'post',
     response: _ => {
       return {
diff --git a/mock/index.js b/mock/index.js
index 6907e861..196e2927 100644
--- a/mock/index.js
+++ b/mock/index.js
@@ -54,17 +54,4 @@ export function mockXHR() {
   }
 }
 
-// for mock server
-const responseFake = (url, type, respond) => {
-  return {
-    url: new RegExp(`/mock${url}`),
-    type: type || 'get',
-    response(req, res) {
-      res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
-    }
-  }
-}
-
-export default mocks.map(route => {
-  return responseFake(route.url, route.type, route.response)
-})
+export default mocks
diff --git a/mock/mock-server.js b/mock/mock-server.js
index 4c4cb2af..806fdacc 100644
--- a/mock/mock-server.js
+++ b/mock/mock-server.js
@@ -2,17 +2,21 @@ const chokidar = require('chokidar')
 const bodyParser = require('body-parser')
 const chalk = require('chalk')
 const path = require('path')
+const Mock = require('mockjs')
 
 const mockDir = path.join(process.cwd(), 'mock')
 
 function registerRoutes(app) {
   let mockLastIndex
   const { default: mocks } = require('./index.js')
-  for (const mock of mocks) {
+  const mocksForServer = mocks.map(route => {
+    return responseFake(route.url, route.type, route.response)
+  })
+  for (const mock of mocksForServer) {
     app[mock.type](mock.url, mock.response)
     mockLastIndex = app._router.stack.length
   }
-  const mockRoutesLength = Object.keys(mocks).length
+  const mockRoutesLength = Object.keys(mocksForServer).length
   return {
     mockRoutesLength: mockRoutesLength,
     mockStartIndex: mockLastIndex - mockRoutesLength
@@ -27,6 +31,18 @@ function unregisterRoutes() {
   })
 }
 
+// for mock server
+const responseFake = (url, type, respond) => {
+  return {
+    url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
+    type: type || 'get',
+    response(req, res) {
+      console.log('request invoke:' + req.path)
+      res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
+    }
+  }
+}
+
 module.exports = app => {
   // es6 polyfill
   require('@babel/register')
diff --git a/mock/remote-search.js b/mock/remote-search.js
index bb33c2f4..60809cb8 100644
--- a/mock/remote-search.js
+++ b/mock/remote-search.js
@@ -13,7 +13,7 @@ NameList.push({ name: 'mock-Pan' })
 export default [
   // username search
   {
-    url: '/search/user',
+    url: '/vue-element-admin/search/user',
     type: 'get',
     response: config => {
       const { name } = config.query
@@ -30,7 +30,7 @@ export default [
 
   // transaction list
   {
-    url: '/transaction/list',
+    url: '/vue-element-admin/transaction/list',
     type: 'get',
     response: _ => {
       return {
diff --git a/mock/role/index.js b/mock/role/index.js
index 39148076..d957493b 100644
--- a/mock/role/index.js
+++ b/mock/role/index.js
@@ -38,7 +38,7 @@ const roles = [
 export default [
   // mock get all routes form server
   {
-    url: '/routes',
+    url: '/vue-element-admin/routes',
     type: 'get',
     response: _ => {
       return {
@@ -50,7 +50,7 @@ export default [
 
   // mock get all roles form server
   {
-    url: '/roles',
+    url: '/vue-element-admin/roles',
     type: 'get',
     response: _ => {
       return {
@@ -62,7 +62,7 @@ export default [
 
   // add role
   {
-    url: '/role',
+    url: '/vue-element-admin/role',
     type: 'post',
     response: {
       code: 20000,
@@ -74,7 +74,7 @@ export default [
 
   // update role
   {
-    url: '/role/[A-Za-z0-9]',
+    url: '/vue-element-admin/role/[A-Za-z0-9]',
     type: 'put',
     response: {
       code: 20000,
@@ -86,7 +86,7 @@ export default [
 
   // delete role
   {
-    url: '/role/[A-Za-z0-9]',
+    url: '/vue-element-admin/role/[A-Za-z0-9]',
     type: 'delete',
     response: {
       code: 20000,
diff --git a/mock/user.js b/mock/user.js
index 43f93a04..859bd6f3 100644
--- a/mock/user.js
+++ b/mock/user.js
@@ -26,7 +26,7 @@ const users = {
 export default [
   // user login
   {
-    url: '/user/login',
+    url: '/vue-element-admin/user/login',
     type: 'post',
     response: config => {
       const { username } = config.body
@@ -49,7 +49,7 @@ export default [
 
   // get user info
   {
-    url: '/user/info\.*',
+    url: '/vue-element-admin/user/info\.*',
     type: 'get',
     response: config => {
       const { token } = config.query
@@ -72,7 +72,7 @@ export default [
 
   // user logout
   {
-    url: '/user/logout',
+    url: '/vue-element-admin/user/logout',
     type: 'post',
     response: _ => {
       return {
diff --git a/package.json b/package.json
index 9942fbac..448d1ec3 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
     "driver.js": "0.9.5",
     "dropzone": "5.5.1",
     "echarts": "4.2.1",
-    "element-ui": "2.7.0",
+    "element-ui": "2.13.0",
     "file-saver": "2.0.1",
     "fuse.js": "3.4.4",
     "js-cookie": "2.2.0",
@@ -59,6 +59,7 @@
     "nprogress": "0.2.0",
     "path-to-regexp": "2.4.0",
     "screenfull": "4.2.0",
+    "script-loader": "0.7.2",
     "showdown": "1.9.0",
     "sortablejs": "1.8.4",
     "tui-editor": "1.3.3",
@@ -96,7 +97,6 @@
     "runjs": "^4.3.2",
     "sass-loader": "^7.1.0",
     "script-ext-html-webpack-plugin": "2.1.3",
-    "script-loader": "0.7.2",
     "serve-static": "^1.13.2",
     "svg-sprite-loader": "4.1.3",
     "svgo": "1.2.0",
diff --git a/plop-templates/store/index.hbs b/plop-templates/store/index.hbs
new file mode 100644
index 00000000..4f8e2dc0
--- /dev/null
+++ b/plop-templates/store/index.hbs
@@ -0,0 +1,16 @@
+{{#if state}}
+const state = {}
+{{/if}}
+
+{{#if mutations}}
+const mutations = {}
+{{/if}}
+
+{{#if actions}}
+const actions = {}
+{{/if}}
+
+export default {
+  namespaced: true,
+  {{options}}
+}
diff --git a/plop-templates/store/prompt.js b/plop-templates/store/prompt.js
new file mode 100644
index 00000000..bcbc11d1
--- /dev/null
+++ b/plop-templates/store/prompt.js
@@ -0,0 +1,62 @@
+const { notEmpty } = require('../utils.js')
+
+module.exports = {
+  description: 'generate store',
+  prompts: [{
+    type: 'input',
+    name: 'name',
+    message: 'store name please',
+    validate: notEmpty('name')
+  },
+  {
+    type: 'checkbox',
+    name: 'blocks',
+    message: 'Blocks:',
+    choices: [{
+      name: 'state',
+      value: 'state',
+      checked: true
+    },
+    {
+      name: 'mutations',
+      value: 'mutations',
+      checked: true
+    },
+    {
+      name: 'actions',
+      value: 'actions',
+      checked: true
+    }
+    ],
+    validate(value) {
+      if (!value.includes('state') || !value.includes('mutations')) {
+        return 'store require at least state and mutations'
+      }
+      return true
+    }
+  }
+  ],
+  actions(data) {
+    const name = '{{name}}'
+    const { blocks } = data
+    const options = ['state', 'mutations']
+    const joinFlag = `,
+  `
+    if (blocks.length === 3) {
+      options.push('actions')
+    }
+
+    const actions = [{
+      type: 'add',
+      path: `src/store/modules/${name}.js`,
+      templateFile: 'plop-templates/store/index.hbs',
+      data: {
+        options: options.join(joinFlag),
+        state: blocks.includes('state'),
+        mutations: blocks.includes('mutations'),
+        actions: blocks.includes('actions')
+      }
+    }]
+    return actions
+  }
+}
diff --git a/plopfile.js b/plopfile.js
index 9f3147e2..57387bf1 100644
--- a/plopfile.js
+++ b/plopfile.js
@@ -1,7 +1,9 @@
 const viewGenerator = require('./plop-templates/view/prompt')
 const componentGenerator = require('./plop-templates/component/prompt')
+const storeGenerator = require('./plop-templates/store/prompt.js')
 
 module.exports = function(plop) {
   plop.setGenerator('view', viewGenerator)
   plop.setGenerator('component', componentGenerator)
+  plop.setGenerator('store', storeGenerator)
 }
diff --git a/src/api/article.js b/src/api/article.js
index f54b8af6..407bda16 100644
--- a/src/api/article.js
+++ b/src/api/article.js
@@ -2,7 +2,7 @@ import request from '@/utils/request'
 
 export function fetchList(query) {
   return request({
-    url: '/article/list',
+    url: '/vue-element-admin/article/list',
     method: 'get',
     params: query
   })
@@ -10,7 +10,7 @@ export function fetchList(query) {
 
 export function fetchArticle(id) {
   return request({
-    url: '/article/detail',
+    url: '/vue-element-admin/article/detail',
     method: 'get',
     params: { id }
   })
@@ -18,7 +18,7 @@ export function fetchArticle(id) {
 
 export function fetchPv(pv) {
   return request({
-    url: '/article/pv',
+    url: '/vue-element-admin/article/pv',
     method: 'get',
     params: { pv }
   })
@@ -26,7 +26,7 @@ export function fetchPv(pv) {
 
 export function createArticle(data) {
   return request({
-    url: '/article/create',
+    url: '/vue-element-admin/article/create',
     method: 'post',
     data
   })
@@ -34,7 +34,7 @@ export function createArticle(data) {
 
 export function updateArticle(data) {
   return request({
-    url: '/article/update',
+    url: '/vue-element-admin/article/update',
     method: 'post',
     data
   })
diff --git a/src/api/remote-search.js b/src/api/remote-search.js
index c7e19117..02e42b46 100644
--- a/src/api/remote-search.js
+++ b/src/api/remote-search.js
@@ -2,7 +2,7 @@ import request from '@/utils/request'
 
 export function searchUser(name) {
   return request({
-    url: '/search/user',
+    url: '/vue-element-admin/search/user',
     method: 'get',
     params: { name }
   })
@@ -10,7 +10,7 @@ export function searchUser(name) {
 
 export function transactionList(query) {
   return request({
-    url: '/transaction/list',
+    url: '/vue-element-admin/transaction/list',
     method: 'get',
     params: query
   })
diff --git a/src/api/role.js b/src/api/role.js
index f6a983f1..959bbd21 100644
--- a/src/api/role.js
+++ b/src/api/role.js
@@ -2,21 +2,21 @@ import request from '@/utils/request'
 
 export function getRoutes() {
   return request({
-    url: '/routes',
+    url: '/vue-element-admin/routes',
     method: 'get'
   })
 }
 
 export function getRoles() {
   return request({
-    url: '/roles',
+    url: '/vue-element-admin/roles',
     method: 'get'
   })
 }
 
 export function addRole(data) {
   return request({
-    url: '/role',
+    url: '/vue-element-admin/role',
     method: 'post',
     data
   })
@@ -24,7 +24,7 @@ export function addRole(data) {
 
 export function updateRole(id, data) {
   return request({
-    url: `/role/${id}`,
+    url: `/vue-element-admin/role/${id}`,
     method: 'put',
     data
   })
@@ -32,7 +32,7 @@ export function updateRole(id, data) {
 
 export function deleteRole(id) {
   return request({
-    url: `/role/${id}`,
+    url: `/vue-element-admin/role/${id}`,
     method: 'delete'
   })
 }
diff --git a/src/api/user.js b/src/api/user.js
index de69f707..b8b8741c 100644
--- a/src/api/user.js
+++ b/src/api/user.js
@@ -2,7 +2,7 @@ import request from '@/utils/request'
 
 export function login(data) {
   return request({
-    url: '/user/login',
+    url: '/vue-element-admin/user/login',
     method: 'post',
     data
   })
@@ -10,7 +10,7 @@ export function login(data) {
 
 export function getInfo(token) {
   return request({
-    url: '/user/info',
+    url: '/vue-element-admin/user/info',
     method: 'get',
     params: { token }
   })
@@ -18,7 +18,7 @@ export function getInfo(token) {
 
 export function logout() {
   return request({
-    url: '/user/logout',
+    url: '/vue-element-admin/user/logout',
     method: 'post'
   })
 }
diff --git a/src/components/Charts/mixins/resize.js b/src/components/Charts/mixins/resize.js
index dccaf1a8..b1e76e94 100644
--- a/src/components/Charts/mixins/resize.js
+++ b/src/components/Charts/mixins/resize.js
@@ -3,32 +3,54 @@ import { debounce } from '@/utils'
 export default {
   data() {
     return {
-      $_sidebarElm: null
+      $_sidebarElm: null,
+      $_resizeHandler: null
     }
   },
   mounted() {
-    this.__resizeHandler = debounce(() => {
-      if (this.chart) {
-        this.chart.resize()
-      }
-    }, 100)
-    window.addEventListener('resize', this.__resizeHandler)
+    this.initListener()
+  },
+  activated() {
+    if (!this.$_resizeHandler) {
+      // avoid duplication init
+      this.initListener()
+    }
 
-    this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
-    this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+    // when keep-alive chart activated, auto resize
+    this.resize()
   },
   beforeDestroy() {
-    window.removeEventListener('resize', this.__resizeHandler)
-
-    this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+    this.destroyListener()
+  },
+  deactivated() {
+    this.destroyListener()
   },
   methods: {
     // use $_ for mixins properties
     // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
     $_sidebarResizeHandler(e) {
       if (e.propertyName === 'width') {
-        this.__resizeHandler()
+        this.$_resizeHandler()
       }
+    },
+    initListener() {
+      this.$_resizeHandler = debounce(() => {
+        this.resize()
+      }, 100)
+      window.addEventListener('resize', this.$_resizeHandler)
+
+      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+    },
+    destroyListener() {
+      window.removeEventListener('resize', this.$_resizeHandler)
+      this.$_resizeHandler = null
+
+      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+    },
+    resize() {
+      const { chart } = this
+      chart && chart.resize()
     }
   }
 }
diff --git a/src/components/MarkdownEditor/index.vue b/src/components/MarkdownEditor/index.vue
index 3d6794b5..1a8a01ee 100644
--- a/src/components/MarkdownEditor/index.vue
+++ b/src/components/MarkdownEditor/index.vue
@@ -12,7 +12,7 @@ import Editor from 'tui-editor'
 import defaultOptions from './default-options'
 
 export default {
-  name: 'MarddownEditor',
+  name: 'MarkdownEditor',
   props: {
     value: {
       type: String,
diff --git a/src/components/Share/DropdownMenu.vue b/src/components/Share/DropdownMenu.vue
index 8ad8e56e..d194a517 100644
--- a/src/components/Share/DropdownMenu.vue
+++ b/src/components/Share/DropdownMenu.vue
@@ -44,6 +44,7 @@ $t: .1s;
   width: 250px;
   position: relative;
   z-index: 1;
+  height: auto!important;
   &-title {
     width: 100%;
     display: block;
@@ -65,10 +66,12 @@ $t: .1s;
     position: absolute;
     width: 100%;
     background: #e0e0e0;
+    color: #000;
     line-height: 60px;
     height: 60px;
     cursor: pointer;
-    font-size: 20px;
+    font-size: 18px;
+    overflow: hidden;
     opacity: 1;
     transition: transform 0.28s ease;
     &:hover {
@@ -90,7 +93,7 @@ $t: .1s;
     .share-dropdown-menu-item {
       @for $i from 1 through $n {
         &:nth-of-type(#{$i}) {
-         transition-delay: ($n - $i)*$t;
+          transition-delay: ($n - $i)*$t;
           transform: translate3d(0, ($i - 1)*60px, 0);
         }
       }
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index d9075a24..37bc1e69 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -36,8 +36,8 @@
           <a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/">
             <el-dropdown-item>Docs</el-dropdown-item>
           </a>
-          <el-dropdown-item divided>
-            <span style="display:block;" @click="logout">Log Out</span>
+          <el-dropdown-item divided @click.native="logout">
+            <span style="display:block;">Log Out</span>
           </el-dropdown-item>
         </el-dropdown-menu>
       </el-dropdown>
diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue
index 2d49dd87..a418c3d7 100644
--- a/src/layout/components/Sidebar/SidebarItem.vue
+++ b/src/layout/components/Sidebar/SidebarItem.vue
@@ -1,5 +1,5 @@
 <template>
-  <div v-if="!item.hidden" class="menu-wrapper">
+  <div v-if="!item.hidden">
     <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
         <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index 24a92f1d..642b4f42 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -9,16 +9,16 @@
         :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
         tag="span"
         class="tags-view-item"
-        @click.middle.native="closeSelectedTag(tag)"
+        @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
         @contextmenu.prevent.native="openMenu(tag,$event)"
       >
         {{ tag.title }}
-        <span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
+        <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
       </router-link>
     </scroll-pane>
     <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
       <li @click="refreshSelectedTag(selectedTag)">Refresh</li>
-      <li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">Close</li>
+      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
       <li @click="closeOthersTags">Close Others</li>
       <li @click="closeAllTags(selectedTag)">Close All</li>
     </ul>
@@ -69,6 +69,9 @@ export default {
     isActive(route) {
       return route.path === this.$route.path
     },
+    isAffix(tag) {
+      return tag.meta && tag.meta.affix
+    },
     filterAffixTags(routes, basePath = '/') {
       let tags = []
       routes.forEach(route => {
@@ -155,7 +158,7 @@ export default {
     toLastView(visitedViews, view) {
       const latestView = visitedViews.slice(-1)[0]
       if (latestView) {
-        this.$router.push(latestView)
+        this.$router.push(latestView.fullPath)
       } else {
         // now the default is to redirect to the home page if there is no tags-view,
         // you can adjust it according to your needs.
diff --git a/src/main.js b/src/main.js
index ef07f389..1049d762 100644
--- a/src/main.js
+++ b/src/main.js
@@ -25,10 +25,10 @@ import * as filters from './filters' // global filters
  * you can execute: mockXHR()
  *
  * Currently MockJs will be used in the production environment,
- * please remove it before going online! ! !
+ * please remove it before going online ! ! !
  */
-import { mockXHR } from '../mock'
 if (process.env.NODE_ENV === 'production') {
+  const { mockXHR } = require('../mock')
   mockXHR()
 }
 
diff --git a/src/router/index.js b/src/router/index.js
index fbe8ad25..2e522b2a 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -45,7 +45,7 @@ export const constantRoutes = [
     hidden: true,
     children: [
       {
-        path: '/redirect/:path*',
+        path: '/redirect/:path(.*)',
         component: () => import('@/views/redirect/index')
       }
     ]
diff --git a/src/store/modules/tagsView.js b/src/store/modules/tagsView.js
index 3e2c1703..57e72421 100644
--- a/src/store/modules/tagsView.js
+++ b/src/store/modules/tagsView.js
@@ -28,13 +28,8 @@ const mutations = {
     }
   },
   DEL_CACHED_VIEW: (state, view) => {
-    for (const i of state.cachedViews) {
-      if (i === view.name) {
-        const index = state.cachedViews.indexOf(i)
-        state.cachedViews.splice(index, 1)
-        break
-      }
-    }
+    const index = state.cachedViews.indexOf(view.name)
+    index > -1 && state.cachedViews.splice(index, 1)
   },
 
   DEL_OTHERS_VISITED_VIEWS: (state, view) => {
@@ -43,12 +38,12 @@ const mutations = {
     })
   },
   DEL_OTHERS_CACHED_VIEWS: (state, view) => {
-    for (const i of state.cachedViews) {
-      if (i === view.name) {
-        const index = state.cachedViews.indexOf(i)
-        state.cachedViews = state.cachedViews.slice(index, index + 1)
-        break
-      }
+    const index = state.cachedViews.indexOf(view.name)
+    if (index > -1) {
+      state.cachedViews = state.cachedViews.slice(index, index + 1)
+    } else {
+      // if index = -1, there is no cached tags
+      state.cachedViews = []
     }
   },
 
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index dbc29003..1391fa4a 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -73,13 +73,18 @@ const actions = {
   },
 
   // user logout
-  logout({ commit, state }) {
+  logout({ commit, state, dispatch }) {
     return new Promise((resolve, reject) => {
       logout(state.token).then(() => {
         commit('SET_TOKEN', '')
         commit('SET_ROLES', [])
         removeToken()
         resetRouter()
+
+        // reset visited views and cached views
+        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
+        dispatch('tagsView/delAllViews', null, { root: true })
+
         resolve()
       }).catch(error => {
         reject(error)
diff --git a/src/styles/element-ui.scss b/src/styles/element-ui.scss
index 9581d56a..955d3cab 100644
--- a/src/styles/element-ui.scss
+++ b/src/styles/element-ui.scss
@@ -77,3 +77,8 @@
 .el-range-editor.el-input__inner {
   display: inline-flex !important;
 }
+
+// to fix el-date-picker css style
+.el-range-separator {
+  box-sizing: content-box;
+}
diff --git a/src/utils/index.js b/src/utils/index.js
index ffb55260..eb760d5e 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -6,7 +6,7 @@
  * Parse the time to string
  * @param {(Object|string|number)} time
  * @param {string} cFormat
- * @returns {string}
+ * @returns {string | null}
  */
 export function parseTime(time, cFormat) {
   if (arguments.length === 0) {
@@ -17,9 +17,17 @@ export function parseTime(time, cFormat) {
   if (typeof time === 'object') {
     date = time
   } else {
-    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
-      time = parseInt(time)
+    if ((typeof time === 'string')) {
+      if ((/^[0-9]+$/.test(time))) {
+        // support "1548221490638"
+        time = parseInt(time)
+      } else {
+        // support safari
+        // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
+        time = time.replace(new RegExp(/-/gm), '/')
+      }
     }
+
     if ((typeof time === 'number') && (time.toString().length === 10)) {
       time = time * 1000
     }
@@ -34,14 +42,11 @@ export function parseTime(time, cFormat) {
     s: date.getSeconds(),
     a: date.getDay()
   }
-  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
-    let value = formatObj[key]
+  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
+    const value = formatObj[key]
     // Note: getDay() returns 0 on Sunday
     if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
-    if (result.length > 0 && value < 10) {
-      value = '0' + value
-    }
-    return value || 0
+    return value.toString().padStart(2, '0')
   })
   return time_str
 }
diff --git a/src/views/dashboard/admin/components/RaddarChart.vue b/src/views/dashboard/admin/components/RaddarChart.vue
index 6823af31..52c8f9fb 100644
--- a/src/views/dashboard/admin/components/RaddarChart.vue
+++ b/src/views/dashboard/admin/components/RaddarChart.vue
@@ -70,7 +70,7 @@ export default {
           indicator: [
             { name: 'Sales', max: 10000 },
             { name: 'Administration', max: 20000 },
-            { name: 'Information Techology', max: 20000 },
+            { name: 'Information Technology', max: 20000 },
             { name: 'Customer Support', max: 20000 },
             { name: 'Development', max: 20000 },
             { name: 'Marketing', max: 20000 }
diff --git a/src/views/documentation/index.vue b/src/views/documentation/index.vue
index 0f4c4d92..d3f77468 100644
--- a/src/views/documentation/index.vue
+++ b/src/views/documentation/index.vue
@@ -1,21 +1,10 @@
 <template>
   <div class="app-container documentation-container">
-    <a
-      class="document-btn"
-      target="_blank"
-      href="https://panjiachen.github.io/vue-element-admin-site/"
-    >Documentation</a>
-    <a
-      class="document-btn"
-      target="_blank"
-      href="https://github.com/PanJiaChen/vue-element-admin/"
-    >Github Repository</a>
-    <a
-      class="document-btn"
-      target="_blank"
-      href="https://panjiachen.gitee.io/vue-element-admin-site/zh/"
-    >国内文档</a>
-    <dropdown-menu :items="articleList" style="float:left;margin-left:50px;" title="系列文章" />
+    <a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/">Documentation</a>
+    <a class="document-btn" target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">Github Repository</a>
+    <a class="document-btn" target="_blank" href="https://panjiachen.gitee.io/vue-element-admin-site/zh/">国内文档</a>
+    <dropdown-menu class="document-btn" :items="articleList" title="系列文章" />
+    <a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/zh/job/">内推招聘</a>
   </div>
 </template>
 
@@ -46,15 +35,19 @@ export default {
 <style lang="scss" scoped>
 .documentation-container {
   margin: 50px;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-evenly;
+
   .document-btn {
-    float: left;
-    margin-left: 50px;
+    flex-shrink: 0;
     display: block;
     cursor: pointer;
     background: black;
     color: white;
     height: 60px;
     width: 200px;
+    margin-bottom: 16px;
     line-height: 60px;
     font-size: 20px;
     text-align: center;
diff --git a/src/views/example/components/ArticleDetail.vue b/src/views/example/components/ArticleDetail.vue
index ef0f1df9..8aa97339 100644
--- a/src/views/example/components/ArticleDetail.vue
+++ b/src/views/example/components/ArticleDetail.vue
@@ -170,8 +170,6 @@ export default {
     if (this.isEdit) {
       const id = this.$route.params && this.$route.params.id
       this.fetchData(id)
-    } else {
-      this.postForm = Object.assign({}, defaultForm)
     }
 
     // Why need to make a copy of this.$route here?
diff --git a/src/views/example/create.vue b/src/views/example/create.vue
index 4d3a24b5..f28ce287 100644
--- a/src/views/example/create.vue
+++ b/src/views/example/create.vue
@@ -6,7 +6,7 @@
 import ArticleDetail from './components/ArticleDetail'
 
 export default {
-  name: 'CreateForm',
+  name: 'CreateArticle',
   components: { ArticleDetail }
 }
 </script>
diff --git a/src/views/excel/export-excel.vue b/src/views/excel/export-excel.vue
index 6152e019..b4d7500b 100644
--- a/src/views/excel/export-excel.vue
+++ b/src/views/excel/export-excel.vue
@@ -13,7 +13,7 @@
       </a>
     </div>
 
-    <el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row>
+    <el-table v-loading="listLoading" :data="list" element-loading-text="Loading..." border fit highlight-current-row>
       <el-table-column align="center" label="Id" width="95">
         <template slot-scope="scope">
           {{ scope.$index }}
diff --git a/src/views/icons/element-icons.js b/src/views/icons/element-icons.js
index df722010..9ea4d63f 100644
--- a/src/views/icons/element-icons.js
+++ b/src/views/icons/element-icons.js
@@ -1,74 +1,3 @@
-const elementIcons = [
-  'info',
-  'error',
-  'success',
-  'warning',
-  'question',
-  'back',
-  'arrow-left',
-  'arrow-down',
-  'arrow-right',
-  'arrow-up',
-  'caret-left',
-  'caret-bottom',
-  'caret-top',
-  'caret-right',
-  'd-arrow-left',
-  'd-arrow-right',
-  'minus',
-  'plus',
-  'remove',
-  'circle-plus',
-  'remove-outline',
-  'circle-plus-outline',
-  'close',
-  'check',
-  'circle-close',
-  'circle-check',
-  'circle-close-outline',
-  'circle-check-outline',
-  'zoom-out',
-  'zoom-in',
-  'd-caret',
-  'sort',
-  'sort-down',
-  'sort-up',
-  'tickets',
-  'document',
-  'goods',
-  'sold-out',
-  'news',
-  'message',
-  'date',
-  'printer',
-  'time',
-  'bell',
-  'mobile-phone',
-  'service',
-  'view',
-  'menu',
-  'more',
-  'more-outline',
-  'star-on',
-  'star-off',
-  'location',
-  'location-outline',
-  'phone',
-  'phone-outline',
-  'picture',
-  'picture-outline',
-  'delete',
-  'search',
-  'edit',
-  'edit-outline',
-  'rank',
-  'refresh',
-  'share',
-  'setting',
-  'upload',
-  'upload2',
-  'download',
-  'loading'
-]
+const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']
 
 export default elementIcons
diff --git a/src/views/icons/index.vue b/src/views/icons/index.vue
index 3677afd6..4c483db4 100644
--- a/src/views/icons/index.vue
+++ b/src/views/icons/index.vue
@@ -6,29 +6,33 @@
     </aside>
     <el-tabs type="border-card">
       <el-tab-pane label="Icons">
-        <div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
-          <el-tooltip placement="top">
-            <div slot="content">
-              {{ generateIconCode(item) }}
-            </div>
-            <div class="icon-item">
-              <svg-icon :icon-class="item" class-name="disabled" />
-              <span>{{ item }}</span>
-            </div>
-          </el-tooltip>
+        <div class="grid">
+          <div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
+            <el-tooltip placement="top">
+              <div slot="content">
+                {{ generateIconCode(item) }}
+              </div>
+              <div class="icon-item">
+                <svg-icon :icon-class="item" class-name="disabled" />
+                <span>{{ item }}</span>
+              </div>
+            </el-tooltip>
+          </div>
         </div>
       </el-tab-pane>
       <el-tab-pane label="Element-UI Icons">
-        <div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
-          <el-tooltip placement="top">
-            <div slot="content">
-              {{ generateElementIconCode(item) }}
-            </div>
-            <div class="icon-item">
-              <i :class="'el-icon-' + item" />
-              <span>{{ item }}</span>
-            </div>
-          </el-tooltip>
+        <div class="grid">
+          <div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
+            <el-tooltip placement="top">
+              <div slot="content">
+                {{ generateElementIconCode(item) }}
+              </div>
+              <div class="icon-item">
+                <i :class="'el-icon-' + item" />
+                <span>{{ item }}</span>
+              </div>
+            </el-tooltip>
+          </div>
         </div>
       </el-tab-pane>
     </el-tabs>
@@ -67,6 +71,12 @@ export default {
   margin: 10px 20px 0;
   overflow: hidden;
 
+  .grid {
+    position: relative;
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+  }
+
   .icon-item {
     margin: 20px;
     height: 85px;
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
index 5fb3f6e3..25906405 100644
--- a/src/views/login/index.vue
+++ b/src/views/login/index.vue
@@ -138,17 +138,9 @@ export default {
     // window.removeEventListener('storage', this.afterQRScan)
   },
   methods: {
-    checkCapslock({ shiftKey, key } = {}) {
-      if (key && key.length === 1) {
-        if (shiftKey && (key >= 'a' && key <= 'z') || !shiftKey && (key >= 'A' && key <= 'Z')) {
-          this.capsTooltip = true
-        } else {
-          this.capsTooltip = false
-        }
-      }
-      if (key === 'CapsLock' && this.capsTooltip === true) {
-        this.capsTooltip = false
-      }
+    checkCapslock(e) {
+      const { key } = e
+      this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
     },
     showPwd() {
       if (this.passwordType === 'password') {
diff --git a/src/views/pdf/download.vue b/src/views/pdf/download.vue
index bd0b3be2..a348c697 100644
--- a/src/views/pdf/download.vue
+++ b/src/views/pdf/download.vue
@@ -26,17 +26,17 @@ export default {
   },
   methods: {
     fetchData() {
-       import('./content.js').then(data => {
-         const { title } = data.default
-         document.title = title
-         this.article = data.default
-         setTimeout(() => {
-           this.fullscreenLoading = false
-           this.$nextTick(() => {
-             window.print()
-           })
-         }, 3000)
-       })
+      import('./content.js').then(data => {
+        const { title } = data.default
+        document.title = title
+        this.article = data.default
+        setTimeout(() => {
+          this.fullscreenLoading = false
+          this.$nextTick(() => {
+            window.print()
+          })
+        }, 3000)
+      })
     }
   }
 }
diff --git a/src/views/profile/components/UserCard.vue b/src/views/profile/components/UserCard.vue
index c2ce1aa8..2476f04a 100644
--- a/src/views/profile/components/UserCard.vue
+++ b/src/views/profile/components/UserCard.vue
@@ -65,7 +65,7 @@ export default {
           name: '',
           email: '',
           avatar: '',
-          roles: ''
+          role: ''
         }
       }
     }
@@ -74,61 +74,61 @@ export default {
 </script>
 
 <style lang="scss" scoped>
- .box-center {
-   margin: 0 auto;
-   display: table;
- }
+.box-center {
+  margin: 0 auto;
+  display: table;
+}
 
- .text-muted {
-   color: #777;
- }
+.text-muted {
+  color: #777;
+}
 
- .user-profile {
-   .user-name {
-     font-weight: bold;
-   }
+.user-profile {
+  .user-name {
+    font-weight: bold;
+  }
 
-   .box-center {
-     padding-top: 10px;
-   }
+  .box-center {
+    padding-top: 10px;
+  }
 
-   .user-role {
-     padding-top: 10px;
-     font-weight: 400;
-     font-size: 14px;
-   }
+  .user-role {
+    padding-top: 10px;
+    font-weight: 400;
+    font-size: 14px;
+  }
 
-   .box-social {
-     padding-top: 30px;
+  .box-social {
+    padding-top: 30px;
 
-     .el-table {
-       border-top: 1px solid #dfe6ec;
-     }
-   }
+    .el-table {
+      border-top: 1px solid #dfe6ec;
+    }
+  }
 
-   .user-follow {
-     padding-top: 20px;
-   }
- }
+  .user-follow {
+    padding-top: 20px;
+  }
+}
 
- .user-bio {
-   margin-top: 20px;
-   color: #606266;
+.user-bio {
+  margin-top: 20px;
+  color: #606266;
 
-   span {
-     padding-left: 4px;
-   }
+  span {
+    padding-left: 4px;
+  }
 
-   .user-bio-section {
-     font-size: 14px;
-     padding: 15px 0;
+  .user-bio-section {
+    font-size: 14px;
+    padding: 15px 0;
 
-     .user-bio-section-header {
-       border-bottom: 1px solid #dfe6ec;
-       padding-bottom: 10px;
-       margin-bottom: 10px;
-       font-weight: bold;
-     }
-   }
- }
+    .user-bio-section-header {
+      border-bottom: 1px solid #dfe6ec;
+      padding-bottom: 10px;
+      margin-bottom: 10px;
+      font-weight: bold;
+    }
+  }
+}
 </style>
diff --git a/src/views/tab/index.vue b/src/views/tab/index.vue
index 862a160f..2a35fa58 100644
--- a/src/views/tab/index.vue
+++ b/src/views/tab/index.vue
@@ -13,11 +13,11 @@
 </template>
 
 <script>
-import tabPane from './components/TabPane'
+import TabPane from './components/TabPane'
 
 export default {
   name: 'Tab',
-  components: { tabPane },
+  components: { TabPane },
   data() {
     return {
       tabMapOptions: [
diff --git a/src/views/table/complex-table.vue b/src/views/table/complex-table.vue
index 3d61f6c6..295c5fc4 100644
--- a/src/views/table/complex-table.vue
+++ b/src/views/table/complex-table.vue
@@ -36,13 +36,13 @@
       @sort-change="sortChange"
     >
       <el-table-column label="ID" prop="id" sortable="custom" align="center" width="80" :class-name="getSortClass('id')">
-        <template slot-scope="scope">
-          <span>{{ scope.row.id }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.id }}</span>
         </template>
       </el-table-column>
       <el-table-column label="Date" width="150px" align="center">
-        <template slot-scope="scope">
-          <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
         </template>
       </el-table-column>
       <el-table-column label="Title" min-width="150px">
@@ -52,18 +52,18 @@
         </template>
       </el-table-column>
       <el-table-column label="Author" width="110px" align="center">
-        <template slot-scope="scope">
-          <span>{{ scope.row.author }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.author }}</span>
         </template>
       </el-table-column>
       <el-table-column v-if="showReviewer" label="Reviewer" width="110px" align="center">
-        <template slot-scope="scope">
-          <span style="color:red;">{{ scope.row.reviewer }}</span>
+        <template slot-scope="{row}">
+          <span style="color:red;">{{ row.reviewer }}</span>
         </template>
       </el-table-column>
       <el-table-column label="Imp" width="80px">
-        <template slot-scope="scope">
-          <svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="meta-item__icon" />
+        <template slot-scope="{row}">
+          <svg-icon v-for="n in + row.importance" :key="n" icon-class="star" class="meta-item__icon" />
         </template>
       </el-table-column>
       <el-table-column label="Readings" align="center" width="95">
@@ -80,7 +80,7 @@
         </template>
       </el-table-column>
       <el-table-column label="Actions" align="center" width="230" class-name="small-padding fixed-width">
-        <template slot-scope="{row}">
+        <template slot-scope="{row,$index}">
           <el-button type="primary" size="mini" @click="handleUpdate(row)">
             Edit
           </el-button>
@@ -90,7 +90,7 @@
           <el-button v-if="row.status!='draft'" size="mini" @click="handleModifyStatus(row,'draft')">
             Draft
           </el-button>
-          <el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(row,'deleted')">
+          <el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row,$index)">
             Delete
           </el-button>
         </template>
@@ -319,13 +319,8 @@ export default {
           const tempData = Object.assign({}, this.temp)
           tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
           updateArticle(tempData).then(() => {
-            for (const v of this.list) {
-              if (v.id === this.temp.id) {
-                const index = this.list.indexOf(v)
-                this.list.splice(index, 1, this.temp)
-                break
-              }
-            }
+            const index = this.list.findIndex(v => v.id === this.temp.id)
+            this.list.splice(index, 1, this.temp)
             this.dialogFormVisible = false
             this.$notify({
               title: 'Success',
@@ -337,14 +332,13 @@ export default {
         }
       })
     },
-    handleDelete(row) {
+    handleDelete(row, index) {
       this.$notify({
         title: 'Success',
         message: 'Delete Successfully',
         type: 'success',
         duration: 2000
       })
-      const index = this.list.indexOf(row)
       this.list.splice(index, 1)
     },
     handleFetchPv(pv) {
@@ -358,7 +352,7 @@ export default {
       import('@/vendor/Export2Excel').then(excel => {
         const tHeader = ['timestamp', 'title', 'type', 'importance', 'status']
         const filterVal = ['timestamp', 'title', 'type', 'importance', 'status']
-        const data = this.formatJson(filterVal, this.list)
+        const data = this.formatJson(filterVal)
         excel.export_json_to_excel({
           header: tHeader,
           data,
@@ -367,8 +361,8 @@ export default {
         this.downloadLoading = false
       })
     },
-    formatJson(filterVal, jsonData) {
-      return jsonData.map(v => filterVal.map(j => {
+    formatJson(filterVal) {
+      return this.list.map(v => filterVal.map(j => {
         if (j === 'timestamp') {
           return parseTime(v[j])
         } else {
@@ -378,11 +372,7 @@ export default {
     },
     getSortClass: function(key) {
       const sort = this.listQuery.sort
-      return sort === `+${key}`
-        ? 'ascending'
-        : sort === `-${key}`
-          ? 'descending'
-          : ''
+      return sort === `+${key}` ? 'ascending' : 'descending'
     }
   }
 }
diff --git a/src/views/table/drag-table.vue b/src/views/table/drag-table.vue
index 954d59eb..3b5c890a 100644
--- a/src/views/table/drag-table.vue
+++ b/src/views/table/drag-table.vue
@@ -3,38 +3,38 @@
     <!-- Note that row-key is necessary to get a correct row order. -->
     <el-table ref="dragTable" v-loading="listLoading" :data="list" row-key="id" border fit highlight-current-row style="width: 100%">
       <el-table-column align="center" label="ID" width="65">
-        <template slot-scope="scope">
-          <span>{{ scope.row.id }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.id }}</span>
         </template>
       </el-table-column>
 
       <el-table-column width="180px" align="center" label="Date">
-        <template slot-scope="scope">
-          <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
         </template>
       </el-table-column>
 
       <el-table-column min-width="300px" label="Title">
-        <template slot-scope="scope">
-          <span>{{ scope.row.title }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.title }}</span>
         </template>
       </el-table-column>
 
       <el-table-column width="110px" align="center" label="Author">
-        <template slot-scope="scope">
-          <span>{{ scope.row.author }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.author }}</span>
         </template>
       </el-table-column>
 
       <el-table-column width="100px" label="Importance">
-        <template slot-scope="scope">
-          <svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="icon-star" />
+        <template slot-scope="{row}">
+          <svg-icon v-for="n in + row.importance" :key="n" icon-class="star" class="icon-star" />
         </template>
       </el-table-column>
 
       <el-table-column align="center" label="Readings" width="95">
-        <template slot-scope="scope">
-          <span>{{ scope.row.pageviews }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.pageviews }}</span>
         </template>
       </el-table-column>
 
diff --git a/src/views/table/inline-edit-table.vue b/src/views/table/inline-edit-table.vue
index 63828145..31e0065f 100644
--- a/src/views/table/inline-edit-table.vue
+++ b/src/views/table/inline-edit-table.vue
@@ -2,26 +2,26 @@
   <div class="app-container">
     <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
       <el-table-column align="center" label="ID" width="80">
-        <template slot-scope="scope">
-          <span>{{ scope.row.id }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.id }}</span>
         </template>
       </el-table-column>
 
       <el-table-column width="180px" align="center" label="Date">
-        <template slot-scope="scope">
-          <span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
         </template>
       </el-table-column>
 
       <el-table-column width="120px" align="center" label="Author">
-        <template slot-scope="scope">
-          <span>{{ scope.row.author }}</span>
+        <template slot-scope="{row}">
+          <span>{{ row.author }}</span>
         </template>
       </el-table-column>
 
       <el-table-column width="100px" label="Importance">
-        <template slot-scope="scope">
-          <svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="meta-item__icon" />
+        <template slot-scope="{row}">
+          <svg-icon v-for="n in + row.importance" :key="n" icon-class="star" class="meta-item__icon" />
         </template>
       </el-table-column>
 
diff --git a/tests/unit/utils/parseTime.spec.js b/tests/unit/utils/parseTime.spec.js
index 77ecb9d5..bc61d1ac 100644
--- a/tests/unit/utils/parseTime.spec.js
+++ b/tests/unit/utils/parseTime.spec.js
@@ -4,6 +4,11 @@ describe('Utils:parseTime', () => {
   it('timestamp', () => {
     expect(parseTime(d)).toBe('2018-07-13 17:54:01')
   })
+
+  it('timestamp string', () => {
+    expect(parseTime((d + ''))).toBe('2018-07-13 17:54:01')
+  })
+
   it('ten digits timestamp', () => {
     expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01')
   })
diff --git a/vue.config.js b/vue.config.js
index 7b8f3a5d..1ec990ea 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -36,18 +36,7 @@ module.exports = {
       warnings: false,
       errors: true
     },
-    proxy: {
-      // change xxx-api/login => mock/login
-      // detail: https://cli.vuejs.org/config/#devserver-proxy
-      [process.env.VUE_APP_BASE_API]: {
-        target: `http://127.0.0.1:${port}/mock`,
-        changeOrigin: true,
-        pathRewrite: {
-          ['^' + process.env.VUE_APP_BASE_API]: ''
-        }
-      }
-    },
-    after: require('./mock/mock-server.js')
+    before: require('./mock/mock-server.js')
   },
   configureWebpack: {
     // provide the app's title in webpack's name field, so that