SidebarItem.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
  7. <template #title>
  8. <span class="menu-title" :title="hasTitle(onlyOneChildTitle)">{{ onlyOneChildTitle }}</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="item.meta ? item.meta.icon : ''" />
  16. <span class="menu-title" :title="hasTitle(itemTitle)">{{ itemTitle }}</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 { parseI18nName } from '@/utils/i18n';
  34. import { RouteRecordRaw } from 'vue-router';
  35. import { useI18n } from 'vue-i18n';
  36. const props = defineProps({
  37. item: {
  38. type: Object as PropType<RouteRecordRaw>,
  39. required: true
  40. },
  41. isNest: {
  42. type: Boolean,
  43. default: false
  44. },
  45. basePath: {
  46. type: String,
  47. default: ''
  48. }
  49. });
  50. const { locale } = useI18n();
  51. const onlyOneChild = ref<any>({});
  52. // 使用计算属性缓存解析后的标题,添加 locale 依赖确保语言切换时更新
  53. const itemTitle = computed(() => {
  54. const _ = locale.value;
  55. return parseI18nName(props.item.meta?.title);
  56. });
  57. const onlyOneChildTitle = computed(() => {
  58. const _ = locale.value;
  59. return parseI18nName(onlyOneChild.value.meta?.title);
  60. });
  61. const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
  62. if (!children) {
  63. children = [];
  64. }
  65. const showingChildren = children.filter((item) => {
  66. if (item.hidden) {
  67. return false;
  68. }
  69. onlyOneChild.value = item;
  70. return true;
  71. });
  72. // When there is only one child router, the child router is displayed by default
  73. if (showingChildren.length === 1) {
  74. return true;
  75. }
  76. // Show parent if there are no child router to display
  77. if (showingChildren.length === 0) {
  78. onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
  79. return true;
  80. }
  81. return false;
  82. };
  83. const resolvePath = (routePath: string, routeQuery?: string): any => {
  84. if (isExternal(routePath)) {
  85. return routePath;
  86. }
  87. if (isExternal(props.basePath as string)) {
  88. return props.basePath;
  89. }
  90. if (routeQuery) {
  91. const query = JSON.parse(routeQuery);
  92. return { path: getNormalPath(props.basePath + '/' + routePath), query: query };
  93. }
  94. return getNormalPath(props.basePath + '/' + routePath);
  95. };
  96. const hasTitle = (title: string | undefined): string => {
  97. if (!title || title.length <= 5) {
  98. return '';
  99. }
  100. return title;
  101. };
  102. </script>