Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
de3b29b5f3 | ||
|
54acf1e0d5 | ||
|
de08e49f19 | ||
|
2472107768 | ||
|
d18902dfee | ||
|
9ba2648689 | ||
|
1d0b26cec8 | ||
|
ea7e139696 |
@@ -2,8 +2,9 @@
|
||||
|
||||
module.exports = {
|
||||
"plugins": {
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
"postcss-import": {},
|
||||
"postcss-url": {},
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
"autoprefixer": {}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-element-admin",
|
||||
"version": "3.5.0",
|
||||
"version": "3.5.1",
|
||||
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
|
||||
"author": "Pan <panfree23@gmail.com>",
|
||||
"license": "MIT",
|
||||
@@ -39,7 +39,7 @@
|
||||
"vue-splitpane": "1.0.2",
|
||||
"vuedraggable": "2.15.0",
|
||||
"vuex": "3.0.1",
|
||||
"xlsx": "^0.11.7"
|
||||
"xlsx": "^0.11.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "7.2.3",
|
||||
@@ -71,6 +71,7 @@
|
||||
"portfinder": "1.0.13",
|
||||
"postcss-import": "11.0.0",
|
||||
"postcss-loader": "2.0.9",
|
||||
"postcss-url": "7.3.0",
|
||||
"pushstate-server": "3.0.1",
|
||||
"rimraf": "2.6.2",
|
||||
"sass-loader": "6.0.6",
|
||||
|
@@ -34,7 +34,7 @@ export default {
|
||||
.international-icon {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
vertical-align: -5px;
|
||||
vertical-align: -5px!important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button :loading="loading" type="primary" @click="handleUpload">select excel file</el-button>
|
||||
<input id="excel-upload-input" type="file" accept=".xlsx, .xls" class="c-hide" @change="handkeFileChange">
|
||||
<div id="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
|
||||
Drop excel file here or
|
||||
<el-button style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">browse</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -22,16 +25,35 @@ export default {
|
||||
generateDate({ header, results }) {
|
||||
this.excelData.header = header
|
||||
this.excelData.results = results
|
||||
this.loading = false
|
||||
this.$emit('on-selected-file', this.excelData)
|
||||
},
|
||||
handleDrop(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
const files = e.dataTransfer.files
|
||||
if (files.length !== 1) {
|
||||
this.$message.error('Only support uploading one file!')
|
||||
return
|
||||
}
|
||||
const itemFile = files[0] // only use files[0]
|
||||
this.readerData(itemFile)
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
handleDragover(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
},
|
||||
handleUpload() {
|
||||
document.getElementById('excel-upload-input').click()
|
||||
},
|
||||
handkeFileChange(e) {
|
||||
this.loading = true
|
||||
const files = e.target.files
|
||||
const itemFile = files[0] // only use files[0]
|
||||
this.readerData(itemFile)
|
||||
},
|
||||
readerData(itemFile) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = e => {
|
||||
const data = e.target.result
|
||||
@@ -75,4 +97,16 @@ export default {
|
||||
display: none;
|
||||
z-index: -9999;
|
||||
}
|
||||
#drop{
|
||||
border: 2px dashed #bbb;
|
||||
width: 600px;
|
||||
height: 160px;
|
||||
line-height: 160px;
|
||||
margin: 0 auto;
|
||||
font-size: 24px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
@@ -2,14 +2,14 @@ import { param2Obj } from '@/utils'
|
||||
|
||||
const userMap = {
|
||||
admin: {
|
||||
role: ['admin'],
|
||||
roles: ['admin'],
|
||||
token: 'admin',
|
||||
introduction: '我是超级管理员',
|
||||
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
|
||||
name: 'Super Admin'
|
||||
},
|
||||
editor: {
|
||||
role: ['editor'],
|
||||
roles: ['editor'],
|
||||
token: 'editor',
|
||||
introduction: '我是编辑',
|
||||
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import { Message } from 'element-ui'
|
||||
import NProgress from 'nprogress' // progress bar
|
||||
import 'nprogress/nprogress.css'// progress bar style
|
||||
import { getToken } from '@/utils/auth' // getToken from cookie
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
// permissiom judge
|
||||
NProgress.configure({ showSpinner: false })// NProgress Configuration
|
||||
|
||||
// permissiom judge function
|
||||
function hasPermission(roles, permissionRoles) {
|
||||
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
|
||||
if (!permissionRoles) return true
|
||||
@@ -16,15 +18,16 @@ const whiteList = ['/login', '/authredirect']// no redirect whitelist
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start() // start progress bar
|
||||
if (getToken()) { // 判断是否有token
|
||||
if (getToken()) { // determine if there has token
|
||||
/* has token*/
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
|
||||
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
|
||||
} else {
|
||||
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
|
||||
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
|
||||
const roles = res.data.role
|
||||
store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
|
||||
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
|
||||
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
|
||||
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
|
||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
|
||||
})
|
||||
@@ -36,21 +39,21 @@ router.beforeEach((to, from, next) => {
|
||||
})
|
||||
} else {
|
||||
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
|
||||
if (hasPermission(store.getters.roles, to.meta.role)) {
|
||||
if (hasPermission(store.getters.roles, to.meta.roles)) {
|
||||
next()//
|
||||
} else {
|
||||
next({ path: '/401', query: { noGoBack: true }})
|
||||
NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
|
||||
next({ path: '/401', replace: true, query: { noGoBack: true }})
|
||||
}
|
||||
// 可删 ↑
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* has no token*/
|
||||
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
|
||||
next()
|
||||
} else {
|
||||
next('/login') // 否则全部重定向到登录页
|
||||
NProgress.done() // router在hash模式下 手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
|
||||
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@@ -18,7 +18,7 @@ import Layout from '../views/layout/Layout'
|
||||
* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
|
||||
* name:'router-name' the name is used by <keep-alive> (must set!!!)
|
||||
* meta : {
|
||||
role: ['admin','editor'] will control the page role (you can set multiple roles)
|
||||
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
|
||||
title: 'title' the name show in submenu and breadcrumb (recommend set)
|
||||
icon: 'svg-name' the icon show in the sidebar,
|
||||
noCache: true if fasle ,the page will no be cached(default is false)
|
||||
@@ -54,7 +54,7 @@ export const constantRouterMap = [
|
||||
]
|
||||
|
||||
export default new Router({
|
||||
// mode: 'history', //后端支持可开
|
||||
// mode: 'history', // require service support
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes: constantRouterMap
|
||||
})
|
||||
@@ -64,7 +64,7 @@ export const asyncRouterMap = [
|
||||
path: '/permission',
|
||||
component: Layout,
|
||||
redirect: '/permission/index',
|
||||
meta: { role: ['admin'] },
|
||||
meta: { roles: ['admin'] }, // you can set roles in root nav
|
||||
children: [{
|
||||
path: 'index',
|
||||
component: _import('permission/index'),
|
||||
@@ -72,7 +72,7 @@ export const asyncRouterMap = [
|
||||
meta: {
|
||||
title: 'permission',
|
||||
icon: 'lock',
|
||||
role: ['admin']
|
||||
roles: ['admin'] // or you can only set roles in sub nav
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
@@ -5,7 +5,7 @@ const app = {
|
||||
sidebar: {
|
||||
opened: !+Cookies.get('sidebarStatus')
|
||||
},
|
||||
language: Cookies.get('language') || 'zh'
|
||||
language: Cookies.get('language') || 'en'
|
||||
},
|
||||
mutations: {
|
||||
TOGGLE_SIDEBAR: state => {
|
||||
|
@@ -6,8 +6,8 @@ import { asyncRouterMap, constantRouterMap } from '@/router'
|
||||
* @param route
|
||||
*/
|
||||
function hasPermission(roles, route) {
|
||||
if (route.meta && route.meta.role) {
|
||||
return roles.some(role => route.meta.role.indexOf(role) >= 0)
|
||||
if (route.meta && route.meta.roles) {
|
||||
return roles.some(role => route.meta.roles.indexOf(role) >= 0)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
|
@@ -67,7 +67,7 @@ const user = {
|
||||
reject('error')
|
||||
}
|
||||
const data = response.data
|
||||
commit('SET_ROLES', data.role)
|
||||
commit('SET_ROLES', data.roles)
|
||||
commit('SET_NAME', data.name)
|
||||
commit('SET_AVATAR', data.avatar)
|
||||
commit('SET_INTRODUCTION', data.introduction)
|
||||
@@ -116,13 +116,13 @@ const user = {
|
||||
},
|
||||
|
||||
// 动态修改权限
|
||||
ChangeRole({ commit }, role) {
|
||||
ChangeRoles({ commit }, role) {
|
||||
return new Promise(resolve => {
|
||||
commit('SET_TOKEN', role)
|
||||
setToken(role)
|
||||
getUserInfo(role).then(response => {
|
||||
const data = response.data
|
||||
commit('SET_ROLES', data.role)
|
||||
commit('SET_ROLES', data.roles)
|
||||
commit('SET_NAME', data.name)
|
||||
commit('SET_AVATAR', data.avatar)
|
||||
commit('SET_INTRODUCTION', data.introduction)
|
||||
|
@@ -1,4 +1,10 @@
|
||||
// translate router.meta.title, be used in breadcrumb sidebar tagsview
|
||||
export function generateTitle(title) {
|
||||
return this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js
|
||||
const hasKey = this.$te('route.' + title)
|
||||
const translatedTitle = this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js
|
||||
|
||||
if (hasKey) {
|
||||
return translatedTitle
|
||||
}
|
||||
return title
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<template v-for="item in routes" v-if="!item.hidden&&item.children">
|
||||
|
||||
<router-link v-if="item.children.length===1 && !item.children[0].children" :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'>
|
||||
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<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>
|
||||
@@ -16,7 +16,7 @@
|
||||
</template>
|
||||
|
||||
<template v-for="child in item.children" v-if="!child.hidden">
|
||||
<sidebar-item class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
|
||||
<sidebar-item :is-nest="true" 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">
|
||||
<el-menu-item :index="item.path+'/'+child.path">
|
||||
@@ -39,6 +39,10 @@ export default {
|
||||
props: {
|
||||
routes: {
|
||||
type: Array
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<scroll-bar>
|
||||
<el-menu mode="vertical" :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#fff" active-text-color="#409EFF">
|
||||
<el-menu mode="vertical" :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF">
|
||||
<sidebar-item :routes="permission_routers"></sidebar-item>
|
||||
</el-menu>
|
||||
</scroll-bar>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="app-container">
|
||||
<div style="margin-bottom:15px;">{{$t('permission.roles')}}: {{roles}}</div>
|
||||
{{$t('permission.switchRoles')}}:
|
||||
<el-radio-group v-model="role">
|
||||
<el-radio-group v-model="switchRoles">
|
||||
<el-radio-button label="editor"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
@@ -15,7 +15,7 @@ export default{
|
||||
name: 'permission',
|
||||
data() {
|
||||
return {
|
||||
role: ''
|
||||
switchRoles: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -24,8 +24,8 @@ export default{
|
||||
])
|
||||
},
|
||||
watch: {
|
||||
role(val) {
|
||||
this.$store.dispatch('ChangeRole', val).then(() => {
|
||||
switchRoles(val) {
|
||||
this.$store.dispatch('ChangeRoles', val).then(() => {
|
||||
this.$router.push({ path: '/permission/index?' + +new Date() })
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user