vue+element后台系统 自己动手撸(一)权限及动态路由
2020-07-28 本文已影响0人
华子_tm
前言
公司计划改版一个后台管理系统,由于之前是不分离的项目,这次想做成分离项目。前期在技术选择上一直在纠结vue还是react。由于react没有专门的开发人员而且开发学习成本高所以选择了vue+element开发后台项目。
开始
vue+element分为两个Vue-Element-admin(成熟系统)和Vue-Element-Template(基础模板)。由于成熟系统很多功能用不上,所以使用了基础版本去开发。在开发过程中,花裤衩大神的手撸篇对我帮助不小。话不多说了。下面将记录一些自己在开发过程中的问题。
一、axios封装
一个项目对于请求的集中处理很重要。这里我对axios进行了封装以满足项目需求。这里主要对请求参数的类型做了区分,post上传用的form-data,而普通的post请求用的application/json,这里你可以根据自己的项目去封装。
src\utils\request.js
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// 创建一个AXIOS实例
const service = axios.create({
// baseURL: process.env.VUE_APP_BASE_API
// baseURL: 'http://aksdkadsk/api/' // 本地测试地址
})
// 请求拦截器
service.interceptors.request.use(
config => {
if (store.getters.token) {
// 让每个请求携带令牌 “Token”是自定义头密钥
config.headers['Token'] = getToken()
}
if (config.data && config.data.$_isFormData === true) {
config.headers['Content-Type'] = 'multipart/form-data'
config.data = config.data.form
} else {
config.headers['Content-Type'] = 'application/json'
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
// 如果您想要获取诸如头或状态之类的http信息 请返回response=>response
response => {
let res
if (typeof (response.data) === 'string') {
res = JSON.parse(response.data)
} else if (typeof (response.data) === 'object') {
res = response.data
}
if (res.Statu_Code === '200') {
return res
} else if (res.Statu_Code === '100') {
return res
} else {
// 403:令牌过期;
if (res.Statu_Code === '403') {
// 重新登录
MessageBox.confirm(res.Msg, '确认注销', {
confirmButtonText: '重新登录',
// cancelButtonText: '取消',
showCancelButton: false,
showClose: false,
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
return Promise.reject(new Error(res.Msg || 'Error'))
} else if (res.Statu_Code === '401') {
// 重新登录
MessageBox.confirm(res.Msg, '提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
return Promise.reject(new Error(res.Msg || 'Error'))
} else if (res.Statu_Code === '402') {
// 重新登录
MessageBox.confirm(res.Msg, '提示', {
confirmButtonText: '确定',
showCancelButton: false,
type: 'warning'
})
return Promise.reject(new Error(res.Msg || 'Error'))
} else {
Message({
message: res.Msg || 'Error',
type: 'error',
duration: 2 * 1000,
offset: 100
})
return Promise.reject(new Error(res.Msg || 'Error'))
}
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 2 * 1000,
offset: 100
})
return Promise.reject(error)
}
)
export default service
登录及权限菜单
一个后台系统的权限是很重要的,这里我们使用了动态路由,也就是按登录人的权限去后台返回对应的菜单,而前台只需要将后端传回的路由进行处理即可。这里主要看下权限处理,只写了路由守卫。
src\permission.js
router.beforeEach(async(to, from, next) => {
NProgress.start()
document.title = getPageTitle(to.meta.title)
// 确定用户是否已登录
if (getToken()) {
if (to.path === '/login') {
// 如果已登录,请重定向到主页
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
store.dispatch('user/getInfo').then((res) => {
const reData = {}
if (res.ROLE_ID.length > 0) {
reData.ROLE_ID = res.ROLE_ID
} else {
reData.ROLE_ID = []
}
// 这里去请求路由并处理
store.dispatch('GenerateRoutes', reData).then(accessRoutes => {
router.addRoutes([...accessRoutes, {
path: '*',
// redirect: '/404',
component: () => import('@/views/404'),
hidden: true
}])
next({ ...to, replace: true })
})
next()
})
.catch(error => {
store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
})
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// 在免费登录白名单中,直接进入
next()
} else {
// 没有访问权限的其他页将重定向到登录页。
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
再来看看路由处理的关键代码,这里罗列了两种方式。注释掉的是一种。没注释掉的是另一种。
src\store\modules\permission.js
// const _import = require('../../router/_import_' + process.env.NODE_ENV)
import { constantRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
const permission = {
state: {
routes: [],
addRoutes: []
},
mutations: {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
},
actions: {
// 生成路由
GenerateRoutes({ commit }, reData) {
return new Promise((resolve, reject) => {
// 向后端请求路由数据
getRouters(reData).then(res => {
// console.log(res)
if (res.Data === null || res.Data === '') {
res.Data = []
}
const accessedRoutes = filterAsyncRouter(res.Data)
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
}).catch(error => {
reject(error)
})
})
}
}
}
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
// Layout组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else {
// route.component = _import(route.component)
route.component = loadView(route.component)
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
}
return true
})
return accessedRouters
}
// 这种可以正常去处理,页面正常加载
const loadView = (view) => {
return (resolve) => require([`@/views${view}/`], resolve)
}
// 这种方式如果没有建立对应的文件会报错,并且页面加载不出来
// const loadView = (view) => { // 路由懒加载
// return () => import(`@/views/${view}`)
// }
export default permission
以上就是我对这歌后台系统的一些踩坑之旅。后面还会更新在开发中遇到的一些问题。