Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
54ac74e14d |
@@ -1,5 +0,0 @@
|
||||
language: node_js
|
||||
node_js: stable
|
||||
script: npm run test
|
||||
notifications:
|
||||
email: false
|
@@ -6,7 +6,6 @@
|
||||
|
||||
[](https://github.com/vuejs/vue)
|
||||
[](https://github.com/ElemeFE/element)
|
||||
[](https://travis-ci.org/PanJiaChen/vue-element-admin)
|
||||
[](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
[]()
|
||||
|
||||
@@ -62,7 +61,7 @@
|
||||
- 动态面包屑
|
||||
- 国际化多语言
|
||||
- 多种动态换肤
|
||||
- 快捷导航(标签页 支持右键操作)
|
||||
- 快捷导航(标签页)
|
||||
- 富文本编辑器
|
||||
- Markdown编辑器
|
||||
- JSON编辑器
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-element-admin",
|
||||
"version": "3.3.0",
|
||||
"version": "3.1.0",
|
||||
"description": "A Vue.js admin",
|
||||
"author": "Pan <panfree23@gmail.com>",
|
||||
"license": "MIT",
|
||||
@@ -10,8 +10,7 @@
|
||||
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
|
||||
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
|
||||
"build:sit-preview": "cross-env NODE_ENV=production env_config=sit npm_config_preview=true npm_config_report=true node build/build.js",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"test": "npm run lint"
|
||||
"lint": "eslint --ext .js,.vue src"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.17.1",
|
||||
|
@@ -10,8 +10,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
|
||||
export default {
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
@@ -27,7 +25,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateTitle,
|
||||
getBreadcrumb() {
|
||||
let matched = this.$route.matched.filter(item => item.name)
|
||||
const first = matched[0]
|
||||
@@ -35,6 +32,9 @@ export default {
|
||||
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
|
||||
}
|
||||
this.levelList = matched
|
||||
},
|
||||
generateTitle(title) {
|
||||
return this.$t('route.' + title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
56
src/components/ErrLog/index.vue
Normal file
56
src/components/ErrLog/index.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
|
||||
<el-button size="small" type="primary">
|
||||
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
|
||||
<path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
|
||||
p-id="1864"></path>
|
||||
</svg>
|
||||
</el-button>
|
||||
</el-badge>
|
||||
<el-dialog title="bug日志" :visible.sync="dialogTableVisible">
|
||||
<el-table :data="logsList">
|
||||
<el-table-column label="message">
|
||||
<template slot-scope="scope">
|
||||
<div>msg:{{ scope.row.err.message }}</div>
|
||||
<br/>
|
||||
<div>url: {{scope.row.url}}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="stack">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.err.stack}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'errLog',
|
||||
props: {
|
||||
logsList: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTableVisible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bug-svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
|
||||
<el-button size="small" type="danger" class="bug-btn">
|
||||
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
|
||||
<path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
|
||||
p-id="1864"></path>
|
||||
</svg>
|
||||
</el-button>
|
||||
</el-badge>
|
||||
|
||||
<el-dialog title="Error Log" :visible.sync="dialogTableVisible" width="80%">
|
||||
<el-table :data="logsList" border>
|
||||
<el-table-column label="Message">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<span class="message-title">Msg:</span>
|
||||
<el-tag type="danger">{{ scope.row.err.message }}</el-tag>
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 10px;">Info: </span>
|
||||
<el-tag type="warning">{{scope.row.vm.$vnode.tag}} error in {{scope.row.info}}</el-tag>
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 16px;">Url: </span>
|
||||
<el-tag type="success">{{scope.row.url}}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Stack">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.err.stack}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'errorLog',
|
||||
props: {
|
||||
logsList: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTableVisible: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bug-btn.el-button--small {
|
||||
padding: 9px 10px;
|
||||
}
|
||||
.bug-svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
.message-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
|
||||
<div class="scroll-container" ref="scrollContainer" @mousewheel="handleScroll">
|
||||
<div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
<script>
|
||||
const delta = 15
|
||||
|
||||
export default {
|
||||
name: 'scrollBar',
|
||||
data() {
|
||||
@@ -18,19 +17,19 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 3
|
||||
e.preventDefault()
|
||||
const $container = this.$refs.scrollContainer
|
||||
const $containerHeight = $container.offsetHeight
|
||||
const $wrapper = this.$refs.scrollWrapper
|
||||
const $wrapperHeight = $wrapper.offsetHeight
|
||||
if (eventDelta > 0) {
|
||||
this.top = Math.min(0, this.top + eventDelta)
|
||||
if (e.wheelDelta > 0) {
|
||||
this.top = Math.min(0, this.top + e.wheelDelta)
|
||||
} else {
|
||||
if ($containerHeight - delta < $wrapperHeight) {
|
||||
if (this.top < -($wrapperHeight - $containerHeight + delta)) {
|
||||
this.top = this.top
|
||||
} else {
|
||||
this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
|
||||
this.top = Math.max(this.top + e.wheelDelta, $containerHeight - $wrapperHeight - delta)
|
||||
}
|
||||
} else {
|
||||
this.top = 0
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">
|
||||
<div class="scroll-container" ref="scrollContainer" @mousewheel="handleScroll">
|
||||
<div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -18,20 +18,20 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 3
|
||||
e.preventDefault()
|
||||
const $container = this.$refs.scrollContainer
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $wrapper = this.$refs.scrollWrapper
|
||||
const $wrapperWidth = $wrapper.offsetWidth
|
||||
|
||||
if (eventDelta > 0) {
|
||||
this.left = Math.min(0, this.left + eventDelta)
|
||||
if (e.wheelDelta > 0) {
|
||||
this.left = Math.min(0, this.left + e.wheelDelta)
|
||||
} else {
|
||||
if ($containerWidth - padding < $wrapperWidth) {
|
||||
if (this.left < -($wrapperWidth - $containerWidth + padding)) {
|
||||
this.left = this.left
|
||||
} else {
|
||||
this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)
|
||||
this.left = Math.max(this.left + e.wheelDelta, $containerWidth - $wrapperWidth - padding)
|
||||
}
|
||||
} else {
|
||||
this.left = 0
|
||||
@@ -64,7 +64,6 @@ export default {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
.scroll-wrapper {
|
||||
position: absolute;
|
||||
}
|
||||
|
@@ -6,8 +6,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getVersion } from '@/utils/index.js'
|
||||
|
||||
const version = require('element-ui/package.json').version // element-ui version from node_modules
|
||||
const version = getVersion('element-ui') // element-ui version from package.json
|
||||
const ORIGINAL_THEME = '#409EFF' // default color
|
||||
|
||||
export default {
|
||||
|
@@ -1,17 +1,14 @@
|
||||
import Vue from 'vue'
|
||||
import store from './store'
|
||||
import errLog from '@/store/errLog'
|
||||
|
||||
// you can set only in production env show the error-log
|
||||
// if (process.env.NODE_ENV === 'production') {
|
||||
Vue.config.errorHandler = function(err, vm, info, a) {
|
||||
Vue.nextTick(() => {
|
||||
store.dispatch('addErrorLog', {
|
||||
// 生产环境错误日志
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
Vue.config.errorHandler = function(err, vm) {
|
||||
console.log(err, window.location.href)
|
||||
errLog.pushLog({
|
||||
err,
|
||||
vm,
|
||||
info,
|
||||
url: window.location.href
|
||||
url: window.location.href,
|
||||
vm
|
||||
})
|
||||
console.error(err, info)
|
||||
})
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
@@ -26,7 +26,7 @@ router.beforeEach((to, from, next) => {
|
||||
const roles = res.data.role
|
||||
store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
|
||||
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
|
||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,replace: true so the navigation will not leave a history record
|
||||
next({ ...to }) // hack方法 确保addRoutes已完成
|
||||
})
|
||||
}).catch(() => {
|
||||
store.dispatch('FedLogOut').then(() => {
|
||||
|
13
src/store/errLog.js
Normal file
13
src/store/errLog.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const errLog = {
|
||||
state: {
|
||||
errLog: []
|
||||
},
|
||||
pushLog(log) {
|
||||
this.state.errLog.unshift(log)
|
||||
},
|
||||
clearLog() {
|
||||
this.state.errLog = []
|
||||
}
|
||||
}
|
||||
|
||||
export default errLog
|
@@ -1,8 +1,8 @@
|
||||
const getters = {
|
||||
sidebar: state => state.app.sidebar,
|
||||
language: state => state.app.language,
|
||||
visitedViews: state => state.tagsView.visitedViews,
|
||||
cachedViews: state => state.tagsView.cachedViews,
|
||||
visitedViews: state => state.app.visitedViews,
|
||||
cachedViews: state => state.app.cachedViews,
|
||||
token: state => state.user.token,
|
||||
avatar: state => state.user.avatar,
|
||||
name: state => state.user.name,
|
||||
@@ -11,7 +11,6 @@ const getters = {
|
||||
roles: state => state.user.roles,
|
||||
setting: state => state.user.setting,
|
||||
permission_routers: state => state.permission.routers,
|
||||
addRouters: state => state.permission.addRouters,
|
||||
errorLogs: state => state.errorLog.logs
|
||||
addRouters: state => state.permission.addRouters
|
||||
}
|
||||
export default getters
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import app from './modules/app'
|
||||
import errorLog from './modules/errorLog'
|
||||
import permission from './modules/permission'
|
||||
import tagsView from './modules/tagsView'
|
||||
import user from './modules/user'
|
||||
import permission from './modules/permission'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
@@ -12,10 +10,8 @@ Vue.use(Vuex)
|
||||
const store = new Vuex.Store({
|
||||
modules: {
|
||||
app,
|
||||
errorLog,
|
||||
permission,
|
||||
tagsView,
|
||||
user
|
||||
user,
|
||||
permission
|
||||
},
|
||||
getters
|
||||
})
|
||||
|
@@ -5,7 +5,9 @@ const app = {
|
||||
sidebar: {
|
||||
opened: !+Cookies.get('sidebarStatus')
|
||||
},
|
||||
language: Cookies.get('language') || 'zh'
|
||||
language: Cookies.get('language') || 'zh',
|
||||
visitedViews: [],
|
||||
cachedViews: []
|
||||
},
|
||||
mutations: {
|
||||
TOGGLE_SIDEBAR: state => {
|
||||
@@ -19,6 +21,32 @@ const app = {
|
||||
SET_LANGUAGE: (state, language) => {
|
||||
state.language = language
|
||||
Cookies.set('language', language)
|
||||
},
|
||||
ADD_VISITED_VIEWS: (state, view) => {
|
||||
if (state.visitedViews.some(v => v.path === view.path)) return
|
||||
state.visitedViews.push({
|
||||
name: view.name,
|
||||
path: view.path,
|
||||
title: view.meta.title || 'no-name'
|
||||
})
|
||||
if (!view.meta.noCache) {
|
||||
state.cachedViews.push(view.name)
|
||||
}
|
||||
},
|
||||
DEL_VISITED_VIEWS: (state, view) => {
|
||||
for (const [i, v] of state.visitedViews.entries()) {
|
||||
if (v.path === view.path) {
|
||||
state.visitedViews.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const i of state.cachedViews) {
|
||||
if (i === view.name) {
|
||||
const index = state.cachedViews.indexOf(i)
|
||||
state.cachedViews.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -27,6 +55,15 @@ const app = {
|
||||
},
|
||||
setLanguage({ commit }, language) {
|
||||
commit('SET_LANGUAGE', language)
|
||||
},
|
||||
addVisitedViews({ commit }, view) {
|
||||
commit('ADD_VISITED_VIEWS', view)
|
||||
},
|
||||
delVisitedViews({ commit, state }, view) {
|
||||
return new Promise((resolve) => {
|
||||
commit('DEL_VISITED_VIEWS', view)
|
||||
resolve([...state.visitedViews])
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +0,0 @@
|
||||
const errorLog = {
|
||||
state: {
|
||||
logs: []
|
||||
},
|
||||
mutations: {
|
||||
ADD_ERROR_LOG: (state, log) => {
|
||||
state.logs.push(log)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addErrorLog({ commit }, log) {
|
||||
commit('ADD_ERROR_LOG', log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default errorLog
|
@@ -1,78 +0,0 @@
|
||||
const tagsView = {
|
||||
state: {
|
||||
visitedViews: [],
|
||||
cachedViews: []
|
||||
},
|
||||
mutations: {
|
||||
ADD_VISITED_VIEWS: (state, view) => {
|
||||
if (state.visitedViews.some(v => v.path === view.path)) return
|
||||
state.visitedViews.push({
|
||||
name: view.name,
|
||||
path: view.path,
|
||||
title: view.meta.title || 'no-name'
|
||||
})
|
||||
if (!view.meta.noCache) {
|
||||
state.cachedViews.push(view.name)
|
||||
}
|
||||
},
|
||||
DEL_VISITED_VIEWS: (state, view) => {
|
||||
for (const [i, v] of state.visitedViews.entries()) {
|
||||
if (v.path === view.path) {
|
||||
state.visitedViews.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const i of state.cachedViews) {
|
||||
if (i === view.name) {
|
||||
const index = state.cachedViews.indexOf(i)
|
||||
state.cachedViews.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
DEL_OTHERS_VIEWS: (state, view) => {
|
||||
for (const [i, v] of state.visitedViews.entries()) {
|
||||
if (v.path === view.path) {
|
||||
state.visitedViews = state.visitedViews.slice(i, i + 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
for (const i of state.cachedViews) {
|
||||
if (i === view.name) {
|
||||
const index = state.cachedViews.indexOf(i)
|
||||
state.cachedViews = state.cachedViews.slice(index, i + 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
DEL_ALL_VIEWS: (state) => {
|
||||
state.visitedViews = []
|
||||
state.cachedViews = []
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addVisitedViews({ commit }, view) {
|
||||
commit('ADD_VISITED_VIEWS', view)
|
||||
},
|
||||
delVisitedViews({ commit, state }, view) {
|
||||
return new Promise((resolve) => {
|
||||
commit('DEL_VISITED_VIEWS', view)
|
||||
resolve([...state.visitedViews])
|
||||
})
|
||||
},
|
||||
delOthersViews({ commit, state }, view) {
|
||||
return new Promise((resolve) => {
|
||||
commit('DEL_OTHERS_VIEWS', view)
|
||||
resolve([...state.visitedViews])
|
||||
})
|
||||
},
|
||||
delAllViews({ commit, state }) {
|
||||
return new Promise((resolve) => {
|
||||
commit('DEL_ALL_VIEWS')
|
||||
resolve([...state.visitedViews])
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default tagsView
|
@@ -98,11 +98,10 @@ div:focus{
|
||||
|
||||
code {
|
||||
background: #eef1f6;
|
||||
padding: 15px 16px;
|
||||
padding: 15px 10px;
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
line-height: 36px;
|
||||
font-size: 14px;
|
||||
a {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
|
@@ -1,3 +0,0 @@
|
||||
export function generateTitle(title) {
|
||||
return this.$t('route.' + title) // $t :this method from vue-i18n ,inject in @/lang/index.js
|
||||
}
|
@@ -265,3 +265,9 @@ export function deepClone(source) {
|
||||
}
|
||||
return targetObj
|
||||
}
|
||||
|
||||
// get dependencies verison from package.json
|
||||
export function getVersion(name) {
|
||||
const p = require('../../package')
|
||||
return p.dependencies[name]
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ import DndList from '@/components/DndList'
|
||||
import { fetchList } from '@/api/article'
|
||||
|
||||
export default {
|
||||
name: 'dndList-demo',
|
||||
name: 'dnd-list-demo',
|
||||
components: { DndList },
|
||||
data() {
|
||||
return {
|
||||
|
@@ -5,9 +5,3 @@
|
||||
<!--error code-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'errorTestA'
|
||||
}
|
||||
</script>
|
@@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created() {
|
||||
this.b = b // eslint-disable-line
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -1,26 +1,21 @@
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<errorA></errorA>
|
||||
<errorB></errorB>
|
||||
<err-code></err-code>
|
||||
|
||||
<h3>请点击右上角bug小图表</h3>
|
||||
<code>
|
||||
现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常.
|
||||
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/#/error?id=%e4%bb%a3%e7%a0%81">文档介绍</a>
|
||||
现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在Vue官网提供了一个方法来捕获处理异常
|
||||
</code>
|
||||
<a href="#">
|
||||
<img src='https://wpimg.wallstcn.com/360e4842-4db5-42d0-b078-f9a84a825546.gif'>
|
||||
</a>
|
||||
<a href="#"><img src='http://panjiachen.github.io/images/errHandler.png'></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import errorA from './errorTestA'
|
||||
import errorB from './errorTestB'
|
||||
import errCode from './errcode'
|
||||
|
||||
export default {
|
||||
name: 'errorLog',
|
||||
components: { errorA, errorB }
|
||||
components: { errCode }
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -11,7 +11,7 @@ export default {
|
||||
name: 'TableMain',
|
||||
computed: {
|
||||
cachedViews() {
|
||||
return this.$store.state.tagsView.cachedViews
|
||||
return this.$store.state.app.cachedViews
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -35,18 +35,14 @@
|
||||
|
||||
<el-table-column min-width="300px" label="标题">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="scope.row.edit">
|
||||
<el-input class="edit-input" size="small" v-model="scope.row.title"></el-input>
|
||||
<el-button class='cancel-btn' size="small" icon="el-icon-refresh" type="warning" @click="cancelEdit(scope.row)">cancel</el-button>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.title }}</span>
|
||||
<el-input v-show="scope.row.edit" size="small" v-model="scope.row.title"></el-input>
|
||||
<span v-show="!scope.row.edit">{{ scope.row.title }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" label="编辑" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.edit" type="success" @click="confirmEdit(scope.row)" size="small" icon="el-icon-circle-check-outline">完成</el-button>
|
||||
<el-button v-else type="primary" @click='scope.row.edit=!scope.row.edit' size="small" icon="el-icon-edit">编辑</el-button>
|
||||
<el-button :type="scope.row.edit?'success':'primary'" @click='scope.row.edit=!scope.row.edit' size="small" icon="edit">{{scope.row.edit?'完成':'编辑'}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -88,40 +84,12 @@ export default {
|
||||
fetchList(this.listQuery).then(response => {
|
||||
const items = response.data.items
|
||||
this.list = items.map(v => {
|
||||
this.$set(v, 'edit', false) // https://vuejs.org/v2/guide/reactivity.html
|
||||
v.originalTitle = v.title // will be used when user click the cancel botton
|
||||
this.$set(v, 'edit', false)
|
||||
return v
|
||||
})
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
cancelEdit(row) {
|
||||
row.title = row.originalTitle
|
||||
row.edit = false
|
||||
this.$message({
|
||||
message: 'The title has been restored to the original value',
|
||||
type: 'warning'
|
||||
})
|
||||
},
|
||||
confirmEdit(row) {
|
||||
row.edit = false
|
||||
row.originalTitle = row.title
|
||||
this.$message({
|
||||
message: 'The title has been edited',
|
||||
type: 'success'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.edit-input {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.cancel-btn {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 13px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -13,8 +13,7 @@ export default {
|
||||
name: 'AppMain',
|
||||
computed: {
|
||||
cachedViews() {
|
||||
// console.log(this.$store.state.tagsView.cachedViews)
|
||||
return this.$store.state.tagsView.cachedViews
|
||||
return this.$store.state.app.cachedViews
|
||||
}
|
||||
// key() {
|
||||
// return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
|
||||
|
@@ -5,7 +5,8 @@
|
||||
<breadcrumb class="breadcrumb-container"></breadcrumb>
|
||||
|
||||
<div class="right-menu">
|
||||
<error-log v-if="errorLogs.length>0" class="errLog-container right-menu-item" :logsList="errorLogs"></error-log>
|
||||
|
||||
<error-log v-if="log.length>0" class="errLog-container right-menu-item" :logsList="log"></error-log>
|
||||
|
||||
<el-tooltip effect="dark" content="全屏" placement="bottom">
|
||||
<screenfull class="screenfull right-menu-item"></screenfull>
|
||||
@@ -56,7 +57,8 @@ import Breadcrumb from '@/components/Breadcrumb'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import ThemePicker from '@/components/ThemePicker'
|
||||
import Screenfull from '@/components/Screenfull'
|
||||
import ErrorLog from '@/components/ErrorLog'
|
||||
import ErrorLog from '@/components/ErrLog'
|
||||
import errLogStore from 'store/errLog'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -66,13 +68,17 @@ export default {
|
||||
ErrorLog,
|
||||
Screenfull
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
log: errLogStore.state.errLog
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'name',
|
||||
'avatar',
|
||||
'language',
|
||||
'errorLogs'
|
||||
'language'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
|
@@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div class="menu-wrapper">
|
||||
<template v-for="item in routes" v-if="!item.hidden&&item.children">
|
||||
<template v-for="item in routes">
|
||||
|
||||
<router-link v-if="item.children.length===1 && !item.children[0].children" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name">
|
||||
<router-link v-if="!item.hidden&&item.children&&item.children.length===1" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name">
|
||||
<el-menu-item :index="item.path+'/'+item.children[0].path" class='submenu-title-noDropdown'>
|
||||
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
|
||||
<span v-if="item.children[0].meta&&item.children[0].meta.title">{{generateTitle(item.children[0].meta.title)}}</span>
|
||||
</el-menu-item>
|
||||
</router-link>
|
||||
|
||||
<el-submenu v-else :index="item.name||item.path" :key="item.name">
|
||||
<el-submenu v-if="!item.hidden&&item.children&&item.children.length>1" :index="item.name||item.path" :key="item.name">
|
||||
<template slot="title">
|
||||
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
|
||||
<span v-if="item.meta&&item.meta.title">{{generateTitle(item.meta.title)}}</span>
|
||||
</template>
|
||||
|
||||
<template v-for="child in item.children" v-if="!child.hidden">
|
||||
<template v-if="!child.hidden" v-for="child in item.children">
|
||||
<sidebar-item class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
|
||||
|
||||
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
|
||||
@@ -32,8 +32,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
|
||||
export default {
|
||||
name: 'SidebarItem',
|
||||
props: {
|
||||
@@ -42,7 +40,9 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateTitle
|
||||
generateTitle(title) {
|
||||
return this.$t('route.' + title)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,65 +1,45 @@
|
||||
<template>
|
||||
<div class="tags-view-container">
|
||||
<scroll-pane class='tags-view-wrapper' ref='scrollPane'>
|
||||
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path" :key="tag.path" @contextmenu.prevent.native="openMenu(tag,$event)">
|
||||
{{generateTitle(tag.title)}}
|
||||
<span class='el-icon-close' @click.prevent.stop='closeSelectedTag(tag)'></span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul class='contextmenu' v-show="visible" :style="{left:left+'px',top:top+'px'}">
|
||||
<li @click="closeSelectedTag(selectedTag)">Close</li>
|
||||
<li @click="closeOthersTags">Close Others</li>
|
||||
<li @click="closeAllTags">Close All</li>
|
||||
</ul>
|
||||
</div>
|
||||
<scroll-pane class='tags-view-container' ref='scrollPane'>
|
||||
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path":key="tag.path">
|
||||
{{$t('route.'+tag.title)}}
|
||||
<span class='el-icon-close' @click='closeViewTags(tag,$event)'></span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollPane from '@/components/ScrollPane'
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
|
||||
export default {
|
||||
components: { ScrollPane },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
top: 0,
|
||||
left: 0,
|
||||
selectedTag: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visitedViews() {
|
||||
return this.$store.state.tagsView.visitedViews
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.addViewTags()
|
||||
this.moveToCurrentTag()
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
window.addEventListener('click', this.closeMenu)
|
||||
} else {
|
||||
window.removeEventListener('click', this.closeMenu)
|
||||
}
|
||||
return this.$store.state.app.visitedViews
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.addViewTags()
|
||||
},
|
||||
methods: {
|
||||
generateTitle, // generateTitle by vue-i18n
|
||||
closeViewTags(view, $event) {
|
||||
this.$store.dispatch('delVisitedViews', view).then((views) => {
|
||||
if (this.isActive(view)) {
|
||||
const latestView = views.slice(-1)[0]
|
||||
if (latestView) {
|
||||
this.$router.push(latestView.path)
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
})
|
||||
$event.preventDefault()
|
||||
},
|
||||
generateRoute() {
|
||||
if (this.$route.name) {
|
||||
return this.$route
|
||||
}
|
||||
return false
|
||||
},
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path || route.name === this.$route.name
|
||||
},
|
||||
addViewTags() {
|
||||
const route = this.generateRoute()
|
||||
if (!route) {
|
||||
@@ -67,6 +47,9 @@ export default {
|
||||
}
|
||||
this.$store.dispatch('addVisitedViews', route)
|
||||
},
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path || route.name === this.$route.name
|
||||
},
|
||||
moveToCurrentTag() {
|
||||
const tags = this.$refs.tag
|
||||
this.$nextTick(() => {
|
||||
@@ -77,35 +60,13 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
closeSelectedTag(view) {
|
||||
this.$store.dispatch('delVisitedViews', view).then((views) => {
|
||||
if (this.isActive(view)) {
|
||||
const latestView = views.slice(-1)[0]
|
||||
if (latestView) {
|
||||
this.$router.push(latestView.path)
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
closeOthersTags() {
|
||||
this.$router.push(this.selectedTag.path)
|
||||
this.$store.dispatch('delOthersViews', this.selectedTag)
|
||||
},
|
||||
closeAllTags() {
|
||||
this.$store.dispatch('delAllViews')
|
||||
this.$router.push('/')
|
||||
},
|
||||
openMenu(tag, e) {
|
||||
this.visible = true
|
||||
this.selectedTag = tag
|
||||
this.left = e.clientX
|
||||
this.top = e.clientY
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
}
|
||||
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.addViewTags()
|
||||
this.moveToCurrentTag()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,61 +74,38 @@ export default {
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.tags-view-container {
|
||||
.tags-view-wrapper {
|
||||
background: #fff;
|
||||
height: 34px;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
height: 34px;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
&.active {
|
||||
background-color: #42b983;
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #eee;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
&.active {
|
||||
background-color: #42b983;
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,8 +113,7 @@ export default {
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
//reset element css of el-icon-close
|
||||
.tags-view-wrapper {
|
||||
.tags-view-container {
|
||||
.tags-view-item {
|
||||
.el-icon-close {
|
||||
width: 16px;
|
||||
|
Reference in New Issue
Block a user