1.动态路由
1.因为我们左侧权限菜单是根据不同用户显示不一样的 所以我们需要使用动态路由 来动态生成右侧路由信息
在总体布局页面添加router
<router-view>
是 Vue Router 提供的组件,用于动态展示匹配到的路由组件内容。- 通过在合适的位置放置
<router-view>
,你可以根据路由路径动态地渲染对应的组件内容。
2.router里面需要添加children
因为我们是多级页面 之后动态路由也是多级的 如果这里不提前写children 之后动态生成的时候找不到Children
3.做动态路由是否生成判断 尽量减少生成次数
1.在store里面添加全局状态
4.permission.js
router.beforeEach((to,from,next)=>{
const whiteList=['/login'] // 白名单
let token=store.getters.GET_TOKEN;
let hasRoutes = store.state.hasRoutes;
let menuList = store.getters.GET_MENULIST
if(token){
if(!hasRoutes){
bindRoute(menuList);
store.commit("SER_ROUTES_STATE",true)
//注意这一行代码 在这一篇文章 详细解释了 如不过写会刷新白屏
next({ ...to, replace: true })
}else{
next();
}
}else{
if(whiteList.includes(to.path)){
next();
}else{
next("/login");
}
}
})
const bindRoute=(menuList)=>{
let newRoutes = router.options.routes;
menuList.forEach(menu=>{
if(menu.children){
menu.children.forEach(m=>{
let route = menuToRoute(m,menu.name)
if(route){
newRoutes[0].children.push(route);
}
})
}
})
//重新添加到路由
newRoutes.forEach(route=>{
router.addRoute(route)
})
}
const menuToRoute=(menu,parentName)=>{
if(!menu.component){
return null;
}else{
let route={
name:menu.name,
path:menu.path,
meta:{
parentName:parentName
}
}
route.component=()=>import('@/views/'+menu.component+'.vue')
return route
}
}
1.menuList是我们在登录时候得到的 树形结构 表结构
2.menuToRounte是动态生成路由 我们传入menu和父级名字(因为父级名字之后会用到)
生成route对象 包括 name path 和父级名字
component对应着目录结构
3.生成之后 然后把他们添加到路由里面
bindRoute
函数用于将菜单列表转换为路由配置,并添加到路由实例中。它遍历菜单列表,并根据每个菜单项生成对应的路由配置,然后将这些路由配置添加到newRoutes
数组中。最后,通过router.addRoute
方法将所有路由配置添加到路由实例中。
menuToRoute
函数用于将菜单项转换为路由配置。如果菜单项没有指定组件,则返回null
;否则,生成一个包含菜单名、路径和父菜单名的路由配置对象,并设置组件为异步加载形式。
2.动态标签页
1.store里面添加标签页数组 一个是当前标签页 一个是全部打开的
state: {
hasRoutes: false,
editableTabsValue: '/index',
editableTabs: [
{
title: '首页',
name: '/index'
}
]
},
ADD_TABS: (state, tab) => {
if (state.editableTabs.findIndex(e => e.name == tab.path) === -1) {
state.editableTabs.push({
title: tab.name,
name: tab.path
})
}
state.editableTabsValue = tab.path
},
RESET_TABS: (state) => {
state.editableTabsValue = '/index',
state.editableTabs = [
{
title: '首页',
name: '/index'
}
]
},
安全退出时候 调用RESET_TABS这个方法 清空
if(result.data.code===200){
store.commit("SER_ROUTES_STATE",false)
store.commit("RESET_TABS")
store.dispatch('logout')
}
在menuIndex里面添加 click点击事件
2.tabIndex具体实现
1.关联store里面数据
涉及到路由 使用useRouter
const router = useRouter();
const editableTabsValue = ref(store.state.editableTabsValue)
const editableTabs = ref(store.state.editableTabs)
因为我们关联的是store里面的数据 所以当我们store里面数据发生更改之后 页面并没有发生改变 所以我们需要对数据进行监听 当发生更改之后 重新赋值
<template>
<!-- 选中-->
<el-tabs
v-model="editableTabsValue"
type="card"
class="demo-tabs"
closable
@tab-remove="removeTab"
@tab-click="clickTab"
>
<!-- 所有tab-->
<el-tab-pane
v-for="item in editableTabs"
:key="item.name"
:label="item.title"
:name="item.name"
>
{{ item.content }}
</el-tab-pane>
</el-tabs>
</template>
<script setup>
import {ref, watch} from 'vue'
import store from '@/store'
import {useRouter} from "vue-router";
const router = useRouter();
const editableTabsValue = ref(store.state.editableTabsValue)
const editableTabs = ref(store.state.editableTabs)
const removeTab = (targetName) => {
const tabs = editableTabs.value
let activeName = editableTabsValue.value
// 这里是Path 使得无法删除首页
if(targetName==='/index'){
return
}
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
editableTabsValue.value = activeName
editableTabs.value = tabs.filter((tab) => tab.name !== targetName)
store.state.editableTabsValue = editableTabsValue.value
store.state.editableTabs=editableTabs.value
// 这个获取的是path
router.push({path:activeName})
}
const refreshTabs =()=>{
editableTabsValue.value=store.state.editableTabsValue;
editableTabs.value=store.state.editableTabs;
}
// 可以拿到标签名称 可以根据Name 也可以根据path
const clickTab =(target)=>{
router.push({name:target.props.label})
}
// 监听变化
watch(store.state,()=>{
refreshTabs();
},{deep:true,immediate:true})
// 深度监听
</script>
<style>
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
.el-main{
padding:0px;
}
.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{
background-color: lightgray;
}
.el-tabs{
height:45px
}
</style>
3.动态面包屑实现
面包屑
这里就使用到了我们前面提到的parentName
三个if
如果是父级名字 并且index大于一 显示并且加 / 系统管理/
如果是最后一个直接显示 角色管理
<template>
<el-icon><HomeFilled /></el-icon>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item,index) in breadcrumbList" :key="index">
<span class="root" v-if="parentName && index>0">{{parentName}} / </span>
<span class="leaf" v-if="index==breadcrumbList.length-1">{{item.name}}</span>
<span class="root" v-else>{{item.name}}</span>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup>
import { ref,watch } from 'vue'
import {HomeFilled} from '@element-plus/icons-vue'
import {useRoute} from 'vue-router'
import store from "@/store";
const route=useRoute();
const breadcrumbList=ref([]);
const parentName=ref("")
const initBreadcrumbList=()=>{
breadcrumbList.value=route.matched;
parentName.value=route.meta.parentName;
}
watch(route,()=>{
initBreadcrumbList();
},{deep:true,immediate:true})
</script>
<style lang="scss" scoped>
.leaf{
cursor:text;
}
.root{
color:#666;
font-weight:600;
}
</style>
4.路由与导航动态绑定实现
为了防止有人不点击 直接输入Url跳转 首先App.vue里面监听 然后添加标签页数据 此时标签页可以动态变化
import { ref ,watch} from 'vue'
import { useRoute,useRouter } from 'vue-router'
const route=useRoute();
const router=useRouter();
const whitePath=['/login','/index','/']
watch(route,(to,from)=>{
console.log("to"+to.name)
console.log(to.path)
if (whitePath.indexOf(to.path)===-1) {
console.log("to.path="+to.path)
let obj = {
name: to.name,
path: to.path
}
store.commit("ADD_TABS", obj)
}
},{deep:true,immediate:true})
修改menu使得菜单栏也可以动态变化