文章目录
- 使用场景
-
一、动态菜单
- 1.创建菜单组件
-
二、动态面包屑
- 1.创建面包屑组件
-
三、动态标签页
- 1.创建标签页组件
-
四、动态路由
- 1.router代码
- 2.store代码
- 总结
使用场景
主要使用Vue3与Element UI,在项目开发中可能会遇到从后端取得数据,到前端去渲染菜单,从而实现动态路由与动态菜单。
一、动态菜单
1.创建动态菜单组件
代码如下:
<template>
<div>
<el-menu :unique-opened="true" :default-openeds="openList"
:default-active="'/index/' + this.$store.state.TabseditableTabsValue" :collapse-transition="true" router
class="el-menu-vertical-demo" style="width: 200px;min-height: calc(100vh );">
<el-sub-menu v-for="(menu, i) in menus" :index="i + 1" :key="i">
<template #title>
<el-icon>
<Avatar />
</el-icon>
<span>{{ menu.authname }}</span>
</template>
<el-menu-item-group>
<el-menu-item @click="getpath(item.path, item.authname, item.name, menu.authname ,menu.path)"
:index="'/index/' + item.path" v-for="(item, index) in menu.children" :key="index">
<el-icon>
<ArrowRight />
</el-icon>{{ item.authname }}
</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
</div>
</template>
<style>
.el-menu-item-group__title {
display: none;
}
</style>
<script>
import { ref } from 'vue'
import Tabs from '@/components/Tabs.vue';
export default {
name: 'Aside',
created() {
this.getrouter();
},
data() {
return {
openList: ['1'],
menus: '',
}
},
components: {
Tabs
},
methods: {
//登录界面请求网络已经拿到数据存入vuex,取出数据渲染使用;
getrouter() {
this.menus=this.$store.state.Routers
},
//点击子菜单触发事件
getpath(path, authname, name, Menu_title ,Menu_path) {
//将面包屑的数据传入vuex,方便面包屑数据变换
this.$store.state.Breadcrumb_title = Menu_title;
this.$store.state.Breadcrumb.title = authname;
this.$store.state.Breadcrumb.path = path;
this.$store.state.Breadcrumb.name = name;
let tabs = this.$store.state.Tabs;
const index = tabs.some(item => {
if (item.title === authname) {
return true;
} else {
return false;
}
})
if (index === false) {
//添加Tab标签页(将数据放入vuex存起来)
let tab = {
title: authname,
name: name,
path: path,
};
this.$store.state.Tabs.push(tab);
this.$store.state.TabseditableTabsValue = name;
} else {
this.$store.state.TabseditableTabsValue = name;
}
this.$router.replace({path: "/index/"+path})
},
},
}
</script>
二、动态面包屑
1.创建面包屑组件
代码如下:
<style>
.el-breadcrumb__item{
cursor: pointer;
}
.el-breadcrumb__item .el-breadcrumb__inner{
cursor: pointer;
}
</style>
<template>
<el-breadcrumb style="transform: translate(-12%,-50%);margin-left: 10.7%;padding: 10px;">
<el-breadcrumb-item>首页</el-breadcrumb-item>
<el-breadcrumb-item >{{this.$store.state.Breadcrumb_title}}</el-breadcrumb-item>
<el-breadcrumb-item replace :to="{path:Breadcrumb.path}" >{{this.$store.state.Breadcrumb.title}}</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script>
export default {
name: 'Breadcrumb',
created(){
},
components: {
},
data() {
return {
Breadcrumb:this.$store.state.Breadcrumb,//从vuex中拿到面包屑数据并渲染
}
},
methods:{
},
}
</script>
三、动态标签页
1.创建标签页组件
代码如下:
<template>
<el-tabs v-model="this.$store.state.TabseditableTabsValue" type="card" @tab-click="tabClick" closable
@tab-remove="removeTab">
<el-tab-pane v-for="(item, index) in editableTabs" :key="index" :label="item.title" :name="item.name"></el-tab-pane>
</el-tabs>
</template>
<script>
import store from '@/store/index.js';
import router from '@/router/index.js';
export default {
name: 'Tabs',
data() {
return {
editableTabs: store.state.Tabs,//从vuex获取Tabs的数据。
tabIndex: 2
}
},
methods: {
//标签页被点击,则换面包屑数据。
tabClick(tab,event) {
store.state.Breadcrumb.title=tab.props.label;
store.state.Breadcrumb.path='/index/'+tab.props.name;
store.state.Breadcrumb.name=tab.props.name;
router.replace({ path:'/index/'+tab.props.name })
},
//删除标签页,则选中前一个标签页,并激活其对应的路由页面。
removeTab(targetName) {
if (targetName == 'Home') {
return
}
store.commit('delTabRouter', targetName)//在vuex找到方法delTabRouter,删除标签。
if (store.state.TabseditableTabsValue == targetName) {
// 设置当前激活的路由
if (this.editableTabs && this.editableTabs.length >= 1) {
store.state.Breadcrumb.title=this.editableTabs[this.editableTabs.length - 1].title;
store.state.Breadcrumb.path='/index/'+this.editableTabs[this.editableTabs.length - 1].name;
store.state.Breadcrumb.name=this.editableTabs[this.editableTabs.length - 1].name;
store.commit('setActiveIndex', this.editableTabs[this.editableTabs.length - 1].name)
router.replace({ path: this.editableTabs[this.editableTabs.length - 1].path })
} else {
router.replace({ path: '/index/Home' })
store.state.TabseditableTabsValue = 'Home';
store.commit('setTabRouter', { path: '/Home', name: 'Home', title: '员工信息表' })
}
}
},
},
}
</script>
四、动态路由
1.router
index.js代码如下:
import { createRouter, createWebHistory } from 'vue-router'
import store from '@/store';
const routes = [//初始静态路由
{
path: '/',
name: 'login',
component: () => import('@/views/Login.vue'),
},
{
path: '/index',
name: 'Homepage',
component: () => import('@/views/Homepage.vue'),
redirect: '/index/Home',
children: [{
path: 'Home',
name: 'Home',
component: () => import('@/views/Home.vue'),
}]
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
var tag = true;//设置全局变量
//路由拦截
router.beforeEach(async (to, form, next) => {
//第一次登录时,全局变量为true执行,第二次刷新,再次执行
if (tag) {
tag = false;
if (to.name === 'login') {
next()
} else {
let Routers = store.state.Routers;
addRoutes(Routers);
next(to.path);
}
} else {
next()
}
})
//添加路由的方法
export function addRoutes(res) {
res.forEach((Router, index) => {
router.addRoute({
path: Router.path,
name: Router.name,
component: () => import('@/views/' + Router.name + '.vue'),
})
let childrens = Router.children;
if (childrens != undefined) {
for (let i = 0; i < childrens.length; i++) {
if (childrens[i] != undefined) {
router.addRoute('Homepage', {
path: childrens[i].path,
name: childrens[i].name,
component: () => import('@/views/' + childrens[i].name + '.vue')
})
}
}
}
})
}
export default router
2.store
index.js代码如下:
import { createStore } from 'vuex';
import router from '@/router';
export default createStore({
state: {
Routers: '',//登录获取的全部路由
setAsyncRoutestMark: 'false',
TabseditableTabsValue: 'Home',//当前选中Tab的名字
Breadcrumb_title: '员工管理',//菜单栏标签
//全部标签
Tabs: [
{
title: '员工信息表',
name: 'Home',
path: '/index/Home',
}
],
//选中的标签
Breadcrumb: {
title: '员工信息表',
name: 'Home',
path: '/index/Home'
}
},
getters: {
getTabs: state => {
return state.Tabs;
},
getRouters: state => {
return state.Routers;
}
},
mutations: {
//添加tabs路由
setTabRouter(state, data) {
state.Tabs.push(data)
},
// 删除tabs路由
delTabRouter(state, data) {
let index = 0;
for (let option of state.Tabs) {
if (option.path == data) {
break
}
index++
}
state.Tabs.splice(index, 1);
// 删完路由后也要保存到session中
// sessionStorage.setItem('Tabs', JSON.stringify(state.Tabs))
},
// 设置当前激活的tabs
setActiveIndex(state, index) {
state.TabseditableTabsValue = index
state.Breadcrumb.name = index;
},
},
actions: {
},
modules: {
}
})
总结思路
1. 路由守卫判定当初次登录时,后端返回路由数据,处理完数据放进vuex存起来。
2.(1)菜单组件去获取vuex的路由数据,进行处理形成自己需要的菜单数据并进行渲染。
(2)标签页组件去vuex获取Tabs数据,进行渲染。
(3) 面包屑直接从vuex里获取数据,进行渲染。
3. 操作(1)当子菜单栏被点击时,在vuex(即store)里面添加Tab数据,同时标签页、面包屑实时渲染。
操作(2)当标签页被删除时,在vuex里删除其数据,同时选中上一次点击的子菜单栏和标签页,面包屑也跟着变化
操作(3) 切换标签,动态菜单跟着切换选中
4. 当路由守卫判定不为初次登录时,则被认为是在刷新,则从sessionStorage缓存中找到数据赋值给store,重新渲染路由,使页面不为空白