Skip to content

Commit 819bcbe

Browse files
committed
feat(breadcrumb): add breadcrumb demo #143
1 parent 930383f commit 819bcbe

File tree

26 files changed

+312
-73
lines changed

26 files changed

+312
-73
lines changed

CHANGELOG.zh_CN.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
- 新增左侧菜单混合模式
77
- 新增 markdown 嵌入表单内示例
88
- 新增主框架外页面示例
9+
- `route.meta` 新增`currentActiveMenu`,`hideTab`,`hideMenu`参数 用于控制详情页面包屑级菜单显示隐藏。
10+
- 新增面包屑导航示例
911

1012
### 🐛 Bug Fixes
1113

@@ -14,6 +16,7 @@
1416
- 修复图表库切换页面导致宽高计算错误
1517
- 修复多语言配置 `Locale.show`导致配置不生效
1618
- 修复路由类型错误
19+
- 修复菜单分割时权限失效问题
1720

1821
## 2.0.0-rc.14 (2020-12-15)
1922

src/components/Menu/src/BasicMenu.vue

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,21 @@
118118
listenerLastChangeTab((route) => {
119119
if (route.name === REDIRECT_NAME) return;
120120
handleMenuChange(route);
121-
}, false);
121+
const currentActiveMenu = route.meta?.currentActiveMenu;
122+
if (currentActiveMenu) {
123+
menuState.selectedKeys = [currentActiveMenu];
124+
setOpenKeys(currentActiveMenu);
125+
}
126+
});
122127
123128
watch(
124129
() => props.items,
125130
() => {
126131
handleMenuChange();
127-
},
128-
{
129-
immediate: true,
130132
}
133+
// {
134+
// immediate: true,
135+
// }
131136
);
132137
133138
async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) {
@@ -149,9 +154,7 @@
149154
return;
150155
}
151156
const path = (route || unref(currentRoute)).path;
152-
if (props.mode !== MenuModeEnum.HORIZONTAL) {
153-
setOpenKeys(path);
154-
}
157+
setOpenKeys(path);
155158
if (props.isHorizontal && unref(getSplit)) {
156159
const parentPath = await getCurrentParentPath(path);
157160
menuState.selectedKeys = [parentPath];

src/components/Menu/src/components/BasicSubMenuItem.vue

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<template>
2-
<BasicMenuItem v-if="!menuHasChildren(item)" v-bind="$props" />
3-
<SubMenu v-else :class="[`${prefixCls}__level${level}`, theme]">
2+
<BasicMenuItem v-if="!menuHasChildren(item) && getShowMenu" v-bind="$props" />
3+
<SubMenu
4+
v-if="menuHasChildren(item) && getShowMenu"
5+
:class="[`${prefixCls}__level${level}`, theme]"
6+
>
47
<template #title>
58
<MenuItemContent v-bind="$props" :item="item" />
69
</template>
@@ -16,7 +19,7 @@
1619
<script lang="ts">
1720
import type { Menu as MenuType } from '/@/router/types';
1821
19-
import { defineComponent } from 'vue';
22+
import { defineComponent, computed } from 'vue';
2023
import { Menu } from 'ant-design-vue';
2124
import { useDesign } from '/@/hooks/web/useDesign';
2225
import { itemProps } from '../props';
@@ -35,8 +38,12 @@
3538
// ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
3639
},
3740
props: itemProps,
38-
setup() {
41+
setup(props) {
3942
const { prefixCls } = useDesign('basic-menu-item');
43+
44+
const getShowMenu = computed(() => {
45+
return !props.item.meta?.hideMenu;
46+
});
4047
function menuHasChildren(menuTreeItem: MenuType): boolean {
4148
return (
4249
Reflect.has(menuTreeItem, 'children') &&
@@ -47,6 +54,7 @@
4754
return {
4855
prefixCls,
4956
menuHasChildren,
57+
getShowMenu,
5058
};
5159
},
5260
});

src/components/Menu/src/useOpenKeys.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export function useOpenKeys(
1818
const { getCollapsed } = useMenuSetting();
1919

2020
function setOpenKeys(path: string) {
21+
if (mode.value === MenuModeEnum.HORIZONTAL) {
22+
return;
23+
}
2124
const menuList = toRaw(menus.value);
2225
if (!unref(accordion)) {
2326
menuState.openKeys = es6Unique([...menuState.openKeys, ...getAllParentPath(menuList, path)]);

src/layouts/default/header/components/Breadcrumb.vue

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<template>
22
<div :class="[prefixCls, `${prefixCls}--${theme}`]">
33
<a-breadcrumb :routes="routes">
4-
<template #itemRender="{ route, routes }">
4+
<template #itemRender="{ route, routes, paths }">
55
<Icon :icon="route.meta.icon" v-if="getShowBreadCrumbIcon && route.meta.icon" />
6-
<span v-if="routes.indexOf(route) === routes.length - 1">
6+
<span v-if="!hasRedirect(routes, route)">
77
{{ t(route.meta.title) }}
88
</span>
9-
<router-link v-else :to="route.path">
9+
<router-link v-else to="" @click="handleClick(route, paths, $event)">
1010
{{ t(route.meta.title) }}
1111
</router-link>
1212
</template>
@@ -30,6 +30,8 @@
3030
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
3131
3232
import { propTypes } from '/@/utils/propTypes';
33+
import { useGo } from '/@/hooks/web/usePage';
34+
import { isString } from '/@/utils/is';
3335
3436
export default defineComponent({
3537
name: 'LayoutBreadcrumb',
@@ -45,22 +47,12 @@
4547
4648
const { t } = useI18n();
4749
watchEffect(() => {
48-
if (currentRoute.value.name === REDIRECT_NAME) {
49-
return;
50-
}
50+
if (currentRoute.value.name === REDIRECT_NAME) return;
51+
5152
const matched = currentRoute.value?.matched;
5253
if (!matched || matched.length === 0) return;
5354
54-
let breadcrumbList = filter(toRaw(matched), (item) => {
55-
if (!item.meta) {
56-
return false;
57-
}
58-
const { title, hideBreadcrumb } = item.meta;
59-
if (!title || hideBreadcrumb) {
60-
return false;
61-
}
62-
return true;
63-
});
55+
let breadcrumbList = filterItem(toRaw(matched));
6456
6557
const filterBreadcrumbList = breadcrumbList.filter(
6658
(item) => item.path !== PageEnum.BASE_HOME
@@ -71,13 +63,86 @@
7163
path: PageEnum.BASE_HOME,
7264
meta: {
7365
title: t('layout.header.home'),
66+
isLink: true,
7467
},
7568
} as unknown) as RouteLocationMatched);
7669
}
77-
routes.value = filterBreadcrumbList.length === 1 ? [] : filterBreadcrumbList;
70+
71+
if (currentRoute.value.meta?.currentActiveMenu) {
72+
filterBreadcrumbList.push((currentRoute.value as unknown) as RouteLocationMatched);
73+
}
74+
// routes.value = filterBreadcrumbList.length === 1 ? [] : filterBreadcrumbList;
75+
routes.value = filterBreadcrumbList;
7876
});
7977
80-
return { routes, t, prefixCls, getShowBreadCrumbIcon };
78+
function filterItem(list: RouteLocationMatched[]) {
79+
let resultList = filter(list, (item) => {
80+
const { meta } = item;
81+
82+
if (!meta) {
83+
return false;
84+
}
85+
const { title, hideBreadcrumb, hideMenu } = meta;
86+
if (!title || hideBreadcrumb || hideMenu) {
87+
return false;
88+
}
89+
90+
return true;
91+
}).filter((item) => !item.meta?.hideBreadcrumb || !item.meta?.hideMenu);
92+
93+
resultList = resultList.filter((item) => item.path !== PageEnum.BASE_HOME);
94+
return resultList;
95+
}
96+
97+
function handleClick(route: RouteLocationMatched, paths: string[], e: Event) {
98+
e?.preventDefault();
99+
const {
100+
children,
101+
redirect,
102+
meta,
103+
// components
104+
} = route;
105+
106+
// const isParent =
107+
// components?.default?.name === 'DefaultLayout' || (components?.default as any)?.parentView;
108+
109+
if (
110+
children?.length &&
111+
!redirect
112+
// && !isParent
113+
) {
114+
e?.stopPropagation();
115+
return;
116+
}
117+
if (meta?.carryParam) {
118+
return;
119+
}
120+
121+
const go = useGo();
122+
if (redirect && isString(redirect)) {
123+
go(redirect);
124+
} else {
125+
const ps = paths.slice(1);
126+
const lastPath = ps.pop() || '';
127+
const parentPath = ps.pop() || '';
128+
let path = `${parentPath}/${lastPath}`;
129+
path = /^\//.test(path) ? path : `/${path}`;
130+
go(path);
131+
}
132+
}
133+
134+
function hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) {
135+
if (route?.meta?.isLink) {
136+
return true;
137+
}
138+
139+
if (routes.indexOf(route) === routes.length - 1) {
140+
return false;
141+
}
142+
return true;
143+
}
144+
145+
return { routes, t, prefixCls, getShowBreadCrumbIcon, handleClick, hasRedirect };
81146
},
82147
});
83148
</script>

src/layouts/default/setting/handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
5959
return { menuSetting: { bgColor: value } };
6060

6161
case HandlerEnum.MENU_SPLIT:
62-
return { menuSetting: { split: value, collapsedShowTitle: true } };
62+
return { menuSetting: { split: value } };
6363

6464
case HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE:
6565
return { menuSetting: { closeMixSidebarOnChange: value } };

src/layouts/default/sider/MixSider.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
<div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
4040
<div
41+
v-show="openMenu"
4142
:class="[
4243
`${prefixCls}-menu-list__title`,
4344
{

src/layouts/default/tabs/index.vue

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
3131
import { Tabs } from 'ant-design-vue';
3232
import TabContent from './components/TabContent.vue';
33+
import type { RouteLocationNormalized } from 'vue-router';
3334
3435
import { useGo } from '/@/hooks/web/usePage';
3536
@@ -43,6 +44,8 @@
4344
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
4445
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
4546
47+
import router from '/@/router';
48+
4649
export default defineComponent({
4750
name: 'MultipleTabs',
4851
components: {
@@ -61,7 +64,9 @@
6164
const go = useGo();
6265
const { getShowQuick, getShowRedo } = useMultipleTabSetting();
6366
64-
const getTabsState = computed(() => tabStore.getTabsState);
67+
const getTabsState = computed(() => {
68+
return tabStore.getTabsState.filter((item) => !item.meta?.hideTab);
69+
});
6570
6671
const unClose = computed(() => unref(getTabsState).length === 1);
6772
@@ -78,13 +83,24 @@
7883
const { name } = route;
7984
if (name === REDIRECT_NAME || !route || !userStore.getTokenState) return;
8085
81-
const { path, fullPath } = route;
82-
const p = fullPath || path;
86+
const { path, fullPath, meta = {} } = route;
8387
88+
const { currentActiveMenu, hideTab } = meta;
89+
const isHide = !hideTab ? null : currentActiveMenu;
90+
const p = isHide || fullPath || path;
8491
if (activeKeyRef.value !== p) {
8592
activeKeyRef.value = p;
8693
}
87-
tabStore.addTabAction(unref(route));
94+
95+
if (isHide) {
96+
const findParentRoute = router
97+
.getRoutes()
98+
.find((item) => item.path === currentActiveMenu);
99+
findParentRoute &&
100+
tabStore.addTabAction((findParentRoute as unknown) as RouteLocationNormalized);
101+
} else {
102+
tabStore.addTabAction(unref(route));
103+
}
88104
});
89105
90106
function handleChange(activeKey: any) {

src/locales/lang/en/routes/demo/feat.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@ export default {
1515
tab: 'Tab with parameters',
1616
tab1: 'Tab with parameter 1',
1717
tab2: 'Tab with parameter 2',
18+
19+
breadcrumb: 'Breadcrumbs',
20+
breadcrumbFlat: 'Flat Mode',
21+
breadcrumbFlatDetail: 'Flat mode details',
22+
23+
breadcrumbChildren: 'Level mode',
24+
breadcrumbChildrenDetail: 'Level mode detail',
1825
};

src/locales/lang/zh_CN/routes/demo/feat.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@ export default {
1515
tab: 'Tab带参',
1616
tab1: 'Tab带参1',
1717
tab2: 'Tab带参2',
18+
19+
breadcrumb: '面包屑导航',
20+
breadcrumbFlat: '平级模式',
21+
breadcrumbFlatDetail: '平级详情',
22+
23+
breadcrumbChildren: '层级模式',
24+
breadcrumbChildrenDetail: '层级详情',
1825
};

0 commit comments

Comments
 (0)