1
0

menu-item.vue 3.0 KB

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