Commit 819bcbe5263c721f1f77cb277d670a9868b229f7
1 parent
930383f9
feat(breadcrumb): add breadcrumb demo #143
Showing
26 changed files
with
312 additions
and
73 deletions
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<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
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
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<T = AppRouteModule>(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<Menu[]> { |
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
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
src/views/demo/feat/breadcrumb/ChildrenListDetail.vue
0 → 100644
src/views/demo/feat/breadcrumb/FlatList.vue
0 → 100644