后台项目总绕不开权限验证,也总绕不开动态路由
动态路由:
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
tab,
},
})
store/tab.js
import Cookie from 'js-cookie'
export default {
state: {
// 全部菜单(左侧导航)
menu: [],
},
mutations: {
// 保存菜单项--保存在vuex和cookie中
setMenu(state, val) {
state.menu = val
// cookie中只能存字符串, 所以需要转换
Cookie.set('menu', JSON.stringify(val))
console.log(val, 'vuex')
},
// 清除菜单项
clearMenu(state) {
state.menu = []
Cookie.remove('menu')
},
// 动态添加菜单---既然需要添加路由,当然需要把路由对象(没错,就是全局的$router)传进来
addMenu(state, router) {
// 如果menu为空
if (!Cookie.get('menu')) {
// 路由为空,直接返回
return
}
// 从cookie中获取menu---记得反序列化
let menu = JSON.parse(Cookie.get('menu'))
// 将从cookie里取到的menu存到vuex(避免刷新时vuex里数据丢失)
state.menu = menu
// 存放路由的数组
let currentMenu = [
{
path: '/',
component: () => import(`@/views/Main`),
children: [],
},
]
// 循环menu,对其中的数据进行过滤后存入currentMenu的children中
menu.forEach((item) => {
// 如果当前项有子路由
if (item.children) {
item.children = item.children.map((item) => {
// 懒加载import的方式务必按照如下格式进行, item.url的值是这样的 'Home/Home' ,这是一个字符串, 我们在本地进行路径的拼接.
item.component = () => import(`@/views/${item.url}`)
return item
})
// 将当前项压入数组 (运用了展开运算符)
currentMenu[0].children.push(...item.children)
} else {
item.component = () => import(`@/views/${item.url}`)
currentMenu[0].children.push(item)
}
})
console.log(currentMenu, 'cur')
// 添加路由
router.addRoutes(currentMenu)
// 如果你的菜单列表是基于router.options.routes来渲染的(比如vue-element-admin就是这么做的) , 那你就需要加上这样一句---来重设routes
// router.options.routes = [...router.options.routes,...currentMenu]
},
},
actions: {},
}
views/Login/index.js
login() {
this.$http.post('api/permission/getMenu', this.form).then((res) => {
res = res.data
console.log(res)
if (res.code === 20000) {
// 先执行一次清空菜单, 防止用户二次登陆
this.$store.commit('clearMenu')
// 将后端返回的菜单项存进vuex
this.$store.commit('setMenu', res.data.menu)
// 登录成功后存储token
this.$store.commit('setToken', res.data.token)
// 在登录时触发路由添加事件
this.$store.commit('addMenu', this.$router)
// 跳转到首页
this.$router.push({ name: 'home' })
} else {
// 使用elementUI的全局提示对象
this.$message.warning(res.data.message)
}
})
},
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.prototype.$http = http
new Vue({
router,
store,
render: (h) => h(App),
created() {
// 在刷新时(即vue初始化时), 触发添加路由事件
store.commit('addMenu', router)
},
}).$mount('#app')
登录Mock接口定义:
import Mock from 'mockjs'
export default {
getMenu: (config) => {
const { username, password } = JSON.parse(config.body)
console.log(JSON.parse(config.body))
// 先判断用户是否存在
if (username === 'admin' || username === 'wp') {
// 判断账号和密码是否对应
if (username === 'admin' && password === '123456') {
return {
code: 20000,
data: {
menu: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home',
},
{
path: '/video',
name: 'video',
label: '视频管理页',
icon: 'video-play',
url: 'VideoManage/VideoManage',
},
{
path: '/user',
name: 'user',
label: '用户管理页',
icon: 'user',
url: 'UserManage/UserManage',
},
{
label: '其他',
icon: 'location',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'Other/PageOne',
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'Other/PageTwo',
},
],
},
],
// 在返回menu的同时返回一个token(利用mock生成随机id来模拟--guid:生成随机字符串)
token: Mock.Random.guid(),
message: '获取成功',
},
}
} else if (username === 'wp' && password === '123456') {
return {
code: 20000,
data: {
menu: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home',
},
{
path: '/video',
name: 'video',
label: '视频管理页',
icon: 'video-play',
url: 'VideoManage/VideoManage',
},
],
// 在返回menu的同时返回一个token(利用mock生成随机id来模拟--guid:生成随机字符串)
token: Mock.Random.guid(),
message: '获取成功',
},
}
} else {
return {
code: -999,
data: {
message: '密码错误',
},
}
}
} else {
return {
code: -999,
data: {
message: '用户不存在',
},
}
}
},
}