Appearance
菜单支持 element icon 以及类型补充 #
2-1 SidebarItem #
src/layout/components/Sidebar/SidebarItem.vue
主要是判断
vue
<template>
<div
v-if="!item.meta || !item.meta.hidden"
class="sidebar-item-container"
>
<!-- 只渲染一个路由 并且路由只有一个子路由时直接渲染这个子路由 -->
<template
v-if="theOnlyOneChildRoute && (!theOnlyOneChildRoute.children || noShowingChildren)"
>
<sidebar-item-link
v-if="theOnlyOneChildRoute.meta"
:to="resolvePath(theOnlyOneChildRoute.path)"
>
<el-menu-item
:index="resolvePath(theOnlyOneChildRoute.path)"
>
<i v-if="icon && icon.includes('el-icon')" :class="icon"></i>
<svg-icon
v-else-if="icon"
class="menu-icon"
:icon-class="icon"
></svg-icon>
<template #title>
<span>{{ theOnlyOneChildRoute.meta.title }}</span>
</template>
</el-menu-item>
</sidebar-item-link>
</template>
<!-- 有多个子路由时 -->
<el-submenu
v-else
:index="resolvePath(item.path)"
popper-append-to-body
>
<template #title>
<i
v-if="item.meta && item.meta.icon.includes('el-icon')"
:class="icon"
></i>
<svg-icon
v-else-if="item.meta && item.meta.icon"
class="menu-icon"
:icon-class="item.meta.icon"
></svg-icon>
<span v-if="item.meta" class="submenu-title">{{ item.meta.title }}</span>
</template>
<template v-if="item.children">
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
>
</sidebar-item>
</template>
</el-submenu>
</div>
</template>
<script lang="ts">
import path from 'path'
import { defineComponent, PropType, computed, toRefs } from 'vue'
import SidebarItemLink from './SidebarItemLink.vue'
import { isExternal } from '@/utils/validate'
import { MenuItemRouter } from '@/router/type'
export default defineComponent({
name: 'SidebarItem',
components: {
SidebarItemLink
},
props: {
item: {
type: Object as PropType<MenuItemRouter>,
required: true
},
basePath: {
type: String,
required: true
}
},
setup (props) {
const { item } = toRefs(props)
// 子路由数量
const showingChildNumber = computed(() => {
const children = (props.item.children || []).filter(child => {
// hidden属性控制路由是否渲染成菜单 像login 401 404等路由都不需要渲染成菜案
if (child.meta && child.meta.hidden) return false
return true
})
return children.length
})
// 只有一个可渲染的子路由直接渲染这个子路由 (由于我们有的路由 layout布局组件是一级路由 二级路由才是我们要渲染成菜单)
const theOnlyOneChildRoute = computed(() => {
// 多个children
if (showingChildNumber.value > 1) {
return null
}
// 子路由只有一个时 并且做个hidden筛选
if (item.value.children) {
for (const child of item.value.children) {
// hidden属性控制路由是否渲染成菜单 像login 401 404等路由都不需要渲染成菜单
if (!child.meta || !child.meta.hidden) {
return child
}
}
}
// showingChildNumber === 0
// 没有可渲染chiildren时 就渲染当前父路由item
return {
...props.item,
path: '' // resolvePath避免resolve拼接时 拼接重复
}
})
// 是否有可渲染子路由
const noShowingChildren = computed(() => showingChildNumber.value === 0)
// menu icon
const icon = computed(() => {
// 子路由 如果没有icon就用父路由的
return (theOnlyOneChildRoute.value?.meta?.icon || (props.item.meta && props.item.meta.icon)) as string
})
// 拼接路径 父路径+子路径(相对路径)
const resolvePath = (childPath: string) => {
// 如果是带协议外链 直接返回
if (isExternal(childPath)) {
return childPath
}
// 如果不是外链 需要和basePath拼接
return path.resolve(props.basePath, childPath)
}
return {
theOnlyOneChildRoute,
icon,
resolvePath,
noShowingChildren
}
}
})
</script>
<style lang="scss">
.sidebar-item-container {
.menu-icon {
margin-right: 16px;
margin-left: 5px;
vertical-align: middle;
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
2-2 路由 meta 对象类型处理 #
在 router 目录下 typings.d.ts 针对 RouteMeta 进行类型补充
RouteMeta类型说明 src/router/typings.d.ts
typescript
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
title?: string; // 路由菜单title
icon?: string; // 路由菜单icon
hidden?: boolean; // 菜单栏不显示
// 路由是否缓存 没有这个属性或false都会缓存 true不缓存
noCache?: boolean;
activeMenu?: string; // 指定菜单激活
breadcrumb?: boolean; // 该路由是否显示面包屑
affix?: boolean; // 固定显示在tagsView中
alwaysShow?: boolean; // 菜单是否一直显示根路由
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SidebarItem 中 router meta 正常使用 #
src/layout/components/Sidebar/SidebarItem.vue icon 断言为 string src/layout/components/Sidebar/SidebarItem.vue
本节参考源码 #
https://gitee.com/brolly/vue3-element-admin/commit/c33c22e68fd1bffbf29e469d81a9e635ec00fdf7