默认情况下如果只有一个子路由 就不显示父路由,alwaysShow为true可始终显示父路由

image.png
设为true
image.png
效果
image.png

2-1 添加alwaysShow

根据router.meta.alwaysShow进行判断

image.png

  1. <template>
  2. <div
  3. v-if="!item.meta || !item.meta.hidden"
  4. class="sidebar-item-container"
  5. >
  6. <!-- 只渲染一个路由 并且路由只有一个子路由时直接渲染这个子路由 -->
  7. <template
  8. v-if="isRenderSingleRoute && theOnlyOneChildRoute"
  9. >
  10. <sidebar-item-link
  11. v-if="theOnlyOneChildRoute.meta"
  12. :to="resolvePath(theOnlyOneChildRoute.path)"
  13. >
  14. <el-menu-item
  15. :index="resolvePath(theOnlyOneChildRoute.path)"
  16. >
  17. <i v-if="icon && icon.includes('el-icon')" :class="icon"></i>
  18. <svg-icon
  19. v-else-if="icon"
  20. class="menu-icon"
  21. :icon-class="icon"
  22. ></svg-icon>
  23. <template #title>
  24. <span>{{ theOnlyOneChildRoute.meta.title }}</span>
  25. </template>
  26. </el-menu-item>
  27. </sidebar-item-link>
  28. </template>
  29. <!-- 有多个子路由时 -->
  30. <el-submenu
  31. v-else
  32. :index="resolvePath(item.path)"
  33. popper-append-to-body
  34. >
  35. <template #title>
  36. <i
  37. v-if="item.meta && item.meta.icon.includes('el-icon')"
  38. :class="icon"
  39. ></i>
  40. <svg-icon
  41. v-else-if="item.meta && item.meta.icon"
  42. class="menu-icon"
  43. :icon-class="item.meta.icon"
  44. ></svg-icon>
  45. <span v-if="item.meta" class="submenu-title">{{ item.meta.title }}</span>
  46. </template>
  47. <template v-if="item.children">
  48. <sidebar-item
  49. v-for="child in item.children"
  50. :key="child.path"
  51. :is-nest="true"
  52. :item="child"
  53. :base-path="resolvePath(child.path)"
  54. >
  55. </sidebar-item>
  56. </template>
  57. </el-submenu>
  58. </div>
  59. </template>
  60. <script lang="ts">
  61. import path from 'path'
  62. import { defineComponent, PropType, computed, toRefs } from 'vue'
  63. import SidebarItemLink from './SidebarItemLink.vue'
  64. import { isExternal } from '@/utils/validate'
  65. import { MenuItemRouter } from '@/router/type'
  66. export default defineComponent({
  67. name: 'SidebarItem',
  68. components: {
  69. SidebarItemLink
  70. },
  71. props: {
  72. item: {
  73. type: Object as PropType<MenuItemRouter>,
  74. required: true
  75. },
  76. basePath: {
  77. type: String,
  78. required: true
  79. }
  80. },
  81. setup (props) {
  82. const { item } = toRefs(props)
  83. // 子路由数量
  84. const showingChildNumber = computed(() => {
  85. const children = (props.item.children || []).filter(child => {
  86. // hidden属性控制路由是否渲染成菜单 像login 401 404等路由都不需要渲染成菜案
  87. if (child.meta && child.meta.hidden) return false
  88. return true
  89. })
  90. return children.length
  91. })
  92. // 只有一个可渲染的子路由直接渲染这个子路由 (由于我们有的路由 layout布局组件是一级路由 二级路由才是我们要渲染成菜单)
  93. const theOnlyOneChildRoute = computed(() => {
  94. // 多个children
  95. if (showingChildNumber.value > 1) {
  96. return null
  97. }
  98. // 子路由只有一个时 并且做个hidden筛选
  99. if (item.value.children) {
  100. for (const child of item.value.children) {
  101. // hidden属性控制路由是否渲染成菜单 像login 401 404等路由都不需要渲染成菜单
  102. if (!child.meta || !child.meta.hidden) {
  103. return child
  104. }
  105. }
  106. }
  107. // showingChildNumber === 0
  108. // 没有可渲染chiildren时 就渲染当前父路由item
  109. return {
  110. ...props.item,
  111. path: '' // resolvePath避免resolve拼接时 拼接重复
  112. }
  113. })
  114. // 是否有可渲染子路由
  115. const noShowingChildren = computed(() => showingChildNumber.value === 0)
  116. // menu icon
  117. const icon = computed(() => {
  118. // 子路由 如果没有icon就用父路由的
  119. return (theOnlyOneChildRoute.value?.meta?.icon || (props.item.meta && props.item.meta.icon)) as string
  120. })
  121. // 拼接路径 父路径+子路径(相对路径)
  122. const resolvePath = (childPath: string) => {
  123. // 如果是带协议外链 直接返回
  124. if (isExternal(childPath)) {
  125. return childPath
  126. }
  127. // 如果不是外链 需要和basePath拼接
  128. return path.resolve(props.basePath, childPath)
  129. }
  130. // 设置 alwaysShow: true,这样它就会忽略上面定义的规则,一直显示根路由 哪怕只有一个子路由也会显示为嵌套的路由菜单
  131. const alwaysShowRootMenu = computed(
  132. () => props.item.meta && props.item.meta.alwaysShow
  133. )
  134. // 是否只有一条可渲染路由
  135. const isRenderSingleRoute = computed(() => !alwaysShowRootMenu.value && (!theOnlyOneChildRoute.value?.children || noShowingChildren.value))
  136. return {
  137. theOnlyOneChildRoute,
  138. icon,
  139. resolvePath,
  140. isRenderSingleRoute
  141. }
  142. }
  143. })
  144. </script>
  145. <style lang="scss">
  146. .sidebar-item-container {
  147. .menu-icon {
  148. margin-right: 16px;
  149. margin-left: 5px;
  150. vertical-align: middle;
  151. }
  152. }
  153. </style>

2-2 测试

修改路由meta属性测试
image.png

本节源码参考

https://gitee.com/brolly/vue3-element-admin/commit/2d17191fc8f3657f3972fb74deb1766170b3717c