Breadcrumb.vue 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. <script setup lang="ts">
  2. import { isEmpty } from 'lodash-es'
  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 !== 'layouts')
  10. })
  11. function resolveDropdownOptions(route: RouteRecordRaw[]): DropdownProps['options'] {
  12. return route.map((item) => {
  13. return {
  14. label: item.meta?.title || 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. :duration="300"
  34. tag="ul"
  35. class="flex"
  36. type="transition"
  37. enter-active-class="transition-[grid-template-columns]"
  38. leave-active-class="transition-[grid-template-columns]"
  39. enter-from-class="grid-cols-[0fr]"
  40. leave-to-class="grid-cols-[0fr]"
  41. enter-to-class="grid-cols-[1fr]"
  42. leave-from-class="grid-cols-[1fr]"
  43. >
  44. <li
  45. v-for="item in routerBreadcrumb"
  46. :key="item.path"
  47. class="grid shrink-0 justify-start overflow-hidden"
  48. >
  49. <div
  50. class="flex min-w-0 items-center"
  51. :class="{
  52. 'not-hover:text-[var(--text-color-3)]': !isCurrentRoute(item.name),
  53. }"
  54. >
  55. <NDropdown
  56. :options="resolveDropdownOptions(item.children)"
  57. placement="bottom-start"
  58. @select="onDropdownSelected"
  59. >
  60. <div
  61. class="flex shrink-0 items-center gap-x-1.5 rounded px-1.5 py-1 transition-[background-color,color]"
  62. :class="{
  63. 'cursor-pointer hover:bg-[var(--button-color-2-hover)]': !isCurrentRoute(item.name),
  64. }"
  65. >
  66. <span
  67. v-if="item.meta?.icon"
  68. class="size-5"
  69. :class="item.meta?.icon"
  70. />
  71. {{ item.meta?.title }}
  72. </div>
  73. </NDropdown>
  74. <span
  75. class="iconify-[fluent--slash-forward-20-regular] w-3.5 text-[var(--text-color-3)]"
  76. v-if="!isCurrentRoute(item.name)"
  77. />
  78. </div>
  79. </li>
  80. </TransitionGroup>
  81. </nav>
  82. </template>