效果圖:
展開

展開

合
點:
首先我們的項目中需要安裝unplugin-vue-components/vite和unplugin-auto-import/vite,可以自動引用組件(我看另外一個開源項目中直接在頁面中引用、使用,在組件中就不需要引用了。。。?;仡^可以試一下子);或者將所有的組件的引用封裝在一起,可以查看 vue ---- 全局組件統(tǒng)一管理(這篇是用vue2寫的,用于vue3得稍作改動);
組件思路是:el-menu,里面是一會兒將要嵌套的小組件~小組件循環(huán)每個路由單項;
組件單項思路(將menu-item抽出來):看el-menu中從有沒有子項就分為el-sub-menu和el-menu-item兩種:el-sub-menu是右側(cè)有小箭頭,代表該項存在子菜單,可展開;el-menu-item沒有小箭頭,代表沒有子菜單;
在menu-item中進行判斷,以有無子項為準,選擇采用以上哪種子項;
渲染el-menu時需要設(shè)置defaultActive默認項,一般為首頁唯一值dashboard;但如果因為頁面刷新,需要實時監(jiān)聽當(dāng)前路由,再將唯一值賦值給defaultActive;
圖中菜單右側(cè)屬于el-menu的部分明顯有一道透明的邊邊。因為el-menu有一個1px透明的邊邊。。。。我們在組件中設(shè)置以下代碼即可;
-
動態(tài)路由的過濾方法全部放在store的permission模塊中;在每個頁面渲染之前需要先用守衛(wèi)攔一下子,一般在src下的permission.ts中完成:
- 需要給出一個完整的側(cè)邊路由和固定路由,方便獲取權(quán)限后進行過濾(pinia);
- 路由守衛(wèi)beforeEach中需要做出的操作內(nèi)容:
- 先判斷是否為登錄態(tài),不是直接去LoginPage,是就接下來判斷是否有角色分配;
- 有角色分配直接next放行,無角色分配就先獲取角色;
- 根據(jù)角色(代碼中的modulePermission)對完整路由進行過濾(分別對路由進行添加、給側(cè)邊菜單賦值);
思路就是這,下面是代碼
sidebar代碼:
<script lang="ts" setup>
import Router from "@/router";
import store from '@/store/index'
const {app, permission} = store();
const list = computed(() => {
return permission.setSideBarRoutes(permission.state.filterRoutes);
})
const defaultActive = computed(() => {
return app.state.activeRoute;
})
// 頁面方法
const toHome = () => {
Router.push({
path: '/'
});
}
</script>
<template>
<div class="sidebar-view">
<div class="logo-view" @click="toHome">
<img src="https://img1.baidu.com/it/u=3940612183,1877024013&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1682010000&t=426150bc8f1786722713152075ee49f3"
alt="" class="logo">
<div v-if="!app.state.isCollapse">嘻嘻嘻嘻嘻嘻嘻</div>
</div>
<div class="menu-view">
<Menu :list="list" :defaultActive="defaultActive" :collapse="app.state.isCollapse" />
</div>
</div>
</template>
<style lang="scss" scoped>
.sidebar-view {
height: 100%;
width: 100%;
background: $primary;
color: #fff;
}
.logo-view {
height: 50px;
display: flex;
justify-content: space-evenly;
align-items: center;
cursor: pointer;
.logo {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
}
.menu-view {
width: 100%;
height: calc(100% - 50px);
overflow-y: auto;
overflow-x: hidden;
}
</style>
menu組件代碼:
<template>
<el-menu class="el-menu-vertical-demo" :default-active="props.defaultActive" router :collapse="collapse"
:background-color="commonStyle.primary" text-color="#fff" active-text-color="#304156">
<menu-item v-for="item in list" :key="item.name" :menu="item" />
</el-menu>
</template>
<script setup lang="ts">
import type {RouteType} from '@/store/types'
import commonStyle from '@/assets/css/variables.module.scss'
interface PropsType {
list: RouteType[]
defaultActive?: string
collapse?: boolean
}
const props = withDefaults(defineProps<PropsType>(), {
defaultActive: '/dashboard'
})
</script>
<style lang="scss" scoped>
.el-menu {
border: none!important;
}
</style>
menuItem組件代碼:
<script setup lang="ts">
const props = defineProps(['menu'])
const menu = computed(() => {
return props.menu;
})
</script>
<template>
<el-sub-menu v-if="menu.children" :index="menu.fullPath">
<template #title>
<el-icon>
<component v-if="menu.icon" :is="menu.icon"></component>
</el-icon>
<span>{{ menu.meta.title }}</span>
</template>
<menu-item v-for="item in menu.children" :key="item.name" :menu="item" />
</el-sub-menu>
<el-menu-item v-else :index="menu.fullPath">
<el-icon>
<component v-if="menu.icon" :is="menu.icon" class="menu-icon"></component>
</el-icon>
<template #title>{{ menu.meta.title }}</template>
</el-menu-item>
</template>
<style lang="scss">
.el-menu-item.is-active {
font-weight: 700;
}
</style>
src下的permission.ts代碼:
import Router,{addRouter} from '@/router'
import store from '@/store/index'
Router.beforeEach(async (to, from, next) => {
const { permission, app } = store();
const whiteList = ['/login', '/dashboard'];
const toPath = to.path;
const isLogin = localStorage.getItem("NJX_TOKEN");
if (isLogin) {
// 權(quán)限判斷
const hasPermission = permission.state.modulePermission.length > 0;
if (hasPermission) {
next();
} else {
permission.clearPermission();
try {
// 獲取角色
await permission.getPermission();
app.setActiveRoute(to);
addRouter(permission.state.filterRoutes);
next({
...to,
replace: true
})
} catch {
Router.push('/login')
}
}
}
else {
const isWhite = whiteList.findIndex(item => toPath === item) !== -1;
if (!isWhite) {
Router.push('/login')
} else {
next()
}
}
})
Router.afterEach((to) => {
document.title = to.meta.title as string;
})
tada~~一個可以配合動態(tài)權(quán)限的菜單就完成啦~
明天加一個login頁的redirect哈