|
@@ -1,26 +1,45 @@
|
|
|
<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.meta.title || route.name) }}
|
|
|
- </span>
|
|
|
- <router-link v-else to="" @click="handleClick(route, paths, $event as Event)">
|
|
|
- {{ t(route.meta.title || route.name) }}
|
|
|
- </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" #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';
|
|
@@ -36,121 +55,114 @@
|
|
|
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;
|
|
|
- }
|
|
|
+ defineOptions({ name: 'LayoutBreadcrumb' });
|
|
|
|
|
|
- const parent = getAllParentPath(menus, path);
|
|
|
- const filterMenus = menus.filter((item) => item.path === parent[0]);
|
|
|
- const matched = getMatched(filterMenus, parent) as any;
|
|
|
+ defineProps({
|
|
|
+ theme: propTypes.oneOf(['dark', 'light']),
|
|
|
+ });
|
|
|
|
|
|
- if (!matched || matched.length === 0){
|
|
|
- routes.value = [];
|
|
|
- return;
|
|
|
- }
|
|
|
+ const routes = ref<RouteLocationMatched[]>([]);
|
|
|
+ const { currentRoute } = useRouter();
|
|
|
+ const { prefixCls } = useDesign('layout-breadcrumb');
|
|
|
+ const { getShowBreadCrumbIcon } = useRootSetting();
|
|
|
+ const go = useGo();
|
|
|
|
|
|
- const breadcrumbList = filterItem(matched);
|
|
|
+ const { t } = useI18n();
|
|
|
+ watchEffect(async () => {
|
|
|
+ if (currentRoute.value.name === REDIRECT_NAME) return;
|
|
|
+ const menus = await getMenus();
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+ const routeMatched = currentRoute.value.matched;
|
|
|
+ const cur = routeMatched?.[routeMatched.length - 1];
|
|
|
+ let path = currentRoute.value.path;
|
|
|
|
|
|
- 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);
|
|
|
- }
|
|
|
+ if (cur && cur?.meta?.currentActiveMenu) {
|
|
|
+ path = cur.meta.currentActiveMenu as string;
|
|
|
+ }
|
|
|
|
|
|
- function handleClick(route: RouteLocationMatched, paths: string[], e: Event) {
|
|
|
- e?.preventDefault();
|
|
|
- const { children, redirect, meta } = route;
|
|
|
+ const parent = getAllParentPath(menus, path);
|
|
|
+ const filterMenus = menus.filter((item) => item.path === parent[0]);
|
|
|
+ const matched = getMatched(filterMenus, parent) as any;
|
|
|
|
|
|
- if (children?.length && !redirect) {
|
|
|
- e?.stopPropagation();
|
|
|
- return;
|
|
|
- }
|
|
|
- if (meta?.carryParam) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (!matched || matched.length === 0) {
|
|
|
+ routes.value = [];
|
|
|
+ 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);
|
|
|
- }
|
|
|
+ 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, 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 hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) {
|
|
|
- return routes.indexOf(route) !== routes.length - 1;
|
|
|
+ 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) {
|
|
|
+ console.log(route);
|
|
|
+ const { children, redirect, meta } = route;
|
|
|
|
|
|
- function getIcon(route) {
|
|
|
- return route.icon || route.meta?.icon;
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- return { routes, t, prefixCls, getIcon, getShowBreadCrumbIcon, handleClick, hasRedirect };
|
|
|
- },
|
|
|
- });
|
|
|
+ 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';
|