menu-item.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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 collapseShowTitle = 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. rootMenu.theme,
  67. b(),
  68. is('active', active),
  69. is('disabled', disabled),
  70. is('collapse-show-title', collapseShowTitle),
  71. ]"
  72. role="menuitem"
  73. @click.stop="handleClick"
  74. >
  75. <VbenTooltip
  76. v-if="showTooltip"
  77. :content-class="[rootMenu.theme]"
  78. side="right"
  79. >
  80. <template #trigger>
  81. <div :class="[nsMenu.be('tooltip', 'trigger')]">
  82. <VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
  83. <slot></slot>
  84. <span v-if="collapseShowTitle" :class="nsMenu.e('name')">
  85. <slot name="title"></slot>
  86. </span>
  87. </div>
  88. </template>
  89. <slot name="title"></slot>
  90. </VbenTooltip>
  91. <div v-show="!showTooltip" :class="[e('content')]">
  92. <VbenMenuBadge
  93. v-if="rootMenu.props.mode !== 'horizontal'"
  94. v-bind="props"
  95. />
  96. <VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
  97. <slot></slot>
  98. <slot name="title"></slot>
  99. </div>
  100. </li>
  101. </template>