Commit 819bcbe5263c721f1f77cb277d670a9868b229f7

Authored by vben
1 parent 930383f9

feat(breadcrumb): add breadcrumb demo #143

CHANGELOG.zh_CN.md
... ... @@ -6,6 +6,8 @@
6 6 - 新增左侧菜单混合模式
7 7 - 新增 markdown 嵌入表单内示例
8 8 - 新增主框架外页面示例
  9 +- `route.meta` 新增`currentActiveMenu`,`hideTab`,`hideMenu`参数 用于控制详情页面包屑级菜单显示隐藏。
  10 +- 新增面包屑导航示例
9 11  
10 12 ### 🐛 Bug Fixes
11 13  
... ... @@ -14,6 +16,7 @@
14 16 - 修复图表库切换页面导致宽高计算错误
15 17 - 修复多语言配置 `Locale.show`导致配置不生效
16 18 - 修复路由类型错误
  19 +- 修复菜单分割时权限失效问题
17 20  
18 21 ## 2.0.0-rc.14 (2020-12-15)
19 22  
... ...
src/components/Menu/src/BasicMenu.vue
... ... @@ -118,16 +118,21 @@
118 118 listenerLastChangeTab((route) => {
119 119 if (route.name === REDIRECT_NAME) return;
120 120 handleMenuChange(route);
121   - }, false);
  121 + const currentActiveMenu = route.meta?.currentActiveMenu;
  122 + if (currentActiveMenu) {
  123 + menuState.selectedKeys = [currentActiveMenu];
  124 + setOpenKeys(currentActiveMenu);
  125 + }
  126 + });
122 127  
123 128 watch(
124 129 () => props.items,
125 130 () => {
126 131 handleMenuChange();
127   - },
128   - {
129   - immediate: true,
130 132 }
  133 + // {
  134 + // immediate: true,
  135 + // }
131 136 );
132 137  
133 138 async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) {
... ... @@ -149,9 +154,7 @@
149 154 return;
150 155 }
151 156 const path = (route || unref(currentRoute)).path;
152   - if (props.mode !== MenuModeEnum.HORIZONTAL) {
153   - setOpenKeys(path);
154   - }
  157 + setOpenKeys(path);
155 158 if (props.isHorizontal && unref(getSplit)) {
156 159 const parentPath = await getCurrentParentPath(path);
157 160 menuState.selectedKeys = [parentPath];
... ...
src/components/Menu/src/components/BasicSubMenuItem.vue
1 1 <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 + >
4 7 <template #title>
5 8 <MenuItemContent v-bind="$props" :item="item" />
6 9 </template>
... ... @@ -16,7 +19,7 @@
16 19 <script lang="ts">
17 20 import type { Menu as MenuType } from '/@/router/types';
18 21  
19   - import { defineComponent } from 'vue';
  22 + import { defineComponent, computed } from 'vue';
20 23 import { Menu } from 'ant-design-vue';
21 24 import { useDesign } from '/@/hooks/web/useDesign';
22 25 import { itemProps } from '../props';
... ... @@ -35,8 +38,12 @@
35 38 // ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
36 39 },
37 40 props: itemProps,
38   - setup() {
  41 + setup(props) {
39 42 const { prefixCls } = useDesign('basic-menu-item');
  43 +
  44 + const getShowMenu = computed(() => {
  45 + return !props.item.meta?.hideMenu;
  46 + });
40 47 function menuHasChildren(menuTreeItem: MenuType): boolean {
41 48 return (
42 49 Reflect.has(menuTreeItem, 'children') &&
... ... @@ -47,6 +54,7 @@
47 54 return {
48 55 prefixCls,
49 56 menuHasChildren,
  57 + getShowMenu,
50 58 };
51 59 },
52 60 });
... ...
src/components/Menu/src/useOpenKeys.ts
... ... @@ -18,6 +18,9 @@ export function useOpenKeys(
18 18 const { getCollapsed } = useMenuSetting();
19 19  
20 20 function setOpenKeys(path: string) {
  21 + if (mode.value === MenuModeEnum.HORIZONTAL) {
  22 + return;
  23 + }
21 24 const menuList = toRaw(menus.value);
22 25 if (!unref(accordion)) {
23 26 menuState.openKeys = es6Unique([...menuState.openKeys, ...getAllParentPath(menuList, path)]);
... ...
src/layouts/default/header/components/Breadcrumb.vue
1 1 <template>
2 2 <div :class="[prefixCls, `${prefixCls}--${theme}`]">
3 3 <a-breadcrumb :routes="routes">
4   - <template #itemRender="{ route, routes }">
  4 + <template #itemRender="{ route, routes, paths }">
5 5 <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)">
7 7 {{ t(route.meta.title) }}
8 8 </span>
9   - <router-link v-else :to="route.path">
  9 + <router-link v-else to="" @click="handleClick(route, paths, $event)">
10 10 {{ t(route.meta.title) }}
11 11 </router-link>
12 12 </template>
... ... @@ -30,6 +30,8 @@
30 30 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
31 31  
32 32 import { propTypes } from '/@/utils/propTypes';
  33 + import { useGo } from '/@/hooks/web/usePage';
  34 + import { isString } from '/@/utils/is';
33 35  
34 36 export default defineComponent({
35 37 name: 'LayoutBreadcrumb',
... ... @@ -45,22 +47,12 @@
45 47  
46 48 const { t } = useI18n();
47 49 watchEffect(() => {
48   - if (currentRoute.value.name === REDIRECT_NAME) {
49   - return;
50   - }
  50 + if (currentRoute.value.name === REDIRECT_NAME) return;
  51 +
51 52 const matched = currentRoute.value?.matched;
52 53 if (!matched || matched.length === 0) return;
53 54  
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));
64 56  
65 57 const filterBreadcrumbList = breadcrumbList.filter(
66 58 (item) => item.path !== PageEnum.BASE_HOME
... ... @@ -71,13 +63,86 @@
71 63 path: PageEnum.BASE_HOME,
72 64 meta: {
73 65 title: t('layout.header.home'),
  66 + isLink: true,
74 67 },
75 68 } as unknown) as RouteLocationMatched);
76 69 }
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;
78 76 });
79 77  
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 };
81 146 },
82 147 });
83 148 </script>
... ...
src/layouts/default/setting/handler.ts
... ... @@ -59,7 +59,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
59 59 return { menuSetting: { bgColor: value } };
60 60  
61 61 case HandlerEnum.MENU_SPLIT:
62   - return { menuSetting: { split: value, collapsedShowTitle: true } };
  62 + return { menuSetting: { split: value } };
63 63  
64 64 case HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE:
65 65 return { menuSetting: { closeMixSidebarOnChange: value } };
... ...
src/layouts/default/sider/MixSider.vue
... ... @@ -38,6 +38,7 @@
38 38  
39 39 <div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
40 40 <div
  41 + v-show="openMenu"
41 42 :class="[
42 43 `${prefixCls}-menu-list__title`,
43 44 {
... ...
src/layouts/default/tabs/index.vue
... ... @@ -30,6 +30,7 @@
30 30  
31 31 import { Tabs } from 'ant-design-vue';
32 32 import TabContent from './components/TabContent.vue';
  33 + import type { RouteLocationNormalized } from 'vue-router';
33 34  
34 35 import { useGo } from '/@/hooks/web/usePage';
35 36  
... ... @@ -43,6 +44,8 @@
43 44 import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
44 45 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
45 46  
  47 + import router from '/@/router';
  48 +
46 49 export default defineComponent({
47 50 name: 'MultipleTabs',
48 51 components: {
... ... @@ -61,7 +64,9 @@
61 64 const go = useGo();
62 65 const { getShowQuick, getShowRedo } = useMultipleTabSetting();
63 66  
64   - const getTabsState = computed(() => tabStore.getTabsState);
  67 + const getTabsState = computed(() => {
  68 + return tabStore.getTabsState.filter((item) => !item.meta?.hideTab);
  69 + });
65 70  
66 71 const unClose = computed(() => unref(getTabsState).length === 1);
67 72  
... ... @@ -78,13 +83,24 @@
78 83 const { name } = route;
79 84 if (name === REDIRECT_NAME || !route || !userStore.getTokenState) return;
80 85  
81   - const { path, fullPath } = route;
82   - const p = fullPath || path;
  86 + const { path, fullPath, meta = {} } = route;
83 87  
  88 + const { currentActiveMenu, hideTab } = meta;
  89 + const isHide = !hideTab ? null : currentActiveMenu;
  90 + const p = isHide || fullPath || path;
84 91 if (activeKeyRef.value !== p) {
85 92 activeKeyRef.value = p;
86 93 }
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 + }
88 104 });
89 105  
90 106 function handleChange(activeKey: any) {
... ...
src/locales/lang/en/routes/demo/feat.ts
... ... @@ -15,4 +15,11 @@ export default {
15 15 tab: 'Tab with parameters',
16 16 tab1: 'Tab with parameter 1',
17 17 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',
18 25 };
... ...
src/locales/lang/zh_CN/routes/demo/feat.ts
... ... @@ -15,4 +15,11 @@ export default {
15 15 tab: 'Tab带参',
16 16 tab1: 'Tab带参1',
17 17 tab2: 'Tab带参2',
  18 +
  19 + breadcrumb: '面包屑导航',
  20 + breadcrumbFlat: '平级模式',
  21 + breadcrumbFlatDetail: '平级详情',
  22 +
  23 + breadcrumbChildren: '层级模式',
  24 + breadcrumbChildrenDetail: '层级详情',
18 25 };
... ...
src/router/helper/routeHelper.ts
... ... @@ -16,7 +16,7 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
16 16 const { component, name } = item;
17 17 const { children } = item;
18 18 if (component) {
19   - item.component = dynamicImport(component);
  19 + item.component = dynamicImport(component as string);
20 20 } else if (name) {
21 21 item.component = getParentLayout(name);
22 22 }
... ... @@ -31,7 +31,7 @@ export function transformObjToRoute&lt;T = AppRouteModule&gt;(routeList: AppRouteModul
31 31 routeList.forEach((route) => {
32 32 if (route.component) {
33 33 if ((route.component as string).toUpperCase() === 'LAYOUT') {
34   - route.component = LayoutMap.get(route.component);
  34 + route.component = LayoutMap.get(route.component as LayoutMapKey);
35 35 } else {
36 36 route.children = [cloneDeep(route)];
37 37 route.component = LAYOUT;
... ...
src/router/menus/index.ts
... ... @@ -71,8 +71,10 @@ export async function getShallowMenus(): Promise&lt;Menu[]&gt; {
71 71 export async function getChildrenMenus(parentPath: string) {
72 72 const menus = await getAsyncMenus();
73 73 const parent = menus.find((item) => item.path === parentPath);
74   - if (!parent) return [] as Menu[];
75   - return parent.children;
  74 + if (!parent || !parent.children) return [] as Menu[];
  75 + const routes = router.getRoutes();
  76 +
  77 + return !isBackMode() ? filter(parent.children, basicFilter(routes)) : parent.children;
76 78 }
77 79  
78 80 // 通用过滤方法
... ...
src/router/menus/modules/demo/comp.ts
... ... @@ -151,9 +151,6 @@ const menu: MenuModule = {
151 151 {
152 152 path: 'loading',
153 153 name: t('routes.demo.comp.loading'),
154   - tag: {
155   - content: 'new',
156   - },
157 154 },
158 155 {
159 156 path: 'tree',
... ... @@ -176,6 +173,9 @@ const menu: MenuModule = {
176 173 {
177 174 name: t('routes.demo.editor.editor'),
178 175 path: 'editor',
  176 + tag: {
  177 + content: 'new',
  178 + },
179 179 children: [
180 180 {
181 181 path: 'markdown',
... ...
src/router/menus/modules/demo/feat.ts
... ... @@ -19,6 +19,7 @@ const menu: MenuModule = {
19 19 path: 'tabs',
20 20 name: t('routes.demo.feat.tabs'),
21 21 },
  22 +
22 23 {
23 24 path: 'context-menu',
24 25 name: t('routes.demo.feat.contextMenu'),
... ... @@ -86,6 +87,27 @@ const menu: MenuModule = {
86 87 ],
87 88 },
88 89 {
  90 + name: t('routes.demo.feat.breadcrumb'),
  91 + path: 'breadcrumb',
  92 + tag: {
  93 + content: 'new',
  94 + },
  95 + children: [
  96 + {
  97 + path: 'flat',
  98 + name: t('routes.demo.feat.breadcrumbFlat'),
  99 + },
  100 + {
  101 + path: 'flatDetail',
  102 + name: t('routes.demo.feat.breadcrumbFlatDetail'),
  103 + },
  104 + {
  105 + path: 'children',
  106 + name: t('routes.demo.feat.breadcrumbChildrenDetail'),
  107 + },
  108 + ],
  109 + },
  110 + {
89 111 path: 'testTab',
90 112 name: t('routes.demo.feat.tab'),
91 113 children: [
... ...
src/router/menus/modules/demo/iframe.ts
... ... @@ -8,14 +8,14 @@ const menu: MenuModule = {
8 8 path: '/frame',
9 9 children: [
10 10 {
11   - path: 'antv',
12   - name: t('routes.demo.iframe.antv'),
13   - },
14   - {
15 11 path: 'doc',
16 12 name: t('routes.demo.iframe.doc'),
17 13 },
18 14 {
  15 + path: 'antv',
  16 + name: t('routes.demo.iframe.antv'),
  17 + },
  18 + {
19 19 path: 'https://vvbin.cn/doc-next/',
20 20 name: t('routes.demo.iframe.docExternal'),
21 21 },
... ...
src/router/menus/modules/demo/level.ts
... ... @@ -6,9 +6,6 @@ const menu: MenuModule = {
6 6 menu: {
7 7 name: t('routes.demo.level.level'),
8 8 path: '/level',
9   - tag: {
10   - dot: true,
11   - },
12 9 children: [
13 10 {
14 11 path: 'menu1',
... ...
src/router/menus/modules/demo/page.ts
... ... @@ -6,9 +6,7 @@ const menu: MenuModule = {
6 6 menu: {
7 7 name: t('routes.demo.page.page'),
8 8 path: '/page-demo',
9   - tag: {
10   - dot: true,
11   - },
  9 +
12 10 children: [
13 11 {
14 12 path: 'form',
... ... @@ -102,9 +100,6 @@ const menu: MenuModule = {
102 100 {
103 101 path: 'list',
104 102 name: t('routes.demo.page.list'),
105   - tag: {
106   - content: 'new',
107   - },
108 103 children: [
109 104 {
110 105 path: 'basic',
... ...
src/router/routes/modules/demo/charts.ts
... ... @@ -14,12 +14,21 @@ const charts: AppRouteModule = {
14 14 },
15 15 children: [
16 16 {
  17 + path: 'apexChart',
  18 + name: 'ApexChart',
  19 + meta: {
  20 + title: t('routes.demo.charts.apexChart'),
  21 + },
  22 + component: () => import('/@/views/demo/echarts/apex/index.vue'),
  23 + },
  24 + {
17 25 path: 'echarts',
18 26 name: 'Echarts',
19 27 component: getParentLayout('Echarts'),
20 28 meta: {
21 29 title: 'Echarts',
22 30 },
  31 + redirect: '/charts/echarts/map',
23 32 children: [
24 33 {
25 34 path: 'map',
... ... @@ -47,14 +56,6 @@ const charts: AppRouteModule = {
47 56 },
48 57 ],
49 58 },
50   - {
51   - path: 'apexChart',
52   - name: 'ApexChart',
53   - meta: {
54   - title: t('routes.demo.charts.apexChart'),
55   - },
56   - component: () => import('/@/views/demo/echarts/apex/index.vue'),
57   - },
58 59 ],
59 60 };
60 61  
... ...
src/router/routes/modules/demo/feat.ts
... ... @@ -29,6 +29,68 @@ const feat: AppRouteModule = {
29 29 title: t('routes.demo.feat.tabs'),
30 30 },
31 31 },
  32 + {
  33 + path: 'breadcrumb',
  34 + name: 'BreadcrumbDemo',
  35 + redirect: '/feat/breadcrumb/flat',
  36 + component: getParentLayout('BreadcrumbDemo'),
  37 + meta: {
  38 + title: t('routes.demo.feat.breadcrumb'),
  39 + },
  40 +
  41 + children: [
  42 + {
  43 + path: 'flat',
  44 + name: 'BreadcrumbFlatDemo',
  45 + component: () => import('/@/views/demo/feat/breadcrumb/FlatList.vue'),
  46 + meta: {
  47 + title: t('routes.demo.feat.breadcrumbFlat'),
  48 + },
  49 + },
  50 + {
  51 + path: 'flatDetail',
  52 + name: 'BreadcrumbFlatDetailDemo',
  53 + component: () => import('/@/views/demo/feat/breadcrumb/FlatListDetail.vue'),
  54 + meta: {
  55 + title: t('routes.demo.feat.breadcrumbFlatDetail'),
  56 + hideMenu: true,
  57 + hideTab: true,
  58 + currentActiveMenu: '/feat/breadcrumb/flat',
  59 + },
  60 + },
  61 + {
  62 + path: 'children',
  63 + name: 'BreadcrumbChildrenDemo',
  64 + component: getParentLayout('BreadcrumbChildrenDemo'),
  65 + redirect: '/feat/breadcrumb/children',
  66 + meta: {
  67 + title: t('routes.demo.feat.breadcrumbFlat'),
  68 + },
  69 + children: [
  70 + {
  71 + path: '',
  72 + name: 'BreadcrumbChildren',
  73 + component: () => import('/@/views/demo/feat/breadcrumb/ChildrenList.vue'),
  74 + meta: {
  75 + title: t('routes.demo.feat.breadcrumbChildren'),
  76 + hideBreadcrumb: true,
  77 + },
  78 + },
  79 + {
  80 + path: 'childrenDetail',
  81 + name: 'BreadcrumbChildrenDetailDemo',
  82 + component: () => import('/@/views/demo/feat/breadcrumb/ChildrenListDetail.vue'),
  83 + meta: {
  84 + currentActiveMenu: '/feat/breadcrumb/children',
  85 + title: t('routes.demo.feat.breadcrumbChildrenDetail'),
  86 + hideTab: true,
  87 + hideMenu: true,
  88 + },
  89 + },
  90 + ],
  91 + },
  92 + ],
  93 + },
32 94  
33 95 {
34 96 path: 'context-menu',
... ...
src/router/routes/modules/demo/iframe.ts
... ... @@ -8,7 +8,7 @@ const iframe: AppRouteModule = {
8 8 path: '/frame',
9 9 name: 'Frame',
10 10 component: LAYOUT,
11   - redirect: '/frame/antv',
  11 + redirect: '/frame/doc',
12 12 meta: {
13 13 icon: 'mdi:page-next-outline',
14 14 title: t('routes.demo.iframe.frame'),
... ... @@ -16,21 +16,21 @@ const iframe: AppRouteModule = {
16 16  
17 17 children: [
18 18 {
19   - path: 'antv',
20   - name: 'Antv',
  19 + path: 'doc',
  20 + name: 'Doc',
21 21 component: IFrame,
22 22 meta: {
23   - frameSrc: 'https://2x.antdv.com/docs/vue/introduce-cn/',
24   - title: t('routes.demo.iframe.antv'),
  23 + frameSrc: 'https://vvbin.cn/doc-next/',
  24 + title: t('routes.demo.iframe.doc'),
25 25 },
26 26 },
27 27 {
28   - path: 'doc',
29   - name: 'Doc',
  28 + path: 'antv',
  29 + name: 'Antv',
30 30 component: IFrame,
31 31 meta: {
32   - frameSrc: 'https://vvbin.cn/doc-next/',
33   - title: t('routes.demo.iframe.doc'),
  32 + frameSrc: 'https://2x.antdv.com/docs/vue/introduce-cn/',
  33 + title: t('routes.demo.iframe.antv'),
34 34 },
35 35 },
36 36 {
... ...
src/router/routes/modules/demo/level.ts
... ... @@ -7,7 +7,7 @@ const permission: AppRouteModule = {
7 7 path: '/level',
8 8 name: 'Level',
9 9 component: LAYOUT,
10   - redirect: '/level/menu1/menu1-1',
  10 + redirect: '/level/menu1/menu1-1/menu1-1-1',
11 11 meta: {
12 12 icon: 'carbon:user-role',
13 13 title: t('routes.demo.level.level'),
... ... @@ -21,6 +21,7 @@ const permission: AppRouteModule = {
21 21 meta: {
22 22 title: 'Menu1',
23 23 },
  24 + redirect: '/level/menu1/menu1-1/menu1-1-1',
24 25 children: [
25 26 {
26 27 path: 'menu1-1',
... ... @@ -29,6 +30,7 @@ const permission: AppRouteModule = {
29 30 meta: {
30 31 title: 'Menu1-1',
31 32 },
  33 + redirect: '/level/menu1/menu1-1/menu1-1-1',
32 34 children: [
33 35 {
34 36 path: 'menu1-1-1',
... ...
src/router/types.ts
... ... @@ -30,13 +30,22 @@ export interface RouteMeta {
30 30  
31 31 // Used internally to mark single-level menus
32 32 single?: boolean;
  33 +
  34 + // Currently active menu
  35 + currentActiveMenu?: string;
  36 +
  37 + // Never show in tab
  38 + hideTab?: boolean;
  39 +
  40 + // Never show in menu
  41 + hideMenu?: boolean;
33 42 }
34 43  
35 44 // @ts-ignore
36 45 export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
37 46 name: string;
38 47 meta: RouteMeta;
39   - component?: Component;
  48 + component?: Component | string;
40 49 components?: Component;
41 50 children?: AppRouteRecordRaw[];
42 51 props?: Record<string, any>;
... ...
src/views/demo/feat/breadcrumb/ChildrenList.vue 0 → 100644
  1 +<template>
  2 + <div class="p-5">
  3 + <router-link to="/feat/breadcrumb/children/childrenDetail">进入子级详情页</router-link>
  4 + </div>
  5 +</template>
  6 +<script lang="ts">
  7 + import { defineComponent } from 'vue';
  8 +
  9 + export default defineComponent({});
  10 +</script>
... ...
src/views/demo/feat/breadcrumb/ChildrenListDetail.vue 0 → 100644
  1 +<template>
  2 + <div> 子级详情页 </div>
  3 +</template>
  4 +<script lang="ts">
  5 + import { defineComponent } from 'vue';
  6 +
  7 + export default defineComponent({});
  8 +</script>
... ...
src/views/demo/feat/breadcrumb/FlatList.vue 0 → 100644
  1 +<template>
  2 + <div class="p-5">
  3 + <router-link to="/feat/breadcrumb/flatDetail">进入平级详情页</router-link>
  4 + </div>
  5 +</template>
  6 +<script lang="ts">
  7 + import { defineComponent } from 'vue';
  8 +
  9 + export default defineComponent({});
  10 +</script>
... ...
src/views/demo/feat/breadcrumb/FlatListDetail.vue 0 → 100644
  1 +<template>
  2 + <div> 平级详情页 </div>
  3 +</template>
  4 +<script lang="ts">
  5 + import { defineComponent } from 'vue';
  6 +
  7 + export default defineComponent({});
  8 +</script>
... ...