SidebarItem.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <template>
  2. <div v-if="!item.hidden">
  3. <template v-if="hasOneShowingChild(item, item.children) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
  4. <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
  5. <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
  6. <svg-icon :icon-class="getIconClass(onlyOneChild)" />
  7. <template #title>
  8. <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
  9. </template>
  10. </el-menu-item>
  11. </app-link>
  12. </template>
  13. <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
  14. <template v-if="item.meta" #title>
  15. <svg-icon :icon-class="getIconClass(item)" />
  16. <span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
  17. </template>
  18. <sidebar-item
  19. v-for="(child, index) in item.children"
  20. :key="child.path + index"
  21. :is-nest="true"
  22. :item="child"
  23. :base-path="resolvePath(child.path)"
  24. class="nest-menu"
  25. />
  26. </el-sub-menu>
  27. </div>
  28. </template>
  29. <script setup lang="ts">
  30. import { isExternal } from '@/utils/validate';
  31. import AppLink from './Link.vue';
  32. import { getNormalPath } from '@/utils/ruoyi';
  33. import { RouteRecordRaw } from 'vue-router';
  34. const rootMenu = inject('rootMenu');
  35. const props = defineProps({
  36. item: {
  37. type: Object as PropType<RouteRecordRaw>,
  38. required: true
  39. },
  40. isNest: {
  41. type: Boolean,
  42. default: false
  43. },
  44. basePath: {
  45. type: String,
  46. default: ''
  47. }
  48. });
  49. const subMenu = ref();
  50. const onlyOneChild = ref<any>({});
  51. const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
  52. if (!children) {
  53. children = [];
  54. }
  55. const showingChildren = children.filter((item) => {
  56. if (item.hidden) {
  57. return false;
  58. } else {
  59. // Temp set(will be used if only has one showing child)
  60. onlyOneChild.value = item;
  61. return true;
  62. }
  63. });
  64. // When there is only one child router, the child router is displayed by default
  65. if (showingChildren.length === 1) {
  66. return true;
  67. }
  68. // Show parent if there are no child router to display
  69. if (showingChildren.length === 0) {
  70. onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
  71. return true;
  72. }
  73. return false;
  74. };
  75. const resolvePath = (routePath: string, routeQuery?: string): any => {
  76. if (props.basePath === '/routineCommandMap') {
  77. return window.location.origin + import.meta.env.VITE_APP_CONTEXT_PATH + '#' + props.basePath;
  78. }
  79. if (isExternal(routePath)) {
  80. return routePath;
  81. }
  82. if (isExternal(props.basePath as string)) {
  83. return props.basePath;
  84. }
  85. if (routeQuery) {
  86. let query = JSON.parse(routeQuery);
  87. return { path: getNormalPath(props.basePath + '/' + routePath), query: query };
  88. }
  89. return getNormalPath(props.basePath + '/' + routePath);
  90. };
  91. const hasTitle = (title: string | undefined): string => {
  92. if (!title || title.length <= 5) {
  93. return '';
  94. }
  95. return title;
  96. };
  97. const getIconClass = (item) => {
  98. let res = '';
  99. // || (subMenu.value && !!subMenu.value.opened)
  100. if (rootMenu.activeIndex === item.path) {
  101. res = item.meta.icon + 'Active';
  102. } else if (item.meta && item.meta.icon) {
  103. res = item.meta.icon;
  104. }
  105. return res;
  106. };
  107. </script>
  108. <style lang="scss" scoped>
  109. .el-menu-item.is-active {
  110. color: #ffffff;
  111. }
  112. .el-sub-menu .el-menu-item.is-active {
  113. background-color: #2c81ff;
  114. position: relative;
  115. &::before {
  116. content: '';
  117. width: 4px;
  118. height: 100%;
  119. background-color: #fff;
  120. position: absolute;
  121. top: 0;
  122. left: 0;
  123. }
  124. }
  125. .is-opened {
  126. :deep(.el-sub-menu__title) {
  127. color: #fff !important;
  128. }
  129. }
  130. </style>