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]: ''