sidebarItem.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <script setup lang="ts">
  2. import path from "path";
  3. import { PropType, ref, nextTick, getCurrentInstance } from "vue";
  4. import { childrenType } from "../../types";
  5. import { useAppStoreHook } from "/@/store/modules/app";
  6. import Icon from "/@/components/ReIcon/src/Icon.vue";
  7. import { findIconReg } from "/@/components/ReIcon";
  8. const instance = getCurrentInstance().appContext.app.config.globalProperties;
  9. const menuMode = instance.$storage.layout?.layout === "vertical";
  10. const pureApp = useAppStoreHook();
  11. const props = defineProps({
  12. item: {
  13. type: Object as PropType<childrenType>
  14. },
  15. isNest: {
  16. type: Boolean,
  17. default: false
  18. },
  19. basePath: {
  20. type: String,
  21. default: ""
  22. }
  23. });
  24. const onlyOneChild: childrenType = ref(null);
  25. // 存放菜单是否存在showTooltip属性标识
  26. const hoverMenuMap = new WeakMap();
  27. // 存储菜单文本dom元素
  28. const menuTextRef = ref(null);
  29. function hoverMenu(key) {
  30. // 如果当前菜单showTooltip属性已存在,退出计算
  31. if (hoverMenuMap.get(key)) return;
  32. nextTick(() => {
  33. // 如果文本内容的整体宽度大于其可视宽度,则文本溢出
  34. menuTextRef.value?.scrollWidth > menuTextRef.value?.clientWidth
  35. ? Object.assign(key, {
  36. showTooltip: true
  37. })
  38. : Object.assign(key, {
  39. showTooltip: false
  40. });
  41. hoverMenuMap.set(key, true);
  42. });
  43. }
  44. function hasOneShowingChild(
  45. children: childrenType[] = [],
  46. parent: childrenType
  47. ) {
  48. const showingChildren = children.filter((item: any) => {
  49. onlyOneChild.value = item;
  50. return true;
  51. });
  52. if (showingChildren.length === 1) {
  53. return true;
  54. }
  55. if (showingChildren.length === 0) {
  56. onlyOneChild.value = { ...parent, path: "", noShowingChildren: true };
  57. return true;
  58. }
  59. return false;
  60. }
  61. function resolvePath(routePath) {
  62. const httpReg = /^http(s?):\/\//;
  63. if (httpReg.test(routePath)) {
  64. return props.basePath + "/" + routePath;
  65. } else {
  66. return path.resolve(props.basePath, routePath);
  67. }
  68. }
  69. </script>
  70. <template>
  71. <template
  72. v-if="
  73. hasOneShowingChild(props.item.children, props.item) &&
  74. (!onlyOneChild.children || onlyOneChild.noShowingChildren)
  75. "
  76. >
  77. <el-menu-item
  78. :index="resolvePath(onlyOneChild.path)"
  79. :class="{ 'submenu-title-noDropdown': !isNest }"
  80. style="display: flex; align-items: center"
  81. >
  82. <el-icon v-show="props.item.meta.icon">
  83. <component
  84. :is="
  85. findIconReg(
  86. onlyOneChild.meta.icon ||
  87. (props.item.meta && props.item.meta.icon)
  88. )
  89. "
  90. ></component>
  91. </el-icon>
  92. <template #title>
  93. <div
  94. :style="{
  95. width: pureApp.sidebar.opened ? '' : '100%',
  96. display: 'flex',
  97. alignItems: 'center',
  98. justifyContent: 'space-between',
  99. overflow: 'hidden'
  100. }"
  101. >
  102. <span v-if="!menuMode">{{ $t(onlyOneChild.meta.title) }}</span>
  103. <el-tooltip
  104. v-else
  105. placement="top"
  106. :offset="-10"
  107. :disabled="!onlyOneChild.showTooltip"
  108. >
  109. <template #content> {{ $t(onlyOneChild.meta.title) }} </template>
  110. <span
  111. ref="menuTextRef"
  112. :style="{
  113. width: pureApp.sidebar.opened ? '125px' : '',
  114. overflow: 'hidden',
  115. textOverflow: 'ellipsis'
  116. }"
  117. @mouseover="hoverMenu(onlyOneChild)"
  118. >
  119. {{ $t(onlyOneChild.meta.title) }}
  120. </span>
  121. </el-tooltip>
  122. <Icon
  123. v-if="onlyOneChild.meta.extraIcon"
  124. :svg="onlyOneChild.meta.extraIcon.svg ? true : false"
  125. :content="`${onlyOneChild.meta.extraIcon.name}`"
  126. />
  127. </div>
  128. </template>
  129. </el-menu-item>
  130. </template>
  131. <el-sub-menu
  132. v-else
  133. ref="subMenu"
  134. :index="resolvePath(props.item.path)"
  135. popper-append-to-body
  136. >
  137. <template #title>
  138. <el-icon v-show="props.item.meta.icon" :class="props.item.meta.icon">
  139. <component
  140. :is="findIconReg(props.item.meta && props.item.meta.icon)"
  141. ></component>
  142. </el-icon>
  143. <span v-if="!menuMode">{{ $t(props.item.meta.title) }}</span>
  144. <el-tooltip
  145. v-else
  146. placement="top"
  147. :offset="-10"
  148. :disabled="!pureApp.sidebar.opened || !props.item.showTooltip"
  149. >
  150. <template #content> {{ $t(props.item.meta.title) }} </template>
  151. <div
  152. ref="menuTextRef"
  153. :style="{
  154. width: pureApp.sidebar.opened ? '125px' : '',
  155. display: 'inline-block',
  156. overflow: 'hidden',
  157. textOverflow: 'ellipsis'
  158. }"
  159. @mouseover="hoverMenu(props.item)"
  160. >
  161. <span style="overflow: hidden; text-overflow: ellipsis">
  162. {{ $t(props.item.meta.title) }}
  163. </span>
  164. </div>
  165. </el-tooltip>
  166. <Icon
  167. v-if="props.item.meta.extraIcon"
  168. :svg="props.item.meta.extraIcon.svg ? true : false"
  169. :content="`${props.item.meta.extraIcon.name}`"
  170. />
  171. </template>
  172. <sidebar-item
  173. v-for="child in props.item.children"
  174. :key="child.path"
  175. :is-nest="true"
  176. :item="child"
  177. :base-path="resolvePath(child.path)"
  178. class="nest-menu"
  179. />
  180. </el-sub-menu>
  181. </template>