<template> <div :class="[prefixCls, `${prefixCls}--${theme}`]"> <a-breadcrumb :routes="routes"> <template #itemRender="{ route, routes: routesMatched, paths }"> <Icon :icon="getIcon(route)" v-if="getShowBreadCrumbIcon && getIcon(route)" /> <span v-if="!hasRedirect(routesMatched, route)"> {{ t(route.name || route.meta.title) }} </span> <router-link v-else to="" @click="handleClick(route, paths, $event)" style="color: inherit"> {{ t(route.name || route.meta.title) }} </router-link> </template> </a-breadcrumb> </div> </template> <script lang="ts"> import type { RouteLocationMatched } from 'vue-router'; import { useRouter } from 'vue-router'; import type { Menu } from '/@/router/types'; import { defineComponent, ref, watchEffect } from 'vue'; import { Breadcrumb } from 'ant-design-vue'; import Icon from '/@/components/Icon'; import { useDesign } from '/@/hooks/web/useDesign'; import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useGo } from '/@/hooks/web/usePage'; import { useI18n } from '/@/hooks/web/useI18n'; import { propTypes } from '/@/utils/propTypes'; import { isString } from '/@/utils/is'; import { filter } from '/@/utils/helper/treeHelper'; import { getMenus } from '/@/router/menus'; import { REDIRECT_NAME } from '/@/router/constant'; import { getAllParentPath } from '/@/router/helper/menuHelper'; export default defineComponent({ name: 'LayoutBreadcrumb', components: { Icon, [Breadcrumb.name]: Breadcrumb }, props: { theme: propTypes.oneOf(['dark', 'light']), }, setup() { const routes = ref<RouteLocationMatched[]>([]); const { currentRoute } = useRouter(); const { prefixCls } = useDesign('layout-breadcrumb'); const { getShowBreadCrumbIcon } = useRootSetting(); const go = useGo(); const { t } = useI18n(); watchEffect(async () => { if (currentRoute.value.name === REDIRECT_NAME) return; const menus = await getMenus(); const routeMatched = currentRoute.value.matched; const cur = routeMatched?.[routeMatched.length - 1]; let path = currentRoute.value.path; if (cur && cur?.meta?.currentActiveMenu) { path = cur.meta.currentActiveMenu as string; } const parent = getAllParentPath(menus, path); const filterMenus = menus.filter((item) => item.path === parent[0]); const matched = getMatched(filterMenus, parent) as any; if (!matched || matched.length === 0) return; const breadcrumbList = filterItem(matched); if (currentRoute.value.meta?.currentActiveMenu) { breadcrumbList.push({ ...currentRoute.value, name: currentRoute.value.meta?.title || currentRoute.value.name, } as unknown as RouteLocationMatched); } routes.value = breadcrumbList; }); function getMatched(menus: Menu[], parent: string[]) { const metched: Menu[] = []; menus.forEach((item) => { if (parent.includes(item.path)) { metched.push({ ...item, name: item.meta?.title || item.name, }); } if (item.children?.length) { metched.push(...getMatched(item.children, parent)); } }); return metched; } function filterItem(list: RouteLocationMatched[]) { return filter(list, (item) => { const { meta, name } = item; if (!meta) { return !!name; } const { title, hideBreadcrumb } = meta; if (!title || hideBreadcrumb) { return false; } return true; }).filter((item) => !item.meta?.hideBreadcrumb); } function handleClick(route: RouteLocationMatched, paths: string[], e: Event) { e?.preventDefault(); const { children, redirect, meta } = route; if (children?.length && !redirect) { e?.stopPropagation(); return; } if (meta?.carryParam) { return; } if (redirect && isString(redirect)) { go(redirect); } else { let goPath = ''; if (paths.length === 1) { goPath = paths[0]; } else { const ps = paths.slice(1); const lastPath = ps.pop() || ''; goPath = `${lastPath}`; } goPath = /^\//.test(goPath) ? goPath : `/${goPath}`; go(goPath); } } function hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) { return routes.indexOf(route) !== routes.length - 1; } function getIcon(route) { return route.icon || route.meta?.icon; } return { routes, t, prefixCls, getIcon, getShowBreadCrumbIcon, handleClick, hasRedirect }; }, }); </script> <style lang="less"> @prefix-cls: ~'@{namespace}-layout-breadcrumb'; .@{prefix-cls} { display: flex; padding: 0 8px; align-items: center; .ant-breadcrumb-link { .anticon { margin-right: 4px; margin-bottom: 2px; } } // update-begin--author:liaozhiyang---date:20240408---for:【QQYUN-8922】面包屑样式调整 &--light { .ant-breadcrumb-link { color: rgba(0, 0, 0, 0.85); a { color: rgba(0, 0, 0, 0.85); &:hover { color: @primary-color; } } } .ant-breadcrumb-separator, .anticon { color: rgba(0, 0, 0, 0.85); } } } html[data-theme='dark'] { .@{prefix-cls} { &--dark { .ant-breadcrumb-link { color: rgba(255, 255, 255, 0.85); a { color: rgba(255, 255, 255, 0.85); &:hover { color: @white; } } } .ant-breadcrumb-separator, .anticon { color: rgba(255, 255, 255, 0.85); } } } } html[data-theme='light'] { .@{prefix-cls} { &--dark { .ant-breadcrumb-link { color: rgba(255, 255, 255, 1); a { color: rgba(255, 255, 255, 1); &:hover { color: @white; } } } .ant-breadcrumb-separator, .anticon { color: rgba(255, 255, 255, 1); } } } } // update-end--author:liaozhiyang---date:20240408---for:【QQYUN-8922】面包屑样式调整 </style>