动态权限菜单
This commit is contained in:
parent
36268af5db
commit
0f62c21d54
|
|
@ -47,7 +47,8 @@ export default {
|
||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
await this.$store.dispatch('user/logout')
|
await this.$store.dispatch('user/logout')
|
||||||
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
|
// 不进行重定向防止权限问题 ?redirect=${this.$route.fullPath}
|
||||||
|
this.$router.push(`/login`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,11 @@ export default {
|
||||||
components: { SidebarItem, Logo },
|
components: { SidebarItem, Logo },
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
|
'permission_routes',
|
||||||
'sidebar'
|
'sidebar'
|
||||||
]),
|
]),
|
||||||
routes() {
|
routes() {
|
||||||
return this.$router.options.routes
|
return this.permission_routes.filter(route => !route.hidden)
|
||||||
},
|
},
|
||||||
activeMenu() {
|
activeMenu() {
|
||||||
const route = this.$route
|
const route = this.$route
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ NProgress.configure({ showSpinner: false }) // NProgress Configuration
|
||||||
const whiteList = ['/login'] // no redirect whitelist
|
const whiteList = ['/login'] // no redirect whitelist
|
||||||
|
|
||||||
router.beforeEach(async(to, from, next) => {
|
router.beforeEach(async(to, from, next) => {
|
||||||
|
console.log('router.beforeEach from: ', from.path, ', to: ', to.path)
|
||||||
// start progress bar
|
// start progress bar
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
|
|
||||||
|
|
@ -26,16 +27,23 @@ router.beforeEach(async(to, from, next) => {
|
||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
NProgress.done()
|
NProgress.done()
|
||||||
} else {
|
} else {
|
||||||
const hasGetUserInfo = store.getters.name
|
const hasRoles = store.getters.roles && store.getters.roles.length > 0
|
||||||
if (hasGetUserInfo) {
|
if (hasRoles) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// get user info
|
// get user info
|
||||||
await store.dispatch('user/getInfo')
|
const { roles } = await store.dispatch('user/getInfo')
|
||||||
|
console.log('router.beforeEach 当前用户角色:', ...roles)
|
||||||
next()
|
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
|
||||||
|
if (accessRoutes && accessRoutes.length > 0) {
|
||||||
|
console.log('router.beforeEach 添加动态路由:', accessRoutes)
|
||||||
|
}
|
||||||
|
// 动态添加可访问路由
|
||||||
|
router.addRoutes(accessRoutes)
|
||||||
|
next({ ...to, replace: true })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('get user role error', error)
|
||||||
// remove token and go to login page to re-login
|
// remove token and go to login page to re-login
|
||||||
await store.dispatch('user/resetToken')
|
await store.dispatch('user/resetToken')
|
||||||
Message.error(error || 'Has Error')
|
Message.error(error || 'Has Error')
|
||||||
|
|
@ -51,6 +59,7 @@ router.beforeEach(async(to, from, next) => {
|
||||||
// in the free login whitelist, go directly
|
// in the free login whitelist, go directly
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
|
console.log('router.beforeEach 没有token,跳转到登录页面')
|
||||||
// other pages that do not have permission to access are redirected to the login page.
|
// other pages that do not have permission to access are redirected to the login page.
|
||||||
next(`/login?redirect=${to.path}`)
|
next(`/login?redirect=${to.path}`)
|
||||||
NProgress.done()
|
NProgress.done()
|
||||||
|
|
|
||||||
|
|
@ -54,69 +54,6 @@ export const constantRoutes = [
|
||||||
meta: { title: '主页', icon: 'dashboard' }
|
meta: { title: '主页', icon: 'dashboard' }
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
path: '/teacher',
|
|
||||||
component: Layout,
|
|
||||||
redirect: '/teacher/table',
|
|
||||||
name: 'Example',
|
|
||||||
meta: { title: '教师', icon: 'el-icon-s-help' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'table',
|
|
||||||
name: 'Table',
|
|
||||||
component: () => import('@/views/student/index'),
|
|
||||||
meta: { title: '学生管理', icon: 'table' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'class',
|
|
||||||
name: 'Class',
|
|
||||||
component: () => import('@/views/class/index'),
|
|
||||||
meta: { title: '班级管理', icon: 'table' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'subject',
|
|
||||||
name: 'Subject',
|
|
||||||
component: () => import('@/views/subject/index'),
|
|
||||||
meta: { title: '科目管理', icon: 'table' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'score',
|
|
||||||
name: 'Score',
|
|
||||||
component: () => import('@/views/score/index'),
|
|
||||||
meta: { title: '成绩管理', icon: 'table' }
|
|
||||||
}
|
|
||||||
// {
|
|
||||||
// path: 'tree',
|
|
||||||
// name: 'Tree',
|
|
||||||
// component: () => import('@/views/tree/index'),
|
|
||||||
// meta: { title: 'Tree', icon: 'tree' }
|
|
||||||
// }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
path: '/student',
|
|
||||||
component: Layout,
|
|
||||||
redirect: '/student/query',
|
|
||||||
name: 'Example',
|
|
||||||
meta: { title: '学生', icon: 'el-icon-s-help' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'score',
|
|
||||||
name: 'Score',
|
|
||||||
component: () => import('@/views/query/index'),
|
|
||||||
meta: { title: '成绩查询', icon: 'table' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'class',
|
|
||||||
name: 'Class',
|
|
||||||
component: () => import('@/views/user/index'),
|
|
||||||
meta: { title: '个人信息', icon: 'table' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '/form',
|
path: '/form',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
@ -227,6 +164,64 @@ export const constantRoutes = [
|
||||||
{ path: '*', redirect: '/404', hidden: true }
|
{ path: '*', redirect: '/404', hidden: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const asyncRoutes = [
|
||||||
|
{
|
||||||
|
path: '/teacher',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/teacher/table',
|
||||||
|
name: 'teacher',
|
||||||
|
meta: { title: '教师', icon: 'el-icon-s-help', roles: ['admin', 'teacher'] },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'table',
|
||||||
|
name: 'Table',
|
||||||
|
component: () => import('@/views/student/index'),
|
||||||
|
meta: { title: '学生管理', icon: 'table' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'class',
|
||||||
|
name: 'Class',
|
||||||
|
component: () => import('@/views/class/index'),
|
||||||
|
meta: { title: '班级管理', icon: 'table' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'subject',
|
||||||
|
name: 'Subject',
|
||||||
|
component: () => import('@/views/subject/index'),
|
||||||
|
meta: { title: '科目管理', icon: 'table' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'score',
|
||||||
|
name: 'Score',
|
||||||
|
component: () => import('@/views/score/index'),
|
||||||
|
meta: { title: '成绩管理', icon: 'table' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/student',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/student/query',
|
||||||
|
name: 'student',
|
||||||
|
meta: { title: '学生', icon: 'el-icon-s-help', roles: ['student'] },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'query',
|
||||||
|
name: 'query',
|
||||||
|
component: () => import('@/views/query/index'),
|
||||||
|
meta: { title: '成绩查询', icon: 'table' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'info',
|
||||||
|
name: 'info',
|
||||||
|
component: () => import('@/views/user/index'),
|
||||||
|
meta: { title: '个人信息', icon: 'table' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const createRouter = () => new Router({
|
const createRouter = () => new Router({
|
||||||
// mode: 'history', // require service support
|
// mode: 'history', // require service support
|
||||||
scrollBehavior: () => ({ y: 0 }),
|
scrollBehavior: () => ({ y: 0 }),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ const getters = {
|
||||||
device: state => state.app.device,
|
device: state => state.app.device,
|
||||||
token: state => state.user.token,
|
token: state => state.user.token,
|
||||||
avatar: state => state.user.avatar,
|
avatar: state => state.user.avatar,
|
||||||
name: state => state.user.name
|
name: state => state.user.name,
|
||||||
|
roles: state => state.user.roles,
|
||||||
|
permission_routes: state => state.permission.routes,
|
||||||
|
addRoutes: state => state.permission.addRoutes
|
||||||
}
|
}
|
||||||
export default getters
|
export default getters
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import getters from './getters'
|
||||||
import app from './modules/app'
|
import app from './modules/app'
|
||||||
import settings from './modules/settings'
|
import settings from './modules/settings'
|
||||||
import user from './modules/user'
|
import user from './modules/user'
|
||||||
|
import permission from './modules/permission'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
|
@ -11,7 +12,8 @@ const store = new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
app,
|
app,
|
||||||
settings,
|
settings,
|
||||||
user
|
user,
|
||||||
|
permission
|
||||||
},
|
},
|
||||||
getters
|
getters
|
||||||
})
|
})
|
||||||
|
|
|
||||||
69
src/store/modules/permission.js
Normal file
69
src/store/modules/permission.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { asyncRoutes, constantRoutes } from '@/router'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用meta.role确定当前用户是否有权限
|
||||||
|
* @param roles
|
||||||
|
* @param route
|
||||||
|
*/
|
||||||
|
function hasPermission(roles, route) {
|
||||||
|
if (route.meta && route.meta.roles) {
|
||||||
|
return roles.some(role => route.meta.roles.includes(role))
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归过滤异步路由表
|
||||||
|
* @param routes asyncRoutes
|
||||||
|
* @param roles
|
||||||
|
*/
|
||||||
|
export function filterAsyncRoutes(routes, roles) {
|
||||||
|
const res = []
|
||||||
|
|
||||||
|
routes.forEach(route => {
|
||||||
|
const tmp = { ...route }
|
||||||
|
if (hasPermission(roles, tmp)) {
|
||||||
|
if (tmp.children) {
|
||||||
|
tmp.children = filterAsyncRoutes(tmp.children, roles)
|
||||||
|
}
|
||||||
|
res.push(tmp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
routes: [],
|
||||||
|
addRoutes: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
SET_ROUTES: (state, routes) => {
|
||||||
|
state.addRoutes = routes
|
||||||
|
state.routes = constantRoutes.concat(routes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
generateRoutes({ commit }, roles) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let accessedRoutes
|
||||||
|
if (roles.includes('admin')) {
|
||||||
|
accessedRoutes = asyncRoutes || []
|
||||||
|
} else {
|
||||||
|
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
|
||||||
|
}
|
||||||
|
commit('SET_ROUTES', accessedRoutes)
|
||||||
|
resolve(accessedRoutes)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,8 @@ const getDefaultState = () => {
|
||||||
return {
|
return {
|
||||||
token: getToken(),
|
token: getToken(),
|
||||||
name: '',
|
name: '',
|
||||||
avatar: ''
|
avatar: '',
|
||||||
|
roles: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,6 +28,9 @@ const mutations = {
|
||||||
},
|
},
|
||||||
SET_UID: (state, uid) => {
|
SET_UID: (state, uid) => {
|
||||||
state.uid = uid
|
state.uid = uid
|
||||||
|
},
|
||||||
|
SET_ROLES: (state, roles) => {
|
||||||
|
state.roles = roles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,13 +79,16 @@ const actions = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo().then(response => {
|
getInfo().then(response => {
|
||||||
const { result } = response
|
const { result } = response
|
||||||
|
console.log('getInfo-userInfo', result)
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return reject('Verification failed, please Login again.')
|
return reject('Verification failed, please Login again.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { username, userId } = result
|
const { roles, username, userId } = result
|
||||||
|
if (!roles || roles.length <= 0) {
|
||||||
|
reject('用户必须至少有一个角色!')
|
||||||
|
}
|
||||||
|
commit('SET_ROLES', roles)
|
||||||
commit('SET_NAME', username)
|
commit('SET_NAME', username)
|
||||||
commit('SET_UID', userId)
|
commit('SET_UID', userId)
|
||||||
resolve(result)
|
resolve(result)
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
<el-input v-model="filter.major" placeholder="请输入专业名称" />
|
<el-input v-model="filter.major" placeholder="请输入专业名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" offset="0">
|
<el-col :span="4" :offset="0">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="onFilter">筛选</el-button>
|
<el-button type="primary" @click="onFilter">筛选</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" offset="0">
|
<el-col :span="4" :offset="0">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="handleAdd">新增</el-button>
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@
|
||||||
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
|
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
|
||||||
|
|
||||||
<div class="tips">
|
<div class="tips">
|
||||||
<span style="margin-right:20px;">username: admin</span>
|
<span style="margin-right:20px;">username: 1000、10001、10002</span>
|
||||||
<span> password: any</span>
|
<span> password: admin、teacher、student</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
@ -106,6 +106,8 @@ export default {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$store.dispatch('user/login', this.loginForm).then(() => {
|
this.$store.dispatch('user/login', this.loginForm).then(() => {
|
||||||
|
console.log('login success, this.redirect', this.redirect)
|
||||||
|
// 固定登录成功后跳转的页面
|
||||||
this.$router.push({ path: this.redirect || '/' })
|
this.$router.push({ path: this.redirect || '/' })
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" offset="0">
|
<el-col :span="4" :offset="0">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="onFilter">筛选</el-button>
|
<el-button type="primary" @click="onFilter">筛选</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4" offset="0">
|
<el-col :span="4" :offset="0">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="handleAdd">新增</el-button>
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user