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