diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..d5408027
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+patreon: panjiachen
+custom: https://panjiachen.github.io/vue-element-admin-site/donate
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index 96be4532..76083546 100755
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -12,3 +12,24 @@ about: Asking questions about use
     Before asking a question, please make sure that you have tried your best to solve this problem.
     If it's a code-related issue, please don't just take screenshots. Please provide an online demo to save each other's time.
 -->
+
+#### Steps to reproduce(问题复现步骤)
+<!--
+1. [xxx]
+2. [xxx]
+3. [xxxx]
+-->
+
+#### Screenshot or Gif(截图或动态图)
+
+
+#### Link to minimal reproduction(最小可在线还原demo)
+
+<!--
+Please only use Codepen, JSFiddle, CodeSandbox or a github repo
+-->
+
+#### Other relevant information(格外信息)
+- Your OS:
+- Node.js version:
+- vue-element-admin version:
diff --git a/package.json b/package.json
index cd1369b8..8fbbf88e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "vue-element-admin",
-  "version": "4.2.0",
+  "version": "4.2.1",
   "description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
   "author": "Pan <panfree23@gmail.com>",
   "license": "MIT",
@@ -78,6 +78,7 @@
     "@vue/cli-plugin-unit-jest": "3.5.3",
     "@vue/cli-service": "3.5.3",
     "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "^9.5.1",
     "babel-core": "7.0.0-bridge.0",
     "babel-eslint": "10.0.1",
     "babel-jest": "23.6.0",
diff --git a/src/components/ImageCropper/index.vue b/src/components/ImageCropper/index.vue
index c30c5b18..c2688e99 100644
--- a/src/components/ImageCropper/index.vue
+++ b/src/components/ImageCropper/index.vue
@@ -839,16 +839,20 @@ export default {
           that.$emit('crop-upload-fail', err, field, ki)
         }
       })
+    },
+    closeHandler(e) {
+      if (this.value && (e.key == 'Escape' || e.keyCode == 27)) {
+        this.off()
+      }
     }
   },
   created() {
     // 绑定按键esc隐藏此插件事件
-    document.addEventListener('keyup', (e) => {
-      if (this.value && (e.key == 'Escape' || e.keyCode == 27)) {
-        this.off()
-      }
-    })
-  }
+    document.addEventListener('keyup', this.closeHandler)
+  },
+  destroyed() {
+    document.removeEventListener('keyup', this.closeHandler)
+  },
 }
 </script>
 
diff --git a/src/components/Tinymce/dynamicLoadScript.js b/src/components/Tinymce/dynamicLoadScript.js
index 46a93290..185f58dc 100644
--- a/src/components/Tinymce/dynamicLoadScript.js
+++ b/src/components/Tinymce/dynamicLoadScript.js
@@ -1,3 +1,11 @@
+let callbacks = []
+
+function loadedTinymce() {
+  // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
+  // check is successfully downloaded script
+  return window.tinymce
+}
+
 const dynamicLoadScript = (src, callback) => {
   const existingScript = document.getElementById(src)
   const cb = callback || function() {}
@@ -7,19 +15,28 @@ const dynamicLoadScript = (src, callback) => {
     script.src = src // src url for the third-party library being loaded.
     script.id = src
     document.body.appendChild(script)
-
+    callbacks.push(cb)
     const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
-    onEnd(script, cb)
+    onEnd(script)
   }
 
-  if (existingScript && cb) cb(null, existingScript)
+  if (existingScript && cb) {
+    if (loadedTinymce()) {
+      cb(null, existingScript)
+    } else {
+      callbacks.push(cb)
+    }
+  }
 
-  function stdOnEnd(script, cb) {
+  function stdOnEnd(script) {
     script.onload = function() {
       // this.onload = null here is necessary
       // because even IE9 works not like others
       this.onerror = this.onload = null
-      cb(null, script)
+      for (const cb of callbacks) {
+        cb(null, script)
+      }
+      callbacks = null
     }
     script.onerror = function() {
       this.onerror = this.onload = null
@@ -27,11 +44,14 @@ const dynamicLoadScript = (src, callback) => {
     }
   }
 
-  function ieOnEnd(script, cb) {
+  function ieOnEnd(script) {
     script.onreadystatechange = function() {
       if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
       this.onreadystatechange = null
-      cb(null, script) // there is no way to catch loading errors in IE8
+      for (const cb of callbacks) {
+        cb(null, script) // there is no way to catch loading errors in IE8
+      }
+      callbacks = null
     }
   }
 }
diff --git a/src/components/Tinymce/index.vue b/src/components/Tinymce/index.vue
index a46b37d6..0c6174c4 100644
--- a/src/components/Tinymce/index.vue
+++ b/src/components/Tinymce/index.vue
@@ -64,7 +64,9 @@ export default {
       fullscreen: false,
       languageTypeList: {
         'en': 'en',
-        'zh': 'zh_CN'
+        'zh': 'zh_CN',
+        'es': 'es_MX',
+        'ja': 'ja'
       }
     }
   },
@@ -114,6 +116,7 @@ export default {
       const _this = this
       window.tinymce.init({
         selector: `#${this.tinymceId}`,
+        language: this.languageTypeList['en'],
         height: this.height,
         body_class: 'panel-body ',
         object_resizing: false,
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 9e12c662..a8976380 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -16,7 +16,7 @@ export default {
       return this.$store.state.tagsView.cachedViews
     },
     key() {
-      return this.$route.fullPath
+      return this.$route.path
     }
   }
 }
diff --git a/src/utils/request.js b/src/utils/request.js
index bc346a8c..2fb95ac0 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -6,7 +6,7 @@ import { getToken } from '@/utils/auth'
 // create an axios instance
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
-  withCredentials: true, // send cookies when cross-domain requests
+  // withCredentials: true, // send cookies when cross-domain requests
   timeout: 5000 // request timeout
 })
 
@@ -48,7 +48,7 @@ service.interceptors.response.use(
     // if the custom code is not 20000, it is judged as an error.
     if (res.code !== 20000) {
       Message({
-        message: res.message || 'error',
+        message: res.message || 'Error',
         type: 'error',
         duration: 5 * 1000
       })
@@ -66,7 +66,7 @@ service.interceptors.response.use(
           })
         })
       }
-      return Promise.reject(res.message || 'error')
+      return Promise.reject(new Error(res.message || 'Error'))
     } else {
       return res
     }
diff --git a/src/views/dashboard/admin/components/BarChart.vue b/src/views/dashboard/admin/components/BarChart.vue
index 47e7a110..be0af34f 100644
--- a/src/views/dashboard/admin/components/BarChart.vue
+++ b/src/views/dashboard/admin/components/BarChart.vue
@@ -5,11 +5,12 @@
 <script>
 import echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
-import { debounce } from '@/utils'
+import resize from './mixins/resize'
 
 const animationDuration = 6000
 
 export default {
+  mixins: [resize],
   props: {
     className: {
       type: String,
@@ -33,18 +34,11 @@ export default {
     this.$nextTick(() => {
       this.initChart()
     })
-    this.__resizeHandler = debounce(() => {
-      if (this.chart) {
-        this.chart.resize()
-      }
-    }, 100)
-    window.addEventListener('resize', this.__resizeHandler)
   },
   beforeDestroy() {
     if (!this.chart) {
       return
     }
-    window.removeEventListener('resize', this.__resizeHandler)
     this.chart.dispose()
     this.chart = null
   },
diff --git a/src/views/dashboard/admin/components/LineChart.vue b/src/views/dashboard/admin/components/LineChart.vue
index d567f08d..e654168d 100644
--- a/src/views/dashboard/admin/components/LineChart.vue
+++ b/src/views/dashboard/admin/components/LineChart.vue
@@ -5,9 +5,10 @@
 <script>
 import echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
-import { debounce } from '@/utils'
+import resize from './mixins/resize'
 
 export default {
+  mixins: [resize],
   props: {
     className: {
       type: String,
@@ -32,8 +33,7 @@ export default {
   },
   data() {
     return {
-      chart: null,
-      sidebarElm: null
+      chart: null
     }
   },
   watch: {
@@ -48,38 +48,18 @@ export default {
     this.$nextTick(() => {
       this.initChart()
     })
-
-    if (this.autoResize) {
-      this.__resizeHandler = debounce(() => {
-        if (this.chart) {
-          this.chart.resize()
-        }
-      }, 100)
-      window.addEventListener('resize', this.__resizeHandler)
-    }
-
-    // 监听侧边栏的变化
-    this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
-    this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
   },
   beforeDestroy() {
     if (!this.chart) {
       return
     }
-    if (this.autoResize) {
-      window.removeEventListener('resize', this.__resizeHandler)
-    }
-
-    this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
-
     this.chart.dispose()
     this.chart = null
   },
   methods: {
-    sidebarResizeHandler(e) {
-      if (e.propertyName === 'width') {
-        this.__resizeHandler()
-      }
+    initChart() {
+      this.chart = echarts.init(this.$el, 'macarons')
+      this.setOptions(this.chartData)
     },
     setOptions({ expectedData, actualData } = {}) {
       this.chart.setOption({
@@ -149,10 +129,6 @@ export default {
           animationEasing: 'quadraticOut'
         }]
       })
-    },
-    initChart() {
-      this.chart = echarts.init(this.$el, 'macarons')
-      this.setOptions(this.chartData)
     }
   }
 }
diff --git a/src/views/dashboard/admin/components/PieChart.vue b/src/views/dashboard/admin/components/PieChart.vue
index d5d59ff3..4d2ef32a 100644
--- a/src/views/dashboard/admin/components/PieChart.vue
+++ b/src/views/dashboard/admin/components/PieChart.vue
@@ -5,9 +5,10 @@
 <script>
 import echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
-import { debounce } from '@/utils'
+import resize from './mixins/resize'
 
 export default {
+  mixins: [resize],
   props: {
     className: {
       type: String,
@@ -31,18 +32,11 @@ export default {
     this.$nextTick(() => {
       this.initChart()
     })
-    this.__resizeHandler = debounce(() => {
-      if (this.chart) {
-        this.chart.resize()
-      }
-    }, 100)
-    window.addEventListener('resize', this.__resizeHandler)
   },
   beforeDestroy() {
     if (!this.chart) {
       return
     }
-    window.removeEventListener('resize', this.__resizeHandler)
     this.chart.dispose()
     this.chart = null
   },
diff --git a/src/views/dashboard/admin/components/RaddarChart.vue b/src/views/dashboard/admin/components/RaddarChart.vue
index cd2257b0..6823af31 100644
--- a/src/views/dashboard/admin/components/RaddarChart.vue
+++ b/src/views/dashboard/admin/components/RaddarChart.vue
@@ -5,11 +5,12 @@
 <script>
 import echarts from 'echarts'
 require('echarts/theme/macarons') // echarts theme
-import { debounce } from '@/utils'
+import resize from './mixins/resize'
 
 const animationDuration = 3000
 
 export default {
+  mixins: [resize],
   props: {
     className: {
       type: String,
@@ -33,18 +34,11 @@ export default {
     this.$nextTick(() => {
       this.initChart()
     })
-    this.__resizeHandler = debounce(() => {
-      if (this.chart) {
-        this.chart.resize()
-      }
-    }, 100)
-    window.addEventListener('resize', this.__resizeHandler)
   },
   beforeDestroy() {
     if (!this.chart) {
       return
     }
-    window.removeEventListener('resize', this.__resizeHandler)
     this.chart.dispose()
     this.chart = null
   },
diff --git a/src/views/dashboard/admin/components/mixins/resize.js b/src/views/dashboard/admin/components/mixins/resize.js
new file mode 100644
index 00000000..bcd17bf0
--- /dev/null
+++ b/src/views/dashboard/admin/components/mixins/resize.js
@@ -0,0 +1,56 @@
+import { debounce } from '@/utils'
+
+export default {
+  data() {
+    return {
+      $_sidebarElm: null
+    }
+  },
+  mounted() {
+    this.$_initResizeEvent()
+    this.$_initSidebarResizeEvent()
+  },
+  beforeDestroy() {
+    this.$_destroyResizeEvent()
+    this.$_destroySidebarResizeEvent()
+  },
+  // to fixed bug when cached by keep-alive
+  // https://github.com/PanJiaChen/vue-element-admin/issues/2116
+  activated() {
+    this.$_initResizeEvent()
+    this.$_initSidebarResizeEvent()
+  },
+  deactivated() {
+    this.$_destroyResizeEvent()
+    this.$_destroySidebarResizeEvent()
+  },
+  methods: {
+    // use $_ for mixins properties
+    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+    $_resizeHandler() {
+      return debounce(() => {
+        if (this.chart) {
+          this.chart.resize()
+        }
+      }, 100)()
+    },
+    $_initResizeEvent() {
+      window.addEventListener('resize', this.$_resizeHandler)
+    },
+    $_destroyResizeEvent() {
+      window.removeEventListener('resize', this.$_resizeHandler)
+    },
+    $_sidebarResizeHandler(e) {
+      if (e.propertyName === 'width') {
+        this.$_resizeHandler()
+      }
+    },
+    $_initSidebarResizeEvent() {
+      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+    },
+    $_destroySidebarResizeEvent() {
+      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+    }
+  }
+}
diff --git a/src/views/example/components/ArticleDetail.vue b/src/views/example/components/ArticleDetail.vue
index bf534806..ef0f1df9 100644
--- a/src/views/example/components/ArticleDetail.vue
+++ b/src/views/example/components/ArticleDetail.vue
@@ -7,7 +7,7 @@
         <PlatformDropdown v-model="postForm.platforms" />
         <SourceUrlDropdown v-model="postForm.source_uri" />
         <el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
-          Publush
+          Publish
         </el-button>
         <el-button v-loading="loading" type="warning" @click="draftForm">
           Draft
@@ -36,7 +36,7 @@
                 </el-col>
 
                 <el-col :span="10">
-                  <el-form-item label-width="120px" label="Publush Time:" class="postInfo-container-item">
+                  <el-form-item label-width="120px" label="Publish Time:" class="postInfo-container-item">
                     <el-date-picker v-model="displayTime" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Select date and time" />
                   </el-form-item>
                 </el-col>
@@ -49,7 +49,7 @@
                       :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
                       :low-threshold="1"
                       :high-threshold="3"
-                      style="margin-top:8px;"
+                      style="display:inline-block"
                     />
                   </el-form-item>
                 </el-col>
diff --git a/src/views/tab/index.vue b/src/views/tab/index.vue
index e5746580..6438a47a 100644
--- a/src/views/tab/index.vue
+++ b/src/views/tab/index.vue
@@ -30,6 +30,18 @@ export default {
       createdTimes: 0
     }
   },
+  watch: {
+    activeName(val) {
+      this.$router.push(`${this.$route.path}?tab=${val}`)
+    }
+  },
+  created() {
+    // init the default  selected tab
+    const tab = this.$route.query.tab
+    if (tab) {
+      this.activeName = tab
+    }
+  },
   methods: {
     showCreatedTimes() {
       this.createdTimes = this.createdTimes + 1
diff --git a/vue.config.js b/vue.config.js
index c5e31642..4a5cd438 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -37,7 +37,7 @@ module.exports = {
       // change xxx-api/login => mock/login
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
-        target: `http://localhost:${port}/mock`,
+        target: `http://127.0.0.1:${port}/mock`,
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''