聊聊element-ui 側(cè)邊欄的router問題
element-ui 側(cè)邊欄的router

所以就動態(tài)獲取api里面的path值作為其router就行。注意帶上上級路由。用字符串拼接的方式::index="'/home/'
對二級導(dǎo)航欄,這里的item是 v-for循環(huán)上級的item.children(一級導(dǎo)航欄)的項。
<el-menu-item :index="'/home/' + item.path"></el-menu-item>
element-ui中側(cè)邊欄的分析及實(shí)現(xiàn)
el-menu 和 el-submenu
1.如果需要實(shí)現(xiàn)一個側(cè)邊欄,會如何設(shè)計?
2.側(cè)邊欄的核心是將根據(jù)權(quán)限過濾后的 router 和 el-menu 組件進(jìn)行映射,所以 el-menu 和 el-submenu 是理解 sidebar 的基礎(chǔ)。
3.el-menu 表示菜單容器組件,如下所示:
default-active:當(dāng)前激活菜單的 index,注意如果存在子菜單,需要填入子菜單 IDunique-opened是否只保持一個子菜單的展開mode模式,枚舉值,horizontal / vertical 這兩種collapse是否水平折疊收起菜單(僅在 mode 為 vertical 時可用)collapse-transition是否開啟折疊動畫@select點(diǎn)擊菜單事件,菜單激活回調(diào) index: 選中菜單項的 index, indexPath: 選中菜單項的 index path@open: sub-menu 展開的回調(diào)@close: sub-menu 收起的回調(diào)
4.el-submenu,子菜單容器,el-menu 表示整個菜單, el-submenu 表示一個具體菜單,只是該菜單還包括了子菜單。 el-submenu 可以通過定制 slot 的 title 來自定義菜單模式。el-submenu 容器內(nèi) default 的 slot 用來存放子菜單,可以包括三種子菜單組件,如下所示:
el-menu-item-group:菜單分組,為一組菜單添加一個標(biāo)題,容器內(nèi)需要存放 el-menu-item,支持通過 title 的 slot 來定制標(biāo)題樣式el-submenu:支持循環(huán)嵌套 el-submenu,可以使得超過兩級子組件得以實(shí)現(xiàn)el-menu-item:子菜單組件
示例代碼,如下所示:
<el-row class="tac"> ? <el-col :span="12"> ? ? <h5>默認(rèn)顏色</h5> ? ? <el-menu ? ? ? default-active="2" ? ? ? class="el-menu-vertical-demo" ? ? ? @open="handleOpen" ? ? ? @close="handleClose"> ? ? ? <el-submenu index="1"> ? ? ? ? <template slot="title"> ? ? ? ? ? <i class="el-icon-location"></i> ? ? ? ? ? <span>導(dǎo)航一</span> ? ? ? ? </template> ? ? ? ? <el-menu-item-group> ? ? ? ? ? <template slot="title">分組一</template> ? ? ? ? ? <el-menu-item index="1-1">選項1</el-menu-item> ? ? ? ? ? <el-menu-item index="1-2">選項2</el-menu-item> ? ? ? ? </el-menu-item-group> ? ? ? ? <el-menu-item-group title="分組2"> ? ? ? ? ? <el-menu-item index="1-3">選項3</el-menu-item> ? ? ? ? </el-menu-item-group> ? ? ? ? <el-submenu index="1-4"> ? ? ? ? ? <template slot="title">選項4</template> ? ? ? ? ? <el-menu-item index="1-4-1">選項1</el-menu-item> ? ? ? ? </el-submenu> ? ? ? </el-submenu> ? ? ? <el-menu-item index="2"> ? ? ? ? <i class="el-icon-menu"></i> ? ? ? ? <span slot="title">導(dǎo)航二</span> ? ? ? </el-menu-item> ? ? ? <el-menu-item index="3" disabled> ? ? ? ? <i class="el-icon-document"></i> ? ? ? ? <span slot="title">導(dǎo)航三</span> ? ? ? </el-menu-item> ? ? ? <el-menu-item index="4"> ? ? ? ? <i class="el-icon-setting"></i> ? ? ? ? <span slot="title">導(dǎo)航四</span> ? ? ? </el-menu-item> ? ? </el-menu> ? </el-col> ? <el-col :span="12"> ? ? <h5>自定義顏色</h5> ? ? <el-menu ? ? ? default-active="2" ? ? ? class="el-menu-vertical-demo" ? ? ? @open="handleOpen" ? ? ? @close="handleClose" ? ? ? background-color="#545c64" ? ? ? text-color="#fff" ? ? ? active-text-color="#ffd04b"> ? ? ? <el-submenu index="1"> ? ? ? ? <template slot="title"> ? ? ? ? ? <i class="el-icon-location"></i> ? ? ? ? ? <span>導(dǎo)航一</span> ? ? ? ? </template> ? ? ? ? <el-menu-item-group> ? ? ? ? ? <template slot="title">分組一</template> ? ? ? ? ? <el-menu-item index="1-1">選項1</el-menu-item> ? ? ? ? ? <el-menu-item index="1-2">選項2</el-menu-item> ? ? ? ? </el-menu-item-group> ? ? ? ? <el-menu-item-group title="分組2"> ? ? ? ? ? <el-menu-item index="1-3">選項3</el-menu-item> ? ? ? ? </el-menu-item-group> ? ? ? ? <el-submenu index="1-4"> ? ? ? ? ? <template slot="title">選項4</template> ? ? ? ? ? <el-menu-item index="1-4-1">選項1</el-menu-item> ? ? ? ? </el-submenu> ? ? ? </el-submenu> ? ? ? <el-menu-item index="2"> ? ? ? ? <i class="el-icon-menu"></i> ? ? ? ? <span slot="title">導(dǎo)航二</span> ? ? ? </el-menu-item> ? ? ? <el-menu-item index="3" disabled> ? ? ? ? <i class="el-icon-document"></i> ? ? ? ? <span slot="title">導(dǎo)航三</span> ? ? ? </el-menu-item> ? ? ? <el-menu-item index="4"> ? ? ? ? <i class="el-icon-setting"></i> ? ? ? ? <span slot="title">導(dǎo)航四</span> ? ? ? </el-menu-item> ? ? </el-menu> ? </el-col> </el-row>
<script>
? export default {
? ? methods: {
? ? ? handleOpen(key, keyPath) {
? ? ? ? console.log(key, keyPath);
? ? ? },
? ? ? handleClose(key, keyPath) {
? ? ? ? console.log(key, keyPath);
? ? ? }
? ? }
? }
</script>sidebar 分析
1.sidebar,如下所示:
activeMenu:通過 meta.activeMenu 屬性,指定路由對應(yīng)的高亮菜單,meta.activeMenu 需要提供一個合法的路由,否則不能生效。isCollapse:NavBar 中點(diǎn)擊按鈕,會修改 Cookie 中的 sidebarStatus,從 vuex 取值時會將 sidebarStatus 轉(zhuǎn)為 Boolean,并判斷默認(rèn)是否需要收縮左側(cè)菜單欄showLogo:判斷 settings.js 中的配置項是否需要展示 logovariables:從 @style/variables.css 中獲取 scss 對象,從而獲取樣式
2.sidebar,代碼實(shí)現(xiàn)如下:
<template>
? <div :class="{'has-logo':showLogo}">
? ? <logo v-if="showLogo" :collapse="isCollapse" />
? ? <el-scrollbar wrap-class="scrollbar-wrapper">
? ? ? <el-menu
? ? ? ? :default-active="activeMenu"
? ? ? ? :collapse="isCollapse"
? ? ? ? :background-color="variables.menuBg"
? ? ? ? :text-color="variables.menuText"
? ? ? ? :unique-opened="false"
? ? ? ? :active-text-color="variables.menuActiveText"
? ? ? ? :collapse-transition="false"
? ? ? ? mode="vertical"
? ? ? >
? ? ? ? <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
? ? ? </el-menu>
? ? </el-scrollbar>
? </div>
</template><script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
export default {
? components: { SidebarItem, Logo },
? computed: {
? ? ...mapGetters([
? ? ? 'permission_routes',
? ? ? 'sidebar'
? ? ]),
? ? activeMenu() {
? ? ? const route = this.$route
? ? ? const { meta, path } = route
? ? ? if (meta.activeMenu) {
? ? ? ? return meta.activeMenu
? ? ? }
? ? ? return path
? ? },
? ? showLogo() {
? ? ? return this.$store.state.settings.sidebarLogo
? ? },
? ? variables() {
? ? ? return variables
? ? },
? ? isCollapse() {
? ? ? return !this.sidebar.opened
? ? }
? }
}
</script>3.sidebar 中通過 sidebar -item 實(shí)現(xiàn)子菜單,sidebar-item 的 props 是 item 為路由對象,basePath 是路由路徑。sidebar-item 的展示邏輯,如下所示:
通過 item.hidden 控制菜單是否展示
通過 hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow 邏輯判斷 template 菜單是否展示,template 代表單一菜單,如下所示:
hasOneShowingChild:判斷是否只有一個需要展示的子路由!onlyOneChild.children||onlyOneChild.noShowingChildren: 判斷需要展示的子菜單,是否包含 children 屬性,如果包含,則說明子菜單可能存在孫子菜單,此時需要再判斷 noShowingChildren 屬性!item.alwaysShow:判斷路由中是否存在 alwaysShow 屬性,如何存在,則返回 false,不展示 template 菜單,也就是只要配置了 alwaysShow 屬性就會直接進(jìn)入 el-submenu 組件
4.對于 hasOneShowingChild 方法,children 是 router 對象的 children 屬性,item 是 router 對象,代碼如下所示:
hasOneShowingChild(children = [], parent) {
? const showingChildren = children.filter(item => {
? ? // 如果 children 中的路由包含 hidden 屬性,則返回 false
? ? if (item.hidden) {
? ? ? return false
? ? } else {
? ? ? // 將子路由賦值給 onlyOneChild,用于只包含一個路由時展示
? ? ? this.onlyOneChild = item
? ? ? return true
? ? }
? })
? // 如果過濾后,只包含展示一個路由,則返回 true
? if (showingChildren.length === 1) {
? ? return true
? }
? // 如果沒有子路由需要展示,則將 onlyOneChild 的 push 設(shè)置空路由,并添加 noShowingChildren
? if (showingChildren.length === 0) {
? ? this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
? ? return true
? }
? // 返回 false,表示不需要展示子路由,或者超過 需要展示的子路由
? return false
}5.對于它們之間的關(guān)系,如下所示:
如果展示 template 組件,首先會展示 app-link 組件,然后是 el-menu-item,最里面嵌套的是 item 組件。item 組件需要 meta 中包含 title 和 icon 屬性,否則將渲染內(nèi)容為空的 vnode 對象。
如果 template 菜單不展示,則展示 el-submenu 菜單,el-submenu 邏輯中采用了嵌套組件的做法,將 sidebar-item 嵌套在 el-submenu 中
el-submenu 中的 sidebar-item 的區(qū)別,第一個是傳入 is-nest 參數(shù),第二個是傳入 base-path 參數(shù)
6.sidebar-item,代碼如下所示:
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template><script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
export default {
name: 'SidebarItem',
components: { Item, AppLink },
mixins: [FixiOSBug],
props: {
// route object
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ''
}
},
data() {
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => {
// 如果 children 中的路由包含 hidden 屬性,則返回 false
if (item.hidden) {
return false
} else {
// 將子路由賦值給 onlyOneChild,用于只包含一個路由時展示
this.onlyOneChild = item
return true
}
})
// 如果過濾后,只包含展示一個路由,則返回 true
if (showingChildren.length === 1) {
return true
}
// 如果沒有子路由需要展示,則將 onlyOneChild 的 push 設(shè)置空路由,并添加 noShowingChildren
if (showingChildren.length === 0) {
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return true
}
// 返回 false,表示不需要展示子路由,或者超過 需要展示的子路由
return false
},
resolvePath(routePath) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
}
}
}
</script>7.app-link,是一個動態(tài)組件,通過 to 參數(shù),如果包含 http 前綴則變成一個 a 標(biāo)簽,否則變成一個 router-link 組件。
8.app-link,代碼如下所示:
<template> ? <component :is="type" v-bind="linkProps(to)"> ? ? <slot /> ? </component> </template>
<script>
import { isExternal } from '@/utils/validate'
export default {
? props: {
? ? to: {
? ? ? type: String,
? ? ? required: true
? ? }
? },
? computed: {
? ? isExternal() {
? ? ? return isExternal(this.to)
? ? },
? ? type() {
? ? ? if (this.isExternal) {
? ? ? ? return 'a'
? ? ? }
? ? ? return 'router-link'
? ? }
? },
? methods: {
? ? linkProps(to) {
? ? ? if (this.isExternal) {
? ? ? ? return {
? ? ? ? ? href: to,
? ? ? ? ? target: '_blank',
? ? ? ? ? rel: 'noopener'
? ? ? ? }
? ? ? }
? ? ? return {
? ? ? ? to: to
? ? ? }
? ? }
? }
}
</script>9.item 組件,通過定義的 render 函數(shù)完成組件渲染。
10item,代碼如下所示:
<script>
export default {
? name: 'MenuItem',
? functional: true,
? props: {
? ? icon: {
? ? ? type: String,
? ? ? default: ''
? ? },
? ? title: {
? ? ? type: String,
? ? ? default: ''
? ? }
? },
? render(h, context) {
? ? const { icon, title } = context.props
? ? const vnodes = []
? ? if (icon) {
? ? ? if (icon.includes('el-icon')) {
? ? ? ? vnodes.push(<i class={[icon, 'sub-el-icon']} />)
? ? ? } else {
? ? ? ? vnodes.push(<svg-icon icon-class={icon}/>)
? ? ? }
? ? }
? ? if (title) {
? ? ? vnodes.push(<span slot='title'>{(title)}</span>)
? ? }
? ? return vnodes
? }
}
</script><style scoped>
.sub-el-icon {
? color: currentColor;
? width: 1em;
? height: 1em;
}
</style>側(cè)邊欄實(shí)現(xiàn)總結(jié)
1.sidebar 主要包括 el-menu 容器組件,el-menu 中遍歷 vuex 中的 routes,生成 sidebar-item 組件。sidebar 主要配置如下所示:
activeMenu:根據(jù)當(dāng)前路由的 meta.activeMenu 屬性控制側(cè)邊欄中高亮菜單isCollapse:根據(jù) Cookie 的 sidebarStatus 控制側(cè)邊欄是否折疊variables:通過 @style/variables.css 填充 el-menu 的基本樣式
2.sidebar-item,分為兩個部分,如下所示:
第一部分是當(dāng)只需要展示一個 children 或者沒有 children 時進(jìn)行展示,展示的組件包括:
app-link:動態(tài)組件,path 為鏈接時,顯示為 a 標(biāo)簽,path 為路由時,顯示為 router-link 組件el-menu-item:菜單項,當(dāng) sidebar-item 為非 nest 組件時,el-menu-item 會增加 submenu-title-noDropdown 的 classitem:el-menu-item 里的內(nèi)容,主要是 icon 和 title,當(dāng) title 為空時,整個菜單項將不會展示
第二部分是當(dāng) children 超過兩項時進(jìn)行展示,展示的組件包括:
el-submenu:子菜單組件容器,用于嵌套子菜單組件
sidebar-item:el-submen 迭代嵌套了 sidebar-item 組件,在 sidebar-item 組件中的變化,設(shè)置了 is-nest 屬性為 true,根據(jù) child.path 生成了 base-path 屬性傳入 sidebar-item 組件
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue+tracking.js 實(shí)現(xiàn)前端人臉檢測功能
這篇文章主要介紹了Vue+tracking.js 實(shí)現(xiàn)前端人臉檢測功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
vue-cli webpack模板項目搭建及打包時路徑問題的解決方法
這篇文章主要介紹了vue-cli webpack模板項目搭建以及打包時路徑問題的解決方法,需要的朋友可以參考下2018-02-02
基于Electron24+Vite4+Vue3搭建桌面端應(yīng)用實(shí)戰(zhàn)教程
這篇文章主要介紹了基于Electron24+Vite4+Vue3搭建桌面端應(yīng)用,這次給大家主要分享的是基于electron最新版本整合vite4.x構(gòu)建vue3桌面端應(yīng)用程序,需要的朋友可以參考下2023-05-05
Vue動態(tài)添加class可能帶來的問題解讀(被覆蓋)
文章討論了在使用Vue.js時,通過動態(tài)class修改元素樣式時可能會遇到的問題,當(dāng)通過JavaScript動態(tài)添加類時,Vue的動態(tài)class會覆蓋掉通過JavaScript添加的類,導(dǎo)致樣式丟失,這個問題在實(shí)際開發(fā)中可能會遇到,尤其是在使用第三方框架2024-12-12

