能自动生成不需要配置的路由是什么
发布网友
发布时间:2022-04-23 07:26
我来回答
共1个回答
热心网友
时间:2022-05-14 17:24
vue 根据菜单自动生成路由(动态配置前端路由)
3.到main.js中
4.先把菜单组件写好,到menu.vue中
5.注册全局组件
6.到router文件夹写好路由模块
7.容器页和加载页
8.写筛选菜单和路由的方法
9. 登录成功后生成路由
6.1 base-router.js中写好我们需要的固定的路由
6.2 lm-router.js中写动态配置路由的方法
6.3 index.js中写路由入口
7.1 layout.vue
7.2 loading.vue
1.创建项目
2.新建文件
在需要权限控制的页面,往往存在根据用户来显示菜单的情况,单独根据用户类型判断显然不是很好,如果后面用户类型发生变化,项目修改维护可能就会比较麻烦,所以比较好的做法是根据后端返回的菜单动态生成页面路由,以达到完全权限控制的目的,并且若权限发生变化,仅需该配置数据即可1.创建项目
首先用vue-cli3创建好项目
2.新建文件
创建好项目后,新建我们需要的文件。结构如图
下载相关依赖包 :element-ui(菜单样式用) 和 axios(获取菜单用)
请点击输入图片描述
npm i element-ui axios --save1
3.到main.js中
import Vue from 'vue'import App from './App.vue'import router from './router'import elementUi from 'element-ui'import 'element-ui/lib/theme-chalk/index.css'Vue.use(elementUi)Vue.config.proctionTip = falsenew Vue({
router,
render: h => h(App)}).$mount('#app')12345671011121314
4.先把菜单组件写好,到menu.vue中
这里使用element-ui的el-menu组件
<template>
<div class="menulist-inner">
<el-menu default-active="/project"
:default-openeds="openedMenu"
background-color="rgba(44,55,71,1)"
text-color="#A7BAC6"
active-text-color="#FFFFFF"
@select="select"
unique-opened
@open="open">
<el-submenu :index="menu.path" v-for="(menu,index) in menus" :key="index">
<template #title>
<div class="menuTitlBox">
<div class="rowBtween menuTitle">
<div class="rowStart">
<div class="iconBox" style="margin-right:10px;">
<i v-if="/icon/.test(menu.icon)" class="iconfont" :class="menu.icon" style="
color:#A7BAC6;font-size:16px;"></i>
<img v-else :src="menu.icon" width="18"/>
</div>
<span class="font16" :class="{whiteText:(!menu.children || !menu.children.length) && new RegExp(menu.path).test($route.path)}">{{menu.label}}</span>
</div>
</div>
<div class="firstMenuBk" v-if="(!menu.children || !menu.children.length) && new RegExp(menu.path).test($route.path)"></div>
</div>
</template>
<div class="menuItemBox rowEnd"
v-for="(cMenu,cIndex) in menu.children"
:key="cIndex"
:class="{activeMenu:RegExp(cMenu.path).test($route.path)}"
@click="select(cMenu)"
>
<span class="rowStart font14">{{cMenu.label}}</span>
</div>
</el-submenu>
</el-menu>
</div></template><script>
import {mapState}from 'vuex'
export default {
name: "Menu",
data() {
return{
menuMinHeight:0,//
openedMenu:[],//展开的菜单
}
},
computed:{
...mapState(['menus'])
},
created(){
let pathArr=this.$route.path.split('/')
this.openedMenu=[`/${pathArr[1]}`]
},
async mounted() {
},
methods: {
//中菜单
select(cMenu){
// console.log(cMenu)
let {routeName}=cMenu this.$router.push({
name:routeName })
},
//菜单展开
open(index,indexPath){
// console.log(indexPath)
this.openedMenu=indexPath let menu=this.menus.filter(item=>item.path===index)
// console.log(menu)
let routeMenuInfo={}
let time=0
if(!menu[0] || !menu[0].children || !menu[0].children.length){
routeMenuInfo=menu[0]
}else{
routeMenuInfo=menu[0].children[0]
time=500
}
let {path,}=routeMenuInfo // console.log(path)
setTimeout(()=>{
this.$router.push(path)
},time)
},
}
}</script><style lang="scss" scoped>
.menulist-inner{
min-height:calc(100vh - 50px);
.menuItemBox{
height:36px;
&:hover{
background:rgba(62,70,120,.4);
&>span{
color: #fff;
}
}
span{
color:#ffffff;
flex-wrap:nowrap;
width:150px;
cursor:pointer;
}
}
.activeMenu{
background:rgba(62,70,120,1);
}
}
.menuTitlBox{
.menuTitle{
position: relative;
z-index: 10;
}
.firstMenuBk{
position: absolute;
left:0;
top:50%;
width:200px;
height:56px;
background:#3e4678;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-o-transform: translateY(-50%);
transform: translateY(-50%);
}
.whiteText{
color:#ffffff;
}
}</style><style>
.el-submenu{
position: relative;
}
.el-menu{
border:none !important;
}
.el-submenu__icon-arrow{
width:0;
height:0;
color:transparent;
}
.el-submenu__icon-arrow:before{
content:'' !important;
}
.el-submenu__icon-arrow:before{
content:'' !important;
}
.el-submenu__title{
background:rgba(44,55,71,1) !important;
margin-bottom:5px;
height:46px !important;
line-height: 46px !important;
}
.el-menu.el-menu--inline{
background-color: rgba(0,0,0,.2) !important;
}
.menulist-inner .menuItemBox.rowEnd span{
color: #A7BAC6;
}
.menulist-inner .menuItemBox.rowEnd.activeMenu span{
color: #fff;
}</style>123456710111213141516171819202122232425262728293031323334353637383940414243444547484950515253545556575859606162636566676869707172737475767778798081828384858687889091929394959697991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631165166167168169170171172173174175176177
5.注册全局组件
将menu组件注册为全局组件,方便需要的地方直接引入
到global-components.js文件中
/*
全局组件引入注册
*/import Menu from "../global-components/menu" //菜单export default {
install(Vue) {
Vue.component('Menu',Menu)
}};1234567101112
6.到router文件夹写好路由模块
6.1 base-router.js中写好我们需要的固定的路由
/**
* 页面上固定路由
*/import Layout from '../views/layout/layout'import Login from '../views/login/login'import Loading from '../views/loading/loading'const routes = [
{
path: '/',
redirect: '/loading',
},
{
path: '/layout',
component:Layout,
redirect: '/loading',
},
{
path: '/login',
name: 'Login',
component: Login },
{
path: '/loading',
name: 'Loading',
component: Loading },]export default routes12345671011121314151617181920212223242526272829
6.2 lm-router.js中写动态配置路由的方法
import {setUserRoutesData} from "../utils/global-methods";const RouterPlugin = function() {
this.$router = null
this.$store = null}RouterPlugin.install = function(router, store) {
this.$router = router this.$store = store this.$router.$lmRouter = {
// 全局配置
safe: this,
// 动态路由
formatRoutes:function (routes) {
let routers=setUserRoutesData(routes)
this.safe.$router.addRoutes(routers)
return routers }
}}export default RouterPlugin12345671011121314151617181920212223
6.3 index.js中写路由入口
import Vue from 'vue'import VueRouter from 'vue-router'import baseRoutes from './base-router'import store from '../store'import LmRouter from './lm-router'Vue.use(VueRouter)const createRouter = () => {
return new VueRouter({
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition } else {
return {
x: 0,
y: to.meta.savedPosition || 0
}
}
},
routes: [...baseRoutes],
mode:'history',//
})}let router = createRouter()/**
resetRouter函数用于重置路由,每一次动态配置路由之前要先重置路由
**/export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
LmRouter.install(router, store)}/**
当用户刷新页面时,路由数据会丢失,如果已经登录,需要重新渲染路由。渲染的路由数据在登录时存在浏览器本地
**/if(sessionStorage.getItem('hasLogin')){
let sessionMenus=localStorage.getItem('menus')
sessionMenus=sessionMenus ? JSON.parse(sessionMenus) : []
let sessionRoutes=localStorage.getItem('userRoutes')
LmRouter.install(router, store)
sessionRoutes=sessionRoutes ? JSON.parse(sessionRoutes) : []
router.$lmRouter.formatRoutes(sessionRoutes, true)
store.dispatch('setUserRoutes',sessionRoutes)
store.dispatch('setMenus',sessionMenus)}router.beforeEach((to, from, next) => {
let hasLogin = sessionStorage.getItem('hasLogin')
// console.log(to, from)
if(to.name===from.name){
return
}
console.log(hasLogin)
if(to.name==='Login'){
sessionStorage.clear()
localStorage.clear()
store.state.userRoutes=[]
store.state.menus=[]
next({replace:true})
resetRouter()
return
}
if (!hasLogin) {
next({path: '/login',replace:true})
return
}
next()})/**
* 解决element-ui点击同一个菜单报错
* @type {VueRouter.push|*}
*/const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)}export default router123456710111213141516171819202122232425262728293031323334353637383940414243444547484950515253545556575859606162636566676869707172737475767778798081
7.容器页和加载页
大部分有二级菜单的页面,父级页面通常只需要路由跳转功能,代码一致,为了精简代码,我们新建通用的路由容器页面layout.vue,二级菜单路由页面经layout页面跳转
为避免用户在浏览器直接输入、粘贴路径时,或者直接访问页面根路径时找不到路由(因为用户可能已经登录了,但这是没有已经登录界面),我们新建loading.vue,当用户粘贴路径,或者访问根路径时,在loading.vue中渲染路由
7.1 layout.vue
<!--Layout--><template>
<div>
<header class="header">头部</header>
<div class="main">
<div class="aside">
<Menu/>
</div>
<div class="mainRight columnStart">
<router-view></router-view>
</div>
</div>
</div></template><script>
export default {
name: 'Layout',
}</script>1234567101112131415161718192021
7.2 loading.vue
<!--加载页面--><template>
<div class="loadingBox">
<div class="loadingContentbox columnCenter">
<i class="el-icon-loading gray999"></i>
<div class="gray999 font16">页面加载中...</div>
</div>
</div></template><script>
import {reqUserRoutes} from "../../api/common";
import {setUserRoutesData,getAndFilterMenus,} from "../../utils/global-methods";
export default {
name: 'Loading',
data() {
return {}
},
computed: {},
created(){
let hasLogin = sessionStorage.getItem('hasLogin')
if(!hasLogin){
this.$router.replace('/login')
}else{
let menus=localStorage.getItem('menus')
let userRoutes=localStorage.getItem('userRoutes')
userRoutes=userRoutes ? JSON.parse(userRoutes) : []
menus=menus ? JSON.parse(menus) : []
if(!menus.length || !userRoutes.length){
this.getMenuRoutes()
return
}
let userInfo=getUserInfoFromLocalStorage()
this.$store.dispatch('setUserInfo',userInfo)
let {permissions=[]}=userInfo this.$store.dispatch('setPermissions',permissions)
this.$router.replace((userRoutes[0] && userRoutes[0].path) ? userRoutes[0].path : '/404')
}
},
methods: {
//获取菜单和路由
async getMenuRoutes(){
let userRoutes=await reqUserRoutes()
console.log(userRoutes)
let menus=getAndFilterMenus(JSON.parse(JSON.stringify(userRoutes)))
if(!menus.length){
this.$router.replace('/login')
return
}
localStorage.setItem('userRoutes',JSON.stringify(userRoutes))
localStorage.setItem('menus',JSON.stringify(menus))
userRoutes=setUserRoutesData([...userRoutes])
this.$store.dispatch('setUserRoutes',userRoutes)
this.$store.dispatch('setMenus',menus)
this.$router.addRoutes([...userRoutes])
this.$router.replace((userRoutes[0] && userRoutes[0].path) ? userRoutes[0].path : '/404')
}
},
}</script><style scoped lang="scss">
.loadingBox{
.loadingContentbox{
position: absolute;
left:50%;
top:50%;
-webkit-transform: translate(-50%,-50%);
-moz-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
-o-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
.el-icon-loading{
font-size: 40px;
margin-bottom:10px;
}
}
}</style>123456710111213141516171819202122232425262728293031323334353637383940414243444547484950515253545556575859606162636566676869707172737475767778798081828384
8.写筛选菜单和路由的方法
到utils,global-methods.js中写菜单筛选和路由数据方法
首先我们假设后端返回的数据为如下结构(仅列出关键字段)
[
{
label:'人员管理',
path:'/person',
isLeftMenu:1,
routeName:'Person',
isContainer:1,
children:[
{
label:'人员列表',
path:'/person/person-list',
routeName:'PersonList',
isLeftMenu:1,
component:'/person/person-list',
},
{
label:'新增人员',
path:'/person/person-add',
routeName:'PersonAdd',
component:'/person/person-add',
isLeftMenu:0
}
]
},
{
label:'订单管理',
path:'/order',
isLeftMenu:1,
routeName:'Order',
component:'/order/order',
isContainer:0,
}]1234567101112131415161718192021222324252627282930313233
其中 label 为菜单显示标题,path 是路由路径,isLeftMenu用于区分是否为菜单,这里isLeftMenu=1表示是菜单,routeName路由名,isContainer用于区分路由是否经过容器组件layout,这里isContainer=1表示使用layout容器,component用于指定组件引入的路径
根据上面的数据结构,我们新建好对应得vue文件
结构如下图:
请点击输入图片描述
// 公共函数模块,用import引用// 根据日期时间值获取字符串各是日期import Layout from '../views/layout/layout'//获取并筛选菜单export const getAndFilterMenus=(menus)=> {
// console.log(menus)
menus=hanldeChildAppRoute(menus)
for(let i=0;i<menus.length;i++){
delete menus[i].component //只有leLeftMeu=1的是菜单
if(!parseInt(menus[i].isLeftMenu)){
menus.splice(i,1)
i--
}
if(menus[i] && menus[i].children){
getAndFilterMenus(menus[i].children)
}
}