修改:从后端获取动态路由

This commit is contained in:
jmal 2021-01-11 18:02:57 +08:00
parent 204ec2ef0e
commit c679a04db3
8 changed files with 399 additions and 239 deletions

View File

@ -0,0 +1,3 @@
<template>
<router-view />
</template>

View File

@ -6,9 +6,9 @@
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item> </el-menu-item>
</app-link> </app-link>
<el-menu-item v-if="onlyOneChild.path === '/setting/manager-users'"> <!-- <el-menu-item v-if="onlyOneChild.path === '/setting/manager-users'">-->
<item title="sdf"/> <!-- <item title="sdf"/>-->
</el-menu-item> <!-- </el-menu-item>-->
</template> </template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>

View File

@ -1,41 +1,46 @@
import router from './router' import router from './router'
import store from './store' import store from './store'
import { Message } from 'element-ui' import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar import NProgress from 'nprogress' // 进度条
import 'nprogress/nprogress.css' // progress bar style import 'nprogress/nprogress.css' // 进度条样式
import { getToken } from '@/utils/auth' // get token from cookie import { getToken } from '@/utils/auth' // 认证获取token
import getPageTitle from '@/utils/get-page-title' import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration NProgress.configure({ showSpinner: false }) // NProgress配置
const whiteList = ['/login','/articles','/s','/s_m','/articles/article','/share'] // no redirect whitelist const whiteList = ['/login','/s','/s_m','/share'] // 白名单
router.beforeEach(async(to, from, next) => { router.beforeEach(async(to, from, next) => {
// start progress bar // 开始进度条
NProgress.start() NProgress.start()
// set page title // 设置页面标题
document.title = getPageTitle(to.meta.title) document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in // 确认用户是否已登录
const hasToken = getToken() const hasToken = getToken()
if (hasToken) { if (hasToken) {
// 已登录的情况
if (to.path === '/login') { if (to.path === '/login') {
// if is logged in, redirect to the home page // 登陆页面
next({ path: '/' }) // 如果已登录,则重定向到主页
setMenuList(next, { path: '/' })
NProgress.done() NProgress.done()
} else { } else {
// 非登陆页面
const hasGetUserInfo = store.getters.name const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) { if (hasGetUserInfo) {
next() // 存在用户信息
setMenuList(next)
} else { } else {
// 不存在用户信息
try { try {
// get user info // 获取用户信息
await store.dispatch('user/getInfo') await store.dispatch('user/getInfo')
next() setMenuList(next)
} catch { } catch {
// remove token and go to login page to re-login // 删除token,然后转到登录页面重新登录
await store.dispatch('user/resetToken') await store.dispatch('user/resetToken')
next(`/login?redirect=${to.path}`) next(`/login?redirect=${to.path}`)
NProgress.done() NProgress.done()
@ -43,19 +48,31 @@ router.beforeEach(async(to, from, next) => {
} }
} }
} else { } else {
/* has no token*/ // 没有登陆的情况
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly // 没有token但是在白名单中,直接进入
next() next()
} else { } else {
// other pages that do not have permission to access are redirected to the login page. // 其他没有token重定向到登录页面。
next(`/login?redirect=${to.path}`) next(`/login?redirect=${to.path}`)
NProgress.done() NProgress.done()
} }
} }
}) })
function setMenuList(next, to){
console.log('menuList', to, store.getters.menuList)
if(store.getters.menuList.length == 0){
store.dispatch('user/setMenuList').then(res => {
console.log('res', res)
next({path:store.getters.menuList[0].path})
})
} else {
next(to)
}
}
router.afterEach(() => { router.afterEach(() => {
// finish progress bar // 完成进度条
NProgress.done() NProgress.done()
}) })

View File

@ -48,220 +48,220 @@ export const constantRoutes = [
hidden: true hidden: true
}, },
{ // {
path: '/', // path: '/',
component: Layout, // component: Layout,
redirect: '/home', // redirect: '/home',
children: [{ // children: [{
path: '', // path: '',
name: 'Home', // name: 'Home',
component: () => import('@/views/home/index'), // component: () => import('@/views/home/index'),
meta: { title: '所有文件', icon: 'ziyuan' } // meta: { title: '所有文件', icon: 'ziyuan' }
}] // }]
}, // },
{ // {
path: '/recently', // path: '/recently',
component: Layout, // component: Layout,
redirect: '/recently', // redirect: '/recently',
children: [ // children: [
{ // {
path: '', // path: '',
name: 'recently', // name: 'recently',
component: () => import('@/views/recently/index'), // component: () => import('@/views/recently/index'),
meta: { title: '最近', icon: 'zuijinyuedu' } // meta: { title: '最近', icon: 'zuijinyuedu' }
} // }
] // ]
}, // },
//
{ // {
path: '/favorite', // path: '/favorite',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: 'index', // path: 'index',
name: 'Form', // name: 'Form',
component: () => import('@/views/favorite/index'), // component: () => import('@/views/favorite/index'),
meta: { title: '收藏', icon: 'Favoritestarrate' } // meta: { title: '收藏', icon: 'Favoritestarrate' }
} // }
] // ]
}, // },
//
{ // {
path: '/share', // path: '/share',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: '', // path: '',
name: 'share', // name: 'share',
component: () => import('@/views/share/index'), // component: () => import('@/views/share/index'),
meta: { title: '分享', icon: 'gongxiangzhongxin' } // meta: { title: '分享', icon: 'gongxiangzhongxin' }
} // }
] // ]
}, // },
//
{ // {
path: '/audio', // path: '/audio',
component: Layout, // component: Layout,
redirect: '/audio', // redirect: '/audio',
children: [ // children: [
{ // {
path: '', // path: '',
name: 'audio', // name: 'audio',
component: () => import('@/views/fileType/audio'), // component: () => import('@/views/fileType/audio'),
meta: { title: '音乐', icon: 'audio' } // meta: { title: '音乐', icon: 'audio' }
} // }
] // ]
}, // },
{ // {
path: '/video', // path: '/video',
component: Layout, // component: Layout,
redirect: '/video', // redirect: '/video',
children: [ // children: [
{ // {
path: '', // path: '',
name: 'video', // name: 'video',
component: () => import('@/views/fileType/video'), // component: () => import('@/views/fileType/video'),
meta: { title: '视频', icon: 'video' } // meta: { title: '视频', icon: 'video' }
} // }
] // ]
}, // },
{ // {
path: '/image', // path: '/image',
component: Layout, // component: Layout,
redirect: '/image', // redirect: '/image',
children: [ // children: [
{ // {
path: '', // path: '',
name: 'image', // name: 'image',
component: () => import('@/views/fileType/image'), // component: () => import('@/views/fileType/image'),
meta: { title: '图片', icon: 'image' } // meta: { title: '图片', icon: 'image' }
} // }
] // ]
}, // },
{ // {
path: '/document', // path: '/document',
component: Layout, // component: Layout,
redirect: '/document', // redirect: '/document',
children: [ // children: [
{ // {
path: '', // path: '',
name: 'document', // name: 'document',
component: () => import('@/views/fileType/document'), // component: () => import('@/views/fileType/document'),
meta: { title: '文档', icon: 'document' } // meta: { title: '文档', icon: 'document' }
} // }
] // ]
}, // },
{ // {
path: '/markdown', // path: '/markdown',
component: Layout, // component: Layout,
hidden: true, // hidden: true,
children: [ // children: [
{ // {
path: 'editor', // path: 'editor',
name: 'markdown', // name: 'markdown',
component: () => import('@/views/markdown/index'), // component: () => import('@/views/markdown/index'),
meta: { title: '创建文档', icon: 'md' } // meta: { title: '创建文档', icon: 'md' }
} // }
] // ]
}, // },
{ // {
path: '/setting/user', // path: '/setting/user',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: '', // path: '',
name: 'setting-user', // name: 'setting-user',
component: () => import('@/views/setting/sys/cusomerInfo'), // component: () => import('@/views/setting/sys/cusomerInfo'),
meta: { title: '个人信息' , icon: 'UserSettings'} // meta: { title: '个人信息' , icon: 'UserSettings'}
} // }
] // ]
}, // },
{ // {
path: '/setting/manager-users', // path: '/setting/manager-users',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: '', // path: '',
name: 'manager-users', // name: 'manager-users',
component: () => import('@/views/setting/sys/cusomerManager'), // component: () => import('@/views/setting/sys/cusomerManager'),
meta: { title: '用户管理' , icon: 'huaban'} // meta: { title: '用户管理' , icon: 'huaban'}
} // }
] // ]
}, // },
{ // {
path: '/setting/manager-roles', // path: '/setting/manager-roles',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: '', // path: '',
name: 'manager-roles', // name: 'manager-roles',
component: () => import('@/views/setting/sys/roleManager'), // component: () => import('@/views/setting/sys/roleManager'),
meta: { title: '角色管理' , icon: 'role'} // meta: { title: '角色管理' , icon: 'role'}
} // }
] // ]
}, // },
{ // {
path: '/setting/manager-menus', // path: '/setting/manager-menus',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: '', // path: '',
name: 'manager-menus', // name: 'manager-menus',
component: () => import('@/views/setting/sys/menuManager'), // component: () => import('@/views/setting/sys/menuManager'),
meta: { title: '菜单管理' , icon: 'menu'} // meta: { title: '菜单管理' , icon: 'menu'}
} // }
] // ]
}, // },
{ // {
path: '/setting/website', // path: '/setting/website',
component: Layout, // component: Layout,
name: 'website', // name: 'website',
meta: { title: '网站管理', icon: 'wangzhanguanli'}, // meta: { title: '网站管理', icon: 'wangzhanguanli'},
children: [ // children: [
{ // {
path: 'manager-articles', // path: 'manager-articles',
name: 'manager-articles', // name: 'manager-articles',
component: () => import('@/views/setting/articles/articlePageManager'), // component: () => import('@/views/setting/articles/articlePageManager'),
meta: { title: '文章' , icon: 'guanlizhuanjiawenzhang'} // meta: { title: '文章' , icon: 'guanlizhuanjiawenzhang'}
}, // },
{ // {
path: 'manager-alone-page', // path: 'manager-alone-page',
name: 'manager-alone-page', // name: 'manager-alone-page',
component: () => import('@/views/setting/articles/alonePageManager'), // component: () => import('@/views/setting/articles/alonePageManager'),
meta: { title: '独立页面' , icon: 'yemianshezhi'} // meta: { title: '独立页面' , icon: 'yemianshezhi'}
}, // },
{ // {
path: 'manager-blog', // path: 'manager-blog',
name: 'manager-blog', // name: 'manager-blog',
component: () => import('@/views/setting/articles/blogManager'), // component: () => import('@/views/setting/articles/blogManager'),
meta: { title: '外观' , icon: 'pifu'} // meta: { title: '外观' , icon: 'pifu'}
}, // },
{ // {
path: 'manager-categories', // path: 'manager-categories',
name: 'manager-categories', // name: 'manager-categories',
component: () => import('@/views/setting/articles/categoryManager'), // component: () => import('@/views/setting/articles/categoryManager'),
meta: { title: '分类', icon: 'fenlei' } // meta: { title: '分类', icon: 'fenlei' }
}, // },
{ // {
path: 'manager-tags', // path: 'manager-tags',
name: 'manager-tags', // name: 'manager-tags',
component: () => import('@/views/setting/articles/tagManager'), // component: () => import('@/views/setting/articles/tagManager'),
meta: { title: '标签', icon: 'biaoqian' } // meta: { title: '标签', icon: 'biaoqian' }
} // }
] // ]
}, // },
{ // {
path: '/setting/manager-cloud', // path: '/setting/manager-cloud',
component: Layout, // component: Layout,
children: [ // children: [
{ // {
path: '', // path: '',
name: 'manager-cloud', // name: 'manager-cloud',
component: () => import('@/views/setting/cloudManager'), // component: () => import('@/views/setting/cloudManager'),
meta: { title: '网盘设置', icon: 'yunshezhi' } // meta: { title: '网盘设置', icon: 'yunshezhi' }
} // }
] // ]
}, // },
{ {
path: '/s', path: '/s',
component: Layout, component: Layout,
@ -340,10 +340,13 @@ const createRouter = () => new Router({
const router = createRouter() const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() { export function resetRouter(params) {
const newRouter = createRouter() const newRouter = createRouter()
// router.matcher = newRouter.matcher // reset router
if(params){
newRouter.addRoutes(params)
}
newRouter.addRoutes(dynamicRouters) newRouter.addRoutes(dynamicRouters)
router.matcher = newRouter.matcher // reset router
} }
router.addRoutes(dynamicRouters) router.addRoutes(dynamicRouters)
export default router export default router

View File

@ -5,6 +5,7 @@ const getters = {
avatar: state => state.user.avatar, avatar: state => state.user.avatar,
name: state => state.user.name, name: state => state.user.name,
showName: state => state.user.showName, showName: state => state.user.showName,
userId: state => state.user.userId userId: state => state.user.userId,
menuList: state => state.user.menuList,
} }
export default getters export default getters

View File

@ -1,6 +1,11 @@
import { login, logout, getInfo } from '@/api/user' import { login, logout, getInfo } from '@/api/user'
import menuApi from '@/api/menu'
import { getToken, setToken, getConsumerId, setConsumerId, removeToken, removeConsumerId , setRememberName, removeRememberName } from '@/utils/auth' import { getToken, setToken, getConsumerId, setConsumerId, removeToken, removeConsumerId , setRememberName, removeRememberName } from '@/utils/auth'
import { resetRouter } from '@/router' import { resetRouter } from '@/router'
import getters from "@/store/getters"
import router from '@/router'
import Layout from '@/layout'
import ParentView from '@/components/ParentView'
const getDefaultState = () => { const getDefaultState = () => {
return { return {
@ -9,6 +14,7 @@ const getDefaultState = () => {
showName: '', showName: '',
avatar: '', avatar: '',
userId: getConsumerId(), userId: getConsumerId(),
menuList: []
} }
} }
@ -32,10 +38,35 @@ const mutations = {
}, },
SET_USERID: (state, userId) => { SET_USERID: (state, userId) => {
state.userId = userId state.userId = userId
},
SET_MENU_LIST:(state,menuList) => {
state.menuList = menuList;
} }
} }
const actions = { const actions = {
async setMenuList({ commit, state }) {
let noeFound = [];
return new Promise( (resolve,reject) => {
noeFound = []
menuApi.menuTree({userId: getters.userId}).then((res) => {
let data = res.data;
let mentTree = getMenuTree(data, []);
console.log('mentTree', mentTree);
state.menuList = mentTree.concat(noeFound);
commit('SET_MENU_LIST',state.menuList);
router.addRoutes(state.menuList)
console.log('state.menuList', state.menuList)
router.options.routes = router.options.routes.concat(state.menuList);
console.log('router',router);
resolve(res)
}).catch(error => {
reject(error)
});
} )
},
// user login // user login
login({ commit }, userInfo) { login({ commit }, userInfo) {
const { username, password, rememberMe } = userInfo const { username, password, rememberMe } = userInfo
@ -110,6 +141,68 @@ const actions = {
} }
} }
function getMenuTree(menuList, arr) {
console.log('mentTreeData', menuList)
arr = menuList.map((menu, index) => {
let router = {}
if(menu.children && menu.children.length){
router.children = findChildren(menu.children)
}
if(menu.component && menu.component.length > 0) {
router.children = [
{
path: '',
name: menu.name,
component: loadView(menu.component),
meta: {title: menu.name, icon: menu.icon}
}
]
}
router.path = menu.path
router.component = Layout
return router
});
return arr;
}
function findChildren(childrenMenu){
let children = []
childrenMenu.forEach((menu, index) => {
if(menu.children && menu.children.length){
if(!menu.component || menu.component.length === 0){
menu.children.forEach(c => {
let router = {}
router.path = c.path
router.name = c.name
router.component = loadView(c.component)
router.meta = {
title: c.name,
icon: c.icon
}
if(c.children && c.children.length){
children = children.concat(findChildren(c.children, c))
return
}
children.push(router)
})
return
}
}
let router = {
path: menu.path,
name: menu.name,
component: loadView(menu.component),
meta: {title: menu.name, icon: menu.icon}
}
children = children.concat(router)
})
return children
}
export const loadView = (view) => {
return (resolve) => require([`@/views${view}`], resolve)
}
export default { export default {
namespaced: true, namespaced: true,
state, state,

View File

@ -235,6 +235,7 @@ export default {
} }
} }
data.delete("createTime") data.delete("createTime")
data.delete("updateTime")
return data return data
}, },
// 保存 // 保存

View File

@ -17,6 +17,21 @@
<el-button size="small" type="primary" :loading="updateLoading" @click.native.prevent="onSave()">保 存</el-button> <el-button size="small" type="primary" :loading="updateLoading" @click.native.prevent="onSave()">保 存</el-button>
</div> </div>
</el-dialog> </el-dialog>
<el-dialog width="400px" title="分配权限" :visible.sync="dialogAuthVisible">
<el-tree
ref="authTree"
:data="menuList"
show-checkbox
node-key="id"
default-expand-all
:default-checked-keys="menuCheckedList"
:props="{value: 'id', label: 'name'}">
</el-tree>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="dialogAuthVisible = false">取 消</el-button>
<el-button size="small" type="primary" :loading="updateLoading" @click.native.prevent="saveAuth()">保 存</el-button>
</div>
</el-dialog>
<el-card class="box-card table-search-header"> <el-card class="box-card table-search-header">
<div slot="header"> <div slot="header">
<div class="box-card-header"> <div class="box-card-header">
@ -64,6 +79,7 @@
<script> <script>
import roleApi from '@/api/role' import roleApi from '@/api/role'
import menuApi from '@/api/menu'
import TableList from "@/components/table/TableList"; import TableList from "@/components/table/TableList";
export default { export default {
@ -76,15 +92,20 @@ export default {
return { return {
title: "角色管理", title: "角色管理",
dataList: [], dataList: [],
menuList: [],
menuCheckedList: [],
loading: false, loading: false,
dialogVisible: false, dialogVisible: false,
dialogAuthVisible: false,
dialogTitle: '', dialogTitle: '',
updateLoading: false, updateLoading: false,
updateAuthLoading: false,
valid: true, valid: true,
form: { form: {
name: '', name: '',
code: '', code: '',
remarks: '', remarks: '',
menuIds: []
}, },
// 分页信息 // 分页信息
pagination: { pagination: {
@ -107,7 +128,7 @@ export default {
{prop: 'createTime',label: '创建时间',sortable: 'custom'}, {prop: 'createTime',label: '创建时间',sortable: 'custom'},
{label: '操作',active: [ {label: '操作',active: [
{name: '修改', icon: 'el-icon-edit', handle: (row) => this.handleEdit(row.id)}, {name: '修改', icon: 'el-icon-edit', handle: (row) => this.handleEdit(row.id)},
{name: '删除', icon: 'el-icon-delete', color: '#ff4d4f', handle: (row) => this.handleDelete([row.id])}, {name: '分配权限', icon: 'el-icon-finished', handle: (row) => this.authorization(row.id)},
], ],
}, },
], ],
@ -127,6 +148,7 @@ export default {
}, },
mounted() { mounted() {
this.getRoleList() this.getRoleList()
this.getMenuTree()
this.resize() this.resize()
}, },
methods: { methods: {
@ -164,6 +186,11 @@ export default {
this.loading = false this.loading = false
}) })
}, },
getMenuTree() {
menuApi.menuTree().then(res => {
this.menuList = res.data
})
},
async add() { async add() {
this.dialogVisible = true this.dialogVisible = true
this.dialogTitle = '添加角色' this.dialogTitle = '添加角色'
@ -199,6 +226,19 @@ export default {
} }
}); });
}, },
// 保存分配权限
saveAuth() {
this.form.menuIds = this.$refs.authTree.getCheckedKeys()
this.updateRole(this.setFormData())
},
// 分配权限
authorization(roleId) {
this.dialogAuthVisible = true
const findIndex = this.dataList.findIndex(data => data.id === roleId)
const role = this.dataList[findIndex]
this.form.id = role.id
this.menuCheckedList = role.menuIds
},
handleEdit(id) { handleEdit(id) {
this.editMove = 2 this.editMove = 2
this.dialogVisible = true this.dialogVisible = true
@ -272,6 +312,7 @@ export default {
} }
this.updateLoading = false this.updateLoading = false
this.dialogVisible = false this.dialogVisible = false
this.dialogAuthVisible = false
this.$message({ this.$message({
message: msg, message: msg,
type: 'success', type: 'success',
@ -280,6 +321,7 @@ export default {
}, },
onError() { onError() {
this.updateLoading = false this.updateLoading = false
this.dialogAuthVisible = false
} }
} }
} }