menu-item.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. <script lang="ts" setup>
  2. import type { MenuItemProps, MenuItemRegistered } from '../interface';
  3. import { computed, onBeforeUnmount, onMounted, reactive, useSlots } from 'vue';
  4. import { useNamespace } from '@vben-core/hooks';
  5. import { VbenIcon, VbenMenuBadge, VbenTooltip } from '@vben-core/shadcn-ui';
  6. import { useMenu, useMenuContext, useSubMenuContext } from '../hooks';
  7. interface Props extends MenuItemProps {}
  8. defineOptions({ name: 'MenuItem' });
  9. const props = withDefaults(defineProps<Props>(), {
  10. disabled: false,
  11. });
  12. const emit = defineEmits<{ click: [MenuItemRegistered] }>();
  13. const slots = useSlots();
  14. const { b, e, is } = useNamespace('menu-item');
  15. const nsMenu = useNamespace('menu');
  16. const rootMenu = useMenuContext();
  17. const subMenu = useSubMenuContext();
  18. const { parentMenu, parentPaths } = useMenu();
  19. const active = computed(() => props.path === rootMenu?.activePath);
  20. const isTopLevelMenuItem = computed(
  21. () => parentMenu.value?.type.name === 'Menu',
  22. );
  23. const getCollapseShowTitle = computed(
  24. () =>
  25. rootMenu.props?.collapseShowTitle &&
  26. isTopLevelMenuItem.value &&
  27. rootMenu.props.collapse,
  28. );
  29. const showTooltip = computed(
  30. () =>
  31. rootMenu.props.mode === 'vertical' &&
  32. isTopLevelMenuItem.value &&
  33. rootMenu.props?.collapse &&
  34. slots.title,
  35. );
  36. const item: MenuItemRegistered = reactive({
  37. active,
  38. parentPaths: parentPaths.value,
  39. path: props.path || '',
  40. });
  41. /**
  42. * 菜单项点击事件
  43. */
  44. function handleClick() {
  45. if (props.disabled) {
  46. return;
  47. }
  48. rootMenu?.handleMenuItemClick?.({
  49. parentPaths: parentPaths.value,
  50. path: props.path,
  51. });
  52. emit('click', item);
  53. }
  54. onMounted(() => {
  55. subMenu?.addSubMenu?.(item);
  56. rootMenu?.addMenuItem?.(item);
  57. });
  58. onBeforeUnmount(() => {
  59. subMenu?.removeSubMenu?.(item);
  60. rootMenu?.removeMenuItem?.(item);
  61. });
  62. </script>
  63. <template>
  64. <li
  65. :class="[
  66. b(),
  67. is('active', active),
  68. is('disabled', disabled),
  69. is('collapse-show-title', getCollapseShowTitle),
  70. ]"
  71. role="menuitem"
  72. @click.stop="handleClick"
  73. >
  74. <VbenTooltip v-if="showTooltip" side="right">
  75. <template #trigger>
  76. <div :class="[nsMenu.be('tooltip', 'trigger')]">
  77. <VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
  78. <slot></slot>
  79. <span v-if="getCollapseShowTitle" :class="nsMenu.e('name')">
  80. <slot name="title"></slot>
  81. </span>
  82. </div>
  83. </template>
  84. <slot name="title"></slot>
  85. </VbenTooltip>
  86. <div v-show="!showTooltip" :class="[e('content')]">
  87. <VbenMenuBadge v-bind="props" />
  88. <VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
  89. <slot></slot>
  90. <slot name="title"></slot>
  91. </div>
  92. </li>
  93. </template>