Breadcrumb.vue 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. <script setup lang="ts">
  2. import { isEmpty } from 'es-toolkit/compat'
  3. import { NDropdown } from 'naive-ui'
  4. import { computed, h } from 'vue'
  5. import router from '@/router'
  6. import type { DropdownProps } from 'naive-ui'
  7. import type { RouteRecordNameGeneric, RouteRecordRaw } from 'vue-router'
  8. const routerBreadcrumb = computed(() => {
  9. return router.currentRoute.value.matched.filter((item) => item.name !== 'layout')
  10. })
  11. function resolveDropdownOptions(route: RouteRecordRaw[]): DropdownProps['options'] {
  12. return route.map((item) => {
  13. return {
  14. label: item.meta?.label,
  15. key: (item.name as string) || item.path,
  16. icon: () => h('span', { class: `${item.meta?.icon} size-5` }),
  17. children: !isEmpty(item.children)
  18. ? resolveDropdownOptions(item.children as RouteRecordRaw[])
  19. : undefined,
  20. }
  21. })
  22. }
  23. const onDropdownSelected: DropdownProps['onSelect'] = (key) => {
  24. router.push({ name: key })
  25. }
  26. function isCurrentRoute(name: RouteRecordNameGeneric) {
  27. return name === router.currentRoute.value.name
  28. }
  29. </script>
  30. <template>
  31. <nav class="min-w-0">
  32. <TransitionGroup
  33. tag="ul"
  34. class="flex"
  35. enter-active-class="transition-grid-template-columns-300 transition-ease-in-out"
  36. leave-active-class="transition-grid-template-columns-300 transition-ease-in-out"
  37. enter-from-class="grid-cols-[0fr]"
  38. leave-to-class="grid-cols-[0fr]"
  39. enter-to-class="grid-cols-[1fr]"
  40. leave-from-class="grid-cols-[1fr]"
  41. >
  42. <li
  43. v-for="item in routerBreadcrumb"
  44. :key="item.path"
  45. class="grid shrink-0 justify-start overflow-hidden"
  46. >
  47. <div
  48. class="flex min-w-0 items-center"
  49. :class="{
  50. 'not-hover:text-[var(--text-color-3)]': !isCurrentRoute(item.name),
  51. }"
  52. >
  53. <NDropdown
  54. :options="resolveDropdownOptions(item.children)"
  55. placement="bottom-start"
  56. @select="onDropdownSelected"
  57. >
  58. <div
  59. class="flex shrink-0 items-center gap-x-1.5 rounded px-1.5 py-1 transition-[background-color,color]"
  60. :class="{
  61. 'cursor-pointer hover:bg-[var(--button-color-2-hover)]': !isCurrentRoute(item.name),
  62. }"
  63. >
  64. <span
  65. v-if="item.meta?.icon"
  66. class="size-5"
  67. :class="item.meta?.icon"
  68. />
  69. {{ item.meta?.label }}
  70. </div>
  71. </NDropdown>
  72. <span
  73. class="i-fluent:slash-forward-20-regular] w-3.5 text-[var(--text-color-3)]"
  74. v-if="!isCurrentRoute(item.name)"
  75. />
  76. </div>
  77. </li>
  78. </TransitionGroup>
  79. </nav>
  80. </template>