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