写在最前:
我站在巨人的肩膀上,总结并分享了如何改造花裤衩大佬vue admin template后台模板的动态菜单路由以及权限管理,文中有我mock的后台接口,能够模拟正式环境登陆退出,希望对你有帮助。
第一步:git命令拉取代码
git clone https://github.com/PanJiaChen/vue-admin-template.git
第二步:剔除烦人的eslint及删除多余路由
//vue.config.js文件
修改第30行 lintOnSave: false
// router目录下的index.js删除不要的路由(我只留了下面两个)
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: 'Dashboard', icon: 'dashboard' }
}]
}
]
我站在巨人的肩膀上,总结并分享了如何改造花裤衩大佬vue admin template后台模板的动态菜单路由以及权限管理,文中有我mock的后台接口,能够模拟正式环境登陆退出,希望对你有帮助。
git clone https://github.com/PanJiaChen/vue-admin-template.git
//vue.config.js文件
修改第30行 lintOnSave: false
// router目录下的index.js删除不要的路由(我只留了下面两个)
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: 'Dashboard', icon: 'dashboard' }
}]
}
]
执行npm i下载依赖自动运行: 可以看到左侧菜单栏只剩dashboard一个菜单
第三步:配置跨域
// vue.config.js文件在第39行删除before: require('./mock/mock-server.js')
//加入下列代码
proxy: {
'/api': {
target: '',//****这里写上后端提供的基础地址(文末有我的接口地址) *****
// ws: true, //是否允许websocket
// secure: false,
changeOrigin: true,
pathRewrite: {'^/api': ''}
}
},
第四步:修改生产和开发环境
//文件.env.development修改
VUE_APP_BASE_API = '/api'
//文件.env.production修改
VUE_APP_BASE_API = '/api'
第五步:修改登录接口和获取用户信息接口
export function login(obj) {
return request({
url: '/user/login', // url = base url + request url
method: 'post',
params: obj
})
}
export function getInfo(token) {
return request({
url: '/user/getUserInfoByToken?token=' + token,
method: 'get'
})
}
第六步:修改actions里面的login接口地址,根据字段正确存储token
// store/modules/user.js
const getDefaultState = () => {
return {
token: getToken(),
name: '',
avatar: '',
roles: [], // 角色权限控制按钮显示
menus: [] // 菜单权限
}
}
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles // 角色权限
},
SET_MENUS: (state, menus) => {
state.menus = menus // 菜单权限
}
}
const actions = {
// 登录
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { token } = response.data.data.data
commit('SET_TOKEN', token) // vuex存储token
setToken(token) // cookie存储token
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response.data.data
if (!data) {
return reject('Verification failed, please Login again.')
}
const { name, avatar, roles, menus } = data
/**
* 获取异步路由后加入404路由能解决刷新后【丢失路由跳转404页面】问题
(因为异步获取路由优先级比静态路由表低,导致404路由在异步添加的路由之前)
*/
menus.push({ 'path': '/404', 'component': '404', 'hide': 'true' }, {
'path': '*',
'redirect': '/404',
'hidden': 'true'
})
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_ROLES', roles) // 角色权限
commit('SET_MENUS', menus) // 菜单权限
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// 登出
logout({ commit, state }) {
return new Promise((resolve, reject) => { // 如果后端有退出接口写在下面即可,这里直接退出也没问题
removeToken() // 必须先移除token
resetRouter()
commit('RESET_STATE')
resolve()
})
},
第七步:修改getters.js存储信息
// store/getters.js
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
menus: state => state.user.menus, // 菜单权限
roles: state => state.user.roles // 角色权限控制按钮
}
export default getters
第八步:修改request.js
// utils/request.js
import router from '@/router'
const BASEURL = process.env.NODE_ENV === 'production' ? '' : '/api'
// 创建axios 赋给变量service
const service = axios.create({
baseURL: BASEURL, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // 5s请求超时
})
// 请求拦截器
service.interceptors.request.use(
config => {
if (store.getters.token) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 改成后端要求的token键值
}
return config
},
error => {
console.log(error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const message = response.data.data.message
const code = response.data.data.code
if (code !== 200) { // 改成后端响应成功的状态码
switch (code) { // 响应错误判断
case 401:
if (router.currentRoute.name === 'login') {
return Promise.reject(new Error(message))
} else {
Message({
message: '您没有权限访问该资源',
type: 'error'
})
}
break
case 403:
Message({
message: message,
type: 'error'
})
break
case 500:
Message({
message: '服务异常,请联系管理员',
type: 'error'
})
break
default:
Message({
message: message,
type: 'error'
})
}
// store.dispatch('user/resetToken').then(() => {
// location.reload() // 重复登录的情况
// })
return Promise.reject(new Error(message || 'Error'))
} else {
return response
}
},
error => {
console.log('err' + error) // 响应错误
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
第九步:新增导入组件的方法
// 在router目录下新建_import_development.js文件
// 开发环境导入组件
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
// 在router目录下新建_import_proudction.js文件
// 生产环境导入组件
module.exports = file => () => import('@/views/' + file + '.vue')
第十步:修改permission.js
import Layout from '@/layout' // 引入Layout
const _import = require('./router/_import_' + process.env.NODE_ENV) // 引入获取组件的方法
// 路由拦截器
router.beforeEach(async(to, from, next) => {
// 进度条加载
NProgress.start()
// 获取page标题
document.title = getPageTitle(to.meta.title)
// 获取token决定用户是否可以登录
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// 如果已登录,则重定向到主页
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// 每次刷新都会执行try获取用户信息,所以getInfo必须要有,后端不提供怎么办?揍他
await store.dispatch('user/getInfo')
// **在这里做动态路由**
if (store.getters.menus.length < 1) {
global.antRouter = []
next()
}
const menus = filterAsyncRouter(store.getters.menus) // 过滤路由
router.addRoutes(menus) // 动态添加路由
global.antRouter = menus // 将路由数据传递给全局变量,做侧边栏菜单渲染工作
next({ ...to, replace: true })
} catch (error) {
// 移除token去登录页
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* 没有token */
if (whiteList.indexOf(to.path) !== -1) {
// 如果该路由在白名单内, 放行
next()
} else {
// 不在白名单内不允许通过重定向到登录页
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
if (route.component === 'Layout') { // Layout组件特殊处理
route.component = Layout
} else {
route.component = _import(route.component) // 导入组件
}
}
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
}
return true
})
return accessedRouters
}
最后一步:替换路由
//layout目录下components下sidebar下index.vue
routes() {
return this.$router.options.routes.concat(global.antRouter) // 新路由连接
},
控制台执行npm run dev: 可以看到左侧菜单栏已经动态添加了菜单
写在最后:
无偿提供接口地址:
基础地址: https://www.fastmock.site/mock/48611e3434449d37f501fc4a60f141a8/mock
登录api: /user/login
根据token获取用户信息api(包含姓名,头像,菜单权限,角色):/user/getUserInfoByToken
username: admin/editor password: 123456
按钮权限控制(如果不需要做按钮权限这part不用看):
还是得看花裤衩大佬的原文;只有见过大佬写的code,才明白什么叫细到极致~
第一步:站在巨人的肩膀看世界
去花裤衩大佬githubsrc文件下找到directive文件并复制粘贴到自己的src文件下,保留permission文件,删除其他。
第二步:测试按钮权限
//在dashboard/index.vue下新增两个测试按钮
<el-tooltip class="item" effect="dark" content="admin权限显示" placement="top-start">
<el-button v-permission="['admin']">admin</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="admin&editor权限都能显示" placement="top-start">
<el-button v-permission="['admin','editor']">editor</el-button>
</el-tooltip>
// 当然你也可以为了方便使用,将它注册到全局
import permission from '@/directive/permission/index.js' // 权限判断指令
export default {
name: 'Dashboard',
computed: {
...mapGetters([
'name'
])
},
directives: { permission } // 自定义指令
}
保存后看到登录admin账户时两个按钮都能显示,当登录editor账户时只能显示editor按钮。使用上面mock地址可以实现动态添加路由的需求,希望能够帮助到掘友萌。
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!gitee: gitee.com/unicorn-zbf…
master分支预览地址:unicorn-zbf.gitee.io/vue-admin-t…
master分支采用本地json文件模拟的数据;unicornZ分支使用fast mock平台模拟真实的接口请求,项目需要在本地运行预览。