Breadcrumb.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <template>
  2. <div :class="[prefixCls, `${prefixCls}--${theme}`]">
  3. <a-breadcrumb :routes="routes">
  4. <template #itemRender="{ route, routes, paths }">
  5. <Icon :icon="route.meta.icon" v-if="getShowBreadCrumbIcon && route.meta.icon" />
  6. <span v-if="!hasRedirect(routes, route)">
  7. {{ t(route.name || route.meta.title) }}
  8. </span>
  9. <router-link v-else to="" @click="handleClick(route, paths, $event)">
  10. {{ t(route.name || route.meta.title) }}
  11. </router-link>
  12. </template>
  13. </a-breadcrumb>
  14. </div>
  15. </template>
  16. <script lang="ts">
  17. import type { RouteLocationMatched } from 'vue-router';
  18. import type { Menu } from '/@/router/types';
  19. import { defineComponent, ref, watchEffect } from 'vue';
  20. import { Breadcrumb } from 'ant-design-vue';
  21. import Icon from '/@/components/Icon';
  22. import { PageEnum } from '/@/enums/pageEnum';
  23. import { useDesign } from '/@/hooks/web/useDesign';
  24. import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  25. import { useGo } from '/@/hooks/web/usePage';
  26. import { useI18n } from '/@/hooks/web/useI18n';
  27. import { useRouter } from 'vue-router';
  28. import { propTypes } from '/@/utils/propTypes';
  29. import { isString } from '/@/utils/is';
  30. import { filter } from '/@/utils/helper/treeHelper';
  31. import { getMenus } from '/@/router/menus';
  32. import { REDIRECT_NAME } from '/@/router/constant';
  33. import { getAllParentPath } from '/@/router/helper/menuHelper';
  34. export default defineComponent({
  35. name: 'LayoutBreadcrumb',
  36. components: { Icon, [Breadcrumb.name]: Breadcrumb },
  37. props: {
  38. theme: propTypes.oneOf(['dark', 'light']),
  39. },
  40. setup() {
  41. const routes = ref<RouteLocationMatched[]>([]);
  42. const { currentRoute } = useRouter();
  43. const { prefixCls } = useDesign('layout-breadcrumb');
  44. const { getShowBreadCrumbIcon } = useRootSetting();
  45. const { t } = useI18n();
  46. watchEffect(async () => {
  47. if (currentRoute.value.name === REDIRECT_NAME) return;
  48. const menus = await getMenus();
  49. const routeMatched = currentRoute.value.matched;
  50. const cur = routeMatched?.[routeMatched.length - 1];
  51. let path = currentRoute.value.path;
  52. if (cur && cur?.meta?.currentActiveMenu) {
  53. path = cur.meta.currentActiveMenu as string;
  54. }
  55. const parent = getAllParentPath(menus, path);
  56. const filterMenus = menus.filter((item) => item.path === parent[0]);
  57. const matched = getMatched(filterMenus, parent) as any;
  58. if (!matched || matched.length === 0) return;
  59. const breadcrumbList = filterItem(matched);
  60. if (currentRoute.value.meta?.currentActiveMenu) {
  61. breadcrumbList.push(({
  62. ...currentRoute.value,
  63. name: currentRoute.value.meta?.title || currentRoute.value.name,
  64. } as unknown) as RouteLocationMatched);
  65. }
  66. routes.value = breadcrumbList;
  67. });
  68. function getMatched(menus: Menu[], parent: string[]) {
  69. const metched: Menu[] = [];
  70. menus.forEach((item) => {
  71. if (parent.includes(item.path)) {
  72. metched.push({
  73. ...item,
  74. name: item.meta?.title || item.name,
  75. });
  76. }
  77. if (item.children?.length) {
  78. metched.push(...getMatched(item.children, parent));
  79. }
  80. });
  81. return metched;
  82. }
  83. function filterItem(list: RouteLocationMatched[]) {
  84. let resultList = filter(list, (item) => {
  85. const { meta, name } = item;
  86. if (!meta) {
  87. return !!name;
  88. }
  89. const { title, hideBreadcrumb, hideMenu } = meta;
  90. if (!title || hideBreadcrumb || hideMenu) {
  91. return false;
  92. }
  93. return true;
  94. }).filter((item) => !item.meta?.hideBreadcrumb || !item.meta?.hideMenu);
  95. return resultList;
  96. }
  97. function handleClick(route: RouteLocationMatched, paths: string[], e: Event) {
  98. e?.preventDefault();
  99. const { children, redirect, meta } = route;
  100. if (children?.length && !redirect) {
  101. e?.stopPropagation();
  102. return;
  103. }
  104. if (meta?.carryParam) {
  105. return;
  106. }
  107. const go = useGo();
  108. if (redirect && isString(redirect)) {
  109. go(redirect);
  110. } else {
  111. let goPath = '';
  112. if (paths.length === 1) {
  113. goPath = paths[0];
  114. } else {
  115. const ps = paths.slice(1);
  116. const lastPath = ps.pop() || '';
  117. goPath = `${lastPath}`;
  118. }
  119. goPath = /^\//.test(goPath) ? goPath : `/${goPath}`;
  120. go(goPath);
  121. }
  122. }
  123. function hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) {
  124. if (routes.indexOf(route) === routes.length - 1) {
  125. return false;
  126. }
  127. return true;
  128. }
  129. return { routes, t, prefixCls, getShowBreadCrumbIcon, handleClick, hasRedirect };
  130. },
  131. });
  132. </script>
  133. <style lang="less">
  134. @prefix-cls: ~'@{namespace}-layout-breadcrumb';
  135. .@{prefix-cls} {
  136. display: flex;
  137. padding: 0 8px;
  138. align-items: center;
  139. .ant-breadcrumb-link {
  140. .anticon {
  141. margin-right: 4px;
  142. margin-bottom: 2px;
  143. }
  144. }
  145. &--light {
  146. .ant-breadcrumb-link {
  147. color: @breadcrumb-item-normal-color;
  148. a {
  149. color: rgba(0, 0, 0, 0.65);
  150. &:hover {
  151. color: @primary-color;
  152. }
  153. }
  154. }
  155. .ant-breadcrumb-separator {
  156. color: @breadcrumb-item-normal-color;
  157. }
  158. }
  159. &--dark {
  160. .ant-breadcrumb-link {
  161. color: rgba(255, 255, 255, 0.6);
  162. a {
  163. color: rgba(255, 255, 255, 0.8);
  164. &:hover {
  165. color: @white;
  166. }
  167. }
  168. }
  169. .ant-breadcrumb-separator,
  170. .anticon {
  171. color: rgba(255, 255, 255, 0.8);
  172. }
  173. }
  174. }
  175. </style>