| | |
| | | <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)"> |
| | | {{ t(route.name || route.meta.title) }} |
| | | </router-link> |
| | | <Breadcrumb> |
| | | <template v-for="routeItem in routes" :key="routeItem.name"> |
| | | <BreadcrumbItem> |
| | | <Icon :icon="getIcon(routeItem)" v-if="getShowBreadCrumbIcon && getIcon(routeItem)" /> |
| | | <span v-if="!hasRedirect(routes, routeItem)"> |
| | | {{ t((routeItem.meta.title || routeItem.name) as string) }} |
| | | </span> |
| | | <router-link v-else to="" @click="handleClick(routeItem)"> |
| | | {{ t((routeItem.meta.title || routeItem.name) as string) }} |
| | | </router-link> |
| | | <template v-if="routeItem.children && !routeItem.meta?.hideChildrenInMenu" #overlay> |
| | | <Menu> |
| | | <template v-for="childItem in routeItem.children" :key="childItem.name"> |
| | | <MenuItem> |
| | | <Icon |
| | | :icon="getIcon(childItem)" |
| | | v-if="getShowBreadCrumbIcon && getIcon(childItem)" |
| | | /> |
| | | <span v-if="!hasRedirect(routes, childItem)"> |
| | | {{ t((childItem.meta?.title || childItem.name) as string) }} |
| | | </span> |
| | | <router-link v-else to="" @click="handleClick(childItem)"> |
| | | {{ t((childItem.meta?.title || childItem.name) as string) }} |
| | | </router-link> |
| | | </MenuItem> |
| | | </template> |
| | | </Menu> |
| | | </template> |
| | | </BreadcrumbItem> |
| | | </template> |
| | | </a-breadcrumb> |
| | | </Breadcrumb> |
| | | </div> |
| | | </template> |
| | | <script lang="ts"> |
| | | <script lang="ts" setup> |
| | | import type { RouteLocationMatched } from 'vue-router'; |
| | | import { useRouter } from 'vue-router'; |
| | | import type { Menu } from '/@/router/types'; |
| | | |
| | | import { defineComponent, ref, watchEffect } from 'vue'; |
| | | import { ref, watchEffect } from 'vue'; |
| | | |
| | | import { Breadcrumb } from 'ant-design-vue'; |
| | | import { Breadcrumb, BreadcrumbItem, Menu, MenuItem } from 'ant-design-vue'; |
| | | import Icon from '@/components/Icon/Icon.vue'; |
| | | |
| | | 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 { 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 { 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'; |
| | | 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(); |
| | | defineOptions({ name: 'LayoutBreadcrumb' }); |
| | | |
| | | 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, hideMenu } = meta; |
| | | if (!title || hideBreadcrumb || hideMenu) { |
| | | 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 }; |
| | | }, |
| | | defineProps({ |
| | | theme: propTypes.oneOf(['dark', 'light']), |
| | | }); |
| | | |
| | | 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) { |
| | | routes.value = []; |
| | | return; |
| | | } |
| | | |
| | | const breadcrumbList = filterItem(matched); |
| | | |
| | | if (currentRoute.value.meta?.currentActiveMenu && !currentRoute.value.meta?.hideBreadcrumb) { |
| | | breadcrumbList.push({ |
| | | ...currentRoute.value, |
| | | name: currentRoute.value.meta?.title || currentRoute.value.name, |
| | | } as unknown as RouteLocationMatched); |
| | | } |
| | | routes.value = breadcrumbList; |
| | | }); |
| | | |
| | | function getMatched(menus, parent: string[]) { |
| | | const matched: any[] = []; |
| | | menus.forEach((item) => { |
| | | if (parent.includes(item.path)) { |
| | | matched.push({ |
| | | ...item, |
| | | name: item.meta?.title || item.name, |
| | | }); |
| | | } |
| | | if (item.children?.length) { |
| | | matched.push(...getMatched(item.children, parent)); |
| | | } |
| | | }); |
| | | return matched; |
| | | } |
| | | |
| | | function filterItem(list: RouteLocationMatched[]) { |
| | | return filter(list, (item) => { |
| | | const { meta, name } = item; |
| | | if (!meta) { |
| | | return !!name; |
| | | } |
| | | const { title, hideBreadcrumb, hideMenu } = meta; |
| | | if (!title || hideBreadcrumb || hideMenu) { |
| | | return false; |
| | | } |
| | | return true; |
| | | }).filter((item) => !item.meta?.hideBreadcrumb); |
| | | } |
| | | |
| | | function handleClick(route) { |
| | | const { children, redirect, meta } = route; |
| | | |
| | | if (children?.length && !redirect) { |
| | | return; |
| | | } |
| | | if (meta?.carryParam) { |
| | | return; |
| | | } |
| | | |
| | | if (redirect && isString(redirect)) { |
| | | go(redirect); |
| | | } else { |
| | | let goPath = ''; |
| | | if (route.path) { |
| | | goPath = route.path; |
| | | } else { |
| | | const lastPath = ''; |
| | | goPath = `${lastPath}`; |
| | | } |
| | | goPath = /^\//.test(goPath) ? goPath : `/${goPath}`; |
| | | go(goPath); |
| | | } |
| | | } |
| | | |
| | | function hasRedirect(routes, route) { |
| | | return routes.indexOf(route) !== routes.length - 1; |
| | | } |
| | | |
| | | function getIcon(route) { |
| | | return route.icon || route.meta?.icon; |
| | | } |
| | | </script> |
| | | <style lang="less"> |
| | | @prefix-cls: ~'@{namespace}-layout-breadcrumb'; |